github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/servicestate/helpers.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 "sort" 25 26 "github.com/snapcore/snapd/overlord/snapstate" 27 "github.com/snapcore/snapd/snap" 28 ) 29 30 // updateSnapstateServices uses ServicesEnabledByHooks and ServicesDisabledByHooks in 31 // snapstate and the provided enabled or disabled list to update the state of services in snapstate. 32 // It is meant for doServiceControl to help track enabling and disabling of services. 33 func updateSnapstateServices(snapst *snapstate.SnapState, enable, disable []*snap.AppInfo) (bool, error) { 34 if len(enable) > 0 && len(disable) > 0 { 35 // We do one op at a time for given service-control task; we could in 36 // theory support both at the same time here, but service-control 37 // ops are run sequentially so we always either enable or disable at 38 // any given time. Not having to worry about that simplifies the 39 // problem of ordering of enable vs disable. 40 return false, fmt.Errorf("internal error: cannot handle enabled and disabled services at the same time") 41 } 42 43 // populate helper lookups of already enabled/disabled services from 44 // snapst. 45 alreadyEnabled := map[string]bool{} 46 alreadyDisabled := map[string]bool{} 47 for _, serviceName := range snapst.ServicesEnabledByHooks { 48 alreadyEnabled[serviceName] = true 49 } 50 for _, serviceName := range snapst.ServicesDisabledByHooks { 51 alreadyDisabled[serviceName] = true 52 } 53 54 toggleServices := func(services []*snap.AppInfo, fromState map[string]bool, toState map[string]bool) (changed bool) { 55 // migrate given services from one map to another, if they do 56 // not exist in the target map 57 for _, service := range services { 58 if !toState[service.Name] { 59 toState[service.Name] = true 60 if fromState[service.Name] { 61 delete(fromState, service.Name) 62 } 63 changed = true 64 } 65 } 66 return changed 67 } 68 69 // we are not disabling and enabling the services at the same time as 70 // checked in the function entry, only one path is possible 71 fromState, toState := alreadyDisabled, alreadyEnabled 72 which := enable 73 if len(disable) > 0 { 74 fromState, toState = alreadyEnabled, alreadyDisabled 75 which = disable 76 } 77 if changed := toggleServices(which, fromState, toState); !changed { 78 // nothing changed 79 return false, nil 80 } 81 // reset and recreate the state 82 snapst.ServicesEnabledByHooks = nil 83 snapst.ServicesDisabledByHooks = nil 84 if len(alreadyEnabled) != 0 { 85 snapst.ServicesEnabledByHooks = make([]string, 0, len(alreadyEnabled)) 86 for srv := range alreadyEnabled { 87 snapst.ServicesEnabledByHooks = append(snapst.ServicesEnabledByHooks, srv) 88 } 89 sort.Strings(snapst.ServicesEnabledByHooks) 90 } 91 if len(alreadyDisabled) != 0 { 92 snapst.ServicesDisabledByHooks = make([]string, 0, len(alreadyDisabled)) 93 for srv := range alreadyDisabled { 94 snapst.ServicesDisabledByHooks = append(snapst.ServicesDisabledByHooks, srv) 95 } 96 sort.Strings(snapst.ServicesDisabledByHooks) 97 } 98 return true, nil 99 }