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  }