1 // Frontend: Host synchronization and logging event loop for programs using
4 // Copyright (C) 2021 Simon Ruderich
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
29 "ruderich.org/simon/safcm"
30 "ruderich.org/simon/safcm/rpc"
41 LogEventFunc func(e Event, failed *bool)
42 SyncHostFunc func(*rpc.Conn, Host) error
47 func (l *Loop) Run(hosts []Host) bool {
48 done := make(chan bool)
49 // Collect events from all hosts and print them
50 events := make(chan Event)
58 l.LogEventFunc(x, &failed)
63 hostsLeft := make(map[string]bool)
64 for _, x := range hosts {
65 hostsLeft[x.Name()] = true
67 var hostsLeftMutex sync.Mutex // protects hostsLeft
69 // Show unfinished hosts on Ctrl-C
70 sigint := make(chan os.Signal, 1) // buffered for Notify()
71 signal.Notify(sigint, os.Interrupt) // = SIGINT = Ctrl-C
73 // Running `ssh` processes get killed by SIGINT which is sent
77 log.Print("Received SIGINT, aborting ...")
79 // Print all queued events
80 events <- Event{} // poison pill
82 // "races" with <-done in the main function and will hang here
83 // if the other is faster. This is fine because then all hosts
84 // were synced successfully.
88 for x := range hostsLeft {
89 hosts = append(hosts, x)
92 log.Fatalf("Failed to sync %s", strings.Join(hosts, ", "))
95 l.events = events // used by l.syncHost() and l.Log()
97 // Sync all hosts concurrently
99 for _, x := range hosts {
102 // Once in sync.Host() and once in the go func below
106 err := l.syncHost(&wg, x)
115 hostsLeftMutex.Lock()
116 defer hostsLeftMutex.Unlock()
117 delete(hostsLeft, x.Name())
122 events <- Event{} // poison pill
128 func (l *Loop) syncHost(wg *sync.WaitGroup, host Host) error {
129 conn := rpc.NewConn(l.DebugConn)
130 // Pass all connection events to main loop
133 x, ok := <-conn.Events
145 err := host.Dial(conn)
152 err = l.SyncHostFunc(conn, host)
157 // Terminate connection to remote host
158 err = conn.Send(safcm.MsgQuitReq{})