1 // Frontend: Host synchronization and logging event loop for programs using
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 // Copyright (C) 2021-2024 Simon Ruderich
17 "ruderich.org/simon/safcm"
18 "ruderich.org/simon/safcm/rpc"
29 LogEventFunc func(e Event, failed *bool)
30 SyncHostFunc func(*rpc.Conn, Host) error
35 func (l *Loop) Run(hosts []Host) bool {
36 done := make(chan bool)
37 // Collect events from all hosts and print them
38 events := make(chan Event)
46 l.LogEventFunc(x, &failed)
51 hostsLeft := make(map[string]bool)
52 for _, x := range hosts {
53 hostsLeft[x.Name()] = true
55 var hostsLeftMutex sync.Mutex // protects hostsLeft
57 // Show unfinished hosts on Ctrl-C
58 sigint := make(chan os.Signal, 1) // buffered for Notify()
59 signal.Notify(sigint, os.Interrupt) // = SIGINT = Ctrl-C
61 // Running `ssh` processes get killed by SIGINT which is sent
65 log.Print("Received SIGINT, aborting ...")
67 // Print all queued events
68 events <- Event{} // poison pill
70 // "races" with <-done in the main function and will hang here
71 // if the other is faster. This is fine because then all hosts
72 // were synced successfully.
76 for x := range hostsLeft {
77 hosts = append(hosts, x)
80 log.Fatalf("Failed to sync %s", strings.Join(hosts, ", "))
83 l.events = events // used by l.syncHost() and l.Log()
85 // Sync all hosts concurrently
87 for _, x := range hosts {
90 // Once in sync.Host() and once in the go func below
94 err := l.syncHost(&wg, x)
103 hostsLeftMutex.Lock()
104 defer hostsLeftMutex.Unlock()
105 delete(hostsLeft, x.Name())
110 events <- Event{} // poison pill
116 func (l *Loop) syncHost(wg *sync.WaitGroup, host Host) error {
117 conn := rpc.NewConn(l.DebugConn)
118 // Pass all connection events to main loop
121 x, ok := <-conn.Events
133 err := host.Dial(conn)
135 conn.Kill() //nolint:errcheck
138 defer conn.Kill() //nolint:errcheck
140 err = l.SyncHostFunc(conn, host)
145 // Terminate connection to remote host
146 err = conn.Send(safcm.MsgQuitReq{})