Beispiel #1
0
// CanInstall checks whether the Snap passes a series of tests required for installation
func canInstall(s *snap.Info, snapf snap.File, curInfo *snap.Info, allowGadget bool, inter interacter) error {
	// verify we have a valid architecture
	if !arch.IsSupportedArchitecture(s.Architectures) {
		return &ErrArchitectureNotSupported{s.Architectures}
	}

	if s.Type == snap.TypeGadget {
		if !allowGadget {
			if currentGadget, err := getGadget(); err == nil {
				if currentGadget.Name() != s.Name() {
					return ErrGadgetPackageInstall
				}
			} else {
				// there should always be a gadget package now
				return ErrGadgetPackageInstall
			}
		}
	}

	if err := checkLicenseAgreement(s, snapf, curInfo, inter); err != nil {
		return err
	}

	return nil
}
Beispiel #2
0
// Download downloads the given snap and returns its filename.
// The file is saved in temporary storage, and should be removed
// after use to prevent the disk from running out of space.
func (s *SnapUbuntuStoreRepository) Download(remoteSnap *snap.Info, pbar progress.Meter, auther Authenticator) (path string, err error) {
	w, err := ioutil.TempFile("", remoteSnap.Name())
	if err != nil {
		return "", err
	}
	defer func() {
		if cerr := w.Close(); cerr != nil && err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(w.Name())
			path = ""
		}
	}()

	url := remoteSnap.AnonDownloadURL
	if url == "" || auther != nil {
		url = remoteSnap.DownloadURL
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", err
	}
	s.applyUbuntuStoreHeaders(req, "", auther)

	if err := download(remoteSnap.Name(), w, req, pbar); err != nil {
		return "", err
	}

	return w.Name(), w.Sync()
}
Beispiel #3
0
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)
	}

}
Beispiel #4
0
// updateSnap "updates" an existing snap from YAML.
func (s *backendSuite) updateSnap(c *C, oldSnapInfo *snap.Info, devMode bool, snapYaml string) *snap.Info {
	newSnapInfo, err := snap.InfoFromSnapYaml([]byte(snapYaml))
	c.Assert(err, IsNil)
	c.Assert(newSnapInfo.Name(), Equals, oldSnapInfo.Name())
	s.removePlugsSlots(c, oldSnapInfo)
	s.addPlugsSlots(c, newSnapInfo)
	err = s.backend.Setup(newSnapInfo, devMode, s.repo)
	c.Assert(err, IsNil)
	return newSnapInfo
}
Beispiel #5
0
func (s *backendSuite) removePlugsSlots(c *C, snapInfo *snap.Info) {
	for _, plug := range s.repo.Plugs(snapInfo.Name()) {
		err := s.repo.RemovePlug(plug.Snap.Name(), plug.Name)
		c.Assert(err, IsNil)
	}
	for _, slot := range s.repo.Slots(snapInfo.Name()) {
		err := s.repo.RemoveSlot(slot.Snap.Name(), slot.Name)
		c.Assert(err, IsNil)
	}
}
Beispiel #6
0
// AddSnap adds plugs and slots declared by the given snap to the repository.
//
// This function can be used to implement snap install or, when used along with
// RemoveSnap, snap upgrade.
//
// AddSnap doesn't change existing plugs/slots. The caller is responsible for
// ensuring that the snap is not present in the repository in any way prior to
// calling this function. If this constraint is violated then no changes are
// made and an error is returned.
//
// Each added plug/slot is validated according to the corresponding interface.
// Unknown interfaces and plugs/slots that don't validate are not added.
// Information about those failures are returned to the caller.
func (r *Repository) AddSnap(snapInfo *snap.Info) error {
	r.m.Lock()
	defer r.m.Unlock()

	snapName := snapInfo.Name()

	if r.plugs[snapName] != nil || r.slots[snapName] != nil {
		return fmt.Errorf("cannot register interfaces for snap %q more than once", snapName)
	}

	bad := BadInterfacesError{
		snap:   snapName,
		issues: make(map[string]string),
	}

	for plugName, plugInfo := range snapInfo.Plugs {
		iface, ok := r.ifaces[plugInfo.Interface]
		if !ok {
			bad.issues[plugName] = "unknown interface"
			continue
		}
		plug := &Plug{PlugInfo: plugInfo}
		if err := iface.SanitizePlug(plug); err != nil {
			bad.issues[plugName] = err.Error()
			continue
		}
		if r.plugs[snapName] == nil {
			r.plugs[snapName] = make(map[string]*Plug)
		}
		r.plugs[snapName][plugName] = plug
	}

	for slotName, slotInfo := range snapInfo.Slots {
		iface, ok := r.ifaces[slotInfo.Interface]
		if !ok {
			bad.issues[slotName] = "unknown interface"
			continue
		}
		slot := &Slot{SlotInfo: slotInfo}
		if err := iface.SanitizeSlot(slot); err != nil {
			bad.issues[slotName] = err.Error()
			continue
		}
		if r.slots[snapName] == nil {
			r.slots[snapName] = make(map[string]*Slot)
		}
		r.slots[snapName][slotName] = slot
	}

	if len(bad.issues) > 0 {
		return &bad
	}
	return nil
}
Beispiel #7
0
func removePackageDesktopFiles(s *snap.Info) error {
	glob := filepath.Join(dirs.SnapDesktopFilesDir, s.Name()+"_*.desktop")
	activeDesktopFiles, err := filepath.Glob(glob)
	if err != nil {
		return fmt.Errorf("cannot get desktop files for %v: %s", glob, err)
	}
	for _, f := range activeDesktopFiles {
		os.Remove(f)
	}

	return nil
}
Beispiel #8
0
func installRemote(mStore *store.SnapUbuntuStoreRepository, remoteSnap *snap.Info, flags InstallFlags, meter progress.Meter) (string, error) {
	downloadedSnap, err := mStore.Download(remoteSnap, meter, nil)
	if err != nil {
		return "", fmt.Errorf("cannot download %s: %s", remoteSnap.Name(), err)
	}
	defer os.Remove(downloadedSnap)

	localSnap, err := (&Overlord{}).InstallWithSideInfo(downloadedSnap, &remoteSnap.SideInfo, flags, meter)
	if err != nil {
		return "", err
	}

	return localSnap.Name(), nil
}
Beispiel #9
0
func doUpdate(mStore *store.SnapUbuntuStoreRepository, rsnap *snap.Info, flags InstallFlags, meter progress.Meter) error {
	_, err := installRemote(mStore, rsnap, flags, meter)
	if err == ErrSideLoaded {
		logger.Noticef("Skipping sideloaded package: %s", rsnap.Name())
		return nil
	} else if err != nil {
		return err
	}

	if err := GarbageCollect(rsnap.Name(), flags, meter); err != nil {
		return err
	}

	return nil
}
Beispiel #10
0
// updateSnap "updates" an existing snap from YAML.
func (s *backendSuite) updateSnap(c *C, oldSnapInfo *snap.Info, devMode bool, snapYaml string, revision int) *snap.Info {
	newSnapInfo, err := snap.InfoFromSnapYaml([]byte(snapYaml))
	c.Assert(err, IsNil)
	newSnapInfo.Revision = revision
	// this won't come from snap.yaml
	newSnapInfo.Developer = "acme"
	c.Assert(newSnapInfo.Name(), Equals, oldSnapInfo.Name())
	err = s.repo.RemoveSnap(oldSnapInfo.Name())
	c.Assert(err, IsNil)
	err = s.repo.AddSnap(newSnapInfo)
	c.Assert(err, IsNil)
	err = s.backend.Setup(newSnapInfo, devMode, s.repo)
	c.Assert(err, IsNil)
	return newSnapInfo
}
Beispiel #11
0
// Setup creates udev rules specific to a given snap.
// If any of the rules are changed or removed then udev database is reloaded.
//
// Since udev has no concept of a complain mode, devMode is ignored.
//
// If the method fails it should be re-tried (with a sensible strategy) by the caller.
func (b *Backend) Setup(snapInfo *snap.Info, devMode bool, repo *interfaces.Repository) error {
	snapName := snapInfo.Name()
	snippets, err := repo.SecuritySnippetsForSnap(snapInfo.Name(), interfaces.SecurityUDev)
	if err != nil {
		return fmt.Errorf("cannot obtain udev security snippets for snap %q: %s", snapName, err)
	}
	content, err := b.combineSnippets(snapInfo, snippets)
	if err != nil {
		return fmt.Errorf("cannot obtain expected udev rules for snap %q: %s", snapName, err)
	}
	glob := fmt.Sprintf("70-%s.rules", interfaces.SecurityTagGlob(snapName))
	dir := dirs.SnapUdevRulesDir
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("cannot create directory for udev rules %q: %s", dir, err)
	}
	return ensureDirState(dir, glob, content, snapName)
}
Beispiel #12
0
func CanRemove(s *snap.Info, active bool) bool {
	// Gadget snaps should not be removed as they are a key
	// building block for Gadgets. Prunning non active ones
	// is acceptible.
	if s.Type == snap.TypeGadget && active {
		return false
	}

	// You never want to remove an active kernel or OS
	if (s.Type == snap.TypeKernel || s.Type == snap.TypeOS) && active {
		return false
	}

	if IsBuiltInSoftware(s.Name()) && active {
		return false
	}
	return true
}
Beispiel #13
0
func setupSnapSecurity(task *state.Task, snapInfo *snap.Info, repo *interfaces.Repository) error {
	st := task.State()
	var snapState snapstate.SnapState
	snapName := snapInfo.Name()
	if err := snapstate.Get(st, snapName, &snapState); err != nil {
		task.Errorf("cannot get state of snap %q: %s", snapName, err)
		return err
	}
	for _, backend := range securityBackends {
		st.Unlock()
		err := backend.Setup(snapInfo, snapState.DevMode(), repo)
		st.Lock()
		if err != nil {
			task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), snapName, err)
			return err
		}
	}
	return nil
}
Beispiel #14
0
// SaveManifest saves the manifest at the designated location for the snap containing information not in the snap.yaml.
func SaveManifest(rsnap *snap.Info) error {
	if rsnap.Revision == 0 {
		return fmt.Errorf("internal error: should not be storring manifests for sideloaded snaps")
	}

	// XXX: we store OfficialName though it may not be the blessed one later
	content, err := yaml.Marshal(&rsnap.SideInfo)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(dirs.SnapMetaDir, 0755); err != nil {
		return err
	}

	p := manifestPath(rsnap.Name(), rsnap.Revision)
	// don't worry about previous contents
	return osutil.AtomicWriteFile(p, content, 0644, 0)
}
Beispiel #15
0
func addSquashfsMount(s *snap.Info, inhibitHooks bool, inter interacter) error {
	squashfsPath := stripGlobalRootDir(s.MountFile())
	whereDir := stripGlobalRootDir(s.MountDir())

	sysd := systemd.New(dirs.GlobalRootDir, inter)
	mountUnitName, err := sysd.WriteMountUnitFile(s.Name(), squashfsPath, whereDir)
	if err != nil {
		return err
	}

	// we always enable the mount unit even in inhibit hooks
	if err := sysd.Enable(mountUnitName); err != nil {
		return err
	}

	if !inhibitHooks {
		return sysd.Start(mountUnitName)
	}

	return nil
}
Beispiel #16
0
// Setup creates dbus configuration files specific to a given snap.
//
// DBus has no concept of a complain mode so devMode is not supported
func (b *Backend) Setup(snapInfo *snap.Info, devMode bool, repo *interfaces.Repository) error {
	snapName := snapInfo.Name()
	// Get the snippets that apply to this snap
	snippets, err := repo.SecuritySnippetsForSnap(snapInfo.Name(), interfaces.SecurityDBus)
	if err != nil {
		return fmt.Errorf("cannot obtain DBus security snippets for snap %q: %s", snapName, err)
	}
	// Get the files that this snap should have
	content, err := b.combineSnippets(snapInfo, snippets)
	if err != nil {
		return fmt.Errorf("cannot obtain expected DBus configuration files for snap %q: %s", snapName, err)
	}
	glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName))
	dir := dirs.SnapBusPolicyDir
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("cannot create directory for DBus configuration files %q: %s", dir, err)
	}
	_, _, err = osutil.EnsureDirState(dir, glob, content)
	if err != nil {
		return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err)
	}
	return nil
}
Beispiel #17
0
// Setup creates and loads apparmor profiles specific to a given snap.
// The snap can be in developer mode to make security violations non-fatal to
// the offending application process.
//
// This method should be called after changing plug, slots, connections between
// them or application present in the snap.
func (b *Backend) Setup(snapInfo *snap.Info, devMode bool, repo *interfaces.Repository) error {
	snapName := snapInfo.Name()
	// Get the snippets that apply to this snap
	snippets, err := repo.SecuritySnippetsForSnap(snapName, interfaces.SecurityAppArmor)
	if err != nil {
		return fmt.Errorf("cannot obtain security snippets for snap %q: %s", snapName, err)
	}
	// Get the files that this snap should have
	content, err := b.combineSnippets(snapInfo, devMode, snippets)
	if err != nil {
		return fmt.Errorf("cannot obtain expected security files for snap %q: %s", snapName, err)
	}
	glob := interfaces.SecurityTagGlob(snapInfo.Name())
	dir := dirs.SnapAppArmorDir
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("cannot create directory for apparmor profiles %q: %s", dir, err)
	}
	_, removed, errEnsure := osutil.EnsureDirState(dir, glob, content)
	// NOTE: load all profiles instead of just the changed profiles.  We're
	// relying on apparmor cache to make this efficient. This gives us
	// certainty that each call to Setup ends up with working profiles.
	all := make([]string, 0, len(content))
	for name := range content {
		all = append(all, name)
	}
	sort.Strings(all)
	errReload := reloadProfiles(all)
	errUnload := unloadProfiles(removed)
	if errEnsure != nil {
		return fmt.Errorf("cannot synchronize security files for snap %q: %s", snapName, errEnsure)
	}
	if errReload != nil {
		return errReload
	}
	return errUnload
}
Beispiel #18
0
// 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)
}
Beispiel #19
0
func addPackageDesktopFiles(s *snap.Info) error {
	if err := os.MkdirAll(dirs.SnapDesktopFilesDir, 0755); err != nil {
		return err
	}

	baseDir := s.MountDir()

	desktopFiles, err := filepath.Glob(filepath.Join(baseDir, "meta", "gui", "*.desktop"))
	if err != nil {
		return fmt.Errorf("cannot get desktop files for %v: %s", baseDir, err)
	}

	for _, df := range desktopFiles {
		content, err := ioutil.ReadFile(df)
		if err != nil {
			return err
		}

		realBaseDir := stripGlobalRootDir(baseDir)
		content = sanitizeDesktopFile(s, realBaseDir, content)

		installedDesktopFileName := filepath.Join(dirs.SnapDesktopFilesDir, fmt.Sprintf("%s_%s", s.Name(), filepath.Base(df)))
		if err := osutil.AtomicWriteFile(installedDesktopFileName, []byte(content), 0755, 0); err != nil {
			return err
		}
	}

	return nil
}
Beispiel #20
0
func cleanupGadgetHardwareUdevRules(s *snap.Info) error {
	oldFiles, err := filepath.Glob(filepath.Join(dirs.SnapUdevRulesDir, fmt.Sprintf("80-snappy_%s_*.rules", s.Name())))
	if err != nil {
		return err
	}

	for _, f := range oldFiles {
		os.Remove(f)
	}

	// cleanup the additional files
	for _, h := range s.Legacy.Gadget.Hardware.Assign {
		jsonAdditionalPath := filepath.Join(dirs.SnapAppArmorDir, fmt.Sprintf("%s.json.additional", h.PartID))
		err = os.Remove(jsonAdditionalPath)
		if err != nil && !os.IsNotExist(err) {
			logger.Noticef("Failed to remove %q: %v", jsonAdditionalPath, err)
		}
	}

	return nil
}
Beispiel #21
0
// removeSnap "removes" an "installed" snap.
func (s *backendSuite) removeSnap(c *C, snapInfo *snap.Info) {
	err := s.backend.Remove(snapInfo.Name())
	c.Assert(err, IsNil)
	s.removePlugsSlots(c, snapInfo)
}
Beispiel #22
0
func writeGadgetHardwareUdevRules(s *snap.Info) error {
	os.MkdirAll(dirs.SnapUdevRulesDir, 0755)

	// cleanup
	if err := cleanupGadgetHardwareUdevRules(s); err != nil {
		return err
	}
	// write new files
	for _, h := range s.Legacy.Gadget.Hardware.Assign {
		rulesContent, err := generateUdevRuleContent(&h)
		if err != nil {
			return err
		}
		outfile := filepath.Join(dirs.SnapUdevRulesDir, fmt.Sprintf("80-snappy_%s_%s.rules", s.Name(), h.PartID))
		if err := osutil.AtomicWriteFile(outfile, []byte(rulesContent), 0644, 0); err != nil {
			return err
		}
	}

	return nil
}
Beispiel #23
0
// Map a localSnap information plus the given active flag to a
// map[string]interface{}, augmenting it with the given (purportedly remote)
// snap.
//
// It is a programming error (->panic) to call mapSnap with both arguments
// nil.
func mapSnap(localSnap *snap.Info, active bool, remoteSnap *snap.Info) map[string]interface{} {
	var version, icon, name, developer, _type, description, summary string
	var revision int

	rollback := -1
	update := -1

	if localSnap == nil && remoteSnap == nil {
		panic("no localSnaps & remoteSnap is nil -- how did i even get here")
	}

	status := "available"
	installedSize := int64(-1)
	downloadSize := int64(-1)
	var prices map[string]float64

	if remoteSnap != nil {
		prices = remoteSnap.Prices
	}

	if localSnap != nil {
		if active {
			status = "active"
		} else {
			status = "installed"
		}
	}

	var ref *snap.Info
	if localSnap != nil {
		ref = localSnap
	} else {
		ref = remoteSnap
	}

	name = ref.Name()
	developer = ref.Developer
	version = ref.Version
	revision = ref.Revision
	_type = string(ref.Type)

	if localSnap != nil {
		icon = snapIcon(localSnap)
		summary = localSnap.Summary()
		description = localSnap.Description()
		installedSize = localSnap.Size
	}

	if remoteSnap != nil {
		if icon == "" {
			icon = remoteSnap.IconURL
		}
		if description == "" {
			description = remoteSnap.Description()
		}
		if summary == "" {
			summary = remoteSnap.Summary()
		}

		downloadSize = remoteSnap.Size
	}

	if localSnap != nil && active {
		if remoteSnap != nil && revision != remoteSnap.Revision {
			update = remoteSnap.Revision
		}

		// WARNING this'll only get the right* rollback if
		// only two things can be installed
		//
		// *) not the actual right rollback because we aren't
		// marking things failed etc etc etc)
		//
		//if len(localSnaps) == 2 {
		//	rollback = localSnaps[1^idx].Revision()
		//}
	}

	result := map[string]interface{}{
		"icon":           icon,
		"name":           name,
		"developer":      developer,
		"status":         status,
		"type":           _type,
		"vendor":         "",
		"revision":       revision,
		"version":        version,
		"description":    description,
		"summary":        summary,
		"installed-size": installedSize,
		"download-size":  downloadSize,
	}

	if len(prices) > 0 {
		result["prices"] = prices
	}

	if localSnap != nil {
		channel := localSnap.Channel
		if channel != "" {
			result["channel"] = channel
		}

		result["install-date"] = snapDate(localSnap)
	}

	if rollback > -1 {
		result["rollback-available"] = rollback
	}

	if update > -1 {
		result["update-available"] = update
	}

	return result
}
Beispiel #24
0
// removeSnap "removes" an "installed" snap.
func (s *backendSuite) removeSnap(c *C, snapInfo *snap.Info) {
	err := s.backend.Remove(snapInfo.Name())
	c.Assert(err, IsNil)
	err = s.repo.RemoveSnap(snapInfo.Name())
	c.Assert(err, IsNil)
}
Beispiel #25
0
// FullName of a snap.Info is Name.Developer
func FullName(p *snap.Info) string {
	return p.Name() + "." + p.Developer
}
Beispiel #26
0
// checkLicenseAgreement returns nil if it's ok to proceed with installing the
// package, as deduced from the license agreement (which might involve asking
// the user), or an error that explains the reason why installation should not
// proceed.
func checkLicenseAgreement(s *snap.Info, snapf snap.File, cur *snap.Info, ag agreer) error {
	if s.LicenseAgreement != "explicit" {
		return nil
	}

	if ag == nil {
		return ErrLicenseNotAccepted
	}

	license, err := snapf.MetaMember("license.txt")
	if err != nil || len(license) == 0 {
		return ErrLicenseNotProvided
	}

	// don't ask for the license if
	// * the previous version also asked for license confirmation, and
	// * the license version is the same
	if cur != nil && (cur.LicenseAgreement == "explicit") && cur.LicenseVersion == s.LicenseVersion {
		return nil
	}

	msg := fmt.Sprintf("%s requires that you accept the following license before continuing", s.Name())
	if !ag.Agreed(msg, string(license)) {
		return ErrLicenseNotAccepted
	}

	return nil
}
Beispiel #27
0
// BareName of a snap.Info is just its Name
func BareName(p *snap.Info) string {
	return p.Name()
}