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