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