// Helper copied to the remote host to run commands and deploy configuration // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2021-2024 Simon Ruderich package remote import ( "fmt" "log" "os" "golang.org/x/term" "ruderich.org/simon/safcm" "ruderich.org/simon/safcm/remote/ainsl" "ruderich.org/simon/safcm/remote/info" "ruderich.org/simon/safcm/remote/run" "ruderich.org/simon/safcm/remote/sync" ) func usage() { log.Fatalf("usage: %[1]s sync\n"+ "usage: %[1]s ainsl [options] ", os.Args[0]) } func Main() { // Timestamps are added by `safcm` log.SetFlags(0) if len(os.Args) < 2 { usage() } var err error switch os.Args[1] { case "sync": if len(os.Args) != 2 { usage() } err = mainLoop() case "ainsl": err = ainsl.Main(os.Args) default: usage() } if err != nil { log.Fatalf("%s: %v", os.Args[0], err) } } func mainLoop() error { if term.IsTerminal(int(os.Stdin.Fd())) || term.IsTerminal(int(os.Stdout.Fd())) { return fmt.Errorf("sync should only be called from `safcm` " + "(redirect stdin/stdout to circumvent this check)") } conn := safcm.NewGobConn(os.Stdin, os.Stdout) var logLevel safcm.LogLevel logFunc := func(level safcm.LogLevel, msg string) { if logLevel >= level { // Handling errors here is complex and quite verbose. // If it happens the connection is gone anyway so skip // the error handling. conn.Send(safcm.MsgLog{ //nolint:errcheck Level: level, Text: msg, }) } } var quitResp safcm.MsgQuitResp for { x, err := conn.Recv() if err != nil { return err } var resp safcm.Msg switch x := x.(type) { case safcm.MsgInfoReq: logLevel = x.LogLevel // set log level globally resp = info.Handle(x, run.ExecRunner{}, logFunc) case safcm.MsgSyncReq: resp = sync.Handle(x, run.ExecRunner{}, logFunc) case safcm.MsgQuitReq: resp = quitResp default: return fmt.Errorf("unsupported message %#v", x) } err = conn.Send(resp) if err != nil { return err } if resp == quitResp { break } } return nil }