diff --git a/dbus/methods.go b/dbus/methods.go index 074148cb..9e3aa9c2 100644 --- a/dbus/methods.go +++ b/dbus/methods.go @@ -862,3 +862,34 @@ func (c *Conn) FreezeUnit(ctx context.Context, unit string) error { func (c *Conn) ThawUnit(ctx context.Context, unit string) error { return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ThawUnit", 0, unit).Store() } + +type Process struct { + Path string // Where this process exists in the unit/cgroup hierarchy + PID uint64 // The numeric process ID (PID) + Command string // The process command and arguments as a string +} + +// GetUnitProcesses returns an array with all currently running processes in a unit *including* its child units. +func (c *Conn) GetUnitProcesses(ctx context.Context, unit string) ([]Process, error) { + result := make([][]interface{}, 0) + if err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.GetUnitProcesses", 0, unit).Store(&result); err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + process := make([]Process, len(result)) + processInterface := make([]interface{}, len(process)) + for i := range process { + processInterface[i] = &process[i] + } + + if err := dbus.Store(resultInterface, processInterface...); err != nil { + return nil, err + } + + return process, nil +} diff --git a/dbus/methods_test.go b/dbus/methods_test.go index 30cc1324..6edc1665 100644 --- a/dbus/methods_test.go +++ b/dbus/methods_test.go @@ -22,6 +22,7 @@ import ( "path" "path/filepath" "reflect" + "strings" "syscall" "testing" "time" @@ -1689,3 +1690,45 @@ func TestFreezer(t *testing.T) { runStopUnit(t, conn, TrUnitProp{target, nil}) } + +func TestListUnitProcesses(t *testing.T) { + ctx := context.Background() + target := "list-me.service" + + conn := setupConn(t) + defer conn.Close() + + setupUnit(target, conn, t) + linkUnit(target, conn, t) + + reschan := make(chan string) + _, err := conn.StartUnitContext(ctx, target, "replace", reschan) + if err != nil { + t.Fatal(err) + } + defer runStopUnit(t, conn, TrUnitProp{target, nil}) + + job := <-reschan + if job != "done" { + t.Fatal("Job is not done:", job) + } + + processes, err := conn.GetUnitProcesses(ctx, target) + + if err != nil { + e, ok := err.(dbus.Error) + if ok && (e.Name == "org.freedesktop.DBus.Error.UnknownMethod" || e.Name == "org.freedesktop.DBus.Error.NotSupported") { + t.SkipNow() + } + t.Fatalf("failed to list processes of %s: %s", target, err) + } + + for _, p := range processes { + if strings.Contains(p.Command, "sleep") { + t.Logf("Found %v.\n", p) + return + } + } + t.Log("processes:", processes) + t.Error("The sleep process was not found in list-me.service unit's process list.") +} diff --git a/fixtures/list-me.service b/fixtures/list-me.service new file mode 100644 index 00000000..7c9ead40 --- /dev/null +++ b/fixtures/list-me.service @@ -0,0 +1,5 @@ +[Unit] +Description=GetUnitProcesses test + +[Service] +ExecStart=/bin/sleep 400