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