github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/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  }
   179  
   180  func (s *emulation) Mount(what, where string, options ...string) error {
   181  	return errNotImplemented
   182  }
   183  
   184  func (s *emulation) Umount(whatOrWhere string) error {
   185  	return errNotImplemented
   186  }