1 // MsgSyncReq: enable and start services on the remote host (systemd)
3 // SPDX-License-Identifier: GPL-3.0-or-later
4 // Copyright (C) 2021-2024 Simon Ruderich
12 "ruderich.org/simon/safcm"
15 func (s *Sync) syncServicesSystemd() error {
16 s.log.Debugf("services: checking %s (systemd detected)",
17 strings.Join(s.req.Services, " "))
18 services, err := s.systemdServiceState(s.req.Services)
23 var start, enable []string
24 for _, name := range s.req.Services {
25 var change safcm.ServiceChange
28 if x.ActiveState != "active" {
29 start = append(start, name)
32 if x.UnitFileState != "enabled" {
33 enable = append(enable, name)
37 if change.Started || change.Enabled {
39 s.resp.ServiceChanges = append(s.resp.ServiceChanges,
43 if len(start) == 0 && len(enable) == 0 {
51 // Reload service files which were possibly changed during file sync
52 // or package installation
53 _, _, err = s.cmd.Run("services", "/bin/systemctl", "daemon-reload")
58 s.log.Verbosef("services: starting %s",
59 strings.Join(start, " "))
60 _, _, err := s.cmd.Run("services", append([]string{
61 "/bin/systemctl", "start", "--",
68 s.log.Verbosef("services: enabling %s",
69 strings.Join(enable, " "))
70 _, _, err := s.cmd.Run("services", append([]string{
71 "/bin/systemctl", "enable", "--",
81 type SystemdService struct {
86 func (s *Sync) systemdServiceState(services []string) (
87 map[string]SystemdService, error) {
89 out, _, err := s.cmd.Run("services", append([]string{
92 "--property=ActiveState,UnitFileState,LoadError",
101 res := make(map[string]SystemdService)
102 for _, block := range strings.Split(string(out), "\n\n") {
103 lines := strings.Split(strings.TrimSpace(block), "\n")
105 return nil, fmt.Errorf("invalid systemctl output: %q",
109 var service SystemdService
110 for _, x := range lines {
112 activePrefix = "ActiveState="
113 unitPrefix = "UnitFileState="
114 errorPrefix = "LoadError="
117 if strings.HasPrefix(x, activePrefix) {
118 service.ActiveState = strings.TrimPrefix(x,
120 } else if strings.HasPrefix(x, unitPrefix) {
121 service.UnitFileState = strings.TrimPrefix(x,
123 } else if strings.HasPrefix(x, errorPrefix) {
124 x := strings.TrimPrefix(x, errorPrefix)
125 // Older systemd versions (e.g. 237) add empty
126 // quotes even if there is no error
127 if x != "" && x != ` ""` {
128 return nil, fmt.Errorf(
129 "systemd unit %q not found",
133 return nil, fmt.Errorf(
134 "invalid systemctl show line %q", x)
137 res[services[i]] = service