Skip to content

Commit

Permalink
Merge pull request #11717 from mvo5/systemd-run-wrapper
Browse files Browse the repository at this point in the history
systemd: add systemd.Run() wrapper for systemd-run
  • Loading branch information
bboozzoo authored Apr 26, 2022
2 parents 66b1013 + 38a2321 commit d6b5ca0
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 1 deletion.
3 changes: 2 additions & 1 deletion kernel/fde/cmd_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ func runFDEinitramfsHelper(name string, stdin []byte) (output []byte, err error)
}
}

// TODO: put this into a new "systemd/run" package
// TODO: use the new systemd.Run() interface once it supports
// running without dbus (i.e. supports running without --pipe)
cmd := exec.Command(
"systemd-run",
"--collect",
Expand Down
4 changes: 4 additions & 0 deletions systemd/emulation.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,7 @@ func (s *emulation) Mount(what, where string, options ...string) error {
func (s *emulation) Umount(whatOrWhere string) error {
return &notImplementedError{"Umount"}
}

func (s *emulation) Run(command []string, opts *RunOptions) ([]byte, error) {
return nil, &notImplementedError{"Run"}
}
47 changes: 47 additions & 0 deletions systemd/systemd.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,25 @@ type Systemd interface {
// threads if enabled, etc) part of the unit, which can be a service or a
// slice.
CurrentTasksCount(unit string) (uint64, error)
// Run a command
Run(command []string, opts *RunOptions) ([]byte, error)
}

// KeyringMode describes how the kernel keyring is setup, see systemd.exec(5)
type KeyringMode string

const (
KeyringModeInherit KeyringMode = "inherit"
KeyringModePrivate KeyringMode = "private"
KeyringModeShared KeyringMode = "shared"
)

// RunOptions can be passed to systemd.Run()
type RunOptions struct {
// XXX: alternative we could just have `Propertes []string` here
// and let the caller do the keyring setup but feels a bit loose
KeyringMode KeyringMode
Stdin io.Reader
}

// A Log is a single entry in the systemd journal.
Expand Down Expand Up @@ -1534,3 +1553,31 @@ func (s *systemd) Umount(whatOrWhere string) error {
}
return nil
}

// Run runs the given command via "sytemd-run" and returns the output
// or an error if the command fails.
func (s *systemd) Run(command []string, opts *RunOptions) ([]byte, error) {
if opts == nil {
opts = &RunOptions{}
}
runArgs := []string{
"--wait",
"--pipe",
"--collect",
"--service-type=exec",
"--quiet",
}
if opts.KeyringMode != "" {
runArgs = append(runArgs, fmt.Sprintf("--property=KeyringMode=%v", opts.KeyringMode))
}
runArgs = append(runArgs, "--")
runArgs = append(runArgs, command...)
cmd := exec.Command("systemd-run", runArgs...)
cmd.Stdin = opts.Stdin

output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("cannot run %q: %v", command, osutil.OutputErr(output, err))
}
return output, nil
}
51 changes: 51 additions & 0 deletions systemd/systemd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,57 @@ func (s *SystemdTestSuite) TestInactiveEnterTimestampMalformedMore(c *C) {
c.Check(stamp.IsZero(), Equals, true)
}

func (s *SystemdTestSuite) TestSystemdRunError(c *C) {
sr := testutil.MockCommand(c, "systemd-run", `echo "fail"; exit 11`)
defer sr.Restore()

sysd := New(SystemMode, s.rep)
output, err := sysd.Run([]string{"bad-cmd", "arg1"}, nil)
c.Check(output, IsNil)
c.Assert(err, ErrorMatches, `cannot run \["bad-cmd" "arg1"\]: fail`)
}

func (s *SystemdTestSuite) TestSystemdRunHappy(c *C) {
sr := testutil.MockCommand(c, "systemd-run", `echo "happy output" && >&2 echo "to stderr"`)
defer sr.Restore()

sysd := New(SystemMode, s.rep)
output, err := sysd.Run([]string{"happy-cmd", "arg1"}, nil)
c.Check(string(output), Equals, "happy output\nto stderr\n")
c.Check(err, IsNil)
c.Check(sr.Calls(), DeepEquals, [][]string{
{"systemd-run", "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", "--", "happy-cmd", "arg1"},
})
}

func (s *SystemdTestSuite) TestSystemdRunHappyWithStdin(c *C) {
sr := testutil.MockCommand(c, "systemd-run", `echo "some output" && cat - `)
defer sr.Restore()

sysd := New(SystemMode, s.rep)
opts := &RunOptions{Stdin: bytes.NewBufferString("stdin input\n")}
output, err := sysd.Run([]string{"cmd-with-stdin", "arg1"}, opts)
c.Check(string(output), Equals, "some output\nstdin input\n")
c.Check(err, IsNil)
c.Check(sr.Calls(), DeepEquals, [][]string{
{"systemd-run", "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", "--", "cmd-with-stdin", "arg1"},
})
}

func (s *SystemdTestSuite) TestSystemdRunKeyringMode(c *C) {
sr := testutil.MockCommand(c, "systemd-run", `echo "happy output"`)
defer sr.Restore()

sysd := New(SystemMode, s.rep)
opts := &RunOptions{KeyringMode: KeyringModePrivate}
output, err := sysd.Run([]string{"happy-cmd", "arg1"}, opts)
c.Check(string(output), Equals, "happy output\n")
c.Check(err, IsNil)
c.Check(sr.Calls(), DeepEquals, [][]string{
{"systemd-run", "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", "--property=KeyringMode=private", "--", "happy-cmd", "arg1"},
})
}

type systemdErrorSuite struct{}

var _ = Suite(&systemdErrorSuite{})
Expand Down

0 comments on commit d6b5ca0

Please sign in to comment.