diff -Nru systemd-237/debian/changelog systemd-237/debian/changelog --- systemd-237/debian/changelog 2019-02-28 22:03:40.000000000 +0100 +++ systemd-237/debian/changelog 2019-03-18 08:40:44.000000000 +0100 @@ -1,3 +1,25 @@ +systemd (237-3ubuntu10.17) bionic; urgency=medium + + * d/p/Support-system-image-read-only-etc.patch: + - re-add support for /etc/writable for core18 (LP: #1778936) + * d/p/fix-race-daemon-reload-8803.patch: + - backport systemd upstream PR#8803 and PR#11121 to fix race + when doing systemctl and systemctl daemon-reload at the + same time LP: #1819728 + + -- Michael Vogt Mon, 18 Mar 2019 08:40:44 +0100 + +systemd (237-3ubuntu10.16) bionic; urgency=medium + + * d/p/Support-system-image-read-only-etc.patch: + - re-add support for /etc/writable for core18 (LP: #1778936) + * d/p/fix-race-daemon-reload-8803.patch: + - backport systemd upstream PR#8803 to fix race when doing + systemctl and systemctl daemon-reload at the same time + LP: #1819728 + + -- Michael Vogt Wed, 13 Mar 2019 07:42:11 +0100 + systemd (237-3ubuntu10.15) bionic; urgency=medium [ Victor Tapia ] diff -Nru systemd-237/debian/patches/fix-race-daemon-reload-8803.patch systemd-237/debian/patches/fix-race-daemon-reload-8803.patch --- systemd-237/debian/patches/fix-race-daemon-reload-8803.patch 1970-01-01 01:00:00.000000000 +0100 +++ systemd-237/debian/patches/fix-race-daemon-reload-8803.patch 2019-03-18 08:40:44.000000000 +0100 @@ -0,0 +1,362 @@ +From: Michael Vogt +Date: Mon, 18 Mar 2019 08:30:41 +0100 +Subject: Backport daemon reload race fix (PR#11121) + +There is a race in the systemd code when daemon-reload happens +in parallel with other systemctl operations (like start/stop). +Upstream fixed this first in PR#8803 but the fix is incomplete +and sometimes causes a segfault (see systemd issue #10716). +The full fix is in PR#11121 and this PR first applied #8803 with +the PR #11121 on top. +Index: systemd-237/src/core/job.c +=================================================================== +--- systemd-237.orig/src/core/job.c ++++ systemd-237/src/core/job.c +@@ -78,7 +78,7 @@ Job* job_new(Unit *unit, JobType type) { + return j; + } + +-void job_free(Job *j) { ++void job_unlink(Job *j) { + assert(j); + assert(!j->installed); + assert(!j->transaction_prev); +@@ -86,21 +86,38 @@ void job_free(Job *j) { + assert(!j->subject_list); + assert(!j->object_list); + +- if (j->in_run_queue) ++ if (j->in_run_queue) { + LIST_REMOVE(run_queue, j->manager->run_queue, j); ++ j->in_run_queue = false; ++ } + +- if (j->in_dbus_queue) ++ if (j->in_dbus_queue) { + LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); ++ j->in_dbus_queue = false; ++ } + +- if (j->in_gc_queue) ++ if (j->in_gc_queue) { + LIST_REMOVE(gc_queue, j->manager->gc_job_queue, j); ++ j->in_gc_queue = false; ++ } + +- sd_event_source_unref(j->timer_event_source); ++ j->timer_event_source = sd_event_source_unref(j->timer_event_source); ++} ++ ++Job* job_free(Job *j) { ++ assert(j); ++ assert(!j->installed); ++ assert(!j->transaction_prev); ++ assert(!j->transaction_next); ++ assert(!j->subject_list); ++ assert(!j->object_list); ++ ++ job_unlink(j); + + sd_bus_track_unref(j->bus_track); + strv_free(j->deserialized_clients); + +- free(j); ++ return mfree(j); + } + + static void job_set_state(Job *j, JobState state) { +@@ -149,7 +166,7 @@ void job_uninstall(Job *j) { + + unit_add_to_gc_queue(j->unit); + +- hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id)); ++ hashmap_remove_value(j->manager->jobs, UINT32_TO_PTR(j->id), j); + j->installed = false; + } + +@@ -239,6 +256,7 @@ Job* job_install(Job *j) { + + int job_install_deserialized(Job *j) { + Job **pj; ++ int r; + + assert(!j->installed); + +@@ -248,10 +266,15 @@ int job_install_deserialized(Job *j) { + } + + pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; +- if (*pj) { +- log_unit_debug(j->unit, "Unit already has a job installed. Not installing deserialized job."); +- return -EEXIST; +- } ++ if (*pj) ++ return log_unit_debug_errno(j->unit, EEXIST, ++ "Unit already has a job installed. Not installing deserialized job."); ++ ++ r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j); ++ if (r == -EEXIST) ++ return log_unit_debug_errno(j->unit, r, "Job ID %" PRIu32 " already used, cannot deserialize job.", j->id); ++ if (r < 0) ++ return log_unit_debug_errno(j->unit, r, "Failed to insert job into jobs hash table: %m"); + + *pj = j; + j->installed = true; +Index: systemd-237/src/core/job.h +=================================================================== +--- systemd-237.orig/src/core/job.h ++++ systemd-237/src/core/job.h +@@ -178,7 +178,8 @@ struct Job { + + Job* job_new(Unit *unit, JobType type); + Job* job_new_raw(Unit *unit); +-void job_free(Job *job); ++void job_unlink(Job *job); ++Job* job_free(Job *job); + Job* job_install(Job *j); + int job_install_deserialized(Job *j); + void job_uninstall(Job *j); +@@ -239,6 +240,8 @@ void job_add_to_gc_queue(Job *j); + int job_get_before(Job *j, Job*** ret); + int job_get_after(Job *j, Job*** ret); + ++DEFINE_TRIVIAL_CLEANUP_FUNC(Job*, job_free); ++ + const char* job_type_to_string(JobType t) _const_; + JobType job_type_from_string(const char *s) _pure_; + +Index: systemd-237/src/core/unit.c +=================================================================== +--- systemd-237.orig/src/core/unit.c ++++ systemd-237/src/core/unit.c +@@ -2325,9 +2325,77 @@ static void unit_update_on_console(Unit + + } + ++static bool unit_process_job(Job *j, UnitActiveState ns, bool reload_success) { ++ bool unexpected = false; ++ ++ assert(j); ++ ++ if (j->state == JOB_WAITING) ++ ++ /* 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_add_to_run_queue(j); ++ ++ /* Let's check whether this state change constitutes a ++ * finished job, or maybe contradicts a running job and ++ * hence needs to invalidate jobs. */ ++ ++ switch (j->type) { ++ ++ case JOB_START: ++ case JOB_VERIFY_ACTIVE: ++ ++ if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) ++ job_finish_and_invalidate(j, JOB_DONE, true, false); ++ else if (j->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { ++ unexpected = true; ++ ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) ++ job_finish_and_invalidate(j, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); ++ } ++ ++ break; ++ ++ case JOB_RELOAD: ++ case JOB_RELOAD_OR_START: ++ case JOB_TRY_RELOAD: ++ ++ if (j->state == JOB_RUNNING) { ++ if (ns == UNIT_ACTIVE) ++ job_finish_and_invalidate(j, reload_success ? JOB_DONE : JOB_FAILED, true, false); ++ else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) { ++ unexpected = true; ++ ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) ++ job_finish_and_invalidate(j, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); ++ } ++ } ++ ++ break; ++ ++ case JOB_STOP: ++ case JOB_RESTART: ++ case JOB_TRY_RESTART: ++ ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) ++ job_finish_and_invalidate(j, JOB_DONE, true, false); ++ else if (j->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { ++ unexpected = true; ++ job_finish_and_invalidate(j, JOB_FAILED, true, false); ++ } ++ ++ break; ++ ++ default: ++ assert_not_reached("Job type unknown"); ++ } ++ ++ return unexpected; ++} ++ + void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { + Manager *m; +- bool unexpected; + + assert(u); + assert(os < _UNIT_ACTIVE_STATE_MAX); +@@ -2367,74 +2435,18 @@ void unit_notify(Unit *u, UnitActiveStat + + unit_update_on_console(u); + +- if (u->job) { +- unexpected = false; +- +- if (u->job->state == JOB_WAITING) +- +- /* 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_add_to_run_queue(u->job); +- +- /* Let's check whether this state change constitutes a +- * finished job, or maybe contradicts a running job and +- * hence needs to invalidate jobs. */ +- +- switch (u->job->type) { +- +- case JOB_START: +- case JOB_VERIFY_ACTIVE: +- +- if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) +- job_finish_and_invalidate(u->job, JOB_DONE, true, false); +- else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { +- unexpected = true; +- +- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) +- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); +- } +- +- break; +- +- case JOB_RELOAD: +- case JOB_RELOAD_OR_START: +- case JOB_TRY_RELOAD: +- +- if (u->job->state == JOB_RUNNING) { +- if (ns == UNIT_ACTIVE) +- job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true, false); +- else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) { +- unexpected = true; +- +- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) +- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); +- } +- } +- +- break; +- +- case JOB_STOP: +- case JOB_RESTART: +- case JOB_TRY_RESTART: +- +- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) +- job_finish_and_invalidate(u->job, JOB_DONE, true, false); +- else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { +- unexpected = true; +- job_finish_and_invalidate(u->job, JOB_FAILED, true, false); +- } +- +- break; ++ if (!MANAGER_IS_RELOADING(m)) { ++ bool unexpected; + +- default: +- assert_not_reached("Job type unknown"); +- } ++ /* Let's propagate state changes to the job */ ++ if (u->job) ++ unexpected = unit_process_job(u->job, ns, reload_success); ++ else ++ unexpected = true; + +- } else +- unexpected = true; +- +- if (!MANAGER_IS_RELOADING(m)) { ++ /* If this state change happened without being requested by a job, then let's retroactively start or ++ * stop dependencies. We skip that step when deserializing, since we don't want to create any ++ * additional jobs just because something is already activated. */ + + /* If this state change happened without being + * requested by a job, then let's retroactively start +@@ -3332,6 +3344,29 @@ void unit_serialize_item_format(Unit *u, + fputc('\n', f); + } + ++static int unit_deserialize_job(Unit *u, FILE *f) { ++ _cleanup_(job_freep) Job *j = NULL; ++ int r; ++ ++ assert(u); ++ assert(f); ++ ++ j = job_new_raw(u); ++ if (!j) ++ return log_oom(); ++ ++ r = job_deserialize(j, f); ++ if (r < 0) ++ return r; ++ ++ r = job_install_deserialized(j); ++ if (r < 0) ++ return r; ++ ++ TAKE_PTR(j); ++ return 0; ++} ++ + int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + ExecRuntime **rt = NULL; + size_t offset; +@@ -3371,32 +3406,11 @@ int unit_deserialize(Unit *u, FILE *f, F + + if (streq(l, "job")) { + if (v[0] == '\0') { +- /* new-style serialized job */ +- Job *j; +- +- j = job_new_raw(u); +- if (!j) +- return log_oom(); +- +- r = job_deserialize(j, f); +- if (r < 0) { +- job_free(j); +- return r; +- } +- +- r = hashmap_put(u->manager->jobs, UINT32_TO_PTR(j->id), j); +- if (r < 0) { +- job_free(j); +- return r; +- } +- +- r = job_install_deserialized(j); +- if (r < 0) { +- hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id)); +- job_free(j); ++ /* New-style serialized job */ ++ r = unit_deserialize_job(u, f); ++ if (r < 0) + return r; +- } +- } else /* legacy for pre-44 */ ++ } else /* Legacy for pre-44 */ + log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v); + continue; + } else if (streq(l, "state-change-timestamp")) { diff -Nru systemd-237/debian/patches/series systemd-237/debian/patches/series --- systemd-237/debian/patches/series 2019-02-28 22:03:40.000000000 +0100 +++ systemd-237/debian/patches/series 2019-03-18 08:34:30.000000000 +0100 @@ -97,3 +97,5 @@ sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch journal-do-not-remove-multiple-spaces-after-identifi.patch stop-mount-error-propagation.patch +Support-system-image-read-only-etc.patch +fix-race-daemon-reload-8803.patch diff -Nru systemd-237/debian/patches/Support-system-image-read-only-etc.patch systemd-237/debian/patches/Support-system-image-read-only-etc.patch --- systemd-237/debian/patches/Support-system-image-read-only-etc.patch 1970-01-01 01:00:00.000000000 +0100 +++ systemd-237/debian/patches/Support-system-image-read-only-etc.patch 2019-03-13 07:42:11.000000000 +0100 @@ -0,0 +1,153 @@ +From: Martin Pitt +Date: Sat, 26 Apr 2014 23:49:32 +0200 +Subject: Support system-image read-only /etc + +On Ubuntu Phone with readonly /etc we symlink +/etc/{adjtime,localtime,timezone,hostname,machine-info} to /etc/writable/, so +we need to update those files instead if the original files are symlinks into +/etc/writable/. + +Forwarded: OMGno, this is a rather nasty hack until we fix system-image to get a writable /etc +Bug-Ubuntu: https://launchpad.net/bugs/1227520 +--- + src/hostname/hostnamed.c | 28 ++++++++++++++++++++++++---- + src/timedate/timedated.c | 31 +++++++++++++++++++++++++------ + 2 files changed, 49 insertions(+), 10 deletions(-) + +Index: systemd-237/src/hostname/hostnamed.c +=================================================================== +--- systemd-237.orig/src/hostname/hostnamed.c ++++ systemd-237/src/hostname/hostnamed.c +@@ -31,6 +31,7 @@ + #include "hostname-util.h" + #include "parse-util.h" + #include "path-util.h" ++#include "fs-util.h" + #include "selinux-util.h" + #include "strv.h" + #include "user-util.h" +@@ -76,6 +77,25 @@ static void context_free(Context *c) { + bus_verify_polkit_async_registry_free(c->polkit_registry); + } + ++/* Hack for Ubuntu phone: check if path is an existing symlink to ++ * /etc/writable; if it is, update that instead */ ++static const char* writable_filename(const char *path) { ++ ssize_t r; ++ static char realfile_buf[PATH_MAX]; ++ _cleanup_free_ char *realfile = NULL; ++ const char *result = path; ++ int orig_errno = errno; ++ ++ r = readlink_and_make_absolute(path, &realfile); ++ if (r >= 0 && startswith(realfile, "/etc/writable")) { ++ snprintf(realfile_buf, sizeof(realfile_buf), "%s", realfile); ++ result = realfile_buf; ++ } ++ ++ errno = orig_errno; ++ return result; ++} ++ + static int context_read_data(Context *c) { + int r; + struct utsname u; +@@ -303,12 +323,12 @@ static int context_write_data_static_hos + + if (isempty(c->data[PROP_STATIC_HOSTNAME])) { + +- if (unlink("/etc/hostname") < 0) ++ if (unlink(writable_filename("/etc/hostname")) < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } +- return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]); ++ return write_string_file_atomic_label(writable_filename("/etc/hostname"), c->data[PROP_STATIC_HOSTNAME]); + } + + static int context_write_data_machine_info(Context *c) { +@@ -354,13 +374,13 @@ static int context_write_data_machine_in + } + + if (strv_isempty(l)) { +- if (unlink("/etc/machine-info") < 0) ++ if (unlink(writable_filename("/etc/machine-info")) < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + +- return write_env_file_label("/etc/machine-info", l); ++ return write_env_file_label(writable_filename("/etc/machine-info"), l); + } + + static int property_get_icon_name( +Index: systemd-237/src/timedate/timedated.c +=================================================================== +--- systemd-237.orig/src/timedate/timedated.c ++++ systemd-237/src/timedate/timedated.c +@@ -82,6 +82,25 @@ static int context_read_data(Context *c) + return 0; + } + ++/* Hack for Ubuntu phone: check if path is an existing symlink to ++ * /etc/writable; if it is, update that instead */ ++static const char* writable_filename(const char *path) { ++ ssize_t r; ++ static char realfile_buf[PATH_MAX]; ++ _cleanup_free_ char *realfile = NULL; ++ const char *result = path; ++ int orig_errno = errno; ++ ++ r = readlink_and_make_absolute(path, &realfile); ++ if (r >= 0 && startswith(realfile, "/etc/writable")) { ++ snprintf(realfile_buf, sizeof(realfile_buf), "%s", realfile); ++ result = realfile_buf; ++ } ++ ++ errno = orig_errno; ++ return result; ++} ++ + static int context_write_data_timezone(Context *c) { + _cleanup_free_ char *p = NULL; + int r = 0; +@@ -90,10 +109,10 @@ static int context_write_data_timezone(C + assert(c); + + if (isempty(c->zone)) { +- if (unlink("/etc/localtime") < 0 && errno != ENOENT) ++ if (unlink(writable_filename("/etc/localtime")) < 0 && errno != ENOENT) + r = -errno; + +- if (unlink("/etc/timezone") < 0 && errno != ENOENT) ++ if (unlink(writable_filename("/etc/timezone")) < 0 && errno != ENOENT) + r = -errno; + + return r; +@@ -103,12 +122,12 @@ static int context_write_data_timezone(C + if (!p) + return log_oom(); + +- r = symlink_atomic(p, "/etc/localtime"); ++ r = symlink_atomic(p, writable_filename("/etc/localtime")); + if (r < 0) + return r; + +- if (stat("/etc/timezone", &st) == 0 && S_ISREG(st.st_mode)) { +- r = write_string_file("/etc/timezone", c->zone, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); ++ if (stat(writable_filename("/etc/timezone"), &st) == 0 && S_ISREG(st.st_mode)) { ++ r = write_string_file(writable_filename("/etc/timezone"), c->zone, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + if (r < 0) + return r; + } +@@ -174,7 +193,7 @@ static int context_write_data_local_rtc( + *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0; + + if (streq(w, NULL_ADJTIME_UTC)) { +- if (unlink("/etc/adjtime") < 0) ++ if (unlink(writable_filename("/etc/adjtime")) < 0) + if (errno != ENOENT) + return -errno; +