printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
if (times) {
- if (times->time > 0)
+ if (timestamp_is_set(times->time))
printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC),
FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal());
if (boot->firmware_time > boot->loader_time)
m++;
- if (boot->loader_time > 0) {
+ if (timestamp_is_set(boot->loader_time)) {
m++;
if (width < 1000.0)
width = 1000.0;
}
- if (boot->initrd_time > 0)
+ if (timestamp_is_set(boot->initrd_time))
m++;
- if (boot->kernel_done_time > 0)
+ if (timestamp_is_set(boot->kernel_done_time))
m++;
for (u = times; u->has_data; u++) {
svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
- if (boot->firmware_time > 0) {
+ if (timestamp_is_set(boot->firmware_time)) {
svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
svg_text(true, -(double) boot->firmware_time, y, "firmware");
y++;
}
- if (boot->loader_time > 0) {
+ if (timestamp_is_set(boot->loader_time)) {
svg_bar("loader", -(double) boot->loader_time, 0, y);
svg_text(true, -(double) boot->loader_time, y, "loader");
y++;
}
- if (boot->kernel_done_time > 0) {
+ if (timestamp_is_set(boot->kernel_done_time)) {
svg_bar("kernel", 0, boot->kernel_done_time, y);
svg_text(true, 0, y, "kernel");
y++;
}
- if (boot->initrd_time > 0) {
+ if (timestamp_is_set(boot->initrd_time)) {
svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
if (boot->initrd_security_start_time < boot->initrd_security_finish_time)
svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y);
}
svg_bar("active", boot->userspace_time, boot->finish_time, y);
- if (boot->security_start_time > 0)
+ if (timestamp_is_set(boot->security_start_time))
svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
svg_bar("deactivating", 0, 300000, y);
svg_text(true, 400000, y, "Deactivating");
y++;
- if (boot->security_start_time > 0) {
+ if (timestamp_is_set(boot->security_start_time)) {
svg_bar("security", 0, 300000, y);
svg_text(true, 400000, y, "Setting up security module");
y++;
if (!text)
return log_oom();
- if (t->firmware_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->firmware_time - t->loader_time, USEC_PER_MSEC), " (firmware) + "))
+ if (timestamp_is_set(t->firmware_time) && !strextend(&text, FORMAT_TIMESPAN(t->firmware_time - t->loader_time, USEC_PER_MSEC), " (firmware) + "))
return log_oom();
- if (t->loader_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->loader_time, USEC_PER_MSEC), " (loader) + "))
+ if (timestamp_is_set(t->loader_time) && !strextend(&text, FORMAT_TIMESPAN(t->loader_time, USEC_PER_MSEC), " (loader) + "))
return log_oom();
- if (t->kernel_done_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->kernel_done_time, USEC_PER_MSEC), " (kernel) + "))
+ if (timestamp_is_set(t->kernel_done_time) && !strextend(&text, FORMAT_TIMESPAN(t->kernel_done_time, USEC_PER_MSEC), " (kernel) + "))
return log_oom();
- if (t->initrd_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC), " (initrd) + "))
+ if (timestamp_is_set(t->initrd_time) && !strextend(&text, FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC), " (initrd) + "))
return log_oom();
if (!strextend(&text, FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC), " (userspace) "))
return log_oom();
- if (t->kernel_done_time > 0)
+ if (timestamp_is_set(t->kernel_done_time))
if (!strextend(&text, "= ", FORMAT_TIMESPAN(t->firmware_time + t->finish_time, USEC_PER_MSEC), " "))
return log_oom();
if (unit_id && timestamp_is_set(activated_time)) {
- usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset;
+ usec_t base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset;
if (!strextend(&text, "\n", unit_id, " reached after ", FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC), " in userspace."))
return log_oom();
} else
t = NULL;
- free(*p);
- *p = t;
+ free_and_replace(*p, t);
return 1;
}
if (!v)
return NULL;
- free(*s);
- *s = v;
+ free_and_replace(*s, v);
}
return l;
if (state != old_state)
log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state));
- unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int automount_coldplug(Unit *u) {
if (state != old_state)
log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state));
- unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
if (state != old_state)
log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state));
- unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state],
- m->reload_result == MOUNT_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE);
+ unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS);
}
static int mount_coldplug(Unit *u) {
if (state != old_state)
log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
- unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
if (state != old_state)
log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int scope_add_default_dependencies(Scope *s) {
if (old_state != state)
log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
- unit_notify(UNIT(s), table[old_state], table[state],
- (s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE) |
- (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0));
+ unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
}
static usec_t service_coldplug_timeout(Service *s) {
assert(s);
- if (s->will_auto_restart)
- return true;
if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART))
return true;
log_unit_debug(UNIT(s), "Service restart not allowed.");
else {
const char *reason;
- bool shall_restart;
- shall_restart = service_shall_restart(s, &reason);
+ allow_restart = service_shall_restart(s, &reason);
log_unit_debug(UNIT(s), "Service will %srestart (%s)",
- shall_restart ? "" : "not ",
+ allow_restart ? "" : "not ",
reason);
- if (shall_restart)
- s->will_auto_restart = true;
}
- if (s->will_auto_restart) {
- s->will_auto_restart = false;
-
+ if (allow_restart) {
/* We make two state changes here: one that maps to the high-level UNIT_INACTIVE/UNIT_FAILED
* state (i.e. a state indicating deactivation), and then one that that maps to the
* high-level UNIT_STARTING state (i.e. a state indicating activation). We do this so that
bool main_pid_alien:1;
bool bus_name_good:1;
bool forbid_restart:1;
- /* Keep restart intention between UNIT_FAILED and UNIT_ACTIVATING */
- bool will_auto_restart:1;
bool start_timeout_defined:1;
bool exec_fd_hot:1;
slice_state_to_string(old_state),
slice_state_to_string(state));
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int slice_add_parent_slice(Slice *s) {
prefix, strna(s->user),
prefix, strna(s->group));
- if (s->keep_alive_time > 0)
+ if (timestamp_is_set(s->keep_alive_time))
fprintf(f,
"%sKeepAliveTimeSec: %s\n",
prefix, FORMAT_TIMESPAN(s->keep_alive_time, USEC_PER_SEC));
log_unit_warning_errno(UNIT(s), r, "SO_KEEPALIVE failed: %m");
}
- if (s->keep_alive_time > 0) {
+ if (timestamp_is_set(s->keep_alive_time)) {
r = setsockopt_int(fd, SOL_TCP, TCP_KEEPIDLE, s->keep_alive_time / USEC_PER_SEC);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "TCP_KEEPIDLE failed: %m");
if (state != old_state)
log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int socket_coldplug(Unit *u) {
if (state != old_state)
log_unit_debug(UNIT(s), "Changed %s -> %s", swap_state_to_string(old_state), swap_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
/* If there other units for the same device node have a job
queued it might be worth checking again if it is runnable
target_state_to_string(old_state),
target_state_to_string(state));
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int target_add_default_dependencies(Target *t) {
if (state != old_state)
log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static void timer_enter_waiting(Timer *t, bool time_change);
(void) serialize_item(f, "state", timer_state_to_string(t->state));
(void) serialize_item(f, "result", timer_result_to_string(t->result));
- if (t->last_trigger.realtime > 0)
+ if (dual_timestamp_is_set(&t->last_trigger))
(void) serialize_usec(f, "last-trigger-realtime", t->last_trigger.realtime);
if (t->last_trigger.monotonic > 0)
if (!UNIT_VTABLE(u)->reload) {
/* Unit doesn't have a reload function, but we need to propagate the reload anyway */
- unit_notify(u, unit_active_state(u), unit_active_state(u), 0);
+ unit_notify(u, unit_active_state(u), unit_active_state(u), /* reload_success = */ true);
return 0;
}
}
}
-static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) {
+static bool unit_process_job(Job *j, UnitActiveState ns, bool reload_success) {
bool unexpected = false;
JobResult result;
if (j->state == JOB_RUNNING) {
if (ns == UNIT_ACTIVE)
- job_finish_and_invalidate(j, (flags & UNIT_NOTIFY_RELOAD_FAILURE) ? JOB_FAILED : JOB_DONE, true, false);
+ job_finish_and_invalidate(j, reload_success ? JOB_DONE : JOB_FAILED, true, false);
else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) {
unexpected = true;
return unexpected;
}
-void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags) {
+void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
const char *reason;
Manager *m;
/* Let's propagate state changes to the job */
if (u->job)
- unexpected = unit_process_job(u->job, ns, flags);
+ unexpected = unit_process_job(u->job, ns, reload_success);
else
unexpected = true;
if (ns != os && ns == UNIT_FAILED) {
log_unit_debug(u, "Unit entered failed state.");
-
- if (!(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
- unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode);
+ unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode);
}
if (UNIT_IS_ACTIVE_OR_RELOADING(ns) && !UNIT_IS_ACTIVE_OR_RELOADING(os)) {
unit_log_resources(u);
}
- if (ns == UNIT_INACTIVE && !IN_SET(os, UNIT_FAILED, UNIT_INACTIVE, UNIT_MAINTENANCE) &&
- !(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
+ if (ns == UNIT_INACTIVE && !IN_SET(os, UNIT_FAILED, UNIT_INACTIVE, UNIT_MAINTENANCE))
unit_start_on_failure(u, "OnSuccess=", UNIT_ATOM_ON_SUCCESS, u->on_success_job_mode);
}
void unit_notify_cgroup_oom(Unit *u, bool managed_oom);
-typedef enum UnitNotifyFlags {
- UNIT_NOTIFY_RELOAD_FAILURE = 1 << 0,
- UNIT_NOTIFY_WILL_AUTO_RESTART = 1 << 1,
-} UnitNotifyFlags;
-
-void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags);
+void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success);
int unit_watch_pid(Unit *u, pid_t pid, bool exclusive);
void unit_unwatch_pid(Unit *u, pid_t pid);
if (!n)
return log_oom();
- free(c->oldest_file);
- c->oldest_file = n;
+ free_and_replace(c->oldest_file, n);
c->oldest_mtime = t;
}
/* truncate the potential old error message */
u->error[0] = '\0';
- free(u->answer);
- u->answer = 0;
+ u->answer = mfree(u->answer);
}
/* upload to this place */
char *t;
t = memdup_suffix0(p + 18, l - 18);
- if (t) {
- free(*identifier);
- *identifier = t;
- }
+ if (t)
+ free_and_replace(*identifier, t);
} else if (l >= 8 &&
startswith(p, "MESSAGE=")) {
char *t;
t = memdup_suffix0(p + 8, l - 8);
- if (t) {
- free(*message);
- *message = t;
- }
+ if (t)
+ free_and_replace(*message, t);
} else if (l > STRLEN("OBJECT_PID=") &&
l < STRLEN("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) &&
if (!k)
return log_oom_debug();
- free(*s);
- *s = k;
+ free_and_replace(*s, k);
return 1;
}
if (!addresses)
return -ENOMEM;
- free(*ret);
- *ret = addresses;
+ free_and_replace(*ret, addresses);
*n_ret = n_addresses;
}
if (!p)
return -ENOMEM;
- free(lease->vendor_specific);
- lease->vendor_specific = p;
+ free_and_replace(lease->vendor_specific, p);
}
lease->vendor_specific_len = len;
if (!p)
return -ENOMEM;
- free(lease->client_id);
- lease->client_id = p;
+ free_and_replace(lease->client_id, p);
lease->client_id_len = client_id_len;
}
if (r < 0)
return r;
- free(c->label);
- c->label = strndup(p, sz);
- if (!c->label)
- return -ENOMEM;
+ r = free_and_strndup(&c->label, p, sz);
+ if (r < 0)
+ return r;
c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
b->auth_iovec[0].iov_base = p;
b->auth_iovec[0].iov_len += l;
- free(b->auth_buffer);
- b->auth_buffer = p;
+ free_and_replace(b->auth_buffer, p);
b->auth_index = 0;
return 0;
}
goto fail;
}
- free(absolute);
- absolute = c;
+ free_and_replace(absolute, c);
max_follow--;
}
* cgroup, let's return the "/" in the public APIs instead, as
* that's easier and less ambiguous for people to grok. */
if (isempty(c)) {
- free(c);
- c = strdup("/");
- if (!c)
- return -ENOMEM;
+ r = free_and_strdup(&c, "/");
+ if (r < 0)
+ return r;
}
free(ai->ai_addr);
free(ai->ai_canonname);
- free(ai);
- ai = next;
+ free_and_replace(ai, next);
}
}
if (r < 0)
return r;
if (r == 0 || !ifname_valid(b)) {
- free(b);
- b = strdup(a);
- if (!b)
- return -ENOMEM;
+ r = free_and_strdup(&b, a);
+ if (r < 0)
+ return r;
}
if (p)
static CalendarComponent* chain_free(CalendarComponent *c) {
while (c) {
CalendarComponent *n = c->next;
- free(c);
- c = n;
+ free_and_replace(c, n);
}
return NULL;
}
static const char *arg_dest = NULL;
static int generate_symlink(void) {
- _cleanup_free_ char *j = NULL;
-
FOREACH_STRING(p, "/system-update", "/etc/system-update") {
- if (laccess(p, F_OK) >= 0)
- goto link_found;
+ if (laccess(p, F_OK) >= 0) {
+ _cleanup_free_ char *j = NULL;
+
+ j = path_join(arg_dest, SPECIAL_DEFAULT_TARGET);
+ if (!j)
+ return log_oom();
+
+ if (symlink(SYSTEM_DATA_UNIT_DIR "/system-update.target", j) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", j);
+
+ return 1;
+ }
if (errno != ENOENT)
log_warning_errno(errno, "Failed to check if %s symlink exists, ignoring: %m", p);
}
return 0;
-
-link_found:
- j = path_join(arg_dest, SPECIAL_DEFAULT_TARGET);
- if (!j)
- return log_oom();
-
- if (symlink(SYSTEM_DATA_UNIT_DIR "/system-update.target", j) < 0)
- return log_error_errno(errno, "Failed to create symlink %s: %m", j);
-
- return 1;
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
if sign_kernel:
linux_signed = tempfile.NamedTemporaryFile(prefix='linux-signed')
- linux = linux_signed.name
+ linux = pathlib.Path(linux_signed.name)
sign(sign_tool, opts.linux, linux, opts=opts)
else:
linux = opts.linux
#include "macro.h"
#include "main-func.h"
#include "process-util.h"
+#include "random-util.h"
#include "special.h"
#include "stdio-util.h"
#include "strv.h"
#include "unit-name.h"
#include "utmp-wtmp.h"
+#include "verbs.h"
typedef struct Context {
sd_bus *bus;
#endif
}
-static usec_t get_startup_monotonic_time(Context *c) {
+static int get_startup_monotonic_time(Context *c, usec_t *ret) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- usec_t t = 0;
int r;
assert(c);
+ assert(ret);
+
+ r = bus_get_property_trivial(
+ c->bus,
+ bus_systemd_mgr,
+ "UserspaceTimestampMonotonic",
+ &error,
+ 't', ret);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to get timestamp, ignoring: %s", bus_error_message(&error, r));
- r = bus_get_property_trivial(c->bus, bus_systemd_mgr, "UserspaceTimestampMonotonic", &error, 't', &t);
- if (r < 0) {
- log_error_errno(r, "Failed to get timestamp: %s", bus_error_message(&error, r));
- return 0;
- }
-
- return t;
+ return 0;
}
static int get_current_runlevel(Context *c) {
const int runlevel;
const char *special;
} table[] = {
- /* The first target of this list that is active or has
- * a job scheduled wins. We prefer runlevels 5 and 3
- * here over the others, since these are the main
- * runlevels used on Fedora. It might make sense to
- * change the order on some distributions. */
+ /* The first target of this list that is active or has a job scheduled wins. We prefer
+ * runlevels 5 and 3 here over the others, since these are the main runlevels used on Fedora.
+ * It might make sense to change the order on some distributions. */
{ '5', SPECIAL_GRAPHICAL_TARGET },
{ '3', SPECIAL_MULTI_USER_TARGET },
{ '1', SPECIAL_RESCUE_TARGET },
};
-
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(c);
- for (size_t i = 0; i < ELEMENTSOF(table); i++) {
- _cleanup_free_ char *state = NULL, *path = NULL;
-
- path = unit_dbus_path_from_name(table[i].special);
- if (!path)
- return log_oom();
-
- r = sd_bus_get_property_string(
- c->bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Unit",
- "ActiveState",
- &error,
- &state);
- if (r < 0)
- return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r));
+ for (unsigned n_attempts = 0;;) {
+ FOREACH_ARRAY(e, table, ELEMENTSOF(table)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *state = NULL, *path = NULL;
+
+ path = unit_dbus_path_from_name(e->special);
+ if (!path)
+ return log_oom();
+
+ r = sd_bus_get_property_string(
+ c->bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveState",
+ &error,
+ &state);
+ if ((r == -ENOTCONN ||
+ sd_bus_error_has_names(&error,
+ SD_BUS_ERROR_NO_REPLY,
+ SD_BUS_ERROR_DISCONNECTED)) &&
+ ++n_attempts < 64) {
+
+ /* systemd might have dropped off momentarily, let's not make this an error,
+ * and wait some random time. Let's pick a random time in the range 0ms…250ms,
+ * linearly scaled by the number of failed attempts. */
+
+ usec_t usec = random_u64_range(UINT64_C(10) * USEC_PER_MSEC +
+ UINT64_C(240) * USEC_PER_MSEC * n_attempts/64);
+ log_debug_errno(r, "Failed to get state of %s, retrying after %s: %s",
+ e->special, FORMAT_TIMESPAN(usec, USEC_PER_MSEC), bus_error_message(&error, r));
+ (void) usleep(usec);
+ goto reconnect;
+ }
+ if (r < 0)
+ return log_warning_errno(r, "Failed to get state of %s: %s", e->special, bus_error_message(&error, r));
+
+ if (STR_IN_SET(state, "active", "reloading"))
+ return e->runlevel;
+ }
- if (STR_IN_SET(state, "active", "reloading"))
- return table[i].runlevel;
- }
+ return 0;
- return 0;
+reconnect:
+ c->bus = sd_bus_flush_close_unref(c->bus);
+ r = bus_connect_system_systemd(&c->bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reconnect to system bus: %m");
+ }
}
-static int on_reboot(Context *c) {
- int r = 0, q;
- usec_t t;
- usec_t boottime;
+static int on_reboot(int argc, char *argv[], void *userdata) {
+ Context *c = ASSERT_PTR(userdata);
+ usec_t t = 0, boottime;
+ int r, q = 0;
- assert(c);
-
- /* We finished start-up, so let's write the utmp
- * record and send the audit msg */
+ /* We finished start-up, so let's write the utmp record and send the audit msg. */
#if HAVE_AUDIT
if (c->audit_fd >= 0)
if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
errno != EPERM)
- r = log_error_errno(errno, "Failed to send audit message: %m");
+ q = log_error_errno(errno, "Failed to send audit message: %m");
#endif
- /* If this call fails it will return 0, which
- * utmp_put_reboot() will then fix to the current time */
- t = get_startup_monotonic_time(c);
+ /* If this call fails, then utmp_put_reboot() will fix to the current time. */
+ (void) get_startup_monotonic_time(c, &t);
boottime = map_clock_usec(t, CLOCK_MONOTONIC, CLOCK_REALTIME);
- /* We query the recorded monotonic time here (instead of the system clock CLOCK_REALTIME),
- * even though we actually want the system clock time. That's because there's a likely
- * chance that the system clock wasn't set right during early boot. By manually converting
- * the monotonic clock to the system clock here we can compensate
- * for incorrectly set clocks during early boot. */
+ /* We query the recorded monotonic time here (instead of the system clock CLOCK_REALTIME), even
+ * though we actually want the system clock time. That's because there's a likely chance that the
+ * system clock wasn't set right during early boot. By manually converting the monotonic clock to the
+ * system clock here we can compensate for incorrectly set clocks during early boot. */
- q = utmp_put_reboot(boottime);
- if (q < 0)
- r = log_error_errno(q, "Failed to write utmp record: %m");
+ r = utmp_put_reboot(boottime);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write utmp record: %m");
- return r;
+ return q;
}
-static int on_shutdown(Context *c) {
- int r = 0, q;
+static int on_shutdown(int argc, char *argv[], void *userdata) {
+ int r, q = 0;
- assert(c);
-
- /* We started shut-down, so let's write the utmp
- * record and send the audit msg */
+ /* We started shut-down, so let's write the utmp record and send the audit msg. */
#if HAVE_AUDIT
+ Context *c = ASSERT_PTR(userdata);
+
if (c->audit_fd >= 0)
if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
errno != EPERM)
- r = log_error_errno(errno, "Failed to send audit message: %m");
+ q = log_error_errno(errno, "Failed to send audit message: %m");
#endif
- q = utmp_put_shutdown();
- if (q < 0)
- r = log_error_errno(q, "Failed to write utmp record: %m");
+ r = utmp_put_shutdown();
+ if (r < 0)
+ return log_error_errno(r, "Failed to write utmp record: %m");
- return r;
+ return q;
}
-static int on_runlevel(Context *c) {
- int r = 0, q, previous, runlevel;
+static int on_runlevel(int argc, char *argv[], void *userdata) {
+ Context *c = ASSERT_PTR(userdata);
+ int r, q = 0, previous, runlevel;
- assert(c);
-
- /* We finished changing runlevel, so let's write the
- * utmp record and send the audit msg */
+ /* We finished changing runlevel, so let's write the utmp record and send the audit msg. */
/* First, get last runlevel */
- q = utmp_get_runlevel(&previous, NULL);
-
- if (q < 0) {
- if (!IN_SET(q, -ESRCH, -ENOENT))
- return log_error_errno(q, "Failed to get current runlevel: %m");
+ r = utmp_get_runlevel(&previous, NULL);
+ if (r < 0) {
+ if (!IN_SET(r, -ESRCH, -ENOENT))
+ return log_error_errno(r, "Failed to get the last runlevel from utmp: %m");
previous = 0;
}
if (runlevel < 0)
return runlevel;
if (runlevel == 0) {
- log_warning("Failed to get new runlevel, utmp update skipped.");
+ log_warning("Failed to get the current runlevel, utmp update skipped.");
return 0;
}
if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s,
"systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM)
- r = log_error_errno(errno, "Failed to send audit message: %m");
+ q = log_error_errno(errno, "Failed to send audit message: %m");
}
#endif
- q = utmp_put_runlevel(runlevel, previous);
- if (q < 0 && !IN_SET(q, -ESRCH, -ENOENT))
- return log_error_errno(q, "Failed to write utmp record: %m");
+ r = utmp_put_runlevel(runlevel, previous);
+ if (r < 0 && !IN_SET(r, -ESRCH, -ENOENT))
+ return log_error_errno(r, "Failed to write utmp record: %m");
- return r;
+ return q;
}
static int run(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "reboot", 1, 1, 0, on_reboot },
+ { "shutdown", 1, 1, 0, on_shutdown },
+ { "runlevel", 1, 1, 0, on_runlevel },
+ {}
+ };
+
_cleanup_(context_clear) Context c = {
#if HAVE_AUDIT
.audit_fd = -EBADF,
};
int r;
- if (argc != 2)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "This program requires one argument.");
-
log_setup();
umask(0022);
/* If the kernel lacks netlink or audit support, don't worry about it. */
c.audit_fd = audit_open();
if (c.audit_fd < 0)
- log_full_errno(IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT) ? LOG_DEBUG : LOG_ERR,
- errno, "Failed to connect to audit log: %m");
+ log_full_errno(IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT) ? LOG_DEBUG : LOG_WARNING,
+ errno, "Failed to connect to audit log, ignoring: %m");
#endif
r = bus_connect_system_systemd(&c.bus);
if (r < 0)
return log_error_errno(r, "Failed to get D-Bus connection: %m");
- if (streq(argv[1], "reboot"))
- return on_reboot(&c);
- if (streq(argv[1], "shutdown"))
- return on_shutdown(&c);
- if (streq(argv[1], "runlevel"))
- return on_runlevel(&c);
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command %s", argv[1]);
+ return dispatch_verb(argc, argv, verbs, &c);
}
DEFINE_MAIN_FUNCTION(run);
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-01-BASIC
-# Order the test unit after systemd-update-utmp-runlevel.service, since
-# the service doesn't play well with daemon-reexec
-# See: https://github.com/systemd/systemd/issues/27167
-After=multi-user.target systemd-update-utmp-runlevel.service
+After=multi-user.target
Wants=systemd-resolved.service systemd-networkd.service
[Service]
# of systemd-analyze blame. See issue #27187.
systemd-analyze blame
+# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'.
+# See issue #27163.
+# shellcheck disable=SC2034
+for _ in {0..10}; do
+ systemctl daemon-reexec &
+ pid_reexec=$!
+ # shellcheck disable=SC2034
+ for _ in {0..10}; do
+ SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel
+ done
+ wait "$pid_reexec"
+done
+
echo OK >/testok