From 9e58ff9c5c3bd46a796a20fc6c304cdab489f334 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 Jul 2010 00:00:59 +0200 Subject: [PATCH] manager: optionally print status updates to console on boot --- src/dbus-manager.c | 2 ++ src/job.c | 3 +++ src/main.c | 56 +++++++++++++++++++++++++++++++++++++++------- src/manager.c | 19 ++++++++++++++-- src/manager.h | 5 ++++- src/mount.c | 1 + src/service.c | 1 + src/swap.c | 1 + src/test-engine.c | 2 +- src/unit.c | 48 +++++++++++++++++++++++++++++++-------- src/unit.h | 5 +++++ src/util.c | 24 ++++++++++++++++++++ src/util.h | 2 ++ 13 files changed, 148 insertions(+), 21 deletions(-) diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 99c2dcd7e..cd6a975bf 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -115,6 +115,7 @@ " \n" \ " \n" \ " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -222,6 +223,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, { "org.freedesktop.systemd1.Manager", "NJobs", bus_manager_append_n_jobs, "u", NULL }, { "org.freedesktop.systemd1.Manager", "Environment", bus_property_append_strv, "as", m->environment }, { "org.freedesktop.systemd1.Manager", "ConfirmSpawn", bus_property_append_bool, "b", &m->confirm_spawn }, + { "org.freedesktop.systemd1.Manager", "ShowStatus", bus_property_append_bool, "b", &m->show_status }, { "org.freedesktop.systemd1.Manager", "UnitPath", bus_property_append_strv, "as", m->lookup_paths.unit_path }, { "org.freedesktop.systemd1.Manager", "SysVInitPath", bus_property_append_strv, "as", m->lookup_paths.sysvinit_path }, { "org.freedesktop.systemd1.Manager", "SysVRcndPath", bus_property_append_strv, "as", m->lookup_paths.sysvrcnd_path }, diff --git a/src/job.c b/src/job.c index a090ec9b1..2199d75f6 100644 --- a/src/job.c +++ b/src/job.c @@ -483,6 +483,9 @@ int job_finish_and_invalidate(Job *j, bool success) { t = j->type; job_free(j); + if (!success) + unit_status_printf(u, "Starting %s... " ANSI_HIGHLIGHT_ON "failed" ANSI_HIGHLIGHT_OFF ".\n", unit_description(u)); + /* Fail depending jobs on failure */ if (!success) { diff --git a/src/main.c b/src/main.c index e51f6b7a3..99bea7591 100644 --- a/src/main.c +++ b/src/main.c @@ -59,6 +59,7 @@ static bool arg_crash_shell = false; static int arg_crash_chvt = -1; static bool arg_confirm_spawn = false; static bool arg_nomodules = false; +static bool arg_show_status = true; static FILE* serialization = NULL; @@ -236,6 +237,7 @@ static int parse_proc_cmdline_word(const char *word) { "4", SPECIAL_RUNLEVEL4_TARGET, "5", SPECIAL_RUNLEVEL5_TARGET }; + bool ignore_quiet = false; if (startswith(word, "systemd.unit=")) return set_default_unit(word + 13); @@ -292,6 +294,16 @@ static int parse_proc_cmdline_word(const char *word) { else arg_crash_chvt = k; + } else if (startswith(word, "systemd.show_status=")) { + int r; + + if ((r = parse_boolean(word + 20)) < 0) + log_warning("Failed to parse show status switch %s, Ignoring.", word + 20); + else { + arg_show_status = r; + ignore_quiet = true; + } + } else if (startswith(word, "systemd.")) { log_warning("Unknown kernel switch %s. Ignoring.", word); @@ -304,13 +316,17 @@ static int parse_proc_cmdline_word(const char *word) { "systemd.log_color=0|1 Highlight important log messages\n" "systemd.log_location=0|1 Include code location in log messages\n" "systemd.dump_core=0|1 Dump core on crash\n" - "systemd.crash_shell=0|1 On crash run shell\n" + "systemd.crash_shell=0|1 Run shell on crash\n" "systemd.crash_chvt=N Change to VT #N on crash\n" - "systemd.confirm_spawn=0|1 Confirm every process spawn"); + "systemd.confirm_spawn=0|1 Confirm every process spawn\n" + "systemd.show_status=0|1 Show status updates on the console during bootup\n"); } else if (streq(word, "nomodules")) arg_nomodules = true; - else { + else if (streq(word, "quiet")) { + if (!ignore_quiet) + arg_show_status = false; + } else { unsigned i; /* SysV compatibility */ @@ -367,7 +383,10 @@ static int parse_argv(int argc, char *argv[]) { ARG_RUNNING_AS, ARG_TEST, ARG_DUMP_CONFIGURATION_ITEMS, + ARG_DUMP_CORE, + ARG_CRASH_SHELL, ARG_CONFIRM_SPAWN, + ARG_SHOW_STATUS, ARG_DESERIALIZE, ARG_INTROSPECT }; @@ -382,7 +401,10 @@ static int parse_argv(int argc, char *argv[]) { { "test", no_argument, NULL, ARG_TEST }, { "help", no_argument, NULL, 'h' }, { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, + { "dump-core", no_argument, NULL, ARG_DUMP_CORE }, + { "crash-shell", no_argument, NULL, ARG_CRASH_SHELL }, { "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN }, + { "show-status", no_argument, NULL, ARG_SHOW_STATUS }, { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, { "introspect", optional_argument, NULL, ARG_INTROSPECT }, { NULL, 0, NULL, 0 } @@ -467,10 +489,22 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_DUMP_CONFIGURATION_ITEMS; break; + case ARG_DUMP_CORE: + arg_dump_core = true; + break; + + case ARG_CRASH_SHELL: + arg_crash_shell = true; + break; + case ARG_CONFIRM_SPAWN: arg_confirm_spawn = true; break; + case ARG_SHOW_STATUS: + arg_show_status = true; + break; + case ARG_DESERIALIZE: { int fd; FILE *f; @@ -543,14 +577,17 @@ static int help(void) { printf("%s [OPTIONS...]\n\n" "Starts up and maintains the system or a session.\n\n" " -h --help Show this help\n" - " --unit=UNIT Set default unit\n" - " --running-as=AS Set running as (system, session)\n" " --test Determine startup sequence, dump it and exit\n" " --dump-configuration-items Dump understood unit configuration items\n" - " --confirm-spawn Ask for confirmation when spawning processes\n" " --introspect[=INTERFACE] Extract D-Bus interface data\n" - " --log-level=LEVEL Set log level\n" + " --unit=UNIT Set default unit\n" + " --running-as=AS Set running as (system, session)\n" + " --dump-core Dump core on crash\n" + " --crash-shell Run shell on crash\n" + " --confirm-spawn Ask for confirmation when spawning processes\n" + " --show-status Show status updates on the console during bootup\n" " --log-target=TARGET Set log target (console, syslog, kmsg, syslog-or-kmsg, null)\n" + " --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n" " --log-color[=0|1] Highlight important log messages\n" " --log-location[=0|1] Include code location in log messages\n", program_invocation_short_name); @@ -734,11 +771,14 @@ int main(int argc, char *argv[]) { loopback_setup(); } - if ((r = manager_new(arg_running_as, arg_confirm_spawn, &m)) < 0) { + if ((r = manager_new(arg_running_as, &m)) < 0) { log_error("Failed to allocate manager object: %s", strerror(-r)); goto finish; } + m->confirm_spawn = arg_confirm_spawn; + m->show_status = arg_show_status; + if ((r = manager_startup(m, serialization, fds)) < 0) log_error("Failed to fully start up daemon: %s", strerror(-r)); diff --git a/src/manager.c b/src/manager.c index 6e571ea8b..e19bacfa7 100644 --- a/src/manager.c +++ b/src/manager.c @@ -182,7 +182,7 @@ static int manager_setup_signals(Manager *m) { return 0; } -int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) { +int manager_new(ManagerRunningAs running_as, Manager **_m) { Manager *m; int r = -ENOMEM; char *p; @@ -197,7 +197,6 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) { dual_timestamp_get(&m->startup_timestamp); m->running_as = running_as; - m->confirm_spawn = confirm_spawn; m->name_data_slot = m->subscribed_data_slot = -1; m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->pin_cgroupfs_fd = -1; @@ -2336,6 +2335,22 @@ finish: return r; } +bool manager_is_booting_or_shutting_down(Manager *m) { + Unit *u; + + assert(m); + + /* Is the initial job still around? */ + if (manager_get_job(m, 1)) + return true; + + /* Is there a job for the shutdown target? */ + if (((u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET)))) + return !!u->meta.job; + + return false; +} + static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { [MANAGER_SYSTEM] = "system", [MANAGER_SESSION] = "session" diff --git a/src/manager.h b/src/manager.h index aff4cb82e..b9971818d 100644 --- a/src/manager.h +++ b/src/manager.h @@ -192,10 +192,11 @@ struct Manager { bool utmp_reboot_written:1; + bool show_status; bool confirm_spawn; }; -int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **m); +int manager_new(ManagerRunningAs running_as, Manager **m); void manager_free(Manager *m); int manager_enumerate(Manager *m); @@ -240,6 +241,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds); int manager_reload(Manager *m); +bool manager_is_booting_or_shutting_down(Manager *m); + const char *manager_running_as_to_string(ManagerRunningAs i); ManagerRunningAs manager_running_as_from_string(const char *s); diff --git a/src/mount.c b/src/mount.c index 6da880eb8..49081d1d9 100644 --- a/src/mount.c +++ b/src/mount.c @@ -1548,6 +1548,7 @@ const UnitVTable mount_vtable = { .no_alias = true, .no_instances = true, .no_isolate = true, + .show_status = true, .init = mount_init, .load = mount_load, diff --git a/src/service.c b/src/service.c index ae8a763e7..d68973416 100644 --- a/src/service.c +++ b/src/service.c @@ -2671,6 +2671,7 @@ DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); const UnitVTable service_vtable = { .suffix = ".service", + .show_status = true, .init = service_init, .done = service_done, diff --git a/src/swap.c b/src/swap.c index d093c2e79..f72ede66d 100644 --- a/src/swap.c +++ b/src/swap.c @@ -547,6 +547,7 @@ const UnitVTable swap_vtable = { .no_instances = true, .no_isolate = true, + .show_status = true, .init = swap_init, .load = swap_load, diff --git a/src/test-engine.c b/src/test-engine.c index 60c6aa2f5..89631c465 100644 --- a/src/test-engine.c +++ b/src/test-engine.c @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path("test2") >= 0); - assert_se(manager_new(MANAGER_SYSTEM, false, &m) >= 0); + assert_se(manager_new(MANAGER_SYSTEM, &m) >= 0); printf("Load1:\n"); assert_se(manager_load_unit(m, "a.service", NULL, &a) >= 0); diff --git a/src/unit.c b/src/unit.c index 8b5714838..701dbc095 100644 --- a/src/unit.c +++ b/src/unit.c @@ -776,6 +776,9 @@ int unit_start(Unit *u) { * before it will start again. */ unit_add_to_dbus_queue(u); + + unit_status_printf(u, "Starting %s...\n", unit_description(u)); + return UNIT_VTABLE(u)->start(u); } @@ -803,6 +806,9 @@ int unit_stop(Unit *u) { return -EBADR; unit_add_to_dbus_queue(u); + + unit_status_printf(u, "Stopping %s...\n", unit_description(u)); + return UNIT_VTABLE(u)->stop(u); } @@ -939,7 +945,6 @@ static void retroactively_stop_dependencies(Unit *u) { } void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { - bool unexpected = false; dual_timestamp ts; assert(u); @@ -969,6 +974,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { path_unit_notify(u, ns); if (u->meta.job) { + bool unexpected = false; if (u->meta.job->state == JOB_WAITING) @@ -1028,16 +1034,17 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { default: assert_not_reached("Job type unknown"); } - } - /* If this state change happened without being requested by a - * job, then let's retroactively start or stop dependencies */ + /* If this state change happened without being + * requested by a job, then let's retroactively start + * or stop dependencies */ - if (unexpected) { - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) - retroactively_start_dependencies(u); - else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) - retroactively_stop_dependencies(u); + if (unexpected) { + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) + retroactively_start_dependencies(u); + else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) + retroactively_stop_dependencies(u); + } } /* Some names are special */ @@ -1997,6 +2004,29 @@ int unit_coldplug(Unit *u) { return 0; } +void unit_status_printf(Unit *u, const char *format, ...) { + va_list ap; + + assert(u); + assert(format); + + if (!UNIT_VTABLE(u)->show_status) + return; + + if (u->meta.manager->running_as != MANAGER_SYSTEM) + return; + + if (!u->meta.manager->show_status) + return; + + if (!manager_is_booting_or_shutting_down(u->meta.manager)) + return; + + va_start(ap, format); + status_vprintf(format, ap); + va_end(ap); +} + static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "service", [UNIT_TIMER] = "timer", diff --git a/src/unit.h b/src/unit.h index b6351d554..abd97f931 100644 --- a/src/unit.h +++ b/src/unit.h @@ -335,6 +335,9 @@ struct UnitVTable { /* Exclude from isolation requests */ bool no_isolate:1; + + /* Show status updates on the console */ + bool show_status:1; }; extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; @@ -459,6 +462,8 @@ int unit_add_node_link(Unit *u, const char *what, bool wants); int unit_coldplug(Unit *u); +void unit_status_printf(Unit *u, const char *format, ...); + const char *unit_type_to_string(UnitType i); UnitType unit_type_from_string(const char *s); diff --git a/src/util.c b/src/util.c index d1120c60a..4795dbcc6 100644 --- a/src/util.c +++ b/src/util.c @@ -2627,6 +2627,30 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) { } } +void status_vprintf(const char *format, va_list ap) { + char *s = NULL; + int fd = -1; + + assert(format); + + /* This independent of logging, as status messages are + * optional and go exclusively to the console. */ + + if (vasprintf(&s, format, ap) < 0) + goto finish; + + if ((fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) + goto finish; + + write(fd, s, strlen(s)); + +finish: + free(s); + + if (fd >= 0) + close_nointr_nofail(fd); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index cb47c7a07..0465915f4 100644 --- a/src/util.h +++ b/src/util.h @@ -291,6 +291,8 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root); cpu_set_t* cpu_set_malloc(unsigned *ncpus); +void status_vprintf(const char *format, va_list ap); + const char *ioprio_class_to_string(int i); int ioprio_class_from_string(const char *s); -- 2.30.2