]> ruderich.org/simon Gitweb - safcm/safcm.git/blobdiff - cmd/safcm-remote/sync/services_systemd.go
First working version
[safcm/safcm.git] / cmd / safcm-remote / sync / services_systemd.go
diff --git a/cmd/safcm-remote/sync/services_systemd.go b/cmd/safcm-remote/sync/services_systemd.go
new file mode 100644 (file)
index 0000000..68bbc7d
--- /dev/null
@@ -0,0 +1,157 @@
+// MsgSyncReq: enable and start services on the remote host (systemd)
+
+// 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"
+       "strings"
+
+       "ruderich.org/simon/safcm"
+)
+
+func (s *Sync) syncServicesSystemd() error {
+       s.log.Debugf("services: detected systemd")
+
+       s.log.Debugf("services: checking %s",
+               strings.Join(s.req.Services, " "))
+       services, err := s.systemdServiceState(s.req.Services)
+       if err != nil {
+               return err
+       }
+
+       var start, enable []string
+       for _, name := range s.req.Services {
+               var change safcm.ServiceChange
+
+               x := services[name]
+               if x.ActiveState != "active" {
+                       start = append(start, name)
+                       change.Started = true
+               }
+               if x.UnitFileState != "enabled" {
+                       enable = append(enable, name)
+                       change.Enabled = true
+               }
+
+               if change.Started || change.Enabled {
+                       change.Name = name
+                       s.resp.ServiceChanges = append(s.resp.ServiceChanges,
+                               change)
+               }
+       }
+       if len(start) == 0 && len(enable) == 0 {
+               return nil
+       }
+
+       if s.req.DryRun {
+               return nil
+       }
+
+       // Reload service files which were possibly changed during file sync
+       // or package installation
+       _, _, err = s.cmd.Run("services", "/bin/systemctl", "daemon-reload")
+       if err != nil {
+               return err
+       }
+       if len(start) != 0 {
+               s.log.Verbosef("services: starting %s",
+                       strings.Join(start, " "))
+               _, _, err := s.cmd.Run("services", append([]string{
+                       "/bin/systemctl", "start", "--",
+               }, start...)...)
+               if err != nil {
+                       return err
+               }
+       }
+       if len(enable) != 0 {
+               s.log.Verbosef("services: enabling %s",
+                       strings.Join(enable, " "))
+               _, _, err := s.cmd.Run("services", append([]string{
+                       "/bin/systemctl", "enable", "--",
+               }, enable...)...)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+type SystemdService struct {
+       ActiveState   string
+       UnitFileState string
+}
+
+func (s *Sync) systemdServiceState(services []string) (
+       map[string]SystemdService, error) {
+
+       out, _, err := s.cmd.Run("services", append([]string{
+               "/bin/systemctl",
+               "show",
+               "--property=ActiveState,UnitFileState,LoadError",
+               "--",
+       }, services...)...)
+       if err != nil {
+               return nil, err
+       }
+
+       i := 0
+
+       res := make(map[string]SystemdService)
+       for _, block := range strings.Split(string(out), "\n\n") {
+               lines := strings.Split(strings.TrimSpace(block), "\n")
+               if len(lines) != 3 {
+                       return nil, fmt.Errorf("invalid systemctl output: %q",
+                               block)
+               }
+
+               var service SystemdService
+               for _, x := range lines {
+                       const (
+                               activePrefix = "ActiveState="
+                               unitPrefix   = "UnitFileState="
+                               errorPrefix  = "LoadError="
+                       )
+
+                       if strings.HasPrefix(x, activePrefix) {
+                               service.ActiveState = strings.TrimPrefix(x,
+                                       activePrefix)
+                       } else if strings.HasPrefix(x, unitPrefix) {
+                               service.UnitFileState = strings.TrimPrefix(x,
+                                       unitPrefix)
+                       } else if strings.HasPrefix(x, errorPrefix) {
+                               x := strings.TrimPrefix(x, errorPrefix)
+                               // Older systemd versions (e.g. 237) add empty
+                               // quotes even if there is no error
+                               if x != "" && x != ` ""` {
+                                       return nil, fmt.Errorf(
+                                               "systemd unit %q not found",
+                                               services[i])
+                               }
+                       } else {
+                               return nil, fmt.Errorf(
+                                       "invalid systemctl show line %q", x)
+                       }
+               }
+               res[services[i]] = service
+
+               i++
+       }
+
+       return res, nil
+}