github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/systemd/backend.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2017 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 systemd implements integration between snappy interfaces and 21 // arbitrary systemd units that may be required for "oneshot" style tasks. 22 package systemd 23 24 import ( 25 "fmt" 26 "os" 27 "path/filepath" 28 "time" 29 30 "github.com/snapcore/snapd/dirs" 31 "github.com/snapcore/snapd/interfaces" 32 "github.com/snapcore/snapd/logger" 33 "github.com/snapcore/snapd/osutil" 34 "github.com/snapcore/snapd/snap" 35 sysd "github.com/snapcore/snapd/systemd" 36 "github.com/snapcore/snapd/timings" 37 ) 38 39 // Backend is responsible for maintaining apparmor profiles for ubuntu-core-launcher. 40 type Backend struct{} 41 42 // Initialize does nothing. 43 func (b *Backend) Initialize() error { 44 return nil 45 } 46 47 // Name returns the name of the backend. 48 func (b *Backend) Name() interfaces.SecuritySystem { 49 return interfaces.SecuritySystemd 50 } 51 52 // Setup creates and starts systemd services specific to a given snap. 53 // 54 // This method should be called after changing plug, slots, connections between 55 // them or application present in the snap. 56 func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error { 57 // Record all the extra systemd services for this snap. 58 snapName := snapInfo.InstanceName() 59 // Get the services that apply to this snap 60 spec, err := repo.SnapSpecification(b.Name(), snapName) 61 if err != nil { 62 return fmt.Errorf("cannot obtain systemd services for snap %q: %s", snapName, err) 63 } 64 content := deriveContent(spec.(*Specification), snapInfo) 65 // synchronize the content with the filesystem 66 dir := dirs.SnapServicesDir 67 if err := os.MkdirAll(dir, 0755); err != nil { 68 return fmt.Errorf("cannot create directory for systemd services %q: %s", dir, err) 69 } 70 glob := interfaces.InterfaceServiceName(snapName, "*") 71 72 systemd := sysd.New(dirs.GlobalRootDir, sysd.SystemMode, &dummyReporter{}) 73 // We need to be carefully here and stop all removed service units before 74 // we remove their files as otherwise systemd is not able to disable/stop 75 // them anymore. 76 if err := disableRemovedServices(systemd, dir, glob, content); err != nil { 77 logger.Noticef("cannot stop removed services: %s", err) 78 } 79 changed, removed, errEnsure := osutil.EnsureDirState(dir, glob, content) 80 // Reload systemd whenever something is added or removed 81 if len(changed) > 0 || len(removed) > 0 { 82 err := systemd.DaemonReload() 83 if err != nil { 84 logger.Noticef("cannot reload systemd state: %s", err) 85 } 86 } 87 // Ensure the service is running right now and on reboots 88 for _, service := range changed { 89 if err := systemd.Enable(service); err != nil { 90 logger.Noticef("cannot enable service %q: %s", service, err) 91 } 92 // If we have a new service here which isn't started yet the restart 93 // operation will start it. 94 if err := systemd.Restart(service, 10*time.Second); err != nil { 95 logger.Noticef("cannot restart service %q: %s", service, err) 96 } 97 } 98 return errEnsure 99 } 100 101 // Remove disables, stops and removes systemd services of a given snap. 102 func (b *Backend) Remove(snapName string) error { 103 systemd := sysd.New(dirs.GlobalRootDir, sysd.SystemMode, &dummyReporter{}) 104 // Remove all the files matching snap glob 105 glob := interfaces.InterfaceServiceName(snapName, "*") 106 _, removed, errEnsure := osutil.EnsureDirState(dirs.SnapServicesDir, glob, nil) 107 for _, service := range removed { 108 if err := systemd.Disable(service); err != nil { 109 logger.Noticef("cannot disable service %q: %s", service, err) 110 } 111 if err := systemd.Stop(service, 5*time.Second); err != nil { 112 logger.Noticef("cannot stop service %q: %s", service, err) 113 } 114 } 115 // Reload systemd whenever something is removed 116 if len(removed) > 0 { 117 err := systemd.DaemonReload() 118 if err != nil { 119 logger.Noticef("cannot reload systemd state: %s", err) 120 } 121 } 122 return errEnsure 123 } 124 125 // NewSpecification returns a new systemd specification. 126 func (b *Backend) NewSpecification() interfaces.Specification { 127 return &Specification{} 128 } 129 130 // SandboxFeatures returns nil 131 func (b *Backend) SandboxFeatures() []string { 132 return nil 133 } 134 135 // deriveContent computes .service files based on requests made to the specification. 136 func deriveContent(spec *Specification, snapInfo *snap.Info) map[string]*osutil.FileState { 137 services := spec.Services() 138 if len(services) == 0 { 139 return nil 140 } 141 content := make(map[string]*osutil.FileState) 142 for name, service := range services { 143 content[name] = &osutil.FileState{ 144 Content: []byte(service.String()), 145 Mode: 0644, 146 } 147 } 148 return content 149 } 150 151 func disableRemovedServices(systemd sysd.Systemd, dir, glob string, content map[string]*osutil.FileState) error { 152 paths, err := filepath.Glob(filepath.Join(dir, glob)) 153 if err != nil { 154 return err 155 } 156 for _, path := range paths { 157 service := filepath.Base(path) 158 if content[service] == nil { 159 if err := systemd.Disable(service); err != nil { 160 logger.Noticef("cannot disable service %q: %s", service, err) 161 } 162 if err := systemd.Stop(service, 5*time.Second); err != nil { 163 logger.Noticef("cannot stop service %q: %s", service, err) 164 } 165 } 166 } 167 return nil 168 } 169 170 type dummyReporter struct{} 171 172 func (dr *dummyReporter) Notify(msg string) { 173 }