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 }