]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - remote/sync/services_systemd.go
Use SPDX license identifiers
[safcm/safcm.git] / remote / sync / services_systemd.go
1 // MsgSyncReq: enable and start services on the remote host (systemd)
2
3 // SPDX-License-Identifier: GPL-3.0-or-later
4 // Copyright (C) 2021-2024  Simon Ruderich
5
6 package sync
7
8 import (
9         "fmt"
10         "strings"
11
12         "ruderich.org/simon/safcm"
13 )
14
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)
19         if err != nil {
20                 return err
21         }
22
23         var start, enable []string
24         for _, name := range s.req.Services {
25                 var change safcm.ServiceChange
26
27                 x := services[name]
28                 if x.ActiveState != "active" {
29                         start = append(start, name)
30                         change.Started = true
31                 }
32                 if x.UnitFileState != "enabled" {
33                         enable = append(enable, name)
34                         change.Enabled = true
35                 }
36
37                 if change.Started || change.Enabled {
38                         change.Name = name
39                         s.resp.ServiceChanges = append(s.resp.ServiceChanges,
40                                 change)
41                 }
42         }
43         if len(start) == 0 && len(enable) == 0 {
44                 return nil
45         }
46
47         if s.req.DryRun {
48                 return nil
49         }
50
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")
54         if err != nil {
55                 return err
56         }
57         if len(start) != 0 {
58                 s.log.Verbosef("services: starting %s",
59                         strings.Join(start, " "))
60                 _, _, err := s.cmd.Run("services", append([]string{
61                         "/bin/systemctl", "start", "--",
62                 }, start...)...)
63                 if err != nil {
64                         return err
65                 }
66         }
67         if len(enable) != 0 {
68                 s.log.Verbosef("services: enabling %s",
69                         strings.Join(enable, " "))
70                 _, _, err := s.cmd.Run("services", append([]string{
71                         "/bin/systemctl", "enable", "--",
72                 }, enable...)...)
73                 if err != nil {
74                         return err
75                 }
76         }
77
78         return nil
79 }
80
81 type SystemdService struct {
82         ActiveState   string
83         UnitFileState string
84 }
85
86 func (s *Sync) systemdServiceState(services []string) (
87         map[string]SystemdService, error) {
88
89         out, _, err := s.cmd.Run("services", append([]string{
90                 "/bin/systemctl",
91                 "show",
92                 "--property=ActiveState,UnitFileState,LoadError",
93                 "--",
94         }, services...)...)
95         if err != nil {
96                 return nil, err
97         }
98
99         i := 0
100
101         res := make(map[string]SystemdService)
102         for _, block := range strings.Split(string(out), "\n\n") {
103                 lines := strings.Split(strings.TrimSpace(block), "\n")
104                 if len(lines) != 3 {
105                         return nil, fmt.Errorf("invalid systemctl output: %q",
106                                 block)
107                 }
108
109                 var service SystemdService
110                 for _, x := range lines {
111                         const (
112                                 activePrefix = "ActiveState="
113                                 unitPrefix   = "UnitFileState="
114                                 errorPrefix  = "LoadError="
115                         )
116
117                         if strings.HasPrefix(x, activePrefix) {
118                                 service.ActiveState = strings.TrimPrefix(x,
119                                         activePrefix)
120                         } else if strings.HasPrefix(x, unitPrefix) {
121                                 service.UnitFileState = strings.TrimPrefix(x,
122                                         unitPrefix)
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",
130                                                 services[i])
131                                 }
132                         } else {
133                                 return nil, fmt.Errorf(
134                                         "invalid systemctl show line %q", x)
135                         }
136                 }
137                 res[services[i]] = service
138
139                 i++
140         }
141
142         return res, nil
143 }