github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/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 preseed bool 42 } 43 44 // Initialize does nothing. 45 func (b *Backend) Initialize(opts *interfaces.SecurityBackendOptions) error { 46 if opts != nil && opts.Preseed { 47 b.preseed = true 48 } 49 return nil 50 } 51 52 // Name returns the name of the backend. 53 func (b *Backend) Name() interfaces.SecuritySystem { 54 return interfaces.SecuritySystemd 55 } 56 57 // Setup creates and starts systemd services specific to a given snap. 58 // 59 // This method should be called after changing plug, slots, connections between 60 // them or application present in the snap. 61 func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error { 62 // Record all the extra systemd services for this snap. 63 snapName := snapInfo.InstanceName() 64 // Get the services that apply to this snap 65 spec, err := repo.SnapSpecification(b.Name(), snapName) 66 if err != nil { 67 return fmt.Errorf("cannot obtain systemd services for snap %q: %s", snapName, err) 68 } 69 content := deriveContent(spec.(*Specification), snapInfo) 70 // synchronize the content with the filesystem 71 dir := dirs.SnapServicesDir 72 if err := os.MkdirAll(dir, 0755); err != nil { 73 return fmt.Errorf("cannot create directory for systemd services %q: %s", dir, err) 74 } 75 glob := interfaces.InterfaceServiceName(snapName, "*") 76 77 var systemd sysd.Systemd 78 if b.preseed { 79 systemd = sysd.NewEmulationMode(dirs.GlobalRootDir) 80 } else { 81 systemd = sysd.New(sysd.SystemMode, &dummyReporter{}) 82 } 83 84 // We need to be carefully here and stop all removed service units before 85 // we remove their files as otherwise systemd is not able to disable/stop 86 // them anymore. 87 if err := b.disableRemovedServices(systemd, dir, glob, content); err != nil { 88 logger.Noticef("cannot stop removed services: %s", err) 89 } 90 changed, removed, errEnsure := osutil.EnsureDirState(dir, glob, content) 91 // Reload systemd whenever something is added or removed 92 if !b.preseed && (len(changed) > 0 || len(removed) > 0) { 93 err := systemd.DaemonReload() 94 if err != nil { 95 logger.Noticef("cannot reload systemd state: %s", err) 96 } 97 } 98 // Ensure the service is running right now and on reboots 99 for _, service := range changed { 100 if err := systemd.Enable(service); err != nil { 101 logger.Noticef("cannot enable service %q: %s", service, err) 102 } 103 if !b.preseed { 104 // If we have a new service here which isn't started yet the restart 105 // operation will start it. 106 if err := systemd.Restart(service, 10*time.Second); err != nil { 107 logger.Noticef("cannot restart service %q: %s", service, err) 108 } 109 } 110 } 111 return errEnsure 112 } 113 114 // Remove disables, stops and removes systemd services of a given snap. 115 func (b *Backend) Remove(snapName string) error { 116 var systemd sysd.Systemd 117 if b.preseed { 118 // removing while preseeding is not a viable scenario, but implemented 119 // for completness. 120 systemd = sysd.NewEmulationMode(dirs.GlobalRootDir) 121 } else { 122 systemd = sysd.New(sysd.SystemMode, &dummyReporter{}) 123 } 124 // Remove all the files matching snap glob 125 glob := interfaces.InterfaceServiceName(snapName, "*") 126 _, removed, errEnsure := osutil.EnsureDirState(dirs.SnapServicesDir, glob, nil) 127 for _, service := range removed { 128 if err := systemd.Disable(service); err != nil { 129 logger.Noticef("cannot disable service %q: %s", service, err) 130 } 131 if !b.preseed { 132 if err := systemd.Stop(service, 5*time.Second); err != nil { 133 logger.Noticef("cannot stop service %q: %s", service, err) 134 } 135 } 136 } 137 // Reload systemd whenever something is removed 138 if !b.preseed && len(removed) > 0 { 139 err := systemd.DaemonReload() 140 if err != nil { 141 logger.Noticef("cannot reload systemd state: %s", err) 142 } 143 } 144 return errEnsure 145 } 146 147 // NewSpecification returns a new systemd specification. 148 func (b *Backend) NewSpecification() interfaces.Specification { 149 return &Specification{} 150 } 151 152 // SandboxFeatures returns nil 153 func (b *Backend) SandboxFeatures() []string { 154 return nil 155 } 156 157 // deriveContent computes .service files based on requests made to the specification. 158 func deriveContent(spec *Specification, snapInfo *snap.Info) map[string]osutil.FileState { 159 services := spec.Services() 160 if len(services) == 0 { 161 return nil 162 } 163 content := make(map[string]osutil.FileState) 164 for name, service := range services { 165 content[name] = &osutil.MemoryFileState{ 166 Content: []byte(service.String()), 167 Mode: 0644, 168 } 169 } 170 return content 171 } 172 173 func (b *Backend) disableRemovedServices(systemd sysd.Systemd, dir, glob string, content map[string]osutil.FileState) error { 174 paths, err := filepath.Glob(filepath.Join(dir, glob)) 175 if err != nil { 176 return err 177 } 178 for _, path := range paths { 179 service := filepath.Base(path) 180 if content[service] == nil { 181 if err := systemd.Disable(service); err != nil { 182 logger.Noticef("cannot disable service %q: %s", service, err) 183 } 184 if !b.preseed { 185 if err := systemd.Stop(service, 5*time.Second); err != nil { 186 logger.Noticef("cannot stop service %q: %s", service, err) 187 } 188 } 189 } 190 } 191 return nil 192 } 193 194 type dummyReporter struct{} 195 196 func (dr *dummyReporter) Notify(msg string) { 197 }