github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/configstate/configcore/services.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 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 configcore
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"time"
    28  
    29  	"github.com/snapcore/snapd/boot"
    30  	"github.com/snapcore/snapd/dirs"
    31  	"github.com/snapcore/snapd/osutil"
    32  	"github.com/snapcore/snapd/overlord/configstate/config"
    33  	"github.com/snapcore/snapd/systemd"
    34  )
    35  
    36  var services = []struct{ configName, systemdName string }{
    37  	{"ssh", "ssh.service"},
    38  	{"rsyslog", "rsyslog.service"},
    39  	{"console-conf", "console-conf@*"},
    40  	{"systemd-resolved", "systemd-resolved.service"},
    41  }
    42  
    43  func init() {
    44  	for _, service := range services {
    45  		s := fmt.Sprintf("core.service.%s.disable", service.configName)
    46  		supportedConfigurations[s] = true
    47  	}
    48  }
    49  
    50  type sysdLogger struct{}
    51  
    52  func (l *sysdLogger) Notify(status string) {
    53  	fmt.Fprintf(Stderr, "sysd: %s\n", status)
    54  }
    55  
    56  // switchDisableSSHService handles the special case of disabling/enabling ssh
    57  // service on core devices.
    58  func switchDisableSSHService(sysd systemd.Systemd, serviceName string, disabled bool, opts *fsOnlyContext) error {
    59  	rootDir := dirs.GlobalRootDir
    60  	if opts != nil {
    61  		rootDir = opts.RootDir
    62  		if err := os.MkdirAll(filepath.Join(rootDir, "/etc/ssh"), 0755); err != nil {
    63  			return err
    64  		}
    65  	}
    66  
    67  	sshCanary := filepath.Join(rootDir, "/etc/ssh/sshd_not_to_be_run")
    68  
    69  	if disabled {
    70  		if err := ioutil.WriteFile(sshCanary, []byte("SSH has been disabled by snapd system configuration\n"), 0644); err != nil {
    71  			return err
    72  		}
    73  		if opts == nil {
    74  			return sysd.Stop(serviceName, 5*time.Minute)
    75  		}
    76  	} else {
    77  		err := os.Remove(sshCanary)
    78  		if err != nil && !os.IsNotExist(err) {
    79  			return err
    80  		}
    81  		// Unmask both sshd.service and ssh.service and ignore the
    82  		// errors, if any. This undoes the damage done by earlier
    83  		// versions of snapd.
    84  		sysd.Unmask("sshd.service")
    85  		sysd.Unmask("ssh.service")
    86  		if opts == nil {
    87  			return sysd.Start(serviceName)
    88  		}
    89  	}
    90  	return nil
    91  }
    92  
    93  // switchDisableConsoleConfService handles the special case of
    94  // disabling/enabling console-conf on core devices.
    95  //
    96  // Note that this option can only be changed via gadget defaults.
    97  // It is not possible to tune this at runtime
    98  func switchDisableConsoleConfService(sysd systemd.Systemd, serviceName string, disabled bool, opts *fsOnlyContext) error {
    99  	consoleConfDisabled := "/var/lib/console-conf/complete"
   100  
   101  	// at runtime we can not change this setting
   102  	if opts == nil {
   103  
   104  		// Special case: during install mode the
   105  		// gadget-defaults will also be set as part of the
   106  		// system install change. However during install mode
   107  		// console-conf has no "complete" file, it just never runs
   108  		// in install mode. So we need to detect this and do nothing
   109  		// or the install mode will fail.
   110  		// XXX: instead of this hack we should look at the config
   111  		//      defaults and compare with the setting and exit if
   112  		//      they are the same but that requires some more changes.
   113  		mode, _, _ := boot.ModeAndRecoverySystemFromKernelCommandLine()
   114  		if mode == boot.ModeInstall {
   115  			return nil
   116  		}
   117  
   118  		hasDisabledFile := osutil.FileExists(filepath.Join(dirs.GlobalRootDir, consoleConfDisabled))
   119  		if disabled != hasDisabledFile {
   120  			return fmt.Errorf("cannot toggle console-conf at runtime, but only initially via gadget defaults")
   121  		}
   122  		return nil
   123  	}
   124  
   125  	if !disabled {
   126  		return nil
   127  	}
   128  
   129  	// disable console-conf at the gadget-defaults time
   130  	consoleConfDisabled = filepath.Join(opts.RootDir, consoleConfDisabled)
   131  	if err := os.MkdirAll(filepath.Dir(consoleConfDisabled), 0755); err != nil {
   132  		return err
   133  	}
   134  	if err := ioutil.WriteFile(consoleConfDisabled, []byte("console-conf has been disabled by the snapd system configuration\n"), 0644); err != nil {
   135  		return err
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  // switchDisableTypicalService switches a service in/out of disabled state
   142  // where "true" means disabled and "false" means enabled.
   143  func switchDisableService(serviceName string, disabled bool, opts *fsOnlyContext) error {
   144  	var sysd systemd.Systemd
   145  	if opts != nil {
   146  		sysd = systemd.NewEmulationMode(opts.RootDir)
   147  	} else {
   148  		sysd = systemd.New(systemd.SystemMode, &sysdLogger{})
   149  	}
   150  
   151  	// some services are special
   152  	switch serviceName {
   153  	case "ssh.service":
   154  		return switchDisableSSHService(sysd, serviceName, disabled, opts)
   155  	case "console-conf@*":
   156  		return switchDisableConsoleConfService(sysd, serviceName, disabled, opts)
   157  	}
   158  
   159  	if opts == nil {
   160  		// ignore the service if not installed
   161  		status, err := sysd.Status(serviceName)
   162  		if err != nil {
   163  			return err
   164  		}
   165  		if len(status) != 1 {
   166  			return fmt.Errorf("internal error: expected status of service %s, got %v", serviceName, status)
   167  		}
   168  		if !status[0].Installed {
   169  			// ignore
   170  			return nil
   171  		}
   172  	}
   173  
   174  	if disabled {
   175  		if opts == nil {
   176  			if err := sysd.Disable(serviceName); err != nil {
   177  				return err
   178  			}
   179  		}
   180  		if err := sysd.Mask(serviceName); err != nil {
   181  			return err
   182  		}
   183  		if opts == nil {
   184  			return sysd.Stop(serviceName, 5*time.Minute)
   185  		}
   186  	} else {
   187  		if err := sysd.Unmask(serviceName); err != nil {
   188  			return err
   189  		}
   190  		if opts == nil {
   191  			if err := sysd.Enable(serviceName); err != nil {
   192  				return err
   193  			}
   194  		}
   195  		if opts == nil {
   196  			return sysd.Start(serviceName)
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  // services that can be disabled
   203  func handleServiceDisableConfiguration(tr config.ConfGetter, opts *fsOnlyContext) error {
   204  	for _, service := range services {
   205  		optionName := fmt.Sprintf("service.%s.disable", service.configName)
   206  		outputStr, err := coreCfg(tr, optionName)
   207  		if err != nil {
   208  			return err
   209  		}
   210  		if outputStr != "" {
   211  			var disabled bool
   212  			switch outputStr {
   213  			case "true":
   214  				disabled = true
   215  			case "false":
   216  				disabled = false
   217  			default:
   218  				return fmt.Errorf("option %q has invalid value %q", optionName, outputStr)
   219  			}
   220  
   221  			if err := switchDisableService(service.systemdName, disabled, opts); err != nil {
   222  				return err
   223  			}
   224  		}
   225  	}
   226  
   227  	return nil
   228  }