]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - remote/main.go
Use SPDX license identifiers
[safcm/safcm.git] / remote / main.go
1 // Helper copied to the remote host to run commands and deploy configuration
2
3 // SPDX-License-Identifier: GPL-3.0-or-later
4 // Copyright (C) 2021-2024  Simon Ruderich
5
6 package remote
7
8 import (
9         "fmt"
10         "log"
11         "os"
12
13         "golang.org/x/term"
14
15         "ruderich.org/simon/safcm"
16         "ruderich.org/simon/safcm/remote/ainsl"
17         "ruderich.org/simon/safcm/remote/info"
18         "ruderich.org/simon/safcm/remote/run"
19         "ruderich.org/simon/safcm/remote/sync"
20 )
21
22 func usage() {
23         log.Fatalf("usage: %[1]s sync\n"+
24                 "usage: %[1]s ainsl [options] <path> <line>",
25                 os.Args[0])
26 }
27
28 func Main() {
29         // Timestamps are added by `safcm`
30         log.SetFlags(0)
31
32         if len(os.Args) < 2 {
33                 usage()
34         }
35
36         var err error
37         switch os.Args[1] {
38         case "sync":
39                 if len(os.Args) != 2 {
40                         usage()
41                 }
42                 err = mainLoop()
43         case "ainsl":
44                 err = ainsl.Main(os.Args)
45         default:
46                 usage()
47         }
48
49         if err != nil {
50                 log.Fatalf("%s: %v", os.Args[0], err)
51         }
52 }
53
54 func mainLoop() error {
55         if term.IsTerminal(int(os.Stdin.Fd())) ||
56                 term.IsTerminal(int(os.Stdout.Fd())) {
57                 return fmt.Errorf("sync should only be called from `safcm` " +
58                         "(redirect stdin/stdout to circumvent this check)")
59         }
60
61         conn := safcm.NewGobConn(os.Stdin, os.Stdout)
62
63         var logLevel safcm.LogLevel
64         logFunc := func(level safcm.LogLevel, msg string) {
65                 if logLevel >= level {
66                         // Handling errors here is complex and quite verbose.
67                         // If it happens the connection is gone anyway so skip
68                         // the error handling.
69                         conn.Send(safcm.MsgLog{ //nolint:errcheck
70                                 Level: level,
71                                 Text:  msg,
72                         })
73                 }
74         }
75
76         var quitResp safcm.MsgQuitResp
77         for {
78                 x, err := conn.Recv()
79                 if err != nil {
80                         return err
81                 }
82
83                 var resp safcm.Msg
84                 switch x := x.(type) {
85                 case safcm.MsgInfoReq:
86                         logLevel = x.LogLevel // set log level globally
87                         resp = info.Handle(x, run.ExecRunner{}, logFunc)
88                 case safcm.MsgSyncReq:
89                         resp = sync.Handle(x, run.ExecRunner{}, logFunc)
90                 case safcm.MsgQuitReq:
91                         resp = quitResp
92                 default:
93                         return fmt.Errorf("unsupported message %#v", x)
94                 }
95
96                 err = conn.Send(resp)
97                 if err != nil {
98                         return err
99                 }
100                 if resp == quitResp {
101                         break
102                 }
103         }
104         return nil
105 }