ipkvm.go 4.0 KB

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