github.com/rigado/snapd@v2.42.5-go-mod+incompatible/wrappers/core18.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 wrappers 21 22 import ( 23 "bytes" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 "regexp" 29 "time" 30 31 "github.com/snapcore/snapd/dirs" 32 "github.com/snapcore/snapd/logger" 33 "github.com/snapcore/snapd/osutil" 34 "github.com/snapcore/snapd/release" 35 "github.com/snapcore/snapd/snap" 36 "github.com/snapcore/snapd/systemd" 37 ) 38 39 // catches units that run /usr/bin/snap (with args), or things in /usr/lib/snapd/ 40 var execStartRe = regexp.MustCompile(`(?m)^ExecStart=(/usr/bin/snap\s+.*|/usr/lib/snapd/.*)$`) 41 42 func writeSnapdToolingMountUnit(sysd systemd.Systemd, prefix string) error { 43 // Not using AddMountUnitFile() because we need 44 // "RequiredBy=snapd.service" 45 content := []byte(fmt.Sprintf(`[Unit] 46 Description=Make the snapd snap tooling available for the system 47 Before=snapd.service 48 49 [Mount] 50 What=%s/usr/lib/snapd 51 Where=/usr/lib/snapd 52 Type=none 53 Options=bind 54 55 [Install] 56 WantedBy=snapd.service 57 `, prefix)) 58 unit := "usr-lib-snapd.mount" 59 fullPath := filepath.Join(dirs.SnapServicesDir, unit) 60 61 err := osutil.EnsureFileState(fullPath, 62 &osutil.FileState{ 63 Content: content, 64 Mode: 0644, 65 }, 66 ) 67 if err == osutil.ErrSameState { 68 return nil 69 } 70 if err != nil { 71 return err 72 } 73 if err := sysd.DaemonReload(); err != nil { 74 return err 75 } 76 if err := sysd.Enable(unit); err != nil { 77 return err 78 } 79 80 if err := sysd.Restart(unit, 5*time.Second); err != nil { 81 return err 82 } 83 84 return nil 85 } 86 87 func writeSnapdServicesOnCore(s *snap.Info, inter interacter) error { 88 // we never write 89 if release.OnClassic { 90 return nil 91 } 92 sysd := systemd.New(dirs.GlobalRootDir, systemd.SystemMode, inter) 93 94 if err := writeSnapdToolingMountUnit(sysd, s.MountDir()); err != nil { 95 return err 96 } 97 98 serviceUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.service")) 99 if err != nil { 100 return err 101 } 102 socketUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.socket")) 103 if err != nil { 104 return err 105 } 106 timerUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.timer")) 107 if err != nil { 108 return err 109 } 110 units := append(socketUnits, serviceUnits...) 111 units = append(units, timerUnits...) 112 113 snapdUnits := make(map[string]*osutil.FileState, len(units)+1) 114 for _, unit := range units { 115 st, err := os.Stat(unit) 116 if err != nil { 117 return err 118 } 119 content, err := ioutil.ReadFile(unit) 120 if err != nil { 121 return err 122 } 123 content = execStartRe.ReplaceAll(content, []byte(fmt.Sprintf(`ExecStart=%s$1`, s.MountDir()))) 124 125 snapdUnits[filepath.Base(unit)] = &osutil.FileState{ 126 Content: content, 127 Mode: st.Mode(), 128 } 129 } 130 changed, removed, err := osutil.EnsureDirStateGlobs(dirs.SnapServicesDir, []string{"snapd.service", "snapd.socket", "snapd.*.service", "snapd.*.timer"}, snapdUnits) 131 if err != nil { 132 // TODO: uhhhh, what do we do in this case? 133 return err 134 } 135 if (len(changed) + len(removed)) == 0 { 136 // nothing to do 137 return nil 138 } 139 // stop all removed units first 140 for _, unit := range removed { 141 if err := sysd.Stop(unit, 5*time.Second); err != nil { 142 logger.Noticef("failed to stop %q: %v", unit, err) 143 } 144 if err := sysd.Disable(unit); err != nil { 145 logger.Noticef("failed to disable %q: %v", unit, err) 146 } 147 } 148 149 // daemon-reload so that we get the new services 150 if len(changed) > 0 { 151 if err := sysd.DaemonReload(); err != nil { 152 return err 153 } 154 } 155 156 // enable/start all the new services 157 for _, unit := range changed { 158 if err := sysd.Enable(unit); err != nil { 159 return err 160 } 161 } 162 163 for _, unit := range changed { 164 // Some units (like the snapd.system-shutdown.service) cannot 165 // be started. Others like "snapd.seeded.service" are started 166 // as dependencies of snapd.service. 167 if bytes.Contains(snapdUnits[unit].Content, []byte("X-Snapd-Snap: do-not-start")) { 168 continue 169 } 170 // Ensure to only restart if the unit was previously 171 // active. This ensures we DTRT on firstboot and do 172 // not stop e.g. snapd.socket because doing that 173 // would mean that the snapd.seeded.service is also 174 // stopped (independently of snapd.socket being 175 // active) which confuses the boot order (the unit 176 // exists before we are fully seeded). 177 isActive, err := sysd.IsActive(unit) 178 if err != nil { 179 return err 180 } 181 if isActive { 182 if err := sysd.Restart(unit, 5*time.Second); err != nil { 183 return err 184 } 185 } else { 186 if err := sysd.Start(unit); err != nil { 187 return err 188 } 189 } 190 } 191 // and finally start snapd.service (it will stop by itself and gets 192 // started by systemd then) 193 if err := sysd.Start("snapd.service"); err != nil { 194 return err 195 } 196 if err := sysd.StartNoBlock("snapd.seeded.service"); err != nil { 197 return err 198 } 199 // we cannot start snapd.autoimport in blocking mode because 200 // it has a "After=snapd.seeded.service" which means that on 201 // seeding a "systemctl start" that blocks would hang forever 202 // and we deadlock. 203 if err := sysd.StartNoBlock("snapd.autoimport.service"); err != nil { 204 return err 205 } 206 207 // Handle the user services 208 if err := writeSnapdUserServicesOnCore(s, inter); err != nil { 209 return err 210 } 211 212 return nil 213 } 214 215 func writeSnapdUserServicesOnCore(s *snap.Info, inter interacter) error { 216 // Ensure /etc/systemd/user exists 217 if err := os.MkdirAll(dirs.SnapUserServicesDir, 0755); err != nil { 218 return err 219 } 220 221 sysd := systemd.New(dirs.GlobalRootDir, systemd.GlobalUserMode, inter) 222 223 serviceUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/lib/systemd/user/*.service")) 224 if err != nil { 225 return err 226 } 227 socketUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/lib/systemd/user/*.socket")) 228 if err != nil { 229 return err 230 } 231 units := append(serviceUnits, socketUnits...) 232 233 snapdUnits := make(map[string]*osutil.FileState, len(units)+1) 234 for _, unit := range units { 235 st, err := os.Stat(unit) 236 if err != nil { 237 return err 238 } 239 content, err := ioutil.ReadFile(unit) 240 if err != nil { 241 return err 242 } 243 content = execStartRe.ReplaceAll(content, []byte(fmt.Sprintf(`ExecStart=%s$1`, s.MountDir()))) 244 245 snapdUnits[filepath.Base(unit)] = &osutil.FileState{ 246 Content: content, 247 Mode: st.Mode(), 248 } 249 } 250 changed, removed, err := osutil.EnsureDirStateGlobs(dirs.SnapUserServicesDir, []string{"snapd.*.service", "snapd.*.socket"}, snapdUnits) 251 if err != nil { 252 // TODO: uhhhh, what do we do in this case? 253 return err 254 } 255 if (len(changed) + len(removed)) == 0 { 256 // nothing to do 257 return nil 258 } 259 // disable all removed units first 260 for _, unit := range removed { 261 if err := sysd.Disable(unit); err != nil { 262 logger.Noticef("failed to disable %q: %v", unit, err) 263 } 264 } 265 266 // enable/start all the new services 267 for _, unit := range changed { 268 if err := sysd.Enable(unit); err != nil { 269 return err 270 } 271 } 272 273 return nil 274 }