github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/configstate/configcore/handlers.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 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 25 "github.com/snapcore/snapd/overlord/configstate/config" 26 "github.com/snapcore/snapd/sysconfig" 27 ) 28 29 type configHandler interface { 30 validate(config.ConfGetter) error 31 handle(sysconfig.Device, config.ConfGetter, *fsOnlyContext) error 32 needsState() bool 33 flags() flags 34 } 35 36 // flags carries extra flags that influence how the handler is called. 37 type flags struct { 38 // coreOnlyConfig tells Run/FilesystemOnlyApply to apply the config on core 39 // systems only. 40 coreOnlyConfig bool 41 // validatedOnlyStateConfig tells that the config requires only validation, 42 // its options are applied dynamically elsewhere. 43 validatedOnlyStateConfig bool 44 // earlyConfigFilter expresses whether the handler supports 45 // any early configuration options (that can and must be 46 // set before even seeding is finished). 47 // If set the function should copy such options from values 48 // to early. 49 earlyConfigFilter filterFunc 50 } 51 52 type fsOnlyHandler struct { 53 validateFunc func(config.ConfGetter) error 54 handleFunc func(sysconfig.Device, config.ConfGetter, *fsOnlyContext) error 55 configFlags flags 56 } 57 58 var handlers []configHandler 59 60 func init() { 61 // Most of these handlers are no-op on classic. 62 // TODO: consider allowing some of these on classic too? 63 // consider erroring on core-only options on classic? 64 65 coreOnly := &flags{coreOnlyConfig: true} 66 67 // watchdog.{runtime-timeout,shutdown-timeout} 68 addFSOnlyHandler(validateWatchdogOptions, handleWatchdogConfiguration, coreOnly) 69 70 // Export experimental.* flags to a place easily accessible from snapd helpers. 71 addFSOnlyHandler(validateExperimentalSettings, doExportExperimentalFlags, &flags{earlyConfigFilter: earlyExperimentalSettingsFilter}) 72 73 // network.disable-ipv6 74 addFSOnlyHandler(validateNetworkSettings, handleNetworkConfiguration, coreOnly) 75 76 // service.*.disable 77 addFSOnlyHandler(nil, handleServiceDisableConfiguration, coreOnly) 78 79 // system.power-key-action 80 addFSOnlyHandler(nil, handlePowerButtonConfiguration, coreOnly) 81 82 // pi-config.* 83 addFSOnlyHandler(nil, handlePiConfiguration, coreOnly) 84 85 // system.disable-backlight-service 86 addFSOnlyHandler(validateBacklightServiceSettings, handleBacklightServiceConfiguration, coreOnly) 87 88 // swap.size 89 addFSOnlyHandler(validateSystemSwapConfiguration, handlesystemSwapConfiguration, coreOnly) 90 91 // system.kernel.printk.console-loglevel 92 addFSOnlyHandler(validateSysctlOptions, handleSysctlConfiguration, coreOnly) 93 94 // journal.persistent 95 addFSOnlyHandler(validateJournalSettings, handleJournalConfiguration, coreOnly) 96 97 // system.timezone 98 addFSOnlyHandler(validateTimezoneSettings, handleTimezoneConfiguration, coreOnly) 99 100 sysconfig.ApplyFilesystemOnlyDefaultsImpl = filesystemOnlyApply 101 } 102 103 // addFSOnlyHandler registers functions to validate and handle a subset of 104 // system config options that do not require to manipulate state but only 105 // the file system. 106 func addFSOnlyHandler(validate func(config.ConfGetter) error, handle func(sysconfig.Device, config.ConfGetter, *fsOnlyContext) error, flags *flags) { 107 if handle == nil { 108 panic("cannot have nil handle with fsOnlyHandler") 109 } 110 h := &fsOnlyHandler{ 111 validateFunc: validate, 112 handleFunc: handle, 113 } 114 if flags != nil { 115 h.configFlags = *flags 116 } 117 handlers = append(handlers, h) 118 } 119 120 func (h *fsOnlyHandler) needsState() bool { 121 return false 122 } 123 124 func (h *fsOnlyHandler) flags() flags { 125 return h.configFlags 126 } 127 128 func (h *fsOnlyHandler) validate(cfg config.ConfGetter) error { 129 if h.validateFunc != nil { 130 return h.validateFunc(cfg) 131 } 132 return nil 133 } 134 135 func (h *fsOnlyHandler) handle(dev sysconfig.Device, cfg config.ConfGetter, opts *fsOnlyContext) error { 136 // handleFunc is guaranteed to be non-nil by addFSOnlyHandler 137 return h.handleFunc(dev, cfg, opts) 138 } 139 140 // filesystemOnlyApply applies filesystem modifications under rootDir, according to the 141 // cfg configuration. This is a subset of core config options that is important 142 // early during boot, before all the configuration is applied as part of 143 // normal execution of configure hook. 144 // Exposed for use via sysconfig.ApplyFilesystemOnlyDefaults. 145 func filesystemOnlyApply(dev sysconfig.Device, rootDir string, values map[string]interface{}) error { 146 if rootDir == "" { 147 return fmt.Errorf("internal error: root directory for configcore.FilesystemOnlyApply() not set") 148 } 149 150 cfg := plainCoreConfig(values) 151 152 ctx := &fsOnlyContext{ 153 RootDir: rootDir, 154 } 155 for _, h := range handlers { 156 if h.needsState() { 157 continue 158 } 159 if err := h.validate(cfg); err != nil { 160 return err 161 } 162 } 163 164 for _, h := range handlers { 165 if h.needsState() { 166 continue 167 } 168 if h.flags().coreOnlyConfig && dev.Classic() { 169 continue 170 } 171 if err := h.handle(dev, cfg, ctx); err != nil { 172 return err 173 } 174 } 175 return nil 176 }