gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/systemd/emulation.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 21 22 import ( 23 "errors" 24 "fmt" 25 "io" 26 "os" 27 "os/exec" 28 "path/filepath" 29 "strings" 30 "time" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/gadget/quantity" 34 "github.com/snapcore/snapd/osutil" 35 "github.com/snapcore/snapd/osutil/squashfs" 36 ) 37 38 type emulation struct { 39 rootDir string 40 } 41 42 var errNotImplemented = errors.New("not implemented in emulation mode") 43 44 func (s *emulation) DaemonReload() error { 45 return errNotImplemented 46 } 47 48 func (s *emulation) DaemonReexec() error { 49 return errNotImplemented 50 } 51 52 func (s *emulation) Enable(service string) error { 53 _, err := systemctlCmd("--root", s.rootDir, "enable", service) 54 return err 55 } 56 57 func (s *emulation) Disable(service string) error { 58 _, err := systemctlCmd("--root", s.rootDir, "disable", service) 59 return err 60 } 61 62 func (s *emulation) Start(service ...string) error { 63 return errNotImplemented 64 } 65 66 func (s *emulation) StartNoBlock(service ...string) error { 67 return errNotImplemented 68 } 69 70 func (s *emulation) Stop(service string, timeout time.Duration) error { 71 return errNotImplemented 72 } 73 74 func (s *emulation) Kill(service, signal, who string) error { 75 return errNotImplemented 76 } 77 78 func (s *emulation) Restart(service string, timeout time.Duration) error { 79 return errNotImplemented 80 } 81 82 func (s *emulation) ReloadOrRestart(service string) error { 83 return errNotImplemented 84 } 85 86 func (s *emulation) RestartAll(service string) error { 87 return errNotImplemented 88 } 89 90 func (s *emulation) Status(units ...string) ([]*UnitStatus, error) { 91 return nil, errNotImplemented 92 } 93 94 func (s *emulation) InactiveEnterTimestamp(unit string) (time.Time, error) { 95 return time.Time{}, errNotImplemented 96 } 97 98 func (s *emulation) CurrentMemoryUsage(unit string) (quantity.Size, error) { 99 return 0, errNotImplemented 100 } 101 102 func (s *emulation) CurrentTasksCount(unit string) (uint64, error) { 103 return 0, errNotImplemented 104 } 105 106 func (s *emulation) IsEnabled(service string) (bool, error) { 107 return false, errNotImplemented 108 } 109 110 func (s *emulation) IsActive(service string) (bool, error) { 111 return false, errNotImplemented 112 } 113 114 func (s *emulation) LogReader(services []string, n int, follow bool) (io.ReadCloser, error) { 115 return nil, errNotImplemented 116 } 117 118 func (s *emulation) AddMountUnitFile(snapName, revision, what, where, fstype string) (string, error) { 119 if osutil.IsDirectory(what) { 120 return "", fmt.Errorf("bind-mounted directory is not supported in emulation mode") 121 } 122 123 // In emulation mode hostFsType is the fs we want to use to manually mount 124 // the snap below, but fstype is used for the created mount unit. 125 // This means that when preseeding in a lxd container, the snap will be 126 // mounted with fuse, but mount unit will use squashfs. 127 mountUnitOptions := append(fsMountOptions(fstype), squashfs.StandardOptions()...) 128 mountUnitName, err := writeMountUnitFile(snapName, revision, what, where, fstype, mountUnitOptions) 129 if err != nil { 130 return "", err 131 } 132 133 hostFsType, actualOptions := hostFsTypeAndMountOptions(fstype) 134 cmd := exec.Command("mount", "-t", hostFsType, what, where, "-o", strings.Join(actualOptions, ",")) 135 if out, err := cmd.CombinedOutput(); err != nil { 136 return "", fmt.Errorf("cannot mount %s (%s) at %s in preseed mode: %s; %s", what, hostFsType, where, err, string(out)) 137 } 138 139 multiUserTargetWantsDir := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants") 140 if err := os.MkdirAll(multiUserTargetWantsDir, 0755); err != nil { 141 return "", err 142 } 143 144 // cannot call systemd, so manually enable the unit by symlinking into multi-user.target.wants 145 mu := MountUnitPath(where) 146 enableUnitPath := filepath.Join(multiUserTargetWantsDir, mountUnitName) 147 if err := os.Symlink(mu, enableUnitPath); err != nil { 148 return "", fmt.Errorf("cannot enable mount unit %s: %v", mountUnitName, err) 149 } 150 return mountUnitName, nil 151 } 152 153 func (s *emulation) RemoveMountUnitFile(mountedDir string) error { 154 unit := MountUnitPath(dirs.StripRootDir(mountedDir)) 155 if !osutil.FileExists(unit) { 156 return nil 157 } 158 159 isMounted, err := osutilIsMounted(mountedDir) 160 if err != nil { 161 return err 162 } 163 if isMounted { 164 // use detach-loop and lazy unmount 165 if output, err := exec.Command("umount", "-d", "-l", mountedDir).CombinedOutput(); err != nil { 166 return osutil.OutputErr(output, err) 167 } 168 } 169 170 multiUserTargetWantsDir := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants") 171 enableUnitPathSymlink := filepath.Join(multiUserTargetWantsDir, filepath.Base(unit)) 172 if err := os.Remove(enableUnitPathSymlink); err != nil { 173 return err 174 } 175 176 if err := os.Remove(unit); err != nil { 177 return err 178 } 179 180 return nil 181 } 182 183 func (s *emulation) Mask(service string) error { 184 _, err := systemctlCmd("--root", s.rootDir, "mask", service) 185 return err 186 } 187 188 func (s *emulation) Unmask(service string) error { 189 _, err := systemctlCmd("--root", s.rootDir, "unmask", service) 190 return err 191 } 192 193 func (s *emulation) Mount(what, where string, options ...string) error { 194 return errNotImplemented 195 } 196 197 func (s *emulation) Umount(whatOrWhere string) error { 198 return errNotImplemented 199 }