示例#1
0
func generateSnapServicesFile(app *snap.AppInfo, baseDir string) (string, error) {
	if err := snap.ValidateApp(app); err != nil {
		return "", err
	}

	desc := fmt.Sprintf("service %s for snap %s - autogenerated DO NO EDIT", app.Name, app.Snap.Name())

	socketFileName := ""
	if app.Socket {
		socketFileName = filepath.Base(app.ServiceSocketFile())
	}

	return systemd.New(dirs.GlobalRootDir, nil).GenServiceFile(
		&systemd.ServiceDescription{
			SnapName:       app.Snap.Name(),
			AppName:        app.Name,
			Version:        app.Snap.Version,
			Revision:       app.Snap.Revision,
			Description:    desc,
			SnapPath:       baseDir,
			Start:          app.Command,
			Stop:           app.Stop,
			PostStop:       app.PostStop,
			StopTimeout:    serviceStopTimeout(app),
			AaProfile:      app.SecurityTag(),
			BusName:        app.BusName,
			Type:           app.Daemon,
			UdevAppName:    app.SecurityTag(),
			Socket:         app.Socket,
			SocketFileName: socketFileName,
			Restart:        app.RestartCond,
		}), nil
}
示例#2
0
// FindServices finds all matching services (empty string matches all)
// and lets you perform different actions (start, stop, etc) on them.
//
// If a snap is specified and no matching snaps are found,
// ErrPackageNotFound is returned. If a snap is specified and the
// matching snaps has no matching services, ErrServiceNotFound is
// returned.
//
// If no snap is specified, an empty result is not an error.
func FindServices(snapName string, serviceName string, pb progress.Meter) (ServiceActor, error) {
	var svcs []*svcT

	repo := NewMetaLocalRepository()
	installed, _ := repo.Installed()

	foundSnap := false
	for _, part := range installed {
		if !part.IsActive() {
			continue
		}

		snap, ok := part.(*SnapPart)
		if !ok {
			// can't happen
			continue
		}
		if snapName != "" && snapName != snap.Name() {
			continue
		}
		foundSnap = true

		yamls := snap.ServiceYamls()
		for i := range yamls {
			if serviceName != "" && serviceName != yamls[i].Name {
				continue
			}
			s := &svcT{
				m:   snap.m,
				svc: &yamls[i],
			}
			svcs = append(svcs, s)
		}
	}
	if snapName != "" {
		if !foundSnap {
			return nil, ErrPackageNotFound
		}
		if len(svcs) == 0 {
			return nil, ErrServiceNotFound
		}
	}

	return &serviceActor{
		svcs: svcs,
		pb:   pb,
		sysd: systemd.New(dirs.GlobalRootDir, pb),
	}, nil
}
示例#3
0
文件: click.go 项目: alecu/snappy
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
}
示例#4
0
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
}
示例#5
0
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
}
示例#6
0
func generateSnapSocketFile(app *snap.AppInfo, baseDir string) (string, error) {
	if err := snap.ValidateApp(app); err != nil {
		return "", err
	}

	// lp: #1515709, systemd will default to 0666 if no socket mode
	// is specified
	if app.SocketMode == "" {
		app.SocketMode = "0660"
	}

	serviceFileName := filepath.Base(app.ServiceFile())

	return systemd.New(dirs.GlobalRootDir, nil).GenSocketFile(
		&systemd.ServiceDescription{
			ServiceFileName: serviceFileName,
			ListenStream:    app.ListenStream,
			SocketMode:      app.SocketMode,
		}), nil
}
示例#7
0
func (m *packageYaml) addSquashfsMount(baseDir string, inhibitHooks bool, inter interacter) error {
	squashfsPath := stripGlobalRootDir(squashfs.BlobPath(baseDir))
	whereDir := stripGlobalRootDir(baseDir)

	sysd := systemd.New(dirs.GlobalRootDir, inter)
	mountUnitName, err := sysd.WriteMountUnitFile(m.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
}
示例#8
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
}
示例#9
0
文件: click.go 项目: alecu/snappy
func generateSnapSocketFile(app *AppYaml, baseDir string, aaProfile string, m *snapYaml) (string, error) {
	if err := verifyAppYaml(app); err != nil {
		return "", err
	}

	// lp: #1515709, systemd will default to 0666 if no socket mode
	// is specified
	if app.SocketMode == "" {
		app.SocketMode = "0660"
	}

	serviceFileName := filepath.Base(generateServiceFileName(m, app))

	return systemd.New(dirs.GlobalRootDir, nil).GenSocketFile(
		&systemd.ServiceDescription{
			ServiceFileName: serviceFileName,
			ListenStream:    app.ListenStream,
			SocketMode:      app.SocketMode,
			SocketUser:      app.SocketUser,
			SocketGroup:     app.SocketGroup,
		}), nil
}
示例#10
0
文件: click.go 项目: alecu/snappy
func generateSnapServicesFile(app *AppYaml, baseDir string, aaProfile string, m *snapYaml) (string, error) {
	if err := verifyAppYaml(app); err != nil {
		return "", err
	}

	udevPartName := m.qualifiedName(originFromBasedir(baseDir))

	desc := app.Description
	if desc == "" {
		desc = fmt.Sprintf("service %s for package %s", app.Name, m.Name)
	}

	socketFileName := ""
	if app.Socket {
		socketFileName = filepath.Base(generateSocketFileName(m, app))
	}

	return systemd.New(dirs.GlobalRootDir, nil).GenServiceFile(
		&systemd.ServiceDescription{
			AppName:        m.Name,
			ServiceName:    app.Name,
			Version:        m.Version,
			Description:    desc,
			AppPath:        baseDir,
			Start:          app.Command,
			Stop:           app.Stop,
			PostStop:       app.PostStop,
			StopTimeout:    time.Duration(app.StopTimeout),
			AaProfile:      aaProfile,
			IsFramework:    m.Type == snap.TypeFramework,
			IsNetworked:    app.Ports != nil && len(app.Ports.External) > 0,
			BusName:        app.BusName,
			Type:           app.Daemon,
			UdevAppName:    udevPartName,
			Socket:         app.Socket,
			SocketFileName: socketFileName,
			Restart:        app.RestartCond,
		}), nil
}
示例#11
0
func generateSnapServicesFile(service ServiceYaml, baseDir string, aaProfile string, m *packageYaml) (string, error) {
	if err := verifyServiceYaml(service); err != nil {
		return "", err
	}

	udevPartName := m.qualifiedName(originFromBasedir(baseDir))

	desc := service.Description
	if desc == "" {
		desc = fmt.Sprintf("service %s for package %s", service.Name, m.Name)
	}

	socketFileName := ""
	if service.Socket {
		socketFileName = filepath.Base(generateSocketFileName(m, service))
	}

	return systemd.New(dirs.GlobalRootDir, nil).GenServiceFile(
		&systemd.ServiceDescription{
			AppName:        m.Name,
			ServiceName:    service.Name,
			Version:        m.Version,
			Description:    desc,
			AppPath:        baseDir,
			Start:          service.Start,
			Stop:           service.Stop,
			PostStop:       service.PostStop,
			StopTimeout:    time.Duration(service.StopTimeout),
			AaProfile:      aaProfile,
			IsFramework:    m.Type == pkg.TypeFramework,
			IsNetworked:    service.Ports != nil && len(service.Ports.External) > 0,
			BusName:        service.BusName,
			Forking:        service.Forking,
			UdevAppName:    udevPartName,
			Socket:         service.Socket,
			SocketFileName: socketFileName,
		}), nil
}
示例#12
0
// Install installs the snap
func (s *SnapPart) Install(inter progress.Meter, flags InstallFlags) (name string, err error) {
	allowOEM := (flags & AllowOEM) != 0
	inhibitHooks := (flags & InhibitHooks) != 0

	if s.IsInstalled() {
		return "", ErrAlreadyInstalled
	}

	if err := s.CanInstall(allowOEM, inter); err != nil {
		return "", err
	}

	manifestData, err := s.deb.ControlMember("manifest")
	if err != nil {
		logger.Noticef("Snap inspect failed for %q: %v", s.Name(), err)
		return "", err
	}

	// the "oem" parts are special
	if s.Type() == pkg.TypeOem {
		if err := installOemHardwareUdevRules(s.m); err != nil {
			return "", err
		}
	}

	fullName := QualifiedName(s)
	dataDir := filepath.Join(dirs.SnapDataDir, fullName, s.Version())

	var oldPart *SnapPart
	if currentActiveDir, _ := filepath.EvalSymlinks(filepath.Join(s.basedir, "..", "current")); currentActiveDir != "" {
		oldPart, err = NewInstalledSnapPart(filepath.Join(currentActiveDir, "meta", "package.yaml"), s.origin)
		if err != nil {
			return "", err
		}
	}

	if err := os.MkdirAll(s.basedir, 0755); err != nil {
		logger.Noticef("Can not create %q: %v", s.basedir, err)
		return "", err
	}

	// if anything goes wrong here we cleanup
	defer func() {
		if err != nil {
			if e := os.RemoveAll(s.basedir); e != nil && !os.IsNotExist(e) {
				logger.Noticef("Failed to remove %q: %v", s.basedir, e)
			}
		}
	}()

	// we need to call the external helper so that we can reliable drop
	// privs
	if err := s.deb.UnpackWithDropPrivs(s.basedir, dirs.GlobalRootDir); err != nil {
		return "", err
	}

	// legacy, the hooks (e.g. apparmor) need this. Once we converted
	// all hooks this can go away
	clickMetaDir := filepath.Join(s.basedir, ".click", "info")
	if err := os.MkdirAll(clickMetaDir, 0755); err != nil {
		return "", err
	}
	if err := writeCompatManifestJSON(clickMetaDir, manifestData, s.origin); err != nil {
		return "", err
	}

	// write the hashes now
	if err := s.deb.ExtractHashes(filepath.Join(s.basedir, "meta")); err != nil {
		return "", err
	}

	// deal with the data:
	//
	// if there was a previous version, stop it
	// from being active so that it stops running and can no longer be
	// started then copy the data
	//
	// otherwise just create a empty data dir
	if oldPart != nil {
		// we need to stop making it active
		err = oldPart.deactivate(inhibitHooks, inter)
		defer func() {
			if err != nil {
				if cerr := oldPart.activate(inhibitHooks, inter); cerr != nil {
					logger.Noticef("Setting old version back to active failed: %v", cerr)
				}
			}
		}()
		if err != nil {
			return "", err
		}

		err = copySnapData(fullName, oldPart.Version(), s.Version())
	} else {
		err = os.MkdirAll(dataDir, 0755)
	}

	defer func() {
		if err != nil {
			if cerr := removeSnapData(fullName, s.Version()); cerr != nil {
				logger.Noticef("When cleaning up data for %s %s: %v", s.Name(), s.Version(), cerr)
			}
		}
	}()

	if err != nil {
		return "", err
	}

	// and finally make active
	err = s.activate(inhibitHooks, inter)
	defer func() {
		if err != nil && oldPart != nil {
			if cerr := oldPart.activate(inhibitHooks, inter); cerr != nil {
				logger.Noticef("When setting old %s version back to active: %v", s.Name(), cerr)
			}
		}
	}()
	if err != nil {
		return "", err
	}

	// oh, one more thing: refresh the security bits
	if !inhibitHooks {
		deps, err := s.Dependents()
		if err != nil {
			return "", err
		}

		sysd := systemd.New(dirs.GlobalRootDir, inter)
		stopped := make(map[string]time.Duration)
		defer func() {
			if err != nil {
				for serviceName := range stopped {
					if e := sysd.Start(serviceName); e != nil {
						inter.Notify(fmt.Sprintf("unable to restart %s with the old %s: %s", serviceName, s.Name(), e))
					}
				}
			}
		}()

		for _, dep := range deps {
			if !dep.IsActive() {
				continue
			}
			for _, svc := range dep.ServiceYamls() {
				serviceName := filepath.Base(generateServiceFileName(dep.m, svc))
				timeout := time.Duration(svc.StopTimeout)
				if err = sysd.Stop(serviceName, timeout); err != nil {
					inter.Notify(fmt.Sprintf("unable to stop %s; aborting install: %s", serviceName, err))
					return "", err
				}
				stopped[serviceName] = timeout
			}
		}

		if err := s.RefreshDependentsSecurity(oldPart, inter); err != nil {
			return "", err
		}

		started := make(map[string]time.Duration)
		defer func() {
			if err != nil {
				for serviceName, timeout := range started {
					if e := sysd.Stop(serviceName, timeout); e != nil {
						inter.Notify(fmt.Sprintf("unable to stop %s with the old %s: %s", serviceName, s.Name(), e))
					}
				}
			}
		}()
		for serviceName, timeout := range stopped {
			if err = sysd.Start(serviceName); err != nil {
				inter.Notify(fmt.Sprintf("unable to restart %s; aborting install: %s", serviceName, err))
				return "", err
			}
			started[serviceName] = timeout
		}
	}

	return s.Name(), nil
}
示例#13
0
文件: click.go 项目: alecu/snappy
func addPackageServices(m *snapYaml, baseDir string, inhibitHooks bool, inter interacter) error {
	for _, app := range m.Apps {
		if app.Daemon == "" {
			continue
		}
		aaProfile, err := getSecurityProfile(m, app.Name, baseDir)
		if err != nil {
			return err
		}
		// this will remove the global base dir when generating the
		// service file, this ensures that /snaps/foo/1.0/bin/start
		// is in the service file when the SetRoot() option
		// is used
		realBaseDir := stripGlobalRootDir(baseDir)
		// Generate service file
		content, err := generateSnapServicesFile(app, realBaseDir, aaProfile, m)
		if err != nil {
			return err
		}
		serviceFilename := generateServiceFileName(m, app)
		os.MkdirAll(filepath.Dir(serviceFilename), 0755)
		if err := helpers.AtomicWriteFile(serviceFilename, []byte(content), 0644, 0); err != nil {
			return err
		}
		// Generate systemd socket file if needed
		if app.Socket {
			content, err := generateSnapSocketFile(app, realBaseDir, aaProfile, m)
			if err != nil {
				return err
			}
			socketFilename := generateSocketFileName(m, app)
			os.MkdirAll(filepath.Dir(socketFilename), 0755)
			if err := helpers.AtomicWriteFile(socketFilename, []byte(content), 0644, 0); err != nil {
				return err
			}
		}
		// If necessary, generate the DBus policy file so the framework
		// service is allowed to start
		if m.Type == snap.TypeFramework && app.BusName != "" {
			content, err := genBusPolicyFile(app.BusName)
			if err != nil {
				return err
			}
			policyFilename := generateBusPolicyFileName(m, app)
			os.MkdirAll(filepath.Dir(policyFilename), 0755)
			if err := helpers.AtomicWriteFile(policyFilename, []byte(content), 0644, 0); err != nil {
				return err
			}
		}

		// daemon-reload and start only if we are not in the
		// inhibitHooks mode
		//
		// *but* always run enable (which just sets a symlink)
		serviceName := filepath.Base(generateServiceFileName(m, app))
		sysd := systemd.New(dirs.GlobalRootDir, inter)
		if !inhibitHooks {
			if err := sysd.DaemonReload(); err != nil {
				return err
			}
		}

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

		if !inhibitHooks {
			if err := sysd.Start(serviceName); err != nil {
				return err
			}
		}

		if app.Socket {
			socketName := filepath.Base(generateSocketFileName(m, app))
			// we always enable the socket even in inhibit hooks
			if err := sysd.Enable(socketName); err != nil {
				return err
			}

			if !inhibitHooks {
				if err := sysd.Start(socketName); err != nil {
					return err
				}
			}
		}
	}

	return nil
}
示例#14
0
func addPackageServices(s *snap.Info, inter interacter) error {
	baseDir := s.MountDir()

	for _, app := range s.Apps {
		if app.Daemon == "" {
			continue
		}

		// this will remove the global base dir when generating the
		// service file, this ensures that /snap/foo/1.0/bin/start
		// is in the service file when the SetRoot() option
		// is used
		realBaseDir := stripGlobalRootDir(baseDir)
		// Generate service file
		content, err := generateSnapServicesFile(app, realBaseDir)
		if err != nil {
			return err
		}
		svcFilePath := app.ServiceFile()
		os.MkdirAll(filepath.Dir(svcFilePath), 0755)
		if err := osutil.AtomicWriteFile(svcFilePath, []byte(content), 0644, 0); err != nil {
			return err
		}
		// Generate systemd socket file if needed
		if app.Socket {
			content, err := generateSnapSocketFile(app, realBaseDir)
			if err != nil {
				return err
			}
			svcSocketFilePath := app.ServiceSocketFile()
			os.MkdirAll(filepath.Dir(svcSocketFilePath), 0755)
			if err := osutil.AtomicWriteFile(svcSocketFilePath, []byte(content), 0644, 0); err != nil {
				return err
			}
		}
		// daemon-reload and enable plus start
		serviceName := filepath.Base(app.ServiceFile())
		sysd := systemd.New(dirs.GlobalRootDir, inter)

		if err := sysd.DaemonReload(); err != nil {
			return err
		}

		// enable the service
		if err := sysd.Enable(serviceName); err != nil {
			return err
		}

		if err := sysd.Start(serviceName); err != nil {
			return err
		}

		if app.Socket {
			socketName := filepath.Base(app.ServiceSocketFile())
			// enable the socket
			if err := sysd.Enable(socketName); err != nil {
				return err
			}

			if err := sysd.Start(socketName); err != nil {
				return err
			}
		}
	}

	return nil
}
示例#15
0
// Install installs the snap
func (s *SnapFile) Install(inter progress.Meter, flags InstallFlags) (name string, err error) {
	allowGadget := (flags & AllowGadget) != 0
	inhibitHooks := (flags & InhibitHooks) != 0

	// we do not Verify() the package here. This is done earlier in
	// NewSnapFile() to ensure that we do not mount/inspect
	// potentially dangerous snaps

	if err := s.CanInstall(allowGadget, inter); err != nil {
		return "", err
	}

	// the "gadget" parts are special
	if s.Type() == snap.TypeGadget {
		if err := installGadgetHardwareUdevRules(s.m); err != nil {
			return "", err
		}
	}

	fullName := QualifiedName(s)
	dataDir := filepath.Join(dirs.SnapDataDir, fullName, s.Version())

	var oldPart *SnapPart
	if currentActiveDir, _ := filepath.EvalSymlinks(filepath.Join(s.instdir, "..", "current")); currentActiveDir != "" {
		oldPart, err = NewInstalledSnapPart(filepath.Join(currentActiveDir, "meta", "package.yaml"), s.origin)
		if err != nil {
			return "", err
		}
	}

	if err := os.MkdirAll(s.instdir, 0755); err != nil {
		logger.Noticef("Can not create %q: %v", s.instdir, err)
		return "", err
	}

	// if anything goes wrong here we cleanup
	defer func() {
		if err != nil {
			if e := os.RemoveAll(s.instdir); e != nil && !os.IsNotExist(e) {
				logger.Noticef("Failed to remove %q: %v", s.instdir, e)
			}
		}
	}()

	// we need to call the external helper so that we can reliable drop
	// privs
	if err := s.deb.Install(s.instdir); err != nil {
		return "", err
	}

	// generate the mount unit for the squashfs
	if err := s.m.addSquashfsMount(s.instdir, inhibitHooks, inter); err != nil {
		return "", err
	}
	// if anything goes wrong we ensure we stop
	defer func() {
		if err != nil {
			if e := s.m.removeSquashfsMount(s.instdir, inter); e != nil {
				logger.Noticef("Failed to remove mount unit for  %s: %s", fullName, e)
			}
		}
	}()

	// FIXME: special handling is bad 'mkay
	if s.m.Type == snap.TypeKernel {
		if err := extractKernelAssets(s, inter, flags); err != nil {
			return "", fmt.Errorf("failed to install kernel %s", err)
		}
	}

	// deal with the data:
	//
	// if there was a previous version, stop it
	// from being active so that it stops running and can no longer be
	// started then copy the data
	//
	// otherwise just create a empty data dir
	if oldPart != nil {
		// we need to stop making it active
		err = oldPart.deactivate(inhibitHooks, inter)
		defer func() {
			if err != nil {
				if cerr := oldPart.activate(inhibitHooks, inter); cerr != nil {
					logger.Noticef("Setting old version back to active failed: %v", cerr)
				}
			}
		}()
		if err != nil {
			return "", err
		}

		err = copySnapData(fullName, oldPart.Version(), s.Version())
	} else {
		err = os.MkdirAll(dataDir, 0755)
	}

	defer func() {
		if err != nil {
			if cerr := removeSnapData(fullName, s.Version()); cerr != nil {
				logger.Noticef("When cleaning up data for %s %s: %v", s.Name(), s.Version(), cerr)
			}
		}
	}()

	if err != nil {
		return "", err
	}

	if !inhibitHooks {
		newPart, err := newSnapPartFromYaml(filepath.Join(s.instdir, "meta", "package.yaml"), s.origin, s.m)
		if err != nil {
			return "", err
		}

		// and finally make active
		err = newPart.activate(inhibitHooks, inter)
		defer func() {
			if err != nil && oldPart != nil {
				if cerr := oldPart.activate(inhibitHooks, inter); cerr != nil {
					logger.Noticef("When setting old %s version back to active: %v", s.Name(), cerr)
				}
			}
		}()
		if err != nil {
			return "", err
		}

		// oh, one more thing: refresh the security bits
		deps, err := newPart.Dependents()
		if err != nil {
			return "", err
		}

		sysd := systemd.New(dirs.GlobalRootDir, inter)
		stopped := make(map[string]time.Duration)
		defer func() {
			if err != nil {
				for serviceName := range stopped {
					if e := sysd.Start(serviceName); e != nil {
						inter.Notify(fmt.Sprintf("unable to restart %s with the old %s: %s", serviceName, s.Name(), e))
					}
				}
			}
		}()

		for _, dep := range deps {
			if !dep.IsActive() {
				continue
			}
			for _, svc := range dep.ServiceYamls() {
				serviceName := filepath.Base(generateServiceFileName(dep.m, svc))
				timeout := time.Duration(svc.StopTimeout)
				if err = sysd.Stop(serviceName, timeout); err != nil {
					inter.Notify(fmt.Sprintf("unable to stop %s; aborting install: %s", serviceName, err))
					return "", err
				}
				stopped[serviceName] = timeout
			}
		}

		if err := newPart.RefreshDependentsSecurity(oldPart, inter); err != nil {
			return "", err
		}

		started := make(map[string]time.Duration)
		defer func() {
			if err != nil {
				for serviceName, timeout := range started {
					if e := sysd.Stop(serviceName, timeout); e != nil {
						inter.Notify(fmt.Sprintf("unable to stop %s with the old %s: %s", serviceName, s.Name(), e))
					}
				}
			}
		}()
		for serviceName, timeout := range stopped {
			if err = sysd.Start(serviceName); err != nil {
				inter.Notify(fmt.Sprintf("unable to restart %s; aborting install: %s", serviceName, err))
				return "", err
			}
			started[serviceName] = timeout
		}
	}

	return s.Name(), nil
}