#include "load-dropin.h"
#include "log.h"
#include "dbus-job.h"
+#include "special.h"
+#include "sync.h"
+#include "virt.h"
JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
JobBusClient *cl;
assert(other->type == JOB_NOP);
j->override = j->override || other->override;
+ j->irreversible = j->irreversible || other->irreversible;
+ j->ignore_order = j->ignore_order || other->ignore_order;
}
Job* job_install(Job *j) {
if (uj) {
if (j->type != JOB_NOP && job_type_is_conflicting(uj->type, j->type))
- job_finish_and_invalidate(uj, JOB_CANCELED, true);
+ job_finish_and_invalidate(uj, JOB_CANCELED, false);
else {
/* not conflicting, i.e. mergeable */
if (j->type == JOB_NOP || 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);
+ log_debug_unit(uj->unit->id,
+ "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 */
/* 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);
+ log_debug_unit(uj->unit->id,
+ "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;
}
*pj = 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);
+ log_debug_unit(j->unit->id,
+ "Installed new job %s/%s as %u",
+ j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
return j;
}
pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
if (*pj) {
- log_debug("Unit %s already has a job installed. Not installing deserialized job.", j->unit->id);
+ log_debug_unit(j->unit->id,
+ "Unit %s already has a job installed. Not installing deserialized job.",
+ j->unit->id);
return -EEXIST;
}
*pj = j;
j->installed = true;
- log_debug("Reinstalled deserialized job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+ log_debug_unit(j->unit->id,
+ "Reinstalled deserialized job %s/%s as %u",
+ j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
return 0;
}
"%s-> Job %u:\n"
"%s\tAction: %s -> %s\n"
"%s\tState: %s\n"
- "%s\tForced: %s\n",
+ "%s\tForced: %s\n"
+ "%s\tIrreversible: %s\n",
prefix, j->id,
prefix, j->unit->id, job_type_to_string(j->type),
prefix, job_state_to_string(j->state),
- prefix, yes_no(j->override));
+ prefix, yes_no(j->override),
+ prefix, yes_no(j->irreversible));
}
/*
}
static void job_change_type(Job *j, JobType newtype) {
- log_debug("Converting job %s/%s -> %s/%s",
- j->unit->id, job_type_to_string(j->type),
- j->unit->id, job_type_to_string(newtype));
+ log_debug_unit(j->unit->id,
+ "Converting job %s/%s -> %s/%s",
+ j->unit->id, job_type_to_string(j->type),
+ j->unit->id, job_type_to_string(newtype));
j->type = newtype;
}
sd_id128_t mid;
mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(mid),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
} else if (t == JOB_STOP)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
else if (t == JOB_RELOAD)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
j->result = result;
- log_debug("Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
+ log_debug_unit(u->id, "Job %s/%s finished, result=%s",
+ u->id, job_type_to_string(t), job_result_to_string(result));
job_print_status_message(u, t, result);
job_log_status_message(u, t, result);
}
/* Trigger OnFailure dependencies that are not generated by
- * the unit itself. We don't tread JOB_CANCELED as failure in
+ * the unit itself. We don't treat JOB_CANCELED as failure in
* this context. And JOB_FAILURE is already handled by the
* unit itself. */
if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
- log_notice("Job %s/%s failed with result '%s'.",
+ log_struct_unit(LOG_NOTICE,
+ u->id,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
+ "Job %s/%s failed with result '%s'.",
u->id,
job_type_to_string(t),
- job_result_to_string(result));
+ job_result_to_string(result),
+ NULL);
unit_trigger_on_failure(u);
}
assert(j);
assert(w == &j->timer_watch);
- log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
+ log_warning_unit(j->unit->id, "Job %s/%s timed out.",
+ j->unit->id, job_type_to_string(j->type));
job_finish_and_invalidate(j, JOB_TIMEOUT, true);
}
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
fprintf(f, "job-override=%s\n", yes_no(j->override));
+ fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
/* Cannot save bus clients. Just note the fact that we're losing
log_debug("Failed to parse job override flag %s", v);
else
j->override = j->override || b;
+ } else if (streq(l, "job-irreversible")) {
+ int b = parse_boolean(v);
+ if (b < 0)
+ log_debug("Failed to parse job irreversible flag %s", v);
+ else
+ j->irreversible = j->irreversible || b;
} else if (streq(l, "job-sent-dbus-new-signal")) {
int b = parse_boolean(v);
if (b < 0)
return 0;
}
+void job_shutdown_magic(Job *j) {
+ assert(j);
+
+ /* The shutdown target gets some special treatment here: we
+ * tell the kernel to begin with flushing its disk caches, to
+ * optimize shutdown time a bit. Ideally we wouldn't hardcode
+ * this magic into PID 1. However all other processes aren't
+ * options either since they'd exit much sooner than PID 1 and
+ * asynchronous sync() would cause their exit to be
+ * delayed. */
+
+ if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
+ return;
+
+ if (j->type != JOB_START)
+ return;
+
+ if (detect_container(NULL) > 0)
+ return;
+
+ asynchronous_sync();
+}
+
static const char* const job_state_table[_JOB_STATE_MAX] = {
[JOB_WAITING] = "waiting",
[JOB_RUNNING] = "running"
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
+ [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
[JOB_ISOLATE] = "isolate",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"