--- /dev/null
+// MsgSyncReq: run commands on the remote host
+
+// Copyright (C) 2021 Simon Ruderich
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package sync
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+
+ "ruderich.org/simon/safcm"
+ "ruderich.org/simon/safcm/cmd/safcm-remote/run"
+)
+
+func (s *Sync) syncCommands() error {
+ // Run triggered commands first
+ for _, path := range s.triggers {
+ for _, x := range s.req.Files[path].TriggerCommands {
+ err := s.syncCommand(x, path)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ // Regular commands afterwards so they can react on triggers if
+ // necessary
+ for _, x := range s.req.Commands {
+ err := s.syncCommand(x, "")
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *Sync) syncCommand(command string, trigger string) error {
+ s.resp.CommandChanges = append(s.resp.CommandChanges,
+ safcm.CommandChange{
+ Command: command,
+ Trigger: trigger,
+ })
+ if s.req.DryRun {
+ return nil
+ }
+ change := &s.resp.CommandChanges[len(s.resp.CommandChanges)-1]
+
+ cmd := exec.Command("/bin/sh", "-c", command)
+ cmd.Env = safcmEnviroment(s.req.Groups)
+ // Cannot use cmd.CombinedOutputCmd() because we need another log
+ // level (here the command is the actual change and not a side effect)
+ // and different error handling.
+ s.log.Verbosef("commands: running %s", run.QuoteForDebug(cmd))
+ out, err := s.cmd.Runner.CombinedOutput(cmd)
+ if len(out) > 0 {
+ s.log.Debug2f("commands: command output:\n%s", out)
+ }
+ change.Output = string(out)
+ if err != nil {
+ change.Error = err.Error()
+ return fmt.Errorf("%q failed: %v", command, err)
+ }
+
+ return nil
+}
+
+func safcmEnviroment(groups []string) []string {
+ env := os.Environ()
+ // Provide additional environment variables so commands can check
+ // group membership
+ env = append(env,
+ fmt.Sprintf("SAFCM_GROUPS=%s", strings.Join(groups, " ")))
+ for _, x := range groups {
+ env = append(env, fmt.Sprintf("SAFCM_GROUP_%s=%s", x, x))
+ }
+ return env
+}