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 }