func UndoSetupSnap(s snap.PlaceInfo, meter progress.Meter) { // SetupSnap did it not made far enough if !osutil.FileExists(s.MountDir()) { return } if err := RemoveSnapFiles(s, meter); err != nil { logger.Noticef("cannot remove snap files: %s", err) } mountDir := s.MountDir() snapPath := s.MountFile() // remove install dir and the snap blob itself for _, path := range []string{ mountDir, snapPath, } { if err := os.RemoveAll(path); err != nil { logger.Noticef("cannot remove snap package at %v: %s", mountDir, err) } } // FIXME: do we need to undo installGadgetHardwareUdevRules via // cleanupGadgetHardwareUdevRules ? it will go away // and can only be used during install right now }
func removeCurrentSymlink(info snap.PlaceInfo, inter interacter) error { var err1, err2 error // the snap "current" symlink currentActiveSymlink := filepath.Join(info.MountDir(), "..", "current") err1 = os.Remove(currentActiveSymlink) if err1 != nil && !os.IsNotExist(err1) { logger.Noticef("Failed to remove %q: %v", currentActiveSymlink, err1) } else { err1 = nil } // the data "current" symlink currentDataSymlink := filepath.Join(filepath.Dir(info.DataDir()), "current") err2 = os.Remove(currentDataSymlink) if err2 != nil && !os.IsNotExist(err2) { logger.Noticef("Failed to remove %q: %v", currentDataSymlink, err2) } else { err2 = nil } if err1 != nil && err2 != nil { return fmt.Errorf("cannot remove snap current symlink: %v and %v", err1, err2) } else if err1 != nil { return fmt.Errorf("cannot remove snap current symlink: %v", err1) } else if err2 != nil { return fmt.Errorf("cannot remove snap current symlink: %v", err2) } return nil }
// RequestSecurityPolicyUpdate checks whether changes to the given policies and // templates impacts the snap, and updates the policy if needed func (s *SnapPart) RequestSecurityPolicyUpdate(policies, templates map[string]bool) error { var foundError error for _, svc := range s.ServiceYamls() { if svc.NeedsAppArmorUpdate(policies, templates) { err := svc.generatePolicyForServiceBinary(s.m, svc.Name, s.basedir) if err != nil { logger.Noticef("Failed to regenerate policy for %s: %v", svc.Name, err) foundError = err } } } for _, bin := range s.Binaries() { if bin.NeedsAppArmorUpdate(policies, templates) { err := bin.generatePolicyForServiceBinary(s.m, bin.Name, s.basedir) if err != nil { logger.Noticef("Failed to regenerate policy for %s: %v", bin.Name, err) foundError = err } } } // FIXME: if there are multiple errors only the last one // will be preserved if foundError != nil { return foundError } return nil }
// RequestSecurityPolicyUpdate checks whether changes to the given policies and // templates impacts the snap, and updates the policy if needed func (s *SnapPart) RequestSecurityPolicyUpdate(policies, templates map[string]bool) error { var foundError error for name, app := range s.Apps() { skill, err := findSkillForApp(s.m, app) if err != nil { logger.Noticef("Failed to find skill for %s: %v", name, err) foundError = err continue } if skill == nil { continue } if skill.NeedsAppArmorUpdate(policies, templates) { err := skill.generatePolicyForServiceBinary(s.m, name, s.basedir) if err != nil { logger.Noticef("Failed to regenerate policy for %s: %v", name, err) foundError = err } } } // FIXME: if there are multiple errors only the last one // will be preserved if foundError != nil { return foundError } return nil }
func makePartFromSystemImageConfigFile(p partition.Interface, channelIniPath string, isActive bool) (part Part, err error) { cfg := goconfigparser.New() f, err := os.Open(channelIniPath) if err != nil { return nil, err } defer f.Close() err = cfg.Read(f) if err != nil { logger.Noticef("Can not parse config %q: %v", channelIniPath, err) return nil, err } st, err := os.Stat(channelIniPath) if err != nil { logger.Noticef("Can not stat %q: %v", channelIniPath, err) return nil, err } currentBuildNumber, err := cfg.Get("service", "build_number") versionDetails, err := cfg.Get("service", "version_detail") channelName, err := cfg.Get("service", "channel") return &SystemImagePart{ isActive: isActive, isInstalled: true, version: currentBuildNumber, versionDetails: versionDetails, channelName: channelName, lastUpdate: st.ModTime(), partition: p}, err }
// VersionCompare compare two version strings and // Returns: // -1 if a is smaller than b // 0 if a equals b // +1 if a is bigger than b func VersionCompare(va, vb string) (res int) { if !VersionIsValid(va) { logger.Noticef("Invalid version %q, using '0' instead. Expect wrong results", va) va = "0" } if !VersionIsValid(vb) { logger.Noticef("Invalid version %q, using '0' instead. Expect wrong results", vb) vb = "0" } if !strings.Contains(va, "-") { va += "-0" } if !strings.Contains(vb, "-") { vb += "-0" } // the main version number (before the "-") mainA := strings.Split(va, "-")[0] mainB := strings.Split(vb, "-")[0] res = compareSubversion(mainA, mainB) if res != 0 { return res } // the subversion revision behind the "-" revA := strings.Split(va, "-")[1] revB := strings.Split(vb, "-")[1] return compareSubversion(revA, revB) }
// XXX: would really like not to expose this but used in daemon tests atm func UpdateCurrentSymlink(info *snap.Info, inter interacter) error { mountDir := info.MountDir() currentActiveSymlink := filepath.Join(mountDir, "..", "current") if err := os.Remove(currentActiveSymlink); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove %q: %v", currentActiveSymlink, err) } dataDir := info.DataDir() dbase := filepath.Dir(dataDir) currentDataSymlink := filepath.Join(dbase, "current") if err := os.Remove(currentDataSymlink); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove %q: %v", currentDataSymlink, err) } // symlink is relative to parent dir if err := os.Symlink(filepath.Base(mountDir), currentActiveSymlink); err != nil { return err } if err := os.MkdirAll(info.DataDir(), 0755); err != nil { return err } // FIXME: create {Os,Kernel}Snap type instead of adding special // cases here if err := setNextBoot(info); err != nil { return err } return os.Symlink(filepath.Base(dataDir), currentDataSymlink) }
func (sd *SecurityDefinitions) generatePolicyForServiceBinary(m *packageYaml, name string, baseDir string) error { p, err := sd.generatePolicyForServiceBinaryResult(m, name, baseDir) if err != nil { return err } os.MkdirAll(filepath.Dir(p.scFn), 0755) err = helpers.AtomicWriteFile(p.scFn, []byte(p.scPolicy), 0644, 0) if err != nil { logger.Noticef("Failed to write seccomp policy for %s: %v", name, err) return err } os.MkdirAll(filepath.Dir(p.aaFn), 0755) err = helpers.AtomicWriteFile(p.aaFn, []byte(p.aaPolicy), 0644, 0) if err != nil { logger.Noticef("Failed to write AppArmor policy for %s: %v", name, err) return err } out, err := loadAppArmorPolicy(p.aaFn) if err != nil { logger.Noticef("Failed to load AppArmor policy for %s: %v\n:%s", name, err, out) return err } return nil }
func (sd *SecurityDefinitions) warnDeprecatedKeys() { if sd.SecurityOverride != nil && sd.SecurityOverride.DeprecatedAppArmor != nil { logger.Noticef("The security-override.apparmor key is no longer supported, please use use security-override directly") } if sd.SecurityOverride != nil && sd.SecurityOverride.DeprecatedSeccomp != nil { logger.Noticef("The security-override.seccomp key is no longer supported, please use use security-override directly") } }
func (s *SnapPart) deactivate(inhibitHooks bool, inter interacter) error { currentSymlink := filepath.Join(s.basedir, "..", "current") // sanity check currentActiveDir, err := filepath.EvalSymlinks(currentSymlink) if err != nil { if os.IsNotExist(err) { return ErrSnapNotActive } return err } if s.basedir != currentActiveDir { return ErrSnapNotActive } // remove generated services, binaries, clickHooks, security policy if err := s.m.removePackageBinaries(s.basedir); err != nil { return err } if err := s.m.removePackageServices(s.basedir, inter); err != nil { return err } if err := s.m.removeSecurityPolicy(s.basedir); err != nil { return err } if s.Type() == pkg.TypeFramework { if err := policy.Remove(s.Name(), s.basedir, dirs.GlobalRootDir); err != nil { return err } } if err := removeClickHooks(s.m, s.origin, inhibitHooks); err != nil { return err } // and finally the current symlink if err := os.Remove(currentSymlink); err != nil { logger.Noticef("Failed to remove %q: %v", currentSymlink, err) } currentDataSymlink := filepath.Join(dirs.SnapDataDir, QualifiedName(s), "current") if err := os.Remove(currentDataSymlink); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove %q: %v", currentDataSymlink, err) } return nil }
func (s *SnapPart) remove(inter interacter) (err error) { if err := s.deactivate(false, inter); err != nil && err != ErrSnapNotActive { return err } // ensure mount unit stops if err := s.m.removeSquashfsMount(s.basedir, inter); err != nil { return err } err = os.RemoveAll(s.basedir) if err != nil { return err } // best effort(?) os.Remove(filepath.Dir(s.basedir)) // remove the snap if err := os.RemoveAll(squashfs.BlobPath(s.basedir)); err != nil { return err } // remove the kernel assets (if any) if s.m.Type == snap.TypeKernel { if err := removeKernelAssets(s, inter); err != nil { logger.Noticef("removing kernel assets failed with %s", err) } } return nil }
// RegenerateAllPolicy will re-generate all policy that needs re-generating func RegenerateAllPolicy(force bool) error { installed, err := NewMetaLocalRepository().Installed() if err != nil { return err } for _, p := range installed { part, ok := p.(*SnapPart) if !ok { continue } basedir := part.basedir yFn := filepath.Join(basedir, "meta", "package.yaml") // FIXME: use ErrPolicyNeedsRegenerating here to check if // re-generation is needed if err := CompareGeneratePolicyFromFile(yFn); err == nil { continue } // re-generate! logger.Noticef("re-generating security policy for %s", yFn) if err := GeneratePolicyFromFile(yFn, force); err != nil { return err } } return nil }
func (s *SnapPart) remove(inter interacter) (err error) { // TODO[JRL]: check the logic here. I'm not sure “remove // everything if active, and the click hooks if not” makes // sense. E.g. are we removing fmk bins on fmk upgrade? Etc. if err := removeClickHooks(s.m, s.origin, false); err != nil { return err } if err := s.deactivate(false, inter); err != nil && err != ErrSnapNotActive { return err } err = os.RemoveAll(s.basedir) if err != nil { return err } // best effort(?) os.Remove(filepath.Dir(s.basedir)) // don't fail if icon can't be removed if helpers.FileExists(iconPath(s)) { if err := os.Remove(iconPath(s)); err != nil { logger.Noticef("Failed to remove store icon %s: %s", iconPath(s), err) } } return nil }
// legacyIntegration sets up the Integration property of packageYaml from its other attributes func (m *packageYaml) legacyIntegration(hasConfig bool) { if m.Integration != nil { // TODO: append "Overriding user-provided values." to the end of the blurb. logger.Noticef(`The "integration" key is deprecated, and all uses of "integration" should be rewritten; see https://developer.ubuntu.com/en/snappy/guides/package-metadata/ (the "binaries" and "services" sections are probably especially relevant)."`) } else { // TODO: do this always, not just when Integration is not set m.Integration = make(map[string]clickAppHook) } for _, v := range m.Binaries { hookName := filepath.Base(v.Name) if _, ok := m.Integration[hookName]; !ok { m.Integration[hookName] = clickAppHook{} } // legacy click hook m.Integration[hookName]["bin-path"] = v.Exec } for _, v := range m.ServiceYamls { hookName := filepath.Base(v.Name) if _, ok := m.Integration[hookName]; !ok { m.Integration[hookName] = clickAppHook{} } } if hasConfig { m.Integration["snappy-config"] = clickAppHook{"apparmor": "meta/snappy-config.apparmor"} } }
// IsSideLoaded determines if the system was installed using a // custom enablement part. func IsSideLoaded(bootloaderDir string) bool { file := filepath.Join(bootloaderDir, InstallYamlFile) if !helpers.FileExists(file) { // the system may have been sideloaded, but we have no // way of knowing :-( return false } InstallYaml, err := parseInstallYaml(file) if err != nil { logger.Noticef("Kernel sideload cannot be read, assuming sideload: %s", err) // file isn't parseable, so let's assume system is sideloaded return true } if InstallYaml.InstallOptions.DevicePart != "" { // system was created with something like: // // "ubuntu-device-flash [...] --device-part=unofficial-assets.tar.xz ..." // return true } return false }
func removePackageServices(m *snapYaml, baseDir string, inter interacter) error { sysd := systemd.New(dirs.GlobalRootDir, inter) for _, app := range m.Apps { if app.Daemon == "" { continue } serviceName := filepath.Base(generateServiceFileName(m, app)) if err := sysd.Disable(serviceName); err != nil { return err } if err := sysd.Stop(serviceName, time.Duration(app.StopTimeout)); err != nil { if !systemd.IsTimeout(err) { return err } inter.Notify(fmt.Sprintf("%s refused to stop, killing.", serviceName)) // ignore errors for kill; nothing we'd do differently at this point sysd.Kill(serviceName, "TERM") time.Sleep(killWait) sysd.Kill(serviceName, "KILL") } if err := os.Remove(generateServiceFileName(m, app)); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove service file for %q: %v", serviceName, err) } if err := os.Remove(generateSocketFileName(m, app)); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove socket file for %q: %v", serviceName, err) } // Also remove DBus system policy file if err := os.Remove(generateBusPolicyFileName(m, app)); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove bus policy file for service %q: %v", serviceName, err) } } // only reload if we actually had services // FIXME: filter for services if len(m.Apps) > 0 { if err := sysd.DaemonReload(); err != nil { return err } } return nil }
func UndoCopyData(newInfo *snap.Info, flags InstallFlags, meter progress.Meter) { // XXX we were copying data, assume InhibitHooks was false if err := RemoveSnapData(newInfo); err != nil { logger.Noticef("When cleaning up data for %s %s: %v", newInfo.Name(), newInfo.Version, err) } }
func (m *packageYaml) removeSquashfsMount(baseDir string, inter interacter) error { sysd := systemd.New(dirs.GlobalRootDir, inter) unit := systemd.MountUnitPath(stripGlobalRootDir(baseDir), "mount") if helpers.FileExists(unit) { // we ignore errors, nothing should stop removals if err := sysd.Disable(filepath.Base(unit)); err != nil { logger.Noticef("Failed to disable %q: %s, but continuing anyway.", unit, err) } if err := sysd.Stop(filepath.Base(unit), time.Duration(1*time.Second)); err != nil { logger.Noticef("Failed to stop %q: %s, but continuing anyway.", unit, err) } if err := os.Remove(unit); err != nil { return err } } return nil }
// enableInstalledSnaps activates the installed preinstalled snaps // on the first boot func enableInstalledSnaps() error { all, err := (&Overlord{}).Installed() if err != nil { return nil } activator := getActivator() pb := progress.MakeProgressBar() for _, sn := range all { logger.Noticef("Acitvating %s", FullName(sn.Info())) if err := activator.SetActive(sn, true, pb); err != nil { // we don't want this to fail for now logger.Noticef("failed to activate %s: %s", FullName(sn.Info()), err) } } return nil }
func removePackageServices(s *snap.Info, inter interacter) error { sysd := systemd.New(dirs.GlobalRootDir, inter) nservices := 0 for _, app := range s.Apps { if app.Daemon == "" { continue } nservices++ serviceName := filepath.Base(app.ServiceFile()) if err := sysd.Disable(serviceName); err != nil { return err } if err := sysd.Stop(serviceName, serviceStopTimeout(app)); err != nil { if !systemd.IsTimeout(err) { return err } inter.Notify(fmt.Sprintf("%s refused to stop, killing.", serviceName)) // ignore errors for kill; nothing we'd do differently at this point sysd.Kill(serviceName, "TERM") time.Sleep(killWait) sysd.Kill(serviceName, "KILL") } if err := os.Remove(app.ServiceFile()); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove service file for %q: %v", serviceName, err) } if err := os.Remove(app.ServiceSocketFile()); err != nil && !os.IsNotExist(err) { logger.Noticef("Failed to remove socket file for %q: %v", serviceName, err) } } // only reload if we actually had services if nservices > 0 { if err := sysd.DaemonReload(); err != nil { return err } } return nil }
// Uninstall removes the given local snap from the system. // // It returns an error on failure func (o *Overlord) Uninstall(s *SnapPart, meter progress.Meter) error { // Gadget snaps should not be removed as they are a key // building block for Gadgets. Prunning non active ones // is acceptible. if s.m.Type == snap.TypeGadget && s.IsActive() { return ErrPackageNotRemovable } // You never want to remove an active kernel or OS if (s.m.Type == snap.TypeKernel || s.m.Type == snap.TypeOS) && s.IsActive() { return ErrPackageNotRemovable } if IsBuiltInSoftware(s.Name()) && s.IsActive() { return ErrPackageNotRemovable } deps, err := s.DependentNames() if err != nil { return err } if len(deps) != 0 { return ErrFrameworkInUse(deps) } if err := s.deactivate(false, meter); err != nil && err != ErrSnapNotActive { return err } // ensure mount unit stops if err := removeSquashfsMount(s.m, s.basedir, meter); err != nil { return err } err = os.RemoveAll(s.basedir) if err != nil { return err } // best effort(?) os.Remove(filepath.Dir(s.basedir)) // remove the snap if err := os.RemoveAll(squashfs.BlobPath(s.basedir)); err != nil { return err } // remove the kernel assets (if any) if s.m.Type == snap.TypeKernel { if err := removeKernelAssets(s, meter); err != nil { logger.Noticef("removing kernel assets failed with %s", err) } } return RemoveAllHWAccess(QualifiedName(s)) }
func parseInstallYamlData(yamlData []byte) (*InstallYaml, error) { var i InstallYaml err := yaml.Unmarshal(yamlData, &i) if err != nil { logger.Noticef("Cannot parse install.yaml %q", yamlData) return nil, err } return &i, nil }
// RemoveGeneratedWrappers removes the generated services, binaries, desktop // wrappers func RemoveGeneratedWrappers(s *snap.Info, inter interacter) error { err1 := removePackageBinaries(s) if err1 != nil { logger.Noticef("Failed to remove binaries for %q: %v", s.Name(), err1) } err2 := removePackageServices(s, inter) if err2 != nil { logger.Noticef("Failed to remove services for %q: %v", s.Name(), err2) } err3 := removePackageDesktopFiles(s) if err3 != nil { logger.Noticef("Failed to remove desktop files for %q: %v", s.Name(), err3) } return firstErr(err1, err2, err3) }
func generatePolicy(m *snapYaml, baseDir string) error { var foundError error // generate default security config for snappy-config if hasConfig(baseDir) { if err := snappyConfig.generatePolicyForServiceBinary(m, "snappy-config", baseDir); err != nil { foundError = err logger.Noticef("Failed to obtain APP_ID for %s: %v", "snappy-config", err) } } for _, app := range m.Apps { skill, err := findSkillForApp(m, app) if err != nil { return err } // if no skill is specified, use the defaultSecurityPolicy if skill == nil { if err = defaultSecurityPolicy.generatePolicyForServiceBinary(m, app.Name, baseDir); err != nil { logger.Noticef("Failed to generate policy for app %s: %v", app.Name, err) } continue } err = skill.generatePolicyForServiceBinary(m, app.Name, baseDir) if err != nil { foundError = err logger.Noticef("Failed to generate policy for service %s: %v", app.Name, err) continue } } // FIXME: if there are multiple errors only the last one // will be preserved if foundError != nil { return foundError } return nil }
// enableSystemSnaps activates the installed kernel/os/gadget snaps // on the first boot func enableSystemSnaps() error { repo := NewMetaLocalRepository() all, err := repo.All() if err != nil { return nil } pb := progress.MakeProgressBar() for _, part := range all { switch part.Type() { case snap.TypeGadget, snap.TypeKernel, snap.TypeOS: logger.Noticef("Acitvating %s", FullName(part)) if err := part.SetActive(true, pb); err != nil { // we don't want this to fail for now logger.Noticef("failed to acitvate %s: %s", FullName(part), err) } } } return nil }
func (r *resp) setError(format string, v ...interface{}) Response { m := errorResult{} newr := &resp{ Type: ResponseTypeError, Result: &m, Status: r.Status, } logger.Noticef(format, v...) m.Message = fmt.Sprintf(format, v...) return newr }
func (m *InterfaceManager) addSnaps() error { snaps, err := snapstate.ActiveInfos(m.state) if err != nil { return err } for _, snapInfo := range snaps { snap.AddImplicitSlots(snapInfo) if err := m.repo.AddSnap(snapInfo); err != nil { logger.Noticef("%s", err) } } return nil }
func doUpdate(part Part, flags InstallFlags, meter progress.Meter) error { if _, err := part.Install(meter, flags); err == ErrSideLoaded { logger.Noticef("Skipping sideloaded package: %s", part.Name()) return nil } else if err != nil { return err } if err := GarbageCollect(part.Name(), flags, meter); err != nil { return err } return nil }
func parsePackageYamlData(yamlData []byte, hasConfig bool) (*packageYaml, error) { var m packageYaml err := yaml.Unmarshal(yamlData, &m) if err != nil { return nil, &ErrInvalidYaml{File: "package.yaml", Err: err, Yaml: yamlData} } if err := validatePackageYamlData("package.yaml", yamlData, &m); err != nil { return nil, err } if m.Architectures == nil { if m.DeprecatedArchitecture == nil { m.Architectures = []string{"all"} } else { m.Architectures = m.DeprecatedArchitecture } } if m.DeprecatedFramework != "" { logger.Noticef(`Use of deprecated "framework" key in yaml`) if len(m.Frameworks) != 0 { return nil, ErrInvalidFrameworkSpecInYaml } m.Frameworks = commasplitter(m.DeprecatedFramework, -1) m.DeprecatedFramework = "" } // For backward compatiblity we allow that there is no "exec:" line // in the binary definition and that its derived from the name. // // Generate the right exec line here for i := range m.Binaries { if m.Binaries[i].Exec == "" { m.Binaries[i].Exec = m.Binaries[i].Name m.Binaries[i].Name = filepath.Base(m.Binaries[i].Exec) } } for i := range m.ServiceYamls { if m.ServiceYamls[i].StopTimeout == 0 { m.ServiceYamls[i].StopTimeout = DefaultTimeout } } m.legacyIntegration(hasConfig) return &m, nil }
func generatePolicy(m *packageYaml, baseDir string) error { var foundError error // generate default security config for snappy-config if hasConfig(baseDir) { if err := snappyConfig.generatePolicyForServiceBinary(m, "snappy-config", baseDir); err != nil { foundError = err logger.Noticef("Failed to obtain APP_ID for %s: %v", "snappy-config", err) } } for _, service := range m.ServiceYamls { err := service.generatePolicyForServiceBinary(m, service.Name, baseDir) if err != nil { foundError = err logger.Noticef("Failed to generate policy for service %s: %v", service.Name, err) continue } } for _, binary := range m.Binaries { err := binary.generatePolicyForServiceBinary(m, binary.Name, baseDir) if err != nil { foundError = err logger.Noticef("Failed to generate policy for binary %s: %v", binary.Name, err) continue } } // FIXME: if there are multiple errors only the last one // will be preserved if foundError != nil { return foundError } return nil }