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