github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/servicestate/service_control.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 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. 14 * 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/>. 17 * 18 */ 19 20 package servicestate 21 22 import ( 23 "fmt" 24 25 tomb "gopkg.in/tomb.v2" 26 27 "github.com/snapcore/snapd/overlord/snapstate" 28 "github.com/snapcore/snapd/overlord/state" 29 "github.com/snapcore/snapd/snap" 30 "github.com/snapcore/snapd/wrappers" 31 ) 32 33 // ServiceAction encapsulates a single service-related action (such as starting, 34 // stopping or restarting) run against services of a given snap. The action is 35 // run for services listed in services attribute, or for all services of the 36 // snap if services list is empty. 37 // The names of services and explicit-services are app names (as defined in snap 38 // yaml). 39 type ServiceAction struct { 40 SnapName string `json:"snap-name"` 41 Action string `json:"action"` 42 ActionModifier string `json:"action-modifier,omitempty"` 43 Services []string `json:"services,omitempty"` 44 // ExplicitServices is used when there are explicit services that should be 45 // restarted. This is used for the `snap restart snap-name.svc1` case, 46 // where we create a task with specific services to work on - in this case 47 // ExplicitServices ends up being the list of services that were explicitly 48 // mentioned by the user to be restarted, regardless of their state. This is 49 // needed because in the case that one does `snap restart snap-name`, 50 // Services gets populated with all services in the snap, which we now 51 // interpret to mean that only inactive services of that set are to be 52 // restarted, but there could be additional explicit services that need to 53 // be restarted at the same time in the case that someone does something 54 // like `snap restart snap-name snap-name.svc1`, we will restart all the 55 // inactive and not disabled services in snap-name, and also svc1 regardless 56 // of the state svc1 is in. 57 ExplicitServices []string `json:"explicit-services,omitempty"` 58 } 59 60 func (m *ServiceManager) doServiceControl(t *state.Task, _ *tomb.Tomb) error { 61 st := t.State() 62 st.Lock() 63 defer st.Unlock() 64 65 perfTimings := state.TimingsForTask(t) 66 defer perfTimings.Save(st) 67 68 var sc ServiceAction 69 err := t.Get("service-action", &sc) 70 if err != nil { 71 return fmt.Errorf("internal error: cannot get service-action: %v", err) 72 } 73 74 var snapst snapstate.SnapState 75 if err := snapstate.Get(st, sc.SnapName, &snapst); err != nil { 76 return err 77 } 78 info, err := snapst.CurrentInfo() 79 if err != nil { 80 return err 81 } 82 83 svcs := info.Services() 84 if len(svcs) == 0 { 85 return nil 86 } 87 88 var services []*snap.AppInfo 89 if len(sc.Services) == 0 { 90 // no services specified, take all services of the snap 91 services = info.Services() 92 } else { 93 for _, svc := range sc.Services { 94 app := info.Apps[svc] 95 if app == nil { 96 return fmt.Errorf("no such service: %s", svc) 97 } 98 if !app.IsService() { 99 return fmt.Errorf("%s is not a service", svc) 100 } 101 services = append(services, app) 102 } 103 } 104 105 meter := snapstate.NewTaskProgressAdapterUnlocked(t) 106 107 var startupOrdered []*snap.AppInfo 108 if sc.Action != "stop" { 109 startupOrdered, err = snap.SortServices(services) 110 if err != nil { 111 return err 112 } 113 } 114 115 // ExplicitServices are snap app names; obtain names of systemd units 116 // expected by wrappers. 117 var explicitServicesSystemdUnits []string 118 for _, name := range sc.ExplicitServices { 119 if app := info.Apps[name]; app != nil { 120 explicitServicesSystemdUnits = append(explicitServicesSystemdUnits, app.ServiceName()) 121 } 122 } 123 124 // Note - state must be unlocked when calling wrappers below. 125 switch sc.Action { 126 case "stop": 127 disable := sc.ActionModifier == "disable" 128 flags := &wrappers.StopServicesFlags{ 129 Disable: disable, 130 } 131 st.Unlock() 132 err := wrappers.StopServices(services, flags, snap.StopReasonOther, meter, perfTimings) 133 st.Lock() 134 if err != nil { 135 return err 136 } 137 if disable { 138 // re-read snapst after reacquiring the lock as it could have changed. 139 if err := snapstate.Get(st, sc.SnapName, &snapst); err != nil { 140 return err 141 } 142 changed, err := updateSnapstateServices(&snapst, nil, services) 143 if err != nil { 144 return err 145 } 146 if changed { 147 snapstate.Set(st, sc.SnapName, &snapst) 148 } 149 } 150 case "start": 151 enable := sc.ActionModifier == "enable" 152 flags := &wrappers.StartServicesFlags{ 153 Enable: enable, 154 } 155 st.Unlock() 156 err = wrappers.StartServices(startupOrdered, nil, flags, meter, perfTimings) 157 st.Lock() 158 if err != nil { 159 return err 160 } 161 if enable { 162 // re-read snapst after reacquiring the lock as it could have changed. 163 if err := snapstate.Get(st, sc.SnapName, &snapst); err != nil { 164 return err 165 } 166 changed, err := updateSnapstateServices(&snapst, startupOrdered, nil) 167 if err != nil { 168 return err 169 } 170 if changed { 171 snapstate.Set(st, sc.SnapName, &snapst) 172 } 173 } 174 case "restart": 175 st.Unlock() 176 err := wrappers.RestartServices(startupOrdered, explicitServicesSystemdUnits, nil, meter, perfTimings) 177 st.Lock() 178 return err 179 case "reload-or-restart": 180 flags := &wrappers.RestartServicesFlags{Reload: true} 181 st.Unlock() 182 err := wrappers.RestartServices(startupOrdered, explicitServicesSystemdUnits, flags, meter, perfTimings) 183 st.Lock() 184 return err 185 default: 186 return fmt.Errorf("unhandled service action: %q", sc.Action) 187 } 188 return nil 189 }