#include "log.h"
#include "dbus-job.h"
+JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
+ JobBusClient *cl;
+ size_t name_len;
+
+ name_len = strlen(name);
+ cl = malloc0(sizeof(JobBusClient) + name_len + 1);
+ if (!cl)
+ return NULL;
+
+ cl->bus = connection;
+ memcpy(cl->name, name, name_len + 1);
+ return cl;
+}
+
Job* job_new(Unit *unit, JobType type) {
Job *j;
}
void job_free(Job *j) {
+ JobBusClient *cl;
+
assert(j);
assert(!j->installed);
assert(!j->transaction_prev);
close_nointr_nofail(j->timer_watch.fd);
}
- free(j->bus_client);
+ while ((cl = j->bus_client_list)) {
+ LIST_REMOVE(JobBusClient, client, j->bus_client_list, cl);
+ free(cl);
+ }
free(j);
}
void job_uninstall(Job *j) {
assert(j->installed);
+ assert(j->unit->job == j);
/* Detach from next 'bigger' objects */
bus_job_send_removed_signal(j);
- if (j->unit->job == j) {
- j->unit->job = NULL;
- unit_add_to_gc_queue(j->unit);
- }
+ j->unit->job = NULL;
+ unit_add_to_gc_queue(j->unit);
hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
j->installed = false;
}
-void job_install(Job *j) {
+static bool job_type_allows_late_merge(JobType t) {
+ /* Tells whether it is OK to merge a job of type 't' with an already
+ * running job.
+ * Reloads cannot be merged this way. Think of the sequence:
+ * 1. Reload of a daemon is in progress; the daemon has already loaded
+ * its config file, but hasn't completed the reload operation yet.
+ * 2. Edit foo's config file.
+ * 3. Trigger another reload to have the daemon use the new config.
+ * Should the second reload job be merged into the first one, the daemon
+ * would not know about the new config.
+ * JOB_RESTART jobs on the other hand can be merged, because they get
+ * patched into JOB_START after stopping the unit. So if we see a
+ * JOB_RESTART running, it means the unit hasn't stopped yet and at
+ * this time the merge is still allowed. */
+ return !(t == JOB_RELOAD || t == JOB_RELOAD_OR_START);
+}
+
+static void job_merge_into_installed(Job *j, Job *other) {
+ assert(j->installed);
+ assert(j->unit == other->unit);
+
+ j->type = job_type_lookup_merge(j->type, other->type);
+ assert(j->type >= 0);
+
+ j->override = j->override || other->override;
+}
+
+Job* job_install(Job *j) {
Job *uj = j->unit->job;
+ assert(!j->installed);
+
if (uj) {
- job_uninstall(uj);
- job_free(uj);
+ if (job_type_is_conflicting(uj->type, j->type))
+ job_finish_and_invalidate(uj, JOB_CANCELED);
+ else {
+ /* not conflicting, i.e. mergeable */
+
+ if (uj->state == JOB_WAITING ||
+ (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
+ job_merge_into_installed(uj, j);
+ log_debug("Merged into installed job %s/%s as %u",
+ uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+ return uj;
+ } else {
+ /* already running and not safe to merge into */
+ /* Patch uj to become a merged job and re-run it. */
+ /* XXX It should be safer to queue j to run after uj finishes, but it is
+ * not currently possible to have more than one installed job per unit. */
+ job_merge_into_installed(uj, j);
+ log_debug("Merged into running job, re-running: %s/%s as %u",
+ uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+ uj->state = JOB_WAITING;
+ return uj;
+ }
+ }
}
+ /* Install the job */
j->unit->job = j;
j->installed = true;
j->manager->n_installed_jobs ++;
log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+ return j;
}
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
case JOB_DONE:
if (u->condition_result)
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Started %s.", unit_description(u));
break;
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s.", unit_description(u));
unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
break;
case JOB_DEPENDENCY:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s.", unit_description(u));
break;
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s.", unit_description(u));
break;
default:
switch (result) {
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s.", unit_description(u));
break;
case JOB_DONE:
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s.", unit_description(u));
break;
default: