1 // MsgSyncReq: enable and start services on the remote host (systemd)
3 // Copyright (C) 2021 Simon Ruderich
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 "ruderich.org/simon/safcm"
27 func (s *Sync) syncServicesSystemd() error {
28 s.log.Debugf("services: detected systemd")
30 s.log.Debugf("services: checking %s",
31 strings.Join(s.req.Services, " "))
32 services, err := s.systemdServiceState(s.req.Services)
37 var start, enable []string
38 for _, name := range s.req.Services {
39 var change safcm.ServiceChange
42 if x.ActiveState != "active" {
43 start = append(start, name)
46 if x.UnitFileState != "enabled" {
47 enable = append(enable, name)
51 if change.Started || change.Enabled {
53 s.resp.ServiceChanges = append(s.resp.ServiceChanges,
57 if len(start) == 0 && len(enable) == 0 {
65 // Reload service files which were possibly changed during file sync
66 // or package installation
67 _, _, err = s.cmd.Run("services", "/bin/systemctl", "daemon-reload")
72 s.log.Verbosef("services: starting %s",
73 strings.Join(start, " "))
74 _, _, err := s.cmd.Run("services", append([]string{
75 "/bin/systemctl", "start", "--",
82 s.log.Verbosef("services: enabling %s",
83 strings.Join(enable, " "))
84 _, _, err := s.cmd.Run("services", append([]string{
85 "/bin/systemctl", "enable", "--",
95 type SystemdService struct {
100 func (s *Sync) systemdServiceState(services []string) (
101 map[string]SystemdService, error) {
103 out, _, err := s.cmd.Run("services", append([]string{
106 "--property=ActiveState,UnitFileState,LoadError",
115 res := make(map[string]SystemdService)
116 for _, block := range strings.Split(string(out), "\n\n") {
117 lines := strings.Split(strings.TrimSpace(block), "\n")
119 return nil, fmt.Errorf("invalid systemctl output: %q",
123 var service SystemdService
124 for _, x := range lines {
126 activePrefix = "ActiveState="
127 unitPrefix = "UnitFileState="
128 errorPrefix = "LoadError="
131 if strings.HasPrefix(x, activePrefix) {
132 service.ActiveState = strings.TrimPrefix(x,
134 } else if strings.HasPrefix(x, unitPrefix) {
135 service.UnitFileState = strings.TrimPrefix(x,
137 } else if strings.HasPrefix(x, errorPrefix) {
138 x := strings.TrimPrefix(x, errorPrefix)
139 // Older systemd versions (e.g. 237) add empty
140 // quotes even if there is no error
141 if x != "" && x != ` ""` {
142 return nil, fmt.Errorf(
143 "systemd unit %q not found",
147 return nil, fmt.Errorf(
148 "invalid systemctl show line %q", x)
151 res[services[i]] = service