github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/backend/link.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2016 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 backend 21 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 27 "github.com/snapcore/snapd/asserts" 28 "github.com/snapcore/snapd/boot" 29 "github.com/snapcore/snapd/logger" 30 "github.com/snapcore/snapd/progress" 31 "github.com/snapcore/snapd/release" 32 "github.com/snapcore/snapd/snap" 33 "github.com/snapcore/snapd/timings" 34 "github.com/snapcore/snapd/wrappers" 35 ) 36 37 func updateCurrentSymlinks(info *snap.Info) (e error) { 38 mountDir := info.MountDir() 39 40 currentActiveSymlink := filepath.Join(mountDir, "..", "current") 41 if err := os.Remove(currentActiveSymlink); err != nil && !os.IsNotExist(err) { 42 logger.Noticef("Cannot remove %q: %v", currentActiveSymlink, err) 43 } 44 45 dataDir := info.DataDir() 46 currentDataSymlink := filepath.Join(dataDir, "..", "current") 47 if err := os.Remove(currentDataSymlink); err != nil && !os.IsNotExist(err) { 48 logger.Noticef("Cannot remove %q: %v", currentDataSymlink, err) 49 } 50 51 if err := os.MkdirAll(dataDir, 0755); err != nil { 52 return err 53 } 54 cleanup := []string{dataDir, ""}[:1] // cleanup has cap(2) 55 defer func() { 56 if e == nil { 57 return 58 } 59 for _, d := range cleanup { 60 if err := os.Remove(d); err != nil { 61 logger.Noticef("Cannot clean up %q: %v", d, err) 62 } 63 } 64 }() 65 66 if err := os.Symlink(filepath.Base(dataDir), currentDataSymlink); err != nil { 67 return err 68 } 69 cleanup = append(cleanup, currentDataSymlink) 70 71 return os.Symlink(filepath.Base(mountDir), currentActiveSymlink) 72 } 73 74 func hasFontConfigCache(info *snap.Info) bool { 75 if info.GetType() == snap.TypeOS || info.GetType() == snap.TypeSnapd { 76 return true 77 } 78 return false 79 } 80 81 // LinkSnap makes the snap available by generating wrappers and setting the current symlinks. 82 func (b Backend) LinkSnap(info *snap.Info, model *asserts.Model, tm timings.Measurer) (e error) { 83 if info.Revision.Unset() { 84 return fmt.Errorf("cannot link snap %q with unset revision", info.InstanceName()) 85 } 86 87 var err error 88 timings.Run(tm, "generate-wrappers", fmt.Sprintf("generate wrappers for snap %s", info.InstanceName()), func(timings.Measurer) { 89 err = generateWrappers(info) 90 }) 91 if err != nil { 92 return err 93 } 94 defer func() { 95 if e == nil { 96 return 97 } 98 timings.Run(tm, "remove-wrappers", fmt.Sprintf("remove wrappers of snap %s", info.InstanceName()), func(timings.Measurer) { 99 removeGeneratedWrappers(info, progress.Null) 100 }) 101 }() 102 103 // fontconfig is only relevant on classic and is carried by 'core' or 104 // 'snapd' snaps 105 // for non-core snaps, fontconfig cache needs to be updated before the 106 // snap applications are runnable 107 if release.OnClassic && !hasFontConfigCache(info) { 108 timings.Run(tm, "update-fc-cache", "update font config caches", func(timings.Measurer) { 109 // XXX: does this need cleaning up? (afaict no) 110 if err := updateFontconfigCaches(); err != nil { 111 logger.Noticef("cannot update fontconfig cache: %v", err) 112 } 113 }) 114 } 115 116 if err := boot.Participant(info, info.GetType(), model, release.OnClassic).SetNextBoot(); err != nil { 117 return err 118 } 119 120 if err := updateCurrentSymlinks(info); err != nil { 121 return err 122 } 123 // if anything below here could return error, you need to 124 // somehow clean up whatever updateCurrentSymlinks did 125 126 // for core snap, fontconfig cache can be updated after the snap has 127 // been made available 128 if release.OnClassic && hasFontConfigCache(info) { 129 timings.Run(tm, "update-fc-cache", "update font config caches", func(timings.Measurer) { 130 if err := updateFontconfigCaches(); err != nil { 131 logger.Noticef("cannot update fontconfig cache: %v", err) 132 } 133 }) 134 } 135 return nil 136 } 137 138 func (b Backend) StartServices(apps []*snap.AppInfo, meter progress.Meter, tm timings.Measurer) error { 139 return wrappers.StartServices(apps, meter, tm) 140 } 141 142 func (b Backend) StopServices(apps []*snap.AppInfo, reason snap.ServiceStopReason, meter progress.Meter, tm timings.Measurer) error { 143 return wrappers.StopServices(apps, reason, meter, tm) 144 } 145 146 func generateWrappers(s *snap.Info) (err error) { 147 var cleanupFuncs []func(*snap.Info) error 148 defer func() { 149 if err != nil { 150 for _, cleanup := range cleanupFuncs { 151 cleanup(s) 152 } 153 } 154 }() 155 156 // add the CLI apps from the snap.yaml 157 if err = wrappers.AddSnapBinaries(s); err != nil { 158 return err 159 } 160 cleanupFuncs = append(cleanupFuncs, wrappers.RemoveSnapBinaries) 161 162 // add the daemons from the snap.yaml 163 if err = wrappers.AddSnapServices(s, progress.Null); err != nil { 164 return err 165 } 166 cleanupFuncs = append(cleanupFuncs, func(s *snap.Info) error { 167 return wrappers.RemoveSnapServices(s, progress.Null) 168 }) 169 170 // add the desktop files 171 if err = wrappers.AddSnapDesktopFiles(s); err != nil { 172 return err 173 } 174 cleanupFuncs = append(cleanupFuncs, wrappers.RemoveSnapDesktopFiles) 175 176 // add the desktop icons 177 if err = wrappers.AddSnapIcons(s); err != nil { 178 return err 179 } 180 cleanupFuncs = append(cleanupFuncs, wrappers.RemoveSnapIcons) 181 182 return nil 183 } 184 185 func removeGeneratedWrappers(s *snap.Info, meter progress.Meter) error { 186 err1 := wrappers.RemoveSnapBinaries(s) 187 if err1 != nil { 188 logger.Noticef("Cannot remove binaries for %q: %v", s.InstanceName(), err1) 189 } 190 191 err2 := wrappers.RemoveSnapServices(s, meter) 192 if err2 != nil { 193 logger.Noticef("Cannot remove services for %q: %v", s.InstanceName(), err2) 194 } 195 196 err3 := wrappers.RemoveSnapDesktopFiles(s) 197 if err3 != nil { 198 logger.Noticef("Cannot remove desktop files for %q: %v", s.InstanceName(), err3) 199 } 200 201 err4 := wrappers.RemoveSnapIcons(s) 202 if err4 != nil { 203 logger.Noticef("Cannot remove desktop icons for %q: %v", s.InstanceName(), err4) 204 } 205 206 return firstErr(err1, err2, err3, err4) 207 } 208 209 // UnlinkSnap makes the snap unavailable to the system removing wrappers and symlinks. 210 func (b Backend) UnlinkSnap(info *snap.Info, meter progress.Meter) error { 211 // remove generated services, binaries etc 212 err1 := removeGeneratedWrappers(info, meter) 213 214 // and finally remove current symlinks 215 err2 := removeCurrentSymlinks(info) 216 217 // FIXME: aggregate errors instead 218 return firstErr(err1, err2) 219 } 220 221 func removeCurrentSymlinks(info snap.PlaceInfo) error { 222 var err1, err2 error 223 224 // the snap "current" symlink 225 currentActiveSymlink := filepath.Join(info.MountDir(), "..", "current") 226 err1 = os.Remove(currentActiveSymlink) 227 if err1 != nil && !os.IsNotExist(err1) { 228 logger.Noticef("Cannot remove %q: %v", currentActiveSymlink, err1) 229 } else { 230 err1 = nil 231 } 232 233 // the data "current" symlink 234 currentDataSymlink := filepath.Join(info.DataDir(), "..", "current") 235 err2 = os.Remove(currentDataSymlink) 236 if err2 != nil && !os.IsNotExist(err2) { 237 logger.Noticef("Cannot remove %q: %v", currentDataSymlink, err2) 238 } else { 239 err2 = nil 240 } 241 242 if err1 != nil && err2 != nil { 243 return fmt.Errorf("cannot remove snap current symlink: %v and %v", err1, err2) 244 } else if err1 != nil { 245 return fmt.Errorf("cannot remove snap current symlink: %v", err1) 246 } else if err2 != nil { 247 return fmt.Errorf("cannot remove snap current symlink: %v", err2) 248 } 249 250 return nil 251 }