chiark / gitweb /
systemadm: implement basic control UI systemadm
authorLennart Poettering <lennart@poettering.net>
Wed, 3 Feb 2010 11:39:12 +0000 (12:39 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 3 Feb 2010 11:40:09 +0000 (12:40 +0100)
.gitignore
Makefile
systemadm.vala [new file with mode: 0644]
systemctl.vala
systemd-interfaces.vala [new file with mode: 0644]

index c15a61b..9c5193d 100644 (file)
@@ -5,3 +5,6 @@ test-job-type
 systemd-logger
 systemctl
 systemctl.c
+systemd-interfaces.c
+systemadm
+systemadm.c
index 5a60eac..41df4e2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ COMMON= \
        dbus-unit.o \
        dbus-job.o
 
-all: systemd test-engine test-job-type systemd-logger systemctl
+all: systemd test-engine test-job-type systemd-logger systemctl systemadm
 
 systemd: main.o $(COMMON)
        $(CC) $(CFLAGS) -o $@ $^  $(LIBS)
@@ -44,7 +44,10 @@ test-job-type: test-job-type.o $(COMMON)
        $(CC) $(CFLAGS) -o $@ $^  $(LIBS)
 
 systemctl: systemctl.vala
-       valac --save-temps systemctl.vala --pkg=dbus-glib-1 --pkg=posix
+       valac -g --save-temps systemctl.vala systemd-interfaces.vala --pkg=dbus-glib-1 --pkg=posix
+
+systemadm: systemadm.vala
+       valac -g --save-temps systemadm.vala systemd-interfaces.vala --pkg=dbus-glib-1 --pkg=posix --pkg gee-1.0 --pkg gtk+-2.0
 
 clean:
-       rm -f *.o systemd test-engine systemctl
+       rm -f *.o systemd test-engine systemctl systemadm
diff --git a/systemadm.vala b/systemadm.vala
new file mode 100644 (file)
index 0000000..18d3a36
--- /dev/null
@@ -0,0 +1,438 @@
+using Gtk;
+using GLib;
+using DBus;
+using Pango;
+
+public class LeftLabel : Label {
+        public LeftLabel(string? text = null) {
+                if (text != null)
+                        set_markup("<b>%s</b>".printf(text));
+                set_alignment(1, 0);
+                set_padding(6, 0);
+        }
+}
+
+public class RightLabel : Label {
+        public RightLabel(string? text = null) {
+                set_text_or_na(text);
+                set_alignment(0, 1);
+                set_ellipsize(EllipsizeMode.START);
+                set_selectable(true);
+        }
+
+        public void set_text_or_na(string? text = null) {
+                if (text == null || text == "")
+                        set_markup("<i>n/a</i>");
+                else
+                        set_text(text);
+        }
+}
+
+public class MainWindow : Window {
+
+        private TreeView unit_view;
+        private TreeView job_view;
+
+        private ListStore unit_model;
+        private ListStore job_model;
+
+        private Button start_button;
+        private Button stop_button;
+        private Button restart_button;
+        private Button reload_button;
+        private Button cancel_button;
+
+        private Connection bus;
+        private Manager manager;
+
+        private RightLabel unit_id_label;
+        private RightLabel unit_description_label;
+        private RightLabel unit_load_state_label;
+        private RightLabel unit_active_state_label;
+        private RightLabel unit_load_path_label;
+        private RightLabel unit_active_enter_timestamp_label;
+        private RightLabel unit_active_exit_timestamp_label;
+        private RightLabel unit_can_start_label;
+        private RightLabel unit_can_reload_label;
+
+        private RightLabel job_id_label;
+        private RightLabel job_state_label;
+        private RightLabel job_type_label;
+
+        public MainWindow() throws DBus.Error {
+                title = "systemdadm";
+                position = WindowPosition.CENTER;
+                set_default_size(1000, 700);
+                set_border_width(12);
+                destroy.connect(Gtk.main_quit);
+
+                Notebook notebook = new Notebook();
+                add(notebook);
+
+                Box unit_vbox = new VBox(false, 6);
+                notebook.append_page(unit_vbox, new Label("Units"));
+                unit_vbox.set_border_width(12);
+
+                Box job_vbox = new VBox(false, 6);
+                notebook.append_page(job_vbox, new Label("Jobs"));
+                job_vbox.set_border_width(12);
+
+
+                unit_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string));
+                job_model = new ListStore(5, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string));
+
+                unit_view = new TreeView.with_model(unit_model);
+                job_view = new TreeView.with_model(job_model);
+
+                unit_view.cursor_changed.connect(unit_changed);
+                job_view.cursor_changed.connect(job_changed);
+
+                unit_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 0);
+                unit_view.insert_column_with_attributes(-1, "Description", new CellRendererText(), "text", 1);
+                unit_view.insert_column_with_attributes(-1, "Load State", new CellRendererText(), "text", 2);
+                unit_view.insert_column_with_attributes(-1, "Active State", new CellRendererText(), "text", 3);
+                unit_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 4);
+
+                job_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 0);
+                job_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 1);
+                job_view.insert_column_with_attributes(-1, "Type", new CellRendererText(), "text", 2);
+                job_view.insert_column_with_attributes(-1, "State", new CellRendererText(), "text", 3);
+
+                ScrolledWindow scroll = new ScrolledWindow(null, null);
+                scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
+                scroll.set_shadow_type(ShadowType.IN);
+                scroll.add(unit_view);
+                unit_vbox.pack_start(scroll, true, true, 0);
+
+                scroll = new ScrolledWindow(null, null);
+                scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
+                scroll.set_shadow_type(ShadowType.IN);
+                scroll.add(job_view);
+                job_vbox.pack_start(scroll, true, true, 0);
+
+                unit_id_label = new RightLabel();
+                unit_description_label = new RightLabel();
+                unit_load_state_label = new RightLabel();
+                unit_active_state_label = new RightLabel();
+                unit_load_path_label = new RightLabel();
+                unit_active_enter_timestamp_label = new RightLabel();
+                unit_active_exit_timestamp_label = new RightLabel();
+                unit_can_start_label = new RightLabel();
+                unit_can_reload_label = new RightLabel();
+
+                job_id_label = new RightLabel();
+                job_state_label = new RightLabel();
+                job_type_label = new RightLabel();
+
+                Table unit_table = new Table(9, 2, false);
+                unit_table.set_row_spacings(6);
+                unit_table.set_border_width(12);
+                unit_vbox.pack_start(unit_table, false, true, 0);
+
+                Table job_table = new Table(2, 2, false);
+                job_table.set_row_spacings(6);
+                job_table.set_border_width(12);
+                job_vbox.pack_start(job_table, false, true, 0);
+
+                unit_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Description:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_description_label, 1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Load State:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_load_state_label, 1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Active State:"), 0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_active_state_label, 1, 2, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Load Path:"), 0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_load_path_label, 1, 2, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_active_enter_timestamp_label, 1, 2, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Active Exit Timestamp:"), 0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_active_exit_timestamp_label, 1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Can Start/Stop:"), 0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_can_start_label, 1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Can Reload:"), 0, 1, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_can_reload_label, 1, 2, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+
+                job_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                job_table.attach(job_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                job_table.attach(new LeftLabel("State:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                job_table.attach(job_state_label, 1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                job_table.attach(new LeftLabel("Type:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                job_table.attach(job_type_label, 1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+
+                ButtonBox bbox = new HButtonBox();
+                bbox.set_layout(ButtonBoxStyle.START);
+                bbox.set_spacing(6);
+                unit_vbox.pack_start(bbox, false, true, 0);
+
+                start_button = new Button.with_mnemonic("_Start");
+                stop_button = new Button.with_mnemonic("Sto_p");
+                reload_button = new Button.with_mnemonic("_Reload");
+                restart_button = new Button.with_mnemonic("Res_tart");
+
+                start_button.clicked.connect(on_start);
+                stop_button.clicked.connect(on_stop);
+                reload_button.clicked.connect(on_reload);
+                restart_button.clicked.connect(on_restart);
+
+                bbox.pack_start(start_button, false, true, 0);
+                bbox.pack_start(stop_button, false, true, 0);
+                bbox.pack_start(restart_button, false, true, 0);
+                bbox.pack_start(reload_button, false, true, 0);
+
+                bbox = new HButtonBox();
+                bbox.set_layout(ButtonBoxStyle.START);
+                bbox.set_spacing(6);
+                job_vbox.pack_start(bbox, false, true, 0);
+
+                cancel_button = new Button.with_mnemonic("_Cancel");
+
+                cancel_button.clicked.connect(on_cancel);
+
+                bbox.pack_start(cancel_button, false, true, 0);
+
+                bus = Bus.get(BusType.SESSION);
+
+                manager = bus.get_object (
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1") as Manager;
+
+                clear_unit();
+                populate_unit_model();
+                populate_job_model();
+        }
+
+        public void populate_unit_model() throws DBus.Error {
+                unit_model.clear();
+
+                var list = manager.list_units();
+
+                foreach (var i in list) {
+                        TreeIter iter;
+
+                        unit_model.append(out iter);
+                        unit_model.set(iter,
+                                       0, i.id,
+                                       1, i.description,
+                                       2, i.load_state,
+                                       3, i.active_state,
+                                       4, i.job_type != "" ? "→ %s".printf(i.job_type) : "",
+                                       5, i.unit_path);
+                }
+        }
+
+        public void populate_job_model() throws DBus.Error {
+                job_model.clear();
+
+                var list = manager.list_jobs();
+
+                foreach (var i in list) {
+                        TreeIter iter;
+
+                        job_model.append(out iter);
+                        job_model.set(iter,
+                                      0, "%u".printf(i.id),
+                                      1, i.name,
+                                      2, "→ %s".printf(i.type),
+                                      3, i.state,
+                                      4, i.job_path);
+                }
+        }
+
+        public Unit? get_current_unit() {
+                TreePath p;
+                unit_view.get_cursor(out p, null);
+
+                if (p == null)
+                        return null;
+
+                TreeIter iter;
+                string path;
+
+                unit_model.get_iter(out iter, p);
+                unit_model.get(iter, 5, out path);
+
+                return bus.get_object (
+                                "org.freedesktop.systemd1",
+                                path,
+                                "org.freedesktop.systemd1.Unit") as Unit;
+        }
+
+        public void unit_changed() {
+                Unit u = get_current_unit();
+
+                if (u == null)
+                        clear_unit();
+                else
+                        show_unit(u);
+        }
+
+        public void clear_unit() {
+                start_button.set_sensitive(false);
+                stop_button.set_sensitive(false);
+                reload_button.set_sensitive(false);
+                restart_button.set_sensitive(false);
+
+                unit_id_label.set_text_or_na();
+                unit_description_label.set_text_or_na();
+                unit_load_state_label.set_text_or_na();
+                unit_active_state_label.set_text_or_na();
+                unit_load_path_label.set_text_or_na();
+                unit_active_enter_timestamp_label.set_text_or_na();
+                unit_active_exit_timestamp_label.set_text_or_na();
+                unit_can_reload_label.set_text_or_na();
+                unit_can_start_label.set_text_or_na();
+        }
+
+        public void show_unit(Unit unit) {
+                unit_id_label.set_text_or_na(unit.id);
+                unit_description_label.set_text_or_na(unit.description);
+                unit_load_state_label.set_text_or_na(unit.load_state);
+                unit_active_state_label.set_text_or_na(unit.active_state);
+                unit_load_path_label.set_text_or_na(unit.load_path);
+
+                uint64 t = unit.active_enter_timestamp;
+                if (t > 0) {
+                        TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) };
+                        unit_active_enter_timestamp_label.set_text_or_na(tv.to_iso8601());
+                } else
+                        unit_active_enter_timestamp_label.set_text_or_na();
+
+                t = unit.active_exit_timestamp;
+                if (t > 0) {
+                        TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) };
+                        unit_active_exit_timestamp_label.set_text_or_na(tv.to_iso8601());
+                } else
+                        unit_active_exit_timestamp_label.set_text_or_na();
+
+                bool b = unit.can_start;
+                start_button.set_sensitive(b);
+                stop_button.set_sensitive(b);
+                restart_button.set_sensitive(b);
+                unit_can_start_label.set_text_or_na(b ? "Yes" : "No");
+
+                b = unit.can_reload;
+                reload_button.set_sensitive(b);
+                unit_can_reload_label.set_text_or_na(b ? "Yes" : "No");
+        }
+
+        public Job? get_current_job() {
+                TreePath p;
+                job_view.get_cursor(out p, null);
+
+                if (p == null)
+                        return null;
+
+                TreeIter iter;
+                string path;
+
+                job_model.get_iter(out iter, p);
+                job_model.get(iter, 4, out path);
+
+                return bus.get_object (
+                                "org.freedesktop.systemd1",
+                                path,
+                                "org.freedesktop.systemd1.Job") as Job;
+        }
+
+        public void job_changed() {
+                Job j = get_current_job();
+
+                if (j == null)
+                        clear_job();
+                else
+                        show_job(j);
+        }
+
+        public void clear_job() {
+                job_id_label.set_text_or_na();
+                job_state_label.set_text_or_na();
+                job_type_label.set_text_or_na();
+        }
+
+        public void show_job(Job job) {
+                job_id_label.set_text_or_na("%u".printf(job.id));
+                job_state_label.set_text_or_na(job.state);
+                job_type_label.set_text_or_na(job.job_type);
+        }
+
+        public void on_start() {
+                Unit u = get_current_unit();
+
+                if (u == null)
+                        return;
+
+                try {
+                        u.start("replace");
+                } catch (DBus.Error e) {
+                        message("%s", e.message);
+                }
+        }
+
+        public void on_stop() {
+                Unit u = get_current_unit();
+
+                if (u == null)
+                        return;
+
+                try {
+                        u.stop("replace");
+                } catch (DBus.Error e) {
+                        message("%s", e.message);
+                }
+        }
+
+        public void on_reload() {
+                Unit u = get_current_unit();
+
+                if (u == null)
+                        return;
+
+                try {
+                        u.reload("replace");
+                } catch (DBus.Error e) {
+                        message("%s", e.message);
+                }
+        }
+
+        public void on_restart() {
+                Unit u = get_current_unit();
+
+                if (u == null)
+                        return;
+
+                try {
+                        u.restart("replace");
+                } catch (DBus.Error e) {
+                        message("%s", e.message);
+                }
+        }
+
+        public void on_cancel() {
+                Job j = get_current_job();
+
+                if (j == null)
+                        return;
+
+                try {
+                        j.cancel();
+                } catch (DBus.Error e) {
+                        message("%s", e.message);
+                }
+        }
+}
+
+int main (string[] args) {
+        Gtk.init(ref args);
+
+        try {
+                MainWindow window = new MainWindow();
+                window.show_all();
+        } catch (DBus.Error e) {
+                message("%s", e.message);
+        }
+
+        Gtk.main();
+        return 0;
+}
index abeb0ac..cbde75d 100644 (file)
@@ -1,45 +1,15 @@
 using DBus;
 using GLib;
 
-[DBus (name = "org.freedesktop.systemd1")]
-public interface Manager : DBus.Object {
-
-        public struct UnitInfo {
-                string id;
-                string description;
-                string load_state;
-                string active_state;
-                ObjectPath unit_path;
-                uint32 job_id;
-                string job_type;
-                ObjectPath job_path;
-        }
-
-        public struct JobInfo {
-                uint32 id;
-                string name;
-                string type;
-                string state;
-                ObjectPath job_path;
-                ObjectPath unit_path;
-        }
-
-        public abstract UnitInfo[] ListUnits() throws DBus.Error;
-        public abstract JobInfo[] ListJobs() throws DBus.Error;
-
-        public abstract ObjectPath LoadUnit(string name) throws DBus.Error;
-
-        public abstract void ClearJobs() throws DBus.Error;
-}
-
 static string type = null;
 static bool all = false;
+static bool replace = false;
 
 public static int job_info_compare(void* key1, void* key2) {
         Manager.JobInfo *j1 = (Manager.JobInfo*) key1;
         Manager.JobInfo *j2 = (Manager.JobInfo*) key2;
 
-        return Posix.strcmp(j1->name, j2->name);
+        return j1->id < j2->id ? -1 : (j1->id > j2->id ? 1 : 0);
 }
 
 public static int unit_info_compare(void* key1, void* key2) {
@@ -54,8 +24,9 @@ public static int unit_info_compare(void* key1, void* key2) {
 }
 
 static const OptionEntry entries[] = {
-        { "type", 't', 0, OptionArg.STRING, out type, "List only particular type of units", "TYPE" },
-        { "all",  'a', 0, OptionArg.NONE,   out all,  "Show all units, including dead ones", null  },
+        { "type",    't', 0, OptionArg.STRING, out type,    "List only particular type of units", "TYPE" },
+        { "all",     'a', 0, OptionArg.NONE,   out all,     "Show all units, including dead ones", null  },
+        { "replace", 0,   0, OptionArg.NONE,   out replace, "When installing a new job, replace existing conflicting ones.", null },
         { null }
 };
 
@@ -79,7 +50,7 @@ int main (string[] args) {
                                 "org.freedesktop.systemd1") as Manager;
 
                 if (args[1] == "list-units" || args.length <= 1) {
-                        var list = manager.ListUnits();
+                        var list = manager.list_units();
                         uint n = 0;
                         Posix.qsort(list, list.length, sizeof(Manager.UnitInfo), unit_info_compare);
 
@@ -109,19 +80,19 @@ int main (string[] args) {
 
 
                 } else if (args[1] == "list-jobs") {
-                        var list = manager.ListJobs();
+                        var list = manager.list_jobs();
                         Posix.qsort(list, list.length, sizeof(Manager.JobInfo), job_info_compare);
 
-                        stdout.printf("%-45s %-17s %-7s\n", "UNIT", "TYPE", "STATE");
+                        stdout.printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
 
                         foreach (var i in list)
-                                stdout.printf("%-45s → %-15s %-7s\n", i.name, i.type, i.state);
+                                stdout.printf("%4u %-45s → %-15s %-7s\n", i.id, i.name, i.type, i.state);
 
                         stdout.printf("\n%u jobs listed.\n", list.length);
 
                 } else if (args[1] == "clear-jobs") {
 
-                        manager.ClearJobs();
+                        manager.clear_jobs();
 
                 } else if (args[1] == "load") {
 
@@ -130,7 +101,65 @@ int main (string[] args) {
                                 return 1;
                         }
 
-                        manager.LoadUnit(args[2]);
+                        for (uint i = 2; i < args.length; i++)
+                                manager.load_unit(args[i]);
+
+                } else if (args[1] == "cancel") {
+
+                        if (args.length < 3) {
+                                stderr.printf("Missing argument.\n");
+                                return 1;
+                        }
+
+                        for (uint i = 2; i < args.length; i++) {
+                                uint32 id;
+
+                                if (args[i].scanf("%u", out id) != 1) {
+                                        stderr.printf("Failed to parse argument.\n");
+                                        return 1;
+                                }
+
+                                ObjectPath p = manager.get_job(id);
+
+                                Job j = bus.get_object (
+                                                "org.freedesktop.systemd1",
+                                                p,
+                                                "org.freedesktop.systemd1.Job") as Job;
+
+                                j.cancel();
+                        }
+
+                } else if (args[1] == "start" ||
+                           args[1] == "stop" ||
+                           args[1] == "reload" ||
+                           args[1] == "restart") {
+
+                        if (args.length < 3) {
+                                stderr.printf("Missing argument.\n");
+                                return 1;
+                        }
+
+                        for (uint i = 2; i < args.length; i++) {
+
+                                ObjectPath p = manager.get_unit(args[i]);
+
+                                Unit u = bus.get_object(
+                                                "org.freedesktop.systemd1",
+                                                p,
+                                                "org.freedesktop.systemd1.Unit") as Unit;
+
+                                string mode = replace ? "replace" : "fail";
+
+                                if (args[1] == "start")
+                                        u.start(mode);
+                                else if (args[1] == "stop")
+                                        u.stop(mode);
+                                else if (args[1] == "restart")
+                                        u.restart(mode);
+                                else if (args[1] == "reload")
+                                        u.reload(mode);
+                        }
+
                 } else {
                         stderr.printf("Unknown command %s.\n", args[1]);
                         return 1;
diff --git a/systemd-interfaces.vala b/systemd-interfaces.vala
new file mode 100644 (file)
index 0000000..bc54617
--- /dev/null
@@ -0,0 +1,61 @@
+using DBus;
+
+[DBus (name = "org.freedesktop.systemd1")]
+public interface Manager : DBus.Object {
+
+        public struct UnitInfo {
+                string id;
+                string description;
+                string load_state;
+                string active_state;
+                ObjectPath unit_path;
+                uint32 job_id;
+                string job_type;
+                ObjectPath job_path;
+        }
+
+        public struct JobInfo {
+                uint32 id;
+                string name;
+                string type;
+                string state;
+                ObjectPath job_path;
+                ObjectPath unit_path;
+        }
+
+        public abstract UnitInfo[] list_units() throws DBus.Error;
+        public abstract JobInfo[] list_jobs() throws DBus.Error;
+
+        public abstract ObjectPath get_unit(string name) throws DBus.Error;
+        public abstract ObjectPath load_unit(string name) throws DBus.Error;
+        public abstract ObjectPath get_job(uint32 id) throws DBus.Error;
+
+        public abstract void clear_jobs() throws DBus.Error;
+}
+
+[DBus (name = "org.freedesktop.systemd1.Unit")]
+public interface Unit : DBus.Object {
+        public abstract string id { owned get; }
+        public abstract string description { owned get; }
+        public abstract string load_state { owned get; }
+        public abstract string active_state { owned get; }
+        public abstract string load_path { owned get; }
+        public abstract uint64 active_enter_timestamp { owned get; }
+        public abstract uint64 active_exit_timestamp { owned get; }
+        public abstract bool can_reload { owned get; }
+        public abstract bool can_start { owned get; }
+
+        public abstract ObjectPath start(string mode) throws DBus.Error;
+        public abstract ObjectPath stop(string mode) throws DBus.Error;
+        public abstract ObjectPath restart(string mode) throws DBus.Error;
+        public abstract ObjectPath reload(string mode) throws DBus.Error;
+}
+
+[DBus (name = "org.freedesktop.systemd1.Job")]
+public interface Job : DBus.Object {
+        public abstract uint32 id { owned get; }
+        public abstract string state { owned get; }
+        public abstract string job_type { owned get; }
+
+        public abstract void cancel() throws DBus.Error;
+}