github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/configstate/configcore/handlers.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 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(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(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 = func(rootDir string, defaults map[string]interface{}, options *sysconfig.FilesystemOnlyApplyOptions) error { 101 return filesystemOnlyApply(rootDir, defaults, options) 102 } 103 } 104 105 // addFSOnlyHandler registers functions to validate and handle a subset of 106 // system config options that do not require to manipulate state but only 107 // the file system. 108 func addFSOnlyHandler(validate func(config.ConfGetter) error, handle func(config.ConfGetter, *fsOnlyContext) error, flags *flags) { 109 if handle == nil { 110 panic("cannot have nil handle with fsOnlyHandler") 111 } 112 h := &fsOnlyHandler{ 113 validateFunc: validate, 114 handleFunc: handle, 115 } 116 if flags != nil { 117 h.configFlags = *flags 118 } 119 handlers = append(handlers, h) 120 } 121 122 func (h *fsOnlyHandler) needsState() bool { 123 return false 124 } 125 126 func (h *fsOnlyHandler) flags() flags { 127 return h.configFlags 128 } 129 130 func (h *fsOnlyHandler) validate(cfg config.ConfGetter) error { 131 if h.validateFunc != nil { 132 return h.validateFunc(cfg) 133 } 134 return nil 135 } 136 137 func (h *fsOnlyHandler) handle(cfg config.ConfGetter, opts *fsOnlyContext) error { 138 // handleFunc is guaranteed to be non-nil by addFSOnlyHandler 139 return h.handleFunc(cfg, opts) 140 } 141 142 // filesystemOnlyApply applies filesystem modifications under rootDir, according to the 143 // cfg configuration. This is a subset of core config options that is important 144 // early during boot, before all the configuration is applied as part of 145 // normal execution of configure hook. 146 // Exposed for use via sysconfig.ApplyFilesystemOnlyDefaults. 147 func filesystemOnlyApply(rootDir string, values map[string]interface{}, opts *sysconfig.FilesystemOnlyApplyOptions) error { 148 if rootDir == "" { 149 return fmt.Errorf("internal error: root directory for configcore.FilesystemOnlyApply() not set") 150 } 151 152 if opts == nil { 153 opts = &sysconfig.FilesystemOnlyApplyOptions{} 154 } 155 156 cfg := plainCoreConfig(values) 157 158 ctx := &fsOnlyContext{ 159 RootDir: rootDir, 160 } 161 for _, h := range handlers { 162 if h.needsState() { 163 continue 164 } 165 if err := h.validate(cfg); err != nil { 166 return err 167 } 168 } 169 170 for _, h := range handlers { 171 if h.needsState() { 172 continue 173 } 174 if h.flags().coreOnlyConfig && opts != nil && opts.Classic { 175 continue 176 } 177 if err := h.handle(cfg, ctx); err != nil { 178 return err 179 } 180 } 181 return nil 182 }