github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/configstate/configcore/swap.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 "io/ioutil" 25 "os" 26 "path/filepath" 27 "time" 28 29 "github.com/mvo5/goconfigparser" 30 31 "github.com/snapcore/snapd/dirs" 32 "github.com/snapcore/snapd/gadget/quantity" 33 "github.com/snapcore/snapd/osutil" 34 "github.com/snapcore/snapd/overlord/configstate/config" 35 "github.com/snapcore/snapd/systemd" 36 ) 37 38 func init() { 39 supportedConfigurations["core.swap.size"] = true 40 } 41 42 func validateSystemSwapConfiguration(tr config.ConfGetter) error { 43 output, err := coreCfg(tr, "swap.size") 44 if err != nil { 45 return err 46 } 47 48 if output == "" { 49 return nil 50 } 51 52 // valid option for swap size is any integer multiple of a megabyte that is 53 // larger than or equal to 1 MB, or 0 for no swap enabled. 54 _, err = parseAndValidateSwapSize(output) 55 return err 56 } 57 58 func parseAndValidateSwapSize(szString string) (quantity.Size, error) { 59 sz, err := quantity.ParseSize(szString) 60 if err != nil { 61 return 0, err 62 } 63 64 switch { 65 case sz < 0: 66 // negative doesn't make sense 67 return 0, fmt.Errorf("swap size setting must be positive size in megabytes") 68 case sz > 0 && sz < quantity.SizeMiB: 69 // too small 70 return 0, fmt.Errorf("swap size setting must be at least one megabyte") 71 case sz%quantity.SizeMiB != 0: 72 // must be even number of megabytes 73 return 0, fmt.Errorf("swap size setting must be an integer number of megabytes") 74 } 75 return sz, nil 76 } 77 78 func handlesystemSwapConfiguration(tr config.ConfGetter, opts *fsOnlyContext) error { 79 var pristineSwapSize, newSwapSize string 80 if err := tr.GetPristine("core", "swap.size", &pristineSwapSize); err != nil && !config.IsNoOption(err) { 81 return err 82 } 83 if err := tr.Get("core", "swap.size", &newSwapSize); err != nil && !config.IsNoOption(err) { 84 return err 85 } 86 if pristineSwapSize == newSwapSize { 87 return nil 88 } 89 90 // if it's unset, then treat it as if the size is "0" to not use swap by 91 // default 92 if newSwapSize == "" { 93 newSwapSize = "0" 94 } 95 96 szBytes, err := parseAndValidateSwapSize(newSwapSize) 97 if err != nil { 98 return err 99 } 100 101 rootDir := dirs.GlobalRootDir 102 if opts != nil { 103 rootDir = opts.RootDir 104 } 105 106 swapConfigPath := filepath.Join(rootDir, "/etc/default/swapfile") 107 108 // TODO: also support writing/setting the location of the swap file setting? 109 110 // default location of the swapfile in case we can't determine the location 111 // from the config file 112 location := "/var/tmp/swapfile.swp" 113 if osutil.FileExists(swapConfigPath) { 114 // then get values from the config file 115 // read the existing file to get the location setting 116 cfg := goconfigparser.New() 117 cfg.AllowNoSectionHeader = true 118 119 if err := cfg.ReadFile(swapConfigPath); err != nil { 120 return err 121 } 122 123 location, err = cfg.Get("", "FILE") 124 if err != nil { 125 return err 126 } 127 } 128 129 // ensure the directory exists 130 if err := os.MkdirAll(filepath.Dir(swapConfigPath), 0755); err != nil { 131 return err 132 } 133 134 // the size of swap needs to be specified in Megabytes, while quantity.Size 135 // is a uint64 of bytes 136 fileContent := fmt.Sprintf("FILE=%s\nSIZE=%d\n", location, szBytes/quantity.SizeMiB) 137 138 // write the swap config file 139 if err := ioutil.WriteFile(swapConfigPath, []byte(fileContent), 0644); err != nil { 140 return err 141 } 142 143 if opts == nil { 144 // if we are not doing this filesystem only, then we need to also 145 // restart the swap service 146 sysd := systemd.NewUnderRoot(dirs.GlobalRootDir, systemd.SystemMode, &backlightSysdLogger{}) 147 148 // TODO: what's an appropriate amount of time to wait here? 149 if err := sysd.Restart("swapfile.service", 60*time.Second); err != nil { 150 return err 151 } 152 } 153 154 return nil 155 }