github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/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 "github.com/snapcore/snapd/overlord/snapstate" 26 "github.com/snapcore/snapd/overlord/state" 27 "github.com/snapcore/snapd/snap" 28 "github.com/snapcore/snapd/wrappers" 29 30 tomb "gopkg.in/tomb.v2" 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 are app names (as defined in snap yaml). 38 type ServiceAction struct { 39 SnapName string `json:"snap-name"` 40 Action string `json:"action"` 41 ActionModifier string `json:"action-modifier,omitempty"` 42 Services []string `json:"services,omitempty"` 43 } 44 45 func (m *ServiceManager) doServiceControl(t *state.Task, _ *tomb.Tomb) error { 46 st := t.State() 47 st.Lock() 48 defer st.Unlock() 49 50 perfTimings := state.TimingsForTask(t) 51 defer perfTimings.Save(st) 52 53 var sc ServiceAction 54 err := t.Get("service-action", &sc) 55 if err != nil { 56 return fmt.Errorf("internal error: cannot get service-action: %v", err) 57 } 58 59 var snapst snapstate.SnapState 60 if err := snapstate.Get(st, sc.SnapName, &snapst); err != nil { 61 return err 62 } 63 info, err := snapst.CurrentInfo() 64 if err != nil { 65 return err 66 } 67 68 svcs := info.Services() 69 if len(svcs) == 0 { 70 return nil 71 } 72 73 var services []*snap.AppInfo 74 if len(sc.Services) == 0 { 75 // no services specified, take all services of the snap 76 services = info.Services() 77 } else { 78 for _, svc := range sc.Services { 79 app := info.Apps[svc] 80 if app == nil { 81 return fmt.Errorf("no such service: %s", svc) 82 } 83 if !app.IsService() { 84 return fmt.Errorf("%s is not a service", svc) 85 } 86 services = append(services, app) 87 } 88 } 89 90 meter := snapstate.NewTaskProgressAdapterUnlocked(t) 91 92 var startupOrdered []*snap.AppInfo 93 if sc.Action != "stop" { 94 startupOrdered, err = snap.SortServices(services) 95 if err != nil { 96 return err 97 } 98 } 99 100 // Note - state must be unlocked when calling wrappers below. 101 switch sc.Action { 102 case "stop": 103 disable := sc.ActionModifier == "disable" 104 flags := &wrappers.StopServicesFlags{ 105 Disable: disable, 106 } 107 st.Unlock() 108 err := wrappers.StopServices(services, flags, snap.StopReasonOther, meter, perfTimings) 109 st.Lock() 110 if err != nil { 111 return err 112 } 113 if disable { 114 // re-read snapst after reacquiring the lock as it could have changed. 115 if err := snapstate.Get(st, sc.SnapName, &snapst); err != nil { 116 return err 117 } 118 changed, err := updateSnapstateServices(&snapst, nil, services) 119 if err != nil { 120 return err 121 } 122 if changed { 123 snapstate.Set(st, sc.SnapName, &snapst) 124 } 125 } 126 case "start": 127 enable := sc.ActionModifier == "enable" 128 flags := &wrappers.StartServicesFlags{ 129 Enable: enable, 130 } 131 st.Unlock() 132 err = wrappers.StartServices(startupOrdered, nil, flags, meter, perfTimings) 133 st.Lock() 134 if err != nil { 135 return err 136 } 137 if enable { 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, startupOrdered, nil) 143 if err != nil { 144 return err 145 } 146 if changed { 147 snapstate.Set(st, sc.SnapName, &snapst) 148 } 149 } 150 case "restart": 151 st.Unlock() 152 err := wrappers.RestartServices(startupOrdered, nil, meter, perfTimings) 153 st.Lock() 154 return err 155 case "reload-or-restart": 156 flags := &wrappers.RestartServicesFlags{Reload: true} 157 st.Unlock() 158 err := wrappers.RestartServices(startupOrdered, flags, meter, perfTimings) 159 st.Lock() 160 return err 161 default: 162 return fmt.Errorf("unhandled service action: %q", sc.Action) 163 } 164 return nil 165 }