systemadm_CPPFLAGS = $(AM_CPPFLAGS) $(DBUSGLIB_CFLAGS) $(GTK_CFLAGS)
systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS)
+
+CLEANFILES = \
+ systemd-interfaces.c \
+ systemctl.c \
+ systemadm.c
"<node>"
" <interface name=\"org.freedesktop.systemd1.Job\">"
" <method name=\"Cancel\"/>"
+ " <signal name=\"Changed\"/>"
" <property name=\"Id\" type=\"u\" access=\"read\"/>"
" <property name=\"Unit\" type=\"(so)\" access=\"read\"/>"
" <property name=\"JobType\" type=\"s\" access=\"read\"/>"
const DBusObjectPathVTable bus_job_vtable = {
.message_function = bus_job_message_handler
};
+
+void bus_job_send_change_signal(Job *j) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+
+ assert(j);
+ assert(j->in_dbus_queue);
+
+ LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
+ j->in_dbus_queue = false;
+
+ if (set_isempty(j->manager->subscribed))
+ return;
+
+ if (!(p = job_dbus_path(j)))
+ goto oom;
+
+ if (j->sent_dbus_new_signal) {
+ /* Send a change signal */
+
+ if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Job", "Changed")))
+ goto oom;
+ } else {
+ /* Send a new signal */
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "JobNew")))
+ goto oom;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &j->id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ }
+
+ if (!dbus_connection_send(j->manager->bus, m, NULL))
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ j->sent_dbus_new_signal = true;
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate job change signal.");
+}
+
+void bus_job_send_removed_signal(Job *j) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+
+ assert(j);
+
+ if (set_isempty(j->manager->subscribed) || !j->sent_dbus_new_signal)
+ return;
+
+ if (!(p = job_dbus_path(j)))
+ goto oom;
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "JobRemoved")))
+ goto oom;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &j->id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (!dbus_connection_send(j->manager->bus, m, NULL))
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate job remove signal.");
+}
" <method name=\"ListJobs\">" \
" <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>" \
" </method>" \
+ " <method name=\"Subscribe\"/>" \
+ " <method name=\"Unsubscribe\"/>" \
+ " <signal name=\"UnitNew\">" \
+ " <arg name=\"id\" type=\"s\"/>" \
+ " <arg name=\"unit\" type=\"o\"/>" \
+ " </signal>" \
+ " <signal name=\"UnitRemoved\">" \
+ " <arg name=\"id\" type=\"s\"/>" \
+ " <arg name=\"unit\" type=\"o\"/>" \
+ " </signal>" \
+ " <signal name=\"JobNew\">" \
+ " <arg name=\"id\" type=\"u\"/>" \
+ " <arg name=\"job\" type=\"o\"/>" \
+ " </signal>" \
+ " <signal name=\"JobRemoved\">" \
+ " <arg name=\"id\" type=\"u\"/>" \
+ " <arg name=\"job\" type=\"o\"/>" \
+ " </signal>" \
" </interface>" \
BUS_PROPERTIES_INTERFACE \
BUS_INTROSPECTABLE_INTERFACE
if (!dbus_message_iter_close_container(&iter, &sub))
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1", "Subscribe")) {
+ char *client;
+
+ if (!(client = strdup(dbus_message_get_sender(message))))
+ goto oom;
+
+ r = set_put(m->subscribed, client);
+
+ if (r < 0)
+ return bus_send_error_reply(m, message, NULL, r);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1", "Unsubscribe")) {
+ char *client;
+
+ if (!(client = set_remove(m->subscribed, (char*) dbus_message_get_sender(message))))
+ return bus_send_error_reply(m, message, NULL, -ENOENT);
+
+ free(client);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
char *introspection = NULL;
FILE *f;
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>"
" <arg name=\"job\" type=\"o\" direction=\"out\"/>"
" </method>"
+ " <signal name=\"Changed\"/>"
" <property name=\"Id\" type=\"s\" access=\"read\"/>"
" <property name=\"Description\" type=\"s\" access=\"read\"/>"
" <property name=\"LoadState\" type=\"s\" access=\"read\"/>"
const DBusObjectPathVTable bus_unit_vtable = {
.message_function = bus_unit_message_handler
};
+
+void bus_unit_send_change_signal(Unit *u) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+
+ assert(u);
+ assert(u->meta.in_dbus_queue);
+
+ LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
+ u->meta.in_dbus_queue = false;
+
+ if (set_isempty(u->meta.manager->subscribed))
+ return;
+
+ if (!(p = unit_dbus_path(u)))
+ goto oom;
+
+ if (u->meta.sent_dbus_new_signal) {
+ /* Send a change signal */
+
+ if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Unit", "Changed")))
+ goto oom;
+ } else {
+ const char *id;
+ /* Send a new signal */
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitNew")))
+ goto oom;
+
+ id = unit_id(u);
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ }
+
+ if (!dbus_connection_send(u->meta.manager->bus, m, NULL))
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ u->meta.sent_dbus_new_signal = true;
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate unit change/new signal.");
+}
+
+void bus_unit_send_removed_signal(Unit *u) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+ const char *id;
+
+ assert(u);
+
+ if (set_isempty(u->meta.manager->subscribed) || !u->meta.sent_dbus_new_signal)
+ return;
+
+ if (!(p = unit_dbus_path(u)))
+ goto oom;
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitRemoved")))
+ goto oom;
+
+ id = unit_id(u);
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (!dbus_connection_send(u->meta.manager->bus, m, NULL))
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate unit remove signal.");
+}
log_error("Failed to rearm timer: %s", strerror(-r));
}
-void bus_dispatch(Manager *m) {
+static DBusHandlerResult bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
+ Manager *m = data;
+ DBusError error;
+
+ assert(connection);
+ assert(message);
+ assert(m);
+
+ dbus_error_init(&error);
+
+ /* log_debug("Got D-Bus request: %s.%s() on %s", */
+ /* dbus_message_get_interface(message), */
+ /* dbus_message_get_member(message), */
+ /* dbus_message_get_path(message)); */
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+ log_error("Warning! D-Bus connection terminated.");
+
+ /* FIXME: we probably should restart D-Bus here */
+
+ } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+ const char *name, *old, *new;
+
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID))
+ log_error("Failed to parse NameOwnerChanged message: %s", error.message);
+ else {
+ if (set_remove(m->subscribed, (char*) name))
+ log_debug("Subscription client vanished: %s (left: %u)", name, set_size(m->subscribed));
+ }
+ }
+
+ dbus_error_free(&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+unsigned bus_dispatch(Manager *m) {
assert(m);
+ if (!m->request_bus_dispatch)
+ return 0;
+
if (dbus_connection_dispatch(m->bus) == DBUS_DISPATCH_COMPLETE)
m->request_bus_dispatch = false;
+
+ return 1;
}
static int request_name(Manager *m) {
if (m->bus)
return 0;
+ if (!(m->subscribed = set_new(string_hash_func, string_compare_func)))
+ return -ENOMEM;
+
dbus_connection_set_change_sigpipe(FALSE);
dbus_error_init(&error);
!dbus_connection_set_timeout_functions(m->bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL) ||
!dbus_connection_register_object_path(m->bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
!dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) ||
- !dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m)) {
+ !dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) ||
+ !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) {
+ bus_done(m);
+ return -ENOMEM;
+ }
+
+ dbus_bus_add_match(m->bus,
+ "type='signal',"
+ "sender='"DBUS_SERVICE_DBUS"',"
+ "interface='"DBUS_INTERFACE_DBUS"',"
+ "path='"DBUS_PATH_DBUS"'",
+ &error);
+
+ if (dbus_error_is_set(&error)) {
+ log_error("Failed to register match: %s", error.message);
+ dbus_error_free(&error);
bus_done(m);
return -ENOMEM;
}
dbus_connection_unref(m->bus);
m->bus = NULL;
}
+
+ if (m->subscribed) {
+ char *c;
+
+ while ((c = set_steal_first(m->subscribed)))
+ free(c);
+
+ set_free(m->subscribed);
+ m->subscribed = NULL;
+ }
}
DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message, const char*introspection, const BusProperty *properties) {
int bus_init(Manager *m);
void bus_done(Manager *m);
-void bus_dispatch(Manager *m);
+unsigned bus_dispatch(Manager *m);
void bus_watch_event(Manager *m, Watch *w, int events);
void bus_timeout_event(Manager *m, Watch *w, int events);
extern const DBusObjectPathVTable bus_job_vtable;
extern const DBusObjectPathVTable bus_unit_vtable;
+void bus_unit_send_change_signal(Unit *u);
+void bus_unit_send_removed_signal(Unit *u);
+
+void bus_job_send_change_signal(Job *j);
+void bus_job_send_removed_signal(Job *j);
+
#endif
device_set_state(DEVICE(u), DEVICE_AVAILABLE);
}
+ unit_add_to_dbus_queue(u);
+
return 0;
fail:
- implement timer
-- implement mount/automount
-
-- more process attributes: cpu affinity, cpu scheduling
+- implement automount
- create session/pgroup for child processes? handle input on console properly? interactive fsck? interactive luks password?
/* Detach from next 'bigger' objects */
if (j->installed) {
+ bus_job_send_removed_signal(j);
+
if (j->unit->meta.job == j)
j->unit->meta.job = NULL;
/* Detach from next 'smaller' objects */
manager_transaction_unlink_job(j->manager, j);
+ if (j->in_run_queue)
+ LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
+
+ if (j->in_dbus_queue)
+ LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
+
free(j);
}
return -EAGAIN;
j->state = JOB_RUNNING;
+ job_add_to_dbus_queue(j);
switch (j->type) {
assert(j->installed);
log_debug("Job %s/%s finished, success=%s", unit_id(j->unit), job_type_to_string(j->type), yes_no(success));
+ job_add_to_dbus_queue(j);
/* Patch restart jobs so that they become normal start jobs */
if (success && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) {
j->state = JOB_RUNNING;
j->type = JOB_START;
- job_schedule_run(j);
+ job_add_to_run_queue(j);
return 0;
}
/* Try to start the next jobs that can be started */
SET_FOREACH(other, u->meta.dependencies[UNIT_AFTER], i)
if (other->meta.job)
- job_schedule_run(other->meta.job);
+ job_add_to_run_queue(other->meta.job);
SET_FOREACH(other, u->meta.dependencies[UNIT_BEFORE], i)
if (other->meta.job)
- job_schedule_run(other->meta.job);
+ job_add_to_run_queue(other->meta.job);
return 0;
}
-void job_schedule_run(Job *j) {
+void job_add_to_run_queue(Job *j) {
assert(j);
assert(j->installed);
j->in_run_queue = true;
}
+void job_add_to_dbus_queue(Job *j) {
+ assert(j);
+ assert(j->installed);
+
+ if (j->in_dbus_queue)
+ return;
+
+ LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j);
+ j->in_dbus_queue = true;
+}
+
char *job_dbus_path(Job *j) {
char *p;
bool in_run_queue:1;
bool matters_to_anchor:1;
bool forced:1;
+ bool in_dbus_queue:1;
+ bool sent_dbus_new_signal:1;
LIST_FIELDS(Job, transaction);
LIST_FIELDS(Job, run_queue);
+ LIST_FIELDS(Job, dbus_queue);
LIST_HEAD(JobDependency, subject_list);
LIST_HEAD(JobDependency, object_list);
bool job_is_runnable(Job *j);
-void job_schedule_run(Job *j);
+void job_add_to_run_queue(Job *j);
+void job_add_to_dbus_queue(Job *j);
+
int job_run_and_invalidate(Job *j);
int job_finish_and_invalidate(Job *j, bool success);
assert(!j->transaction_next);
assert(!j->transaction_prev);
- job_schedule_run(j);
+ job_add_to_run_queue(j);
+ job_add_to_dbus_queue(j);
}
/* As last step, kill all remaining job dependencies. */
return hashmap_get(m->units, name);
}
-void manager_dispatch_load_queue(Manager *m) {
+unsigned manager_dispatch_load_queue(Manager *m) {
Meta *meta;
+ unsigned n = 0;
assert(m);
/* Make sure we are not run recursively */
if (m->dispatching_load_queue)
- return;
+ return 0;
m->dispatching_load_queue = true;
assert(meta->in_load_queue);
unit_load(UNIT(meta));
+ n++;
}
m->dispatching_load_queue = false;
+ return n;
}
int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
}
unit_add_to_load_queue(ret);
+ unit_add_to_dbus_queue(ret);
+
manager_dispatch_load_queue(m);
*_ret = ret;
job_free(j);
}
-void manager_dispatch_run_queue(Manager *m) {
+unsigned manager_dispatch_run_queue(Manager *m) {
Job *j;
+ unsigned n = 0;
if (m->dispatching_run_queue)
- return;
+ return 0;
m->dispatching_run_queue = true;
assert(j->in_run_queue);
job_run_and_invalidate(j);
+ n++;
}
m->dispatching_run_queue = false;
+ return n;
+}
+
+unsigned manager_dispatch_dbus_queue(Manager *m) {
+ Job *j;
+ Meta *meta;
+ unsigned n = 0;
+
+ assert(m);
+
+ if (m->dispatching_dbus_queue)
+ return 0;
+
+ m->dispatching_dbus_queue = true;
+
+ while ((meta = m->dbus_unit_queue)) {
+ Unit *u = (Unit*) meta;
+ assert(u->meta.in_dbus_queue);
+
+ bus_unit_send_change_signal(u);
+ n++;
+ }
+
+ while ((j = m->dbus_job_queue)) {
+ assert(j->in_dbus_queue);
+
+ bus_job_send_change_signal(j);
+ n++;
+ }
+
+ m->dispatching_dbus_queue = false;
+ return n;
}
static int manager_dispatch_sigchld(Manager *m) {
sleep(1);
}
- manager_dispatch_run_queue(m);
+ if (manager_dispatch_load_queue(m) > 0)
+ continue;
- if (m->request_bus_dispatch) {
- bus_dispatch(m);
+ if (manager_dispatch_run_queue(m) > 0)
+ continue;
+
+ if (bus_dispatch(m) > 0)
+ continue;
+
+ if (manager_dispatch_dbus_queue(m) > 0)
continue;
- }
if ((n = epoll_wait(m->epoll_fd, &event, 1, -1)) < 0) {
/* Jobs that need to be run */
LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */
+ /* Units and jobs that have not yet been announced via
+ * D-Bus. When something about a job changes it is added here
+ * if it is not in there yet. This allows easy coalescing of
+ * D-Bus change signals. */
+ LIST_HEAD(Meta, dbus_unit_queue);
+ LIST_HEAD(Job, dbus_job_queue);
+
/* Jobs to be added */
Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */
JobDependency *transaction_anchor;
bool dispatching_load_queue:1;
bool dispatching_run_queue:1;
+ bool dispatching_dbus_queue:1;
bool is_init:1;
/* Data specific to the D-Bus subsystem */
DBusConnection *bus;
+ Set *subscribed;
};
Manager* manager_new(void);
void manager_clear_jobs(Manager *m);
-void manager_dispatch_load_queue(Manager *m);
-void manager_dispatch_run_queue(Manager *m);
+unsigned manager_dispatch_load_queue(Manager *m);
+unsigned manager_dispatch_run_queue(Manager *m);
+unsigned manager_dispatch_dbus_queue(Manager *m);
int manager_loop(Manager *m);
if ((r = mount_add_path_links(MOUNT(u))) < 0)
goto fail;
+ unit_add_to_dbus_queue(u);
+
return 0;
fail:
position = WindowPosition.CENTER;
set_default_size(1000, 700);
set_border_width(12);
- destroy.connect(Gtk.main_quit);
+ destroy += Gtk.main_quit;
Notebook notebook = new Notebook();
add(notebook);
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_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(Unit));
+ job_model = new ListStore(5, typeof(string), typeof(string), typeof(string), typeof(string), typeof(Job));
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.cursor_changed += unit_changed;
+ job_view.cursor_changed += 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);
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);
+ start_button.clicked += on_start;
+ stop_button.clicked += on_stop;
+ reload_button.clicked += on_reload;
+ restart_button.clicked += on_restart;
bbox.pack_start(start_button, false, true, 0);
bbox.pack_start(stop_button, false, true, 0);
cancel_button = new Button.with_mnemonic("_Cancel");
- cancel_button.clicked.connect(on_cancel);
+ cancel_button.clicked += on_cancel;
bbox.pack_start(cancel_button, false, true, 0);
bus = Bus.get(BusType.SESSION);
- manager = bus.get_object (
+ manager = bus.get_object(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1") as Manager;
+ manager.unit_new += on_unit_new;
+ manager.job_new += on_job_new;
+ manager.unit_removed += on_unit_removed;
+ manager.job_removed += on_job_removed;
+
+ manager.subscribe();
+
clear_unit();
populate_unit_model();
populate_job_model();
foreach (var i in list) {
TreeIter iter;
+ Unit u = bus.get_object(
+ "org.freedesktop.systemd1",
+ i.unit_path,
+ "org.freedesktop.systemd1.Unit") as Unit;
+
unit_model.append(out iter);
unit_model.set(iter,
0, i.id,
2, i.load_state,
3, i.active_state,
4, i.job_type != "" ? "→ %s".printf(i.job_type) : "",
- 5, i.unit_path);
+ 5, u);
}
}
foreach (var i in list) {
TreeIter iter;
+ Job j = bus.get_object(
+ "org.freedesktop.systemd1",
+ i.job_path,
+ "org.freedesktop.systemd1.Job") as Job;
+
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);
+ 4, j);
}
}
return null;
TreeIter iter;
- string path;
+ Unit u;
unit_model.get_iter(out iter, p);
- unit_model.get(iter, 5, out path);
+ unit_model.get(iter, 5, out u);
- return bus.get_object (
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Unit") as Unit;
+ return u;
}
public void unit_changed() {
return null;
TreeIter iter;
- string path;
+ Job *j;
job_model.get_iter(out iter, p);
- job_model.get(iter, 4, out path);
+ job_model.get(iter, 4, out j);
- return bus.get_object (
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Job") as Job;
+ return j;
}
public void job_changed() {
message("%s", e.message);
}
}
+
+ public void on_unit_new(string id, ObjectPath path) {
+ stderr.printf("new path %s", path);
+
+ Unit u = bus.get_object(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Unit") as Unit;
+
+ string t = "";
+ Unit.JobLink jl = u.job;
+
+ if (jl.id != 0) {
+ Job j = bus.get_object(
+ "org.freedesktop.systemd1",
+ jl.path,
+ "org.freedesktop.systemd1.Job") as Job;
+
+ t = j.job_type;
+ }
+
+ TreeIter iter;
+ unit_model.append(out iter);
+ unit_model.set(iter,
+ 0, u.id,
+ 1, u.description,
+ 2, u.load_state,
+ 3, u.active_state,
+ 4, t != "" ? "→ %s".printf(t) : "",
+ 5, u);
+ }
+
+ public void on_job_new(uint32 id, ObjectPath path) {
+ stderr.printf("new path %s", path);
+
+ Job j = bus.get_object(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Job") as Job;
+
+ TreeIter iter;
+ job_model.append(out iter);
+ job_model.set(iter,
+ 0, "%u".printf(j.id),
+ 1, j.unit.id,
+ 2, "→ %s".printf(j.job_type),
+ 3, j.state,
+ 4, j);
+ }
+
+ public void on_unit_removed(string id, ObjectPath path) {
+ stdout.printf("Unit %s removed.\n", id);
+
+ /* FIXME */
+ }
+
+ public void on_job_removed(uint32 id, ObjectPath path) {
+ stdout.printf("Job %u removed.\n", id);
+
+ /* FIXME */
+ }
}
int main (string[] args) {
return Posix.strcmp(u1->id, u2->id);
}
+public void on_unit_new(string id, ObjectPath path) {
+ stdout.printf("Unit %s added.\n", id);
+}
+
+public void on_job_new(uint32 id, ObjectPath path) {
+ stdout.printf("Job %u added.\n", id);
+}
+
+public void on_unit_removed(string id, ObjectPath path) {
+ stdout.printf("Unit %s removed.\n", id);
+}
+
+public void on_job_removed(uint32 id, ObjectPath path) {
+ stdout.printf("Job %u removed.\n", id);
+}
+
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 },
int main (string[] args) {
- OptionContext context = new OptionContext(" -- Control systemd");
+ OptionContext context = new OptionContext(" [COMMAND [ARGUMENT...]]");
context.add_main_entries(entries, null);
+ context.set_description(
+ "Commands:\n" +
+ " list-units List units\n" +
+ " list-jobs List jobs\n" +
+ " clear-jobs Cancel all jobs\n" +
+ " load [NAME...] Load one or more units\n" +
+ " cancel [JOB...] Cancel one or more jobs\n" +
+ " start [NAME...] Start on or more units\n" +
+ " stop [NAME...] Stop on or more units\n" +
+ " restart [NAME...] Restart on or more units\n" +
+ " reload [NAME...] Reload on or more units\n" +
+ " monitor Monitor unit/job changes\n");
try {
context.parse(ref args);
u.reload(mode);
}
+ } else if (args[1] == "monitor") {
+
+ manager.subscribe();
+
+ manager.unit_new += on_unit_new;
+ manager.unit_removed += on_unit_removed;
+ manager.job_new += on_job_new;
+ manager.job_removed += on_job_removed;
+
+ MainLoop l = new MainLoop();
+ l.run();
+
} else {
stderr.printf("Unknown command %s.\n", args[1]);
return 1;
public abstract ObjectPath get_job(uint32 id) throws DBus.Error;
public abstract void clear_jobs() throws DBus.Error;
+
+ public abstract void subscribe() throws DBus.Error;
+ public abstract void unsubscribe() throws DBus.Error;
+
+ public abstract signal void unit_new(string id, ObjectPath path);
+ public abstract signal void unit_removed(string id, ObjectPath path);
+ public abstract signal void job_new(uint32 id, ObjectPath path);
+ public abstract signal void job_removed(uint32 id, ObjectPath path);
}
[DBus (name = "org.freedesktop.systemd1.Unit")]
public interface Unit : DBus.Object {
+ public struct JobLink {
+ uint32 id;
+ ObjectPath path;
+ }
+
public abstract string id { owned get; }
public abstract string description { owned get; }
public abstract string load_state { 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 JobLink job { owned get; /* FIXME: this setter is a temporary fix to make valac not segfault */ set; }
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;
+
+ public abstract signal void changed();
}
[DBus (name = "org.freedesktop.systemd1.Job")]
public interface Job : DBus.Object {
+ public struct UnitLink {
+ string id;
+ ObjectPath path;
+ }
+
public abstract uint32 id { owned get; }
public abstract string state { owned get; }
public abstract string job_type { owned get; }
+ public abstract UnitLink unit { owned get; /* FIXME: this setter is a temporary fix to make valac not segfault */ set; }
public abstract void cancel() throws DBus.Error;
+
+ public abstract signal void changed();
}
if (!u->meta.id)
u->meta.id = s;
+ unit_add_to_dbus_queue(u);
return 0;
}
return -ENOENT;
u->meta.id = s;
+
+ unit_add_to_dbus_queue(u);
return 0;
}
free(u->meta.description);
u->meta.description = s;
+
+ unit_add_to_dbus_queue(u);
return 0;
}
u->meta.in_load_queue = true;
}
+void unit_add_to_dbus_queue(Unit *u) {
+ assert(u);
+
+ if (u->meta.load_state == UNIT_STUB || u->meta.in_dbus_queue || set_isempty(u->meta.manager->subscribed))
+ return;
+
+ LIST_PREPEND(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
+ u->meta.in_dbus_queue = true;
+}
+
static void bidi_set_free(Unit *u, Set *s) {
Iterator i;
Unit *other;
assert(u);
+ bus_unit_send_removed_signal(u);
+
/* Detach from next 'bigger' objects */
SET_FOREACH(t, u->meta.names, i)
if (u->meta.in_load_queue)
LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta);
+ if (u->meta.in_dbus_queue)
+ LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
+
if (u->meta.load_state == UNIT_LOADED)
if (UNIT_VTABLE(u)->done)
UNIT_VTABLE(u)->done(u);
if ((r = ensure_merge(&u->meta.dependencies[d], other->meta.dependencies[d])) < 0)
return r;
+ unit_add_to_dbus_queue(u);
+
return 0;
}
goto fail;
u->meta.load_state = UNIT_LOADED;
+ unit_add_to_dbus_queue(u);
return 0;
fail:
u->meta.load_state = UNIT_FAILED;
+ unit_add_to_dbus_queue(u);
return r;
}
* restart" state where it waits for a holdoff timer to elapse
* before it will start again. */
+ unit_add_to_dbus_queue(u);
return UNIT_VTABLE(u)->start(u);
}
if (state == UNIT_DEACTIVATING)
return 0;
+ unit_add_to_dbus_queue(u);
return UNIT_VTABLE(u)->stop(u);
}
if (unit_active_state(u) != UNIT_ACTIVE)
return -ENOEXEC;
+ unit_add_to_dbus_queue(u);
return UNIT_VTABLE(u)->reload(u);
}
/* So we reached a different state for this
* job. Let's see if we can run it now if it
* failed previously due to EAGAIN. */
- job_schedule_run(u->meta.job);
+ job_add_to_run_queue(u->meta.job);
else {
assert(u->meta.job->state == JOB_RUNNING);
/* Maybe we finished startup and are now ready for being
* stopped because unneeded? */
unit_check_uneeded(u);
+
+ unit_add_to_dbus_queue(u);
}
int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
return r;
}
+ unit_add_to_dbus_queue(u);
return 0;
}
Job *job;
bool in_load_queue:1;
+ bool in_dbus_queue:1;
+ bool sent_dbus_new_signal:1;
/* If we go down, pull down everything that depends on us, too */
bool recursive_stop;
/* Per type list */
LIST_FIELDS(Meta, units_per_type);
+
+ /* D-Bus queue */
+ LIST_FIELDS(Meta, dbus_queue);
};
#include "service.h"
int unit_set_description(Unit *u, const char *description);
void unit_add_to_load_queue(Unit *u);
+void unit_add_to_dbus_queue(Unit *u);
int unit_merge(Unit *u, Unit *other);