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: checking %s (systemd detected)",
29 strings.Join(s.req.Services, " "))
30 services, err := s.systemdServiceState(s.req.Services)
35 var start, enable []string
36 for _, name := range s.req.Services {
37 var change safcm.ServiceChange
40 if x.ActiveState != "active" {
41 start = append(start, name)
44 if x.UnitFileState != "enabled" {
45 enable = append(enable, name)
49 if change.Started || change.Enabled {
51 s.resp.ServiceChanges = append(s.resp.ServiceChanges,
55 if len(start) == 0 && len(enable) == 0 {
63 // Reload service files which were possibly changed during file sync
64 // or package installation
65 _, _, err = s.cmd.Run("services", "/bin/systemctl", "daemon-reload")
70 s.log.Verbosef("services: starting %s",
71 strings.Join(start, " "))
72 _, _, err := s.cmd.Run("services", append([]string{
73 "/bin/systemctl", "start", "--",
80 s.log.Verbosef("services: enabling %s",
81 strings.Join(enable, " "))
82 _, _, err := s.cmd.Run("services", append([]string{
83 "/bin/systemctl", "enable", "--",
93 type SystemdService struct {
98 func (s *Sync) systemdServiceState(services []string) (
99 map[string]SystemdService, error) {
101 out, _, err := s.cmd.Run("services", append([]string{
104 "--property=ActiveState,UnitFileState,LoadError",
113 res := make(map[string]SystemdService)
114 for _, block := range strings.Split(string(out), "\n\n") {
115 lines := strings.Split(strings.TrimSpace(block), "\n")
117 return nil, fmt.Errorf("invalid systemctl output: %q",
121 var service SystemdService
122 for _, x := range lines {
124 activePrefix = "ActiveState="
125 unitPrefix = "UnitFileState="
126 errorPrefix = "LoadError="
129 if strings.HasPrefix(x, activePrefix) {
130 service.ActiveState = strings.TrimPrefix(x,
132 } else if strings.HasPrefix(x, unitPrefix) {
133 service.UnitFileState = strings.TrimPrefix(x,
135 } else if strings.HasPrefix(x, errorPrefix) {
136 x := strings.TrimPrefix(x, errorPrefix)
137 // Older systemd versions (e.g. 237) add empty
138 // quotes even if there is no error
139 if x != "" && x != ` ""` {
140 return nil, fmt.Errorf(
141 "systemd unit %q not found",
145 return nil, fmt.Errorf(
146 "invalid systemctl show line %q", x)
149 res[services[i]] = service