+ 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);
+}
+
+int job_serialize(Job *j, FILE *f, FDSet *fds) {
+ fprintf(f, "job-id=%u\n", j->id);
+ 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
+ * them. job_send_message() will fallback to broadcasting. */
+ fprintf(f, "job-forgot-bus-clients=%s\n",
+ yes_no(j->forgot_bus_clients || j->bus_client_list));
+ if (j->timer_watch.type == WATCH_JOB_TIMER) {
+ int copy = fdset_put_dup(fds, j->timer_watch.fd);
+ if (copy < 0)
+ return copy;
+ fprintf(f, "job-timer-watch-fd=%d\n", copy);
+ }
+
+ /* End marker */
+ fputc('\n', f);
+ return 0;
+}
+
+int job_deserialize(Job *j, FILE *f, FDSet *fds) {
+ for (;;) {
+ char line[LINE_MAX], *l, *v;
+ size_t k;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ return 0;
+ return -errno;
+ }
+
+ char_array_0(line);
+ l = strstrip(line);
+
+ /* End marker */
+ if (l[0] == 0)
+ return 0;
+
+ k = strcspn(l, "=");
+
+ if (l[k] == '=') {
+ l[k] = 0;
+ v = l+k+1;
+ } else
+ v = l+k;
+
+ if (streq(l, "job-id")) {
+ if (safe_atou32(v, &j->id) < 0)
+ log_debug("Failed to parse job id value %s", v);
+ } else if (streq(l, "job-type")) {
+ JobType t = job_type_from_string(v);
+ if (t < 0)
+ log_debug("Failed to parse job type %s", v);
+ else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
+ log_debug("Cannot deserialize job of type %s", v);
+ else
+ j->type = t;
+ } else if (streq(l, "job-state")) {
+ JobState s = job_state_from_string(v);
+ if (s < 0)
+ log_debug("Failed to parse job state %s", v);
+ else
+ j->state = s;
+ } else if (streq(l, "job-override")) {
+ int b = parse_boolean(v);
+ if (b < 0)
+ 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)
+ log_debug("Failed to parse job sent_dbus_new_signal flag %s", v);
+ else
+ j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
+ } else if (streq(l, "job-ignore-order")) {
+ int b = parse_boolean(v);
+ if (b < 0)
+ log_debug("Failed to parse job ignore_order flag %s", v);
+ else
+ j->ignore_order = j->ignore_order || b;
+ } else if (streq(l, "job-forgot-bus-clients")) {
+ int b = parse_boolean(v);
+ if (b < 0)
+ log_debug("Failed to parse job forgot_bus_clients flag %s", v);
+ else
+ j->forgot_bus_clients = j->forgot_bus_clients || b;
+ } else if (streq(l, "job-timer-watch-fd")) {
+ int fd;
+ if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse job-timer-watch-fd value %s", v);
+ else {
+ if (j->timer_watch.type == WATCH_JOB_TIMER)
+ close_nointr_nofail(j->timer_watch.fd);
+
+ j->timer_watch.type = WATCH_JOB_TIMER;
+ j->timer_watch.fd = fdset_remove(fds, fd);
+ j->timer_watch.data.job = j;
+ }
+ }
+ }
+}
+
+int job_coldplug(Job *j) {
+ struct epoll_event ev = {
+ .data.ptr = &j->timer_watch,
+ .events = EPOLLIN,
+ };
+
+ if (j->timer_watch.type != WATCH_JOB_TIMER)
+ return 0;
+
+ if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, j->timer_watch.fd, &ev) < 0)
+ return -errno;
+
+ 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 (j->type != JOB_START)
+ return;
+
+ if (j->unit->manager->running_as != SYSTEMD_SYSTEM)
+ return;
+
+ if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
+ return;
+
+ if (detect_container(NULL) > 0)
+ return;
+
+ asynchronous_sync();