11package kvm
22
33import (
4+ "bufio"
45 "encoding/json"
56 "fmt"
7+ "io"
68 "os"
9+ "os/exec"
10+ "path/filepath"
11+ "strings"
712 "sync"
813
914 "kvm/internal/logging"
@@ -75,8 +80,7 @@ func (m *KeyboardMacro) Validate() error {
7580}
7681
7782type Config struct {
78- CloudToken string `json:"cloud_token"`
79- GoogleIdentity string `json:"google_identity"`
83+ STUN string `json:"stun"`
8084 JigglerEnabled bool `json:"jiggler_enabled"`
8185 AutoUpdateEnabled bool `json:"auto_update_enabled"`
8286 IncludePreRelease bool `json:"include_pre_release"`
@@ -102,6 +106,8 @@ type Config struct {
102106 TailScaleXEdge bool `json:"tailscale_xedge"`
103107 ZeroTierNetworkID string `json:"zerotier_network_id"`
104108 ZeroTierAutoStart bool `json:"zerotier_autostart"`
109+ FrpcAutoStart bool `json:"frpc_autostart"`
110+ FrpcToml string `json:"frpc_toml"`
105111 IO0Status bool `json:"io0_status"`
106112 IO1Status bool `json:"io1_status"`
107113 AudioMode string `json:"audio_mode"`
@@ -111,8 +117,10 @@ type Config struct {
111117}
112118
113119const configPath = "/userdata/kvm_config.json"
120+ const sdConfigPath = "/mnt/sdcard/kvm_config.json"
114121
115122var defaultConfig = & Config {
123+ STUN : "stun:stun.l.google.com:19302" ,
116124 AutoUpdateEnabled : false , // Set a default value
117125 ActiveExtension : "" ,
118126 KeyboardMacros : []KeyboardMacro {},
@@ -135,13 +143,15 @@ var defaultConfig = &Config{
135143 RelativeMouse : true ,
136144 Keyboard : true ,
137145 MassStorage : true ,
138- Audio : true ,
146+ Audio : false , //At any given time, only one of Audio and Mtp can be set to true
147+ Mtp : false ,
139148 },
140149 NetworkConfig : & network.NetworkConfig {},
141150 DefaultLogLevel : "INFO" ,
142151 ZeroTierAutoStart : false ,
143152 TailScaleAutoStart : false ,
144153 TailScaleXEdge : false ,
154+ FrpcAutoStart : false ,
145155 IO0Status : true ,
146156 IO1Status : true ,
147157 AudioMode : "disabled" ,
@@ -165,6 +175,14 @@ func LoadConfig() {
165175
166176 // load the default config
167177 config = defaultConfig
178+ if config .UsbConfig .SerialNumber == "" {
179+ serialNumber , err := extractSerialNumber ()
180+ if err != nil {
181+ logger .Warn ().Err (err ).Msg ("failed to extract serial number" )
182+ } else {
183+ config .UsbConfig .SerialNumber = serialNumber
184+ }
185+ }
168186
169187 file , err := os .Open (configPath )
170188 if err != nil {
@@ -177,6 +195,10 @@ func LoadConfig() {
177195 loadedConfig := * defaultConfig
178196 if err := json .NewDecoder (file ).Decode (& loadedConfig ); err != nil {
179197 logger .Warn ().Err (err ).Msg ("config file JSON parsing failed" )
198+ os .Remove (configPath )
199+ if _ , err := os .Stat (sdConfigPath ); err == nil {
200+ os .Remove (sdConfigPath )
201+ }
180202 return
181203 }
182204
@@ -200,6 +222,74 @@ func LoadConfig() {
200222 logger .Info ().Str ("path" , configPath ).Msg ("config loaded" )
201223}
202224
225+ func copyFile (src , dst string ) error {
226+ in , err := os .Open (src )
227+ if err != nil {
228+ return err
229+ }
230+ defer in .Close ()
231+
232+ if err := os .MkdirAll (filepath .Dir (dst ), 0755 ); err != nil {
233+ return err
234+ }
235+
236+ out , err := os .Create (dst )
237+ if err != nil {
238+ return err
239+ }
240+ defer func () {
241+ if cerr := out .Close (); cerr != nil && err == nil {
242+ err = cerr
243+ }
244+ }()
245+
246+ if _ , err := io .Copy (out , in ); err != nil {
247+ return err
248+ }
249+
250+ if err := out .Sync (); err != nil {
251+ return err
252+ }
253+
254+ return nil
255+ }
256+
257+ func SyncConfigSD (isUpdate bool ) {
258+ resp , err := rpcGetSDMountStatus ()
259+ if err != nil {
260+ logger .Error ().Err (err ).Msg ("failed to get sd mount status" )
261+ return
262+ }
263+
264+ if resp .Status == SDMountOK {
265+ if _ , err := os .Stat (configPath ); err != nil {
266+ if err := SaveConfig (); err != nil {
267+ logger .Error ().Err (err ).Msg ("failed to create kvm_config.json" )
268+ return
269+ }
270+ }
271+
272+ if isUpdate {
273+ if _ , err := os .Stat (sdConfigPath ); err == nil {
274+ if err := copyFile (sdConfigPath , configPath ); err != nil {
275+ logger .Error ().Err (err ).Msg ("failed to copy kvm_config.json from sdcard to userdata" )
276+ return
277+ }
278+ } else {
279+ if err := copyFile (configPath , sdConfigPath ); err != nil {
280+ logger .Error ().Err (err ).Msg ("failed to copy kvm_config.json from userdata to sdcard" )
281+ return
282+ }
283+ }
284+ } else {
285+ if err := copyFile (configPath , sdConfigPath ); err != nil {
286+ logger .Error ().Err (err ).Msg ("failed to copy kvm_config.json from userdata to sdcard" )
287+ return
288+ }
289+ }
290+ }
291+ }
292+
203293func SaveConfig () error {
204294 configLock .Lock ()
205295 defer configLock .Unlock ()
@@ -218,6 +308,8 @@ func SaveConfig() error {
218308 return fmt .Errorf ("failed to encode config: %w" , err )
219309 }
220310
311+ SyncConfigSD (false )
312+
221313 return nil
222314}
223315
@@ -226,3 +318,95 @@ func ensureConfigLoaded() {
226318 LoadConfig ()
227319 }
228320}
321+
322+ var systemInfoWriteLock sync.Mutex
323+
324+ func writeSystemInfoImg () error {
325+ systemInfoWriteLock .Lock ()
326+ defer systemInfoWriteLock .Unlock ()
327+
328+ imgPath := filepath .Join (imagesFolder , "system_info.img" )
329+ unverifiedimgPath := filepath .Join (imagesFolder , "system_info.img" ) + ".unverified"
330+ mountPoint := "/mnt/system_info"
331+
332+ run := func (cmd string , args ... string ) error {
333+ c := exec .Command (cmd , args ... )
334+ c .Stdout = os .Stdout
335+ c .Stderr = os .Stderr
336+ return c .Run ()
337+ }
338+
339+ if _ , err := os .Stat (unverifiedimgPath ); err == nil {
340+ err := os .Rename (unverifiedimgPath , imgPath )
341+ if err != nil {
342+ return fmt .Errorf ("failed to rename %s to %s: %v" , unverifiedimgPath , imgPath , err )
343+ }
344+ return nil
345+ }
346+
347+ isMounted := false
348+ if f , err := os .Open ("/proc/mounts" ); err == nil {
349+ defer f .Close ()
350+ scanner := bufio .NewScanner (f )
351+ for scanner .Scan () {
352+ fields := strings .Fields (scanner .Text ())
353+ if len (fields ) >= 2 && fields [1 ] == mountPoint {
354+ isMounted = true
355+ break
356+ }
357+ }
358+ }
359+
360+ if isMounted {
361+ logger .Info ().Msgf ("%s is mounted, umounting...\n " , mountPoint )
362+ _ = run ("umount" , mountPoint )
363+ }
364+
365+ if _ , err := os .Stat (mountPoint ); err == nil {
366+ if err := os .Remove (mountPoint ); err != nil {
367+ return fmt .Errorf ("failed to remove %s: %v" , mountPoint , err )
368+ }
369+ }
370+
371+ if _ , err := os .Stat (imgPath ); err == nil {
372+ if err := copyFile (imgPath , unverifiedimgPath ); err != nil {
373+ logger .Error ().Err (err ).Msg ("failed to copy system_info.img" )
374+ return err
375+ }
376+ } else {
377+ if err := run ("dd" , "if=/dev/zero" , "of=" + unverifiedimgPath , "bs=1M" , "count=4" ); err != nil {
378+ return fmt .Errorf ("dd failed: %v" , err )
379+ }
380+
381+ if err := run ("mkfs.vfat" , unverifiedimgPath ); err != nil {
382+ return fmt .Errorf ("mkfs.vfat failed: %v" , err )
383+ }
384+ }
385+
386+ if err := os .MkdirAll (mountPoint , 0755 ); err != nil {
387+ return fmt .Errorf ("mkdir failed: %v" , err )
388+ }
389+
390+ if err := run ("mount" , "-o" , "loop" , unverifiedimgPath , mountPoint ); err != nil {
391+ return fmt .Errorf ("mount failed: %v" , err )
392+ }
393+
394+ if err := run ("cp" , "/etc/hostname" , mountPoint + "/hostname.txt" ); err != nil {
395+ return fmt .Errorf ("copy hostname failed: %v" , err )
396+ }
397+ if err := run ("sh" , "-c" , "ip addr show > " + mountPoint + "/network_info.txt" ); err != nil {
398+ return fmt .Errorf ("write network info failed: %v" , err )
399+ }
400+
401+ _ = run ("umount" , mountPoint )
402+ if err := os .RemoveAll (mountPoint ); err != nil {
403+ return fmt .Errorf ("failed to remove %s: %v" , mountPoint , err )
404+ }
405+
406+ if err := os .Rename (unverifiedimgPath , imgPath ); err != nil {
407+ return fmt .Errorf ("failed to rename %s to %s: %v" , unverifiedimgPath , imgPath , err )
408+ }
409+
410+ logger .Info ().Msg ("system_info.img update successfully" )
411+ return nil
412+ }
0 commit comments