Skip to content

Instantly share code, notes, and snippets.

@ghedo
Last active May 4, 2018 13:51
Show Gist options
  • Save ghedo/4b005ce111591781e3fd to your computer and use it in GitHub Desktop.
Save ghedo/4b005ce111591781e3fd to your computer and use it in GitHub Desktop.
Start a systemd unit and wait until it stops
package main
import "log"
import "os"
import "github.com/docopt/docopt-go"
import "github.com/godbus/dbus"
const target string = "org.freedesktop.systemd1";
const object string = "/org/freedesktop/systemd1";
const interf string = "org.freedesktop.systemd1.Manager";
const signal string = "org.freedesktop.systemd1.Manager.JobRemoved";
func main() {
var conn *dbus.Conn;
log.SetFlags(0);
usage := `Usage: systemd-start-and-wait [--session] <unit>`
args, err := docopt.Parse(usage, nil, true, "", false)
if err != nil {
log.Fatalf("Invalid arguments: %s", err);
}
target := args["<unit>"].(string);
if args["--session"].(bool) {
conn, err = dbus.SessionBus();
} else {
conn, err = dbus.SystemBus();
}
if err != nil {
log.Fatalf("Error connecting to systemd: %s", err);
}
c, err := Subscribe(conn);
if err != nil {
log.Fatalf("Error subscribing to systemd: %s", err);
}
err = StartUnit(conn, target, "replace");
if err != nil {
log.Fatalf("Error starting unit: %s", err);
}
for v := range c {
if v.Name != signal {
continue;
}
unit := v.Body[2].(string);
result := v.Body[3].(string);
if unit == target && result == "done" {
state, err := GetUnitState(conn, unit);
if err != nil {
log.Fatalf("Error getting unit state: %s", err);
}
if state == "inactive" {
os.Exit(0);
}
}
}
}
func Subscribe(conn *dbus.Conn) (chan *dbus.Signal, error) {
conn.BusObject().Call(
"org.freedesktop.DBus.AddMatch", 0,
"type='signal',interface='" + interf + "'",
);
c := make(chan *dbus.Signal, 10);
conn.Signal(c);
obj := conn.Object(target, dbus.ObjectPath(object));
err := obj.Call(interf + ".Subscribe", 0).Store();
if err != nil {
return nil, err;
}
return c, nil;
}
func StartUnit(conn *dbus.Conn, name string, mode string) error {
var path dbus.ObjectPath;
obj := conn.Object(target, dbus.ObjectPath(object));
err := obj.Call(interf + ".StartUnit", 0, name, mode).Store(&path);
if err != nil {
return err;
}
return nil;
}
func GetUnitState(conn *dbus.Conn, name string) (string, error) {
var path dbus.ObjectPath;
var state dbus.Variant;
obj := conn.Object(target, dbus.ObjectPath(object));
err := obj.Call(interf + ".GetUnit", 0, name).Store(&path);
if err != nil {
return "", err;
}
unit := conn.Object(target, path);
err = unit.Call(
"org.freedesktop.DBus.Properties.Get", 0,
"org.freedesktop.systemd1.Unit", "ActiveState",
).Store(&state);
if err != nil {
return "", err;
}
return state.Value().(string), nil;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment