From cf13eaa30700d3f3c7f43b3e842cf15bea96e3aa Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 25 Jul 2025 15:52:30 -0700 Subject: [PATCH 1/2] dbus: add AttachProcessesToUnit Signed-off-by: Kir Kolyshkin --- dbus/methods.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dbus/methods.go b/dbus/methods.go index 074148cb..285eaddb 100644 --- a/dbus/methods.go +++ b/dbus/methods.go @@ -862,3 +862,8 @@ 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() } + +// AttachProcessesToUnit moves existing processes, identified by pids, into an existing systemd unit. +func (c *Conn) AttachProcessesToUnit(ctx context.Context, unit, subcgroup string, pids []uint32) error { + return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.AttachProcessesToUnit", 0, unit, subcgroup, pids).Store() +} From 190804470dbe9200b58abe09f35559a6f9152373 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 28 Jul 2025 19:26:53 -0700 Subject: [PATCH 2/2] dbus: add unit tests for AttachProcessesToUnit Signed-off-by: Kir Kolyshkin --- dbus/methods_test.go | 88 +++++++++++++++++++++++++++++++ fixtures/attach-processes.service | 6 +++ 2 files changed, 94 insertions(+) create mode 100644 fixtures/attach-processes.service diff --git a/dbus/methods_test.go b/dbus/methods_test.go index 30cc1324..79ad6809 100644 --- a/dbus/methods_test.go +++ b/dbus/methods_test.go @@ -1689,3 +1689,91 @@ func TestFreezer(t *testing.T) { runStopUnit(t, conn, TrUnitProp{target, nil}) } + +func testAttachProcessesToUnit(t *testing.T, subcgroup string) { + target := "attach-processes.service" + conn := setupConn(t) + defer conn.Close() + + setupUnit(target, conn, t) + linkUnit(target, conn, t) + + // Start the unit first. + reschan := make(chan string) + _, err := conn.StartUnit(target, "replace", reschan) + if err != nil { + t.Fatal(err) + } + job := <-reschan + if job != "done" { + t.Fatal("Job is not done:", job) + } + + // Cleanup. + defer func() { + _, err = conn.StopUnit(target, "replace", reschan) + if err != nil { + t.Fatal(err) + } + <-reschan + }() + + if subcgroup != "" { + // Pre-create a sub-cgroup. + prop, err := conn.GetServiceProperty(target, "ControlGroup") + if err != nil { + t.Fatal(err) + } + path := prop.Value.Value().(string) + err = os.Mkdir(filepath.Join("/sys/fs/cgroup", path, subcgroup), 0777) + if err != nil { + t.Fatal(err) + } + + } + + // Start a test process that we can attach. + cmd := exec.Command("/bin/sleep", "400") + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + defer func() { + cmd.Process.Kill() + cmd.Wait() + }() + + pid := uint32(cmd.Process.Pid) + + // Test attaching the process to the unit. + ctx := context.Background() + err = conn.AttachProcessesToUnit(ctx, target, subcgroup, []uint32{pid}) + if err != nil { + // AttachProcessesToUnit might not be supported on all systemd versions. + 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 attach process %d to unit %s: %s", pid, target, err) + } + + // Verify the process was attached by getting the unit it belongs to. + attachedPath, err := conn.GetUnitByPID(ctx, pid) + if err != nil { + t.Fatal(err) + } + + attachedUnit := unitName(attachedPath) + if attachedUnit != target { + t.Fatalf("process was not attached to correct unit: got %s, want %s", attachedUnit, target) + } +} + +func TestAttachProcessesToUnit(t *testing.T) { + testAttachProcessesToUnit(t, "") +} + +func TestAttachProcessesToUnitWithSubcgroup(t *testing.T) { + testAttachProcessesToUnit(t, "/test-subcgroup") +} diff --git a/fixtures/attach-processes.service b/fixtures/attach-processes.service new file mode 100644 index 00000000..c82197c7 --- /dev/null +++ b/fixtures/attach-processes.service @@ -0,0 +1,6 @@ +[Unit] +Description=attach processes test + +[Service] +Delegate=yes +ExecStart=/bin/sleep 400