X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=cmd%2Fsafcm-remote%2Fsync%2Fservices_systemd.go;fp=cmd%2Fsafcm-remote%2Fsync%2Fservices_systemd.go;h=68bbc7d3e08c07e1e665e2cc7ac175d25a1e7679;hb=f2f2bc47e8729548f3c10117f7f008b547c4afc5;hp=0000000000000000000000000000000000000000;hpb=dc0d431a778a50e6732b9eb91384a07a207b772d;p=safcm%2Fsafcm.git diff --git a/cmd/safcm-remote/sync/services_systemd.go b/cmd/safcm-remote/sync/services_systemd.go new file mode 100644 index 0000000..68bbc7d --- /dev/null +++ b/cmd/safcm-remote/sync/services_systemd.go @@ -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 . + +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 +}