ipkvm.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. "os"
  7. "os/signal"
  8. "path/filepath"
  9. "strings"
  10. "syscall"
  11. "github.com/gorilla/csrf"
  12. "imuslab.com/dezukvm/dezukvmd/mod/dezukvm"
  13. "imuslab.com/dezukvm/dezukvmd/mod/utils"
  14. )
  15. var (
  16. dezukvmManager *dezukvm.DezukVM
  17. listeningServerMux *http.ServeMux
  18. )
  19. func init_ipkvm_mode() error {
  20. listeningServerMux = http.NewServeMux()
  21. //Create a new DezukVM manager
  22. dezukvmManager = dezukvm.NewKvmHostInstance(&dezukvm.RuntimeOptions{
  23. EnableLog: true,
  24. })
  25. // Experimental
  26. connectedUsbKvms, err := dezukvm.ScanConnectedUsbKvmDevices()
  27. if err != nil {
  28. return err
  29. }
  30. for _, dev := range connectedUsbKvms {
  31. err := dezukvmManager.AddUsbKvmDevice(dev)
  32. if err != nil {
  33. return err
  34. }
  35. }
  36. err = dezukvmManager.StartAllUsbKvmDevices()
  37. if err != nil {
  38. return err
  39. }
  40. // ~Experimental
  41. // Handle program exit to close the HID controller
  42. c := make(chan os.Signal, 1)
  43. signal.Notify(c, os.Interrupt, syscall.SIGTERM)
  44. go func() {
  45. <-c
  46. log.Println("Shutting down DezuKVM...")
  47. if dezukvmManager != nil {
  48. dezukvmManager.Close()
  49. }
  50. log.Println("Shutdown complete.")
  51. os.Exit(0)
  52. }()
  53. // Middleware to inject CSRF token into HTML files served from www
  54. listeningServerMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  55. // Only inject for .html files
  56. path := r.URL.Path
  57. if path == "/" {
  58. path = "/index.html"
  59. }
  60. if strings.HasSuffix(path, ".html") {
  61. // Read the HTML file from disk
  62. targetFilePath := filepath.Join("www", filepath.Clean(path))
  63. content, err := os.ReadFile(targetFilePath)
  64. if err != nil {
  65. http.NotFound(w, r)
  66. return
  67. }
  68. htmlContent := string(content)
  69. // Replace CSRF token placeholder
  70. htmlContent = strings.ReplaceAll(htmlContent, "{{.csrfToken}}", csrf.Token(r))
  71. w.Header().Set("Content-Type", "text/html")
  72. w.Write([]byte(htmlContent))
  73. return
  74. }
  75. // Fallback to static file server for non-HTML files
  76. http.FileServer(http.Dir("www")).ServeHTTP(w, r)
  77. })
  78. // Register DezukVM related APIs
  79. register_ipkvm_apis(listeningServerMux)
  80. csrfMiddleware := csrf.Protect(
  81. []byte(nodeUUID),
  82. csrf.CookieName("dezukvm_csrf_token"),
  83. csrf.Secure(false),
  84. csrf.Path("/"),
  85. csrf.SameSite(csrf.SameSiteLaxMode),
  86. )
  87. err = http.ListenAndServe(":9000", csrfMiddleware(listeningServerMux))
  88. return err
  89. }
  90. func register_ipkvm_apis(mux *http.ServeMux) {
  91. mux.HandleFunc("/api/v1/stream/{uuid}/video", func(w http.ResponseWriter, r *http.Request) {
  92. instanceUUID := r.PathValue("uuid")
  93. fmt.Println("Requested video stream for instance UUID:", instanceUUID)
  94. dezukvmManager.HandleVideoStreams(w, r, instanceUUID)
  95. })
  96. mux.HandleFunc("/api/v1/stream/{uuid}/audio", func(w http.ResponseWriter, r *http.Request) {
  97. instanceUUID := r.PathValue("uuid")
  98. dezukvmManager.HandleAudioStreams(w, r, instanceUUID)
  99. })
  100. mux.HandleFunc("/api/v1/hid/{uuid}/events", func(w http.ResponseWriter, r *http.Request) {
  101. instanceUUID := r.PathValue("uuid")
  102. dezukvmManager.HandleHIDEvents(w, r, instanceUUID)
  103. })
  104. mux.HandleFunc("/api/v1/mass_storage/switch", func(w http.ResponseWriter, r *http.Request) {
  105. if r.Method != http.MethodPost {
  106. http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  107. return
  108. }
  109. instanceUUID, err := utils.PostPara(r, "uuid")
  110. if err != nil {
  111. http.Error(w, "Missing or invalid uuid parameter", http.StatusBadRequest)
  112. return
  113. }
  114. side, err := utils.PostPara(r, "side")
  115. if err != nil {
  116. http.Error(w, "Missing or invalid side parameter", http.StatusBadRequest)
  117. return
  118. }
  119. switch side {
  120. case "kvm":
  121. dezukvmManager.HandleMassStorageSideSwitch(w, r, instanceUUID, true)
  122. case "remote":
  123. dezukvmManager.HandleMassStorageSideSwitch(w, r, instanceUUID, false)
  124. default:
  125. http.Error(w, "Invalid side parameter", http.StatusBadRequest)
  126. }
  127. })
  128. mux.HandleFunc("/api/v1/instances", func(w http.ResponseWriter, r *http.Request) {
  129. if r.Method == http.MethodGet {
  130. dezukvmManager.HandleListInstances(w, r)
  131. } else {
  132. http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  133. }
  134. })
  135. }