diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dbus-socket.c | 2 | ||||
-rw-r--r-- | src/dbus.c | 382 | ||||
-rw-r--r-- | src/execute.c | 152 | ||||
-rw-r--r-- | src/exit-status.c | 3 | ||||
-rw-r--r-- | src/exit-status.h | 3 | ||||
-rw-r--r-- | src/fsck.c | 2 | ||||
-rw-r--r-- | src/install.c | 5 | ||||
-rw-r--r-- | src/job.c | 13 | ||||
-rw-r--r-- | src/kmsg-syslogd.c | 3 | ||||
-rw-r--r-- | src/label.c | 2 | ||||
-rw-r--r-- | src/load-fragment-gperf.gperf.m4 | 2 | ||||
-rw-r--r-- | src/log.c | 27 | ||||
-rw-r--r-- | src/log.h | 1 | ||||
-rw-r--r-- | src/logind-session.c | 4 | ||||
-rw-r--r-- | src/manager.c | 2 | ||||
-rw-r--r-- | src/manager.h | 1 | ||||
-rw-r--r-- | src/mount.c | 6 | ||||
-rw-r--r-- | src/pam-module.c | 16 | ||||
-rw-r--r-- | src/path.c | 360 | ||||
-rw-r--r-- | src/path.h | 9 | ||||
-rw-r--r-- | src/rc-local-generator.c | 107 | ||||
-rw-r--r-- | src/service.c | 188 | ||||
-rw-r--r-- | src/service.h | 3 | ||||
-rw-r--r-- | src/shutdownd.c | 6 | ||||
-rw-r--r-- | src/socket.c | 8 | ||||
-rw-r--r-- | src/socket.h | 1 | ||||
-rw-r--r-- | src/swap.c | 5 | ||||
-rw-r--r-- | src/systemctl.c | 15 | ||||
-rw-r--r-- | src/tmpfiles.c | 333 | ||||
-rw-r--r-- | src/unit.c | 51 | ||||
-rw-r--r-- | src/unit.h | 2 | ||||
-rw-r--r-- | src/update-utmp.c | 4 | ||||
-rw-r--r-- | src/util.c | 8 | ||||
-rw-r--r-- | src/utmp-wtmp.c | 34 | ||||
-rw-r--r-- | src/utmp-wtmp.h | 6 |
35 files changed, 1221 insertions, 545 deletions
diff --git a/src/dbus-socket.c b/src/dbus-socket.c index 2a1a17d780..37ab7eb3e2 100644 --- a/src/dbus-socket.c +++ b/src/dbus-socket.c @@ -51,6 +51,7 @@ " <property name=\"FreeBind\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"Transparent\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"Broadcast\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"PassCred\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"Mark\" type=\"i\" access=\"read\"/>\n" \ " <property name=\"MaxConnections\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"NAccepted\" type=\"u\" access=\"read\"/>\n" \ @@ -113,6 +114,7 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMes { "org.freedesktop.systemd1.Socket", "FreeBind", bus_property_append_bool, "b", &u->socket.free_bind }, { "org.freedesktop.systemd1.Socket", "Transparent", bus_property_append_bool, "b", &u->socket.transparent }, { "org.freedesktop.systemd1.Socket", "Broadcast", bus_property_append_bool, "b", &u->socket.broadcast }, + { "org.freedesktop.systemd1.Socket", "PassCred", bus_property_append_bool, "b", &u->socket.pass_cred }, { "org.freedesktop.systemd1.Socket", "Mark", bus_property_append_int, "i", &u->socket.mark }, { "org.freedesktop.systemd1.Socket", "MaxConnections", bus_property_append_unsigned, "u", &u->socket.max_connections }, { "org.freedesktop.systemd1.Socket", "NConnections", bus_property_append_unsigned, "u", &u->socket.n_connections }, diff --git a/src/dbus.c b/src/dbus.c index daa2c84a05..f9250f1354 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -48,6 +48,11 @@ #define CONNECTIONS_MAX 52 +/* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */ +#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" +/* Only used as a fallback */ +#define DBUS_SESSION_BUS_DEFAULT_ADDRESS "autolaunch:" + static const char bus_properties_interface[] = BUS_PROPERTIES_INTERFACE; static const char bus_introspectable_interface[] = BUS_INTROSPECTABLE_INTERFACE; @@ -767,37 +772,19 @@ static void bus_new_connection( dbus_connection_ref(new_connection); } -static int bus_init_system(Manager *m) { - DBusError error; - int r; - - assert(m); - - dbus_error_init(&error); - - if (m->system_bus) - return 0; - - if (m->running_as == MANAGER_SYSTEM && m->api_bus) - m->system_bus = m->api_bus; - else { - if (!(m->system_bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) { - log_debug("Failed to get system D-Bus connection, retrying later: %s", bus_error_message(&error)); - r = 0; - goto fail; - } - - if ((r = bus_setup_loop(m, m->system_bus)) < 0) - goto fail; - } +static int init_registered_system_bus(Manager *m) { + char *id; if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) { log_error("Not enough memory"); - r = -ENOMEM; - goto fail; + return -ENOMEM; } if (m->running_as != MANAGER_SYSTEM) { + DBusError error; + + dbus_error_init(&error); + dbus_bus_add_match(m->system_bus, "type='signal'," "interface='org.freedesktop.systemd1.Agent'," @@ -807,59 +794,28 @@ static int bus_init_system(Manager *m) { if (dbus_error_is_set(&error)) { log_error("Failed to register match: %s", bus_error_message(&error)); - r = -EIO; - goto fail; + dbus_error_free(&error); + return -1; } } - if (m->api_bus != m->system_bus) { - char *id; - log_debug("Successfully connected to system D-Bus bus %s as %s", - strnull((id = dbus_connection_get_server_id(m->system_bus))), - strnull(dbus_bus_get_unique_name(m->system_bus))); - dbus_free(id); - } + log_debug("Successfully connected to system D-Bus bus %s as %s", + strnull((id = dbus_connection_get_server_id(m->system_bus))), + strnull(dbus_bus_get_unique_name(m->system_bus))); + dbus_free(id); return 0; - -fail: - bus_done_system(m); - dbus_error_free(&error); - - return r; } -static int bus_init_api(Manager *m) { - DBusError error; +static int init_registered_api_bus(Manager *m) { int r; - assert(m); - - dbus_error_init(&error); - - if (m->api_bus) - return 0; - - if (m->running_as == MANAGER_SYSTEM && m->system_bus) - m->api_bus = m->system_bus; - else { - if (!(m->api_bus = dbus_bus_get_private(m->running_as == MANAGER_USER ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) { - log_debug("Failed to get API D-Bus connection, retrying later: %s", bus_error_message(&error)); - r = 0; - goto fail; - } - - if ((r = bus_setup_loop(m, m->api_bus)) < 0) - goto fail; - } - if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) || !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) || !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) || !dbus_connection_add_filter(m->api_bus, api_bus_message_filter, m, NULL)) { log_error("Not enough memory"); - r = -ENOMEM; - goto fail; + return -ENOMEM; } /* Get NameOwnerChange messages */ @@ -869,13 +825,7 @@ static int bus_init_api(Manager *m) { "interface='"DBUS_INTERFACE_DBUS"'," "member='NameOwnerChanged'," "path='"DBUS_PATH_DBUS"'", - &error); - - if (dbus_error_is_set(&error)) { - log_error("Failed to register match: %s", bus_error_message(&error)); - r = -EIO; - goto fail; - } + NULL); /* Get activation requests */ dbus_bus_add_match(m->api_bus, @@ -884,33 +834,225 @@ static int bus_init_api(Manager *m) { "interface='org.freedesktop.systemd1.Activator'," "member='ActivationRequest'," "path='"DBUS_PATH_DBUS"'", - &error); - - if (dbus_error_is_set(&error)) { - log_error("Failed to register match: %s", bus_error_message(&error)); - r = -EIO; - goto fail; - } + NULL); - if ((r = request_name(m)) < 0) - goto fail; + r = request_name(m); + if (r < 0) + return r; - if ((r = query_name_list(m)) < 0) - goto fail; + r = query_name_list(m); + if (r < 0) + return r; - if (m->api_bus != m->system_bus) { + if (m->running_as == MANAGER_USER) { char *id; log_debug("Successfully connected to API D-Bus bus %s as %s", strnull((id = dbus_connection_get_server_id(m->api_bus))), strnull(dbus_bus_get_unique_name(m->api_bus))); dbus_free(id); + } else + log_debug("Successfully initialized API on the system bus"); + + return 0; +} + +static void bus_register_cb(DBusPendingCall *pending, void *userdata) { + Manager *m = userdata; + DBusConnection **conn; + DBusMessage *reply; + DBusError error; + const char *name; + int r = 0; + + dbus_error_init(&error); + + conn = dbus_pending_call_get_data(pending, m->conn_data_slot); + assert(conn == &m->system_bus || conn == &m->api_bus); + + reply = dbus_pending_call_steal_reply(pending); + + switch (dbus_message_get_type(reply)) { + case DBUS_MESSAGE_TYPE_ERROR: + assert_se(dbus_set_error_from_message(&error, reply)); + log_warning("Failed to register to bus: %s", bus_error_message(&error)); + r = -1; + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse Hello reply: %s", bus_error_message(&error)); + r = -1; + break; + } + + log_debug("Received name %s in reply to Hello", name); + if (!dbus_bus_set_unique_name(*conn, name)) { + log_error("Failed to set unique name"); + r = -1; + break; + } + + if (conn == &m->system_bus) { + r = init_registered_system_bus(m); + if (r == 0 && m->running_as == MANAGER_SYSTEM) + r = init_registered_api_bus(m); + } else + r = init_registered_api_bus(m); + + break; + default: + assert_not_reached("Invalid reply message"); + } + + dbus_message_unref(reply); + dbus_error_free(&error); + + if (r < 0) { + if (conn == &m->system_bus) { + log_debug("Failed setting up the system bus"); + bus_done_system(m); + } else { + log_debug("Failed setting up the API bus"); + bus_done_api(m); + } + } +} + +static int manager_bus_async_register(Manager *m, DBusConnection **conn) { + DBusMessage *message = NULL; + DBusPendingCall *pending = NULL; + + message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "Hello"); + if (!message) + goto oom; + + if (!dbus_connection_send_with_reply(*conn, message, &pending, -1)) + goto oom; + + if (!dbus_pending_call_set_data(pending, m->conn_data_slot, conn, NULL)) + goto oom; + + if (!dbus_pending_call_set_notify(pending, bus_register_cb, m, NULL)) + goto oom; + + dbus_message_unref(message); + dbus_pending_call_unref(pending); + + return 0; +oom: + if (pending) { + dbus_pending_call_cancel(pending); + dbus_pending_call_unref(pending); + } + + if (message) + dbus_message_unref(message); + + return -ENOMEM; +} + +static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type) { + const char *address; + DBusConnection *connection; + DBusError error; + + switch (type) { + case DBUS_BUS_SYSTEM: + address = getenv("DBUS_SYSTEM_BUS_ADDRESS"); + if (!address || !address[0]) + address = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS; + break; + case DBUS_BUS_SESSION: + address = getenv("DBUS_SESSION_BUS_ADDRESS"); + if (!address || !address[0]) + address = DBUS_SESSION_BUS_DEFAULT_ADDRESS; + break; + default: + assert_not_reached("Invalid bus type"); + } + + dbus_error_init(&error); + + connection = dbus_connection_open_private(address, &error); + if (!connection) { + log_warning("Failed to open private bus connection: %s", bus_error_message(&error)); + goto fail; + } + + return connection; +fail: + if (connection) + dbus_connection_close(connection); + dbus_error_free(&error); + return NULL; +} + +static int bus_init_system(Manager *m) { + int r; + + if (m->system_bus) + return 0; + + m->system_bus = manager_bus_connect_private(m, DBUS_BUS_SYSTEM); + if (!m->system_bus) { + log_debug("Failed to connect to system D-Bus, retrying later"); + r = 0; + goto fail; } + r = bus_setup_loop(m, m->system_bus); + if (r < 0) + goto fail; + + r = manager_bus_async_register(m, &m->system_bus); + if (r < 0) + goto fail; + return 0; +fail: + bus_done_system(m); + + return r; +} + +static int bus_init_api(Manager *m) { + int r; + + if (m->api_bus) + return 0; + + if (m->running_as == MANAGER_SYSTEM) { + m->api_bus = m->system_bus; + /* In this mode there is no distinct connection to the API bus, + * the API is published on the system bus. + * bus_register_cb() is aware of that and will init the API + * when the system bus gets registered. + * No need to setup anything here. */ + return 0; + } + + m->api_bus = manager_bus_connect_private(m, DBUS_BUS_SESSION); + if (!m->api_bus) { + log_debug("Failed to connect to API D-Bus, retrying later"); + r = 0; + goto fail; + } + + r = bus_setup_loop(m, m->api_bus); + if (r < 0) + goto fail; + r = manager_bus_async_register(m, &m->api_bus); + if (r < 0) + goto fail; + + return 0; fail: bus_done_api(m); - dbus_error_free(&error); return r; } @@ -989,22 +1131,20 @@ int bus_init(Manager *m, bool try_bus_connect) { int r; if (set_ensure_allocated(&m->bus_connections, trivial_hash_func, trivial_compare_func) < 0 || - set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0) { - log_error("Not enough memory"); - return -ENOMEM; - } + set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0) + goto oom; if (m->name_data_slot < 0) - if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot)) { - log_error("Not enough memory"); - return -ENOMEM; - } + if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot)) + goto oom; + + if (m->conn_data_slot < 0) + if (!dbus_pending_call_allocate_data_slot(&m->conn_data_slot)) + goto oom; if (m->subscribed_data_slot < 0) - if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot)) { - log_error("Not enough memory"); - return -ENOMEM; - } + if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot)) + goto oom; if (try_bus_connect) { if ((r = bus_init_system(m)) < 0 || @@ -1016,6 +1156,9 @@ int bus_init(Manager *m, bool try_bus_connect) { return r; return 0; +oom: + log_error("Not enough memory"); + return -ENOMEM; } static void shutdown_connection(Manager *m, DBusConnection *c) { @@ -1053,48 +1196,46 @@ static void shutdown_connection(Manager *m, DBusConnection *c) { } dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL); - dbus_connection_flush(c); + /* system manager cannot afford to block on DBus */ + if (m->running_as != MANAGER_SYSTEM) + dbus_connection_flush(c); dbus_connection_close(c); dbus_connection_unref(c); } static void bus_done_api(Manager *m) { - assert(m); - - if (m->api_bus) { - if (m->system_bus == m->api_bus) - m->system_bus = NULL; + if (!m->api_bus) + return; + if (m->running_as == MANAGER_USER) shutdown_connection(m, m->api_bus); - m->api_bus = NULL; - } + m->api_bus = NULL; - if (m->queued_message) { - dbus_message_unref(m->queued_message); - m->queued_message = NULL; - } + if (m->queued_message) { + dbus_message_unref(m->queued_message); + m->queued_message = NULL; + } } static void bus_done_system(Manager *m) { - assert(m); + if (!m->system_bus) + return; - if (m->system_bus == m->api_bus) + if (m->running_as == MANAGER_SYSTEM) bus_done_api(m); - if (m->system_bus) { - shutdown_connection(m, m->system_bus); - m->system_bus = NULL; - } + shutdown_connection(m, m->system_bus); + m->system_bus = NULL; } static void bus_done_private(Manager *m) { + if (!m->private_bus) + return; - if (m->private_bus) { - dbus_server_disconnect(m->private_bus); - dbus_server_unref(m->private_bus); - m->private_bus = NULL; - } + dbus_server_disconnect(m->private_bus); + dbus_server_unref(m->private_bus); + m->private_bus = NULL; } void bus_done(Manager *m) { @@ -1116,6 +1257,9 @@ void bus_done(Manager *m) { if (m->name_data_slot >= 0) dbus_pending_call_free_data_slot(&m->name_data_slot); + if (m->conn_data_slot >= 0) + dbus_pending_call_free_data_slot(&m->conn_data_slot); + if (m->subscribed_data_slot >= 0) dbus_connection_free_data_slot(&m->subscribed_data_slot); } diff --git a/src/execute.c b/src/execute.c index 866e8bf2f6..abbbfddedc 100644 --- a/src/execute.c +++ b/src/execute.c @@ -716,6 +716,7 @@ static int setup_pam( pam_handle_t *handle = NULL; sigset_t ss, old_ss; int pam_code = PAM_SUCCESS; + int err; char **e = NULL; bool close_session = false; pid_t pam_pid = 0, parent_pid; @@ -835,6 +836,11 @@ static int setup_pam( return 0; fail: + if (pam_code != PAM_SUCCESS) + err = -EPERM; /* PAM errors do not map to errno */ + else + err = -errno; + if (handle) { if (close_session) pam_code = pam_close_session(handle, PAM_DATA_SILENT); @@ -851,7 +857,7 @@ fail: kill(pam_pid, SIGCONT); } - return EXIT_PAM; + return err; } #endif @@ -983,7 +989,7 @@ int exec_spawn(ExecCommand *command, } if (pid == 0) { - int i; + int i, err; sigset_t ss; const char *username = NULL, *home = NULL; uid_t uid = (uid_t) -1; @@ -1009,6 +1015,7 @@ int exec_spawn(ExecCommand *command, if (sigemptyset(&ss) < 0 || sigprocmask(SIG_SETMASK, &ss, NULL) < 0) { + err = -errno; r = EXIT_SIGNAL_MASK; goto fail_child; } @@ -1016,14 +1023,17 @@ int exec_spawn(ExecCommand *command, /* Close sockets very early to make sure we don't * block init reexecution because it cannot bind its * sockets */ - if (close_all_fds(socket_fd >= 0 ? &socket_fd : fds, - socket_fd >= 0 ? 1 : n_fds) < 0) { + log_forget_fds(); + err = close_all_fds(socket_fd >= 0 ? &socket_fd : fds, + socket_fd >= 0 ? 1 : n_fds); + if (err < 0) { r = EXIT_FDS; goto fail_child; } if (!context->same_pgrp) if (setsid() < 0) { + err = -errno; r = EXIT_SETSID; goto fail_child; } @@ -1031,12 +1041,14 @@ int exec_spawn(ExecCommand *command, if (context->tcpwrap_name) { if (socket_fd >= 0) if (!socket_tcpwrap(socket_fd, context->tcpwrap_name)) { + err = -EACCES; r = EXIT_TCPWRAP; goto fail_child; } for (i = 0; i < (int) n_fds; i++) { if (!socket_tcpwrap(fds[i], context->tcpwrap_name)) { + err = -EACCES; r = EXIT_TCPWRAP; goto fail_child; } @@ -1052,11 +1064,14 @@ int exec_spawn(ExecCommand *command, /* Set up terminal for the question */ if ((r = setup_confirm_stdio(context, - &saved_stdin, &saved_stdout))) + &saved_stdin, &saved_stdout))) { + err = -errno; goto fail_child; + } /* Now ask the question. */ if (!(line = exec_command_line(argv))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1065,18 +1080,21 @@ int exec_spawn(ExecCommand *command, free(line); if (r < 0 || response == 'n') { + err = -ECANCELED; r = EXIT_CONFIRM; goto fail_child; } else if (response == 's') { - r = 0; + err = r = 0; goto fail_child; } /* Release terminal for the question */ if ((r = restore_confirm_stdio(context, &saved_stdin, &saved_stdout, - &keep_stdin, &keep_stdout))) + &keep_stdin, &keep_stdout))) { + err = -errno; goto fail_child; + } } /* If a socket is connected to STDIN/STDOUT/STDERR, we @@ -1084,28 +1102,35 @@ int exec_spawn(ExecCommand *command, if (socket_fd >= 0) fd_nonblock(socket_fd, false); - if (!keep_stdin) - if (setup_input(context, socket_fd, apply_tty_stdin) < 0) { + if (!keep_stdin) { + err = setup_input(context, socket_fd, apply_tty_stdin); + if (err < 0) { r = EXIT_STDIN; goto fail_child; } + } - if (!keep_stdout) - if (setup_output(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin) < 0) { + if (!keep_stdout) { + err = setup_output(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin); + if (err < 0) { r = EXIT_STDOUT; goto fail_child; } + } - if (setup_error(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin) < 0) { + err = setup_error(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin); + if (err < 0) { r = EXIT_STDERR; goto fail_child; } - if (cgroup_bondings) - if (cgroup_bonding_install_list(cgroup_bondings, 0) < 0) { + if (cgroup_bondings) { + err = cgroup_bonding_install_list(cgroup_bondings, 0); + if (err < 0) { r = EXIT_CGROUP; goto fail_child; } + } if (context->oom_score_adjust_set) { char t[16]; @@ -1126,6 +1151,7 @@ int exec_spawn(ExecCommand *command, if (write_one_line_file("/proc/self/oom_adj", t) < 0 && errno != EACCES) { + err = -errno; r = EXIT_OOM_ADJUST; goto fail_child; } @@ -1134,6 +1160,7 @@ int exec_spawn(ExecCommand *command, if (context->nice_set) if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) { + err = -errno; r = EXIT_NICE; goto fail_child; } @@ -1146,6 +1173,7 @@ int exec_spawn(ExecCommand *command, if (sched_setscheduler(0, context->cpu_sched_policy | (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), ¶m) < 0) { + err = -errno; r = EXIT_SETSCHEDULER; goto fail_child; } @@ -1153,57 +1181,69 @@ int exec_spawn(ExecCommand *command, if (context->cpuset) if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { + err = -errno; r = EXIT_CPUAFFINITY; goto fail_child; } if (context->ioprio_set) if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) { + err = -errno; r = EXIT_IOPRIO; goto fail_child; } if (context->timer_slack_nsec_set) if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { + err = -errno; r = EXIT_TIMERSLACK; goto fail_child; } if (context->utmp_id) - utmp_put_init_process(0, context->utmp_id, getpid(), getsid(0), context->tty_path); + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); if (context->user) { username = context->user; - if (get_user_creds(&username, &uid, &gid, &home) < 0) { + err = get_user_creds(&username, &uid, &gid, &home); + if (err < 0) { r = EXIT_USER; goto fail_child; } - if (is_terminal_input(context->std_input)) - if (chown_terminal(STDIN_FILENO, uid) < 0) { + if (is_terminal_input(context->std_input)) { + err = chown_terminal(STDIN_FILENO, uid); + if (err < 0) { r = EXIT_STDIN; goto fail_child; } + } - if (cgroup_bondings && context->control_group_modify) - if (cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid) < 0 || - cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid) < 0) { + if (cgroup_bondings && context->control_group_modify) { + err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid); + if (err >= 0) + err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid); + if (err < 0) { r = EXIT_CGROUP; goto fail_child; } + } } - if (apply_permissions) - if (enforce_groups(context, username, gid) < 0) { + if (apply_permissions) { + err = enforce_groups(context, username, gid); + if (err < 0) { r = EXIT_GROUP; goto fail_child; } + } umask(context->umask); #ifdef HAVE_PAM if (context->pam_name && username) { - if (setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds) != 0) { + err = setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds); + if (err < 0) { r = EXIT_PAM; goto fail_child; } @@ -1211,6 +1251,7 @@ int exec_spawn(ExecCommand *command, #endif if (context->private_network) { if (unshare(CLONE_NEWNET) < 0) { + err = -errno; r = EXIT_NETWORK; goto fail_child; } @@ -1222,23 +1263,28 @@ int exec_spawn(ExecCommand *command, strv_length(context->read_only_dirs) > 0 || strv_length(context->inaccessible_dirs) > 0 || context->mount_flags != MS_SHARED || - context->private_tmp) - if ((r = setup_namespace( - context->read_write_dirs, - context->read_only_dirs, - context->inaccessible_dirs, - context->private_tmp, - context->mount_flags)) < 0) + context->private_tmp) { + err = setup_namespace(context->read_write_dirs, + context->read_only_dirs, + context->inaccessible_dirs, + context->private_tmp, + context->mount_flags); + if (err < 0) { + r = EXIT_NAMESPACE; goto fail_child; + } + } if (apply_chroot) { if (context->root_directory) if (chroot(context->root_directory) < 0) { + err = -errno; r = EXIT_CHROOT; goto fail_child; } if (chdir(context->working_directory ? context->working_directory : "/") < 0) { + err = -errno; r = EXIT_CHDIR; goto fail_child; } @@ -1249,11 +1295,13 @@ int exec_spawn(ExecCommand *command, if (asprintf(&d, "%s/%s", context->root_directory ? context->root_directory : "", context->working_directory ? context->working_directory : "") < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } if (chdir(d) < 0) { + err = -errno; free(d); r = EXIT_CHDIR; goto fail_child; @@ -1264,9 +1312,12 @@ int exec_spawn(ExecCommand *command, /* We repeat the fd closing here, to make sure that * nothing is leaked from the PAM modules */ - if (close_all_fds(fds, n_fds) < 0 || - shift_fds(fds, n_fds) < 0 || - flags_fds(fds, n_fds, context->non_blocking) < 0) { + err = close_all_fds(fds, n_fds); + if (err >= 0) + err = shift_fds(fds, n_fds); + if (err >= 0) + err = flags_fds(fds, n_fds, context->non_blocking); + if (err < 0) { r = EXIT_FDS; goto fail_child; } @@ -1278,22 +1329,27 @@ int exec_spawn(ExecCommand *command, continue; if (setrlimit(i, context->rlimit[i]) < 0) { + err = -errno; r = EXIT_LIMITS; goto fail_child; } } - if (context->capability_bounding_set_drop) - if (do_capability_bounding_set_drop(context->capability_bounding_set_drop) < 0) { + if (context->capability_bounding_set_drop) { + err = do_capability_bounding_set_drop(context->capability_bounding_set_drop); + if (err < 0) { r = EXIT_CAPABILITIES; goto fail_child; } + } - if (context->user) - if (enforce_user(context, uid) < 0) { + if (context->user) { + err = enforce_user(context, uid); + if (err < 0) { r = EXIT_USER; goto fail_child; } + } /* PR_GET_SECUREBITS is not privileged, while * PR_SET_SECUREBITS is. So to suppress @@ -1301,18 +1357,21 @@ int exec_spawn(ExecCommand *command, * PR_SET_SECUREBITS unless necessary. */ if (prctl(PR_GET_SECUREBITS) != context->secure_bits) if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) { + err = -errno; r = EXIT_SECUREBITS; goto fail_child; } if (context->capabilities) if (cap_set_proc(context->capabilities) < 0) { + err = -errno; r = EXIT_CAPABILITIES; goto fail_child; } } if (!(our_env = new0(char*, 7))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1320,12 +1379,14 @@ int exec_spawn(ExecCommand *command, if (n_fds > 0) if (asprintf(our_env + n_env++, "LISTEN_PID=%lu", (unsigned long) getpid()) < 0 || asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } if (home) if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1333,6 +1394,7 @@ int exec_spawn(ExecCommand *command, if (username) if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 || asprintf(our_env + n_env++, "USER=%s", username) < 0) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1341,6 +1403,7 @@ int exec_spawn(ExecCommand *command, context->std_output == EXEC_OUTPUT_TTY || context->std_error == EXEC_OUTPUT_TTY) if (!(our_env[n_env++] = strdup(default_term_for_tty(tty_path(context))))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1355,11 +1418,13 @@ int exec_spawn(ExecCommand *command, files_env, pam_env, NULL))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } if (!(final_argv = replace_env_argv(argv, final_env))) { + err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } @@ -1367,9 +1432,17 @@ int exec_spawn(ExecCommand *command, final_env = strv_env_clean(final_env); execve(command->path, final_argv, final_env); + err = -errno; r = EXIT_EXEC; fail_child: + if (r != 0) { + log_open(); + log_warning("Failed at step %s spawning %s: %s", + exit_status_to_string(r, EXIT_STATUS_SYSTEMD), + command->path, strerror(-err)); + } + strv_free(our_env); strv_free(final_env); strv_free(pam_env); @@ -1787,8 +1860,7 @@ void exec_status_start(ExecStatus *s, pid_t pid) { void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) { assert(s); - if ((s->pid && s->pid != pid) || - !s->start_timestamp.realtime <= 0) + if (s->pid && s->pid != pid) zero(*s); s->pid = pid; diff --git a/src/exit-status.c b/src/exit-status.c index 8ed1a0e362..ab8907d32c 100644 --- a/src/exit-status.c +++ b/src/exit-status.c @@ -119,6 +119,9 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { case EXIT_NETWORK: return "NETWORK"; + + case EXIT_NAMESPACE: + return "NAMESPACE"; } } diff --git a/src/exit-status.h b/src/exit-status.h index 3e977b10ef..44ef879562 100644 --- a/src/exit-status.h +++ b/src/exit-status.h @@ -65,7 +65,8 @@ typedef enum ExitStatus { EXIT_STDERR, EXIT_TCPWRAP, EXIT_PAM, - EXIT_NETWORK + EXIT_NETWORK, + EXIT_NAMESPACE } ExitStatus; diff --git a/src/fsck.c b/src/fsck.c index 3477ba16ad..d3ac83c25e 100644 --- a/src/fsck.c +++ b/src/fsck.c @@ -80,7 +80,7 @@ static void start_target(const char *target, bool isolate) { if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - /* Don't print a waring if we aren't called during + /* Don't print a warning if we aren't called during * startup */ if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB)) log_error("Failed to start unit: %s", bus_error_message(&error)); diff --git a/src/install.c b/src/install.c index cfbd50ead9..1fb1f9d580 100644 --- a/src/install.c +++ b/src/install.c @@ -72,9 +72,8 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d case UNIT_FILE_SYSTEM: if (root_dir && runtime) - return -EINVAL; - - if (runtime) + asprintf(&p, "%s/run/systemd/system", root_dir); + else if (runtime) p = strdup("/run/systemd/system"); else if (root_dir) asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH); @@ -484,19 +484,20 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { switch (result) { case JOB_DONE: - unit_status_printf(u, "Started %s.\n", unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u)); break; case JOB_FAILED: - unit_status_printf(u, "Starting %s " ANSI_HIGHLIGHT_ON "failed" ANSI_HIGHLIGHT_OFF ", see 'systemctl status %s' for details.\n", unit_description(u), u->meta.id); + unit_status_printf(u, ANSI_HIGHLIGHT_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u)); + unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->meta.id); break; case JOB_DEPENDENCY: - unit_status_printf(u, "Starting %s " ANSI_HIGHLIGHT_ON "aborted" ANSI_HIGHLIGHT_OFF " because a dependency failed.\n", unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u)); break; case JOB_TIMEOUT: - unit_status_printf(u, "Starting %s " ANSI_HIGHLIGHT_ON "timed out" ANSI_HIGHLIGHT_OFF ".\n", unit_description(u), u->meta.id); + unit_status_printf(u, ANSI_HIGHLIGHT_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u)); break; default: @@ -508,12 +509,12 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { switch (result) { case JOB_TIMEOUT: - unit_status_printf(u, "Stopping %s " ANSI_HIGHLIGHT_ON "timed out" ANSI_HIGHLIGHT_OFF ".\n", unit_description(u), u->meta.id); + unit_status_printf(u, ANSI_HIGHLIGHT_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u)); break; case JOB_DONE: case JOB_FAILED: - unit_status_printf(u, "Stopped %s.\n", unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u)); break; default: diff --git a/src/kmsg-syslogd.c b/src/kmsg-syslogd.c index 70cc0730ee..8cc423a299 100644 --- a/src/kmsg-syslogd.c +++ b/src/kmsg-syslogd.c @@ -114,9 +114,6 @@ static int server_init(Server *s) { return -EINVAL; } - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - log_error("SO_PASSCRED failed: %m"); - zero(ev); ev.events = EPOLLIN; ev.data.fd = fd; diff --git a/src/label.c b/src/label.c index d9d195b50f..2c887a0fe5 100644 --- a/src/label.c +++ b/src/label.c @@ -109,7 +109,7 @@ int label_fix(const char *path, bool ignore_enoent) { return 0; if (r == 0) { - r = setfilecon(path, fcon); + r = lsetfilecon(path, fcon); freecon(fcon); /* If the FS doesn't support labels, then exit without warning */ diff --git a/src/load-fragment-gperf.gperf.m4 b/src/load-fragment-gperf.gperf.m4 index 41797d20c0..35ec00557e 100644 --- a/src/load-fragment-gperf.gperf.m4 +++ b/src/load-fragment-gperf.gperf.m4 @@ -177,6 +177,7 @@ Socket.PipeSize, config_parse_size, 0, Socket.FreeBind, config_parse_bool, 0, offsetof(Socket, free_bind) Socket.Transparent, config_parse_bool, 0, offsetof(Socket, transparent) Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) +Socket.PassCred, config_parse_bool, 0, offsetof(Socket, pass_cred) Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize) @@ -209,6 +210,7 @@ m4_dnl Path.PathExists, config_parse_path_spec, 0, 0 Path.PathExistsGlob, config_parse_path_spec, 0, 0 Path.PathChanged, config_parse_path_spec, 0, 0 +Path.PathModified, config_parse_path_spec, 0, 0 Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0 Path.Unit, config_parse_path_unit, 0, 0 Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory) @@ -118,6 +118,9 @@ static int create_log_socket(int type) { struct timeval tv; int fd; + if (getpid() == 1) + /* systemd should not block on syslog */ + type |= SOCK_NONBLOCK; if ((fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0)) < 0) return -errno; @@ -237,6 +240,10 @@ void log_close(void) { log_close_syslog(); } +void log_forget_fds(void) { + console_fd = kmsg_fd = syslog_fd = -1; +} + void log_set_max_level(int level) { assert((level & LOG_PRIMASK) == level); @@ -326,7 +333,8 @@ static int write_to_syslog( for (;;) { ssize_t n; - if ((n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL)) < 0) + n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL); + if (n < 0) return -errno; if (!syslog_is_stream || @@ -403,8 +411,10 @@ static int log_dispatch( log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_SYSLOG) { - if ((k = write_to_syslog(level, file, line, func, buffer)) < 0) { - log_close_syslog(); + k = write_to_syslog(level, file, line, func, buffer); + if (k < 0) { + if (k != -EAGAIN) + log_close_syslog(); log_open_kmsg(); } else if (k > 0) r++; @@ -415,16 +425,19 @@ static int log_dispatch( log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_KMSG)) { - if ((k = write_to_kmsg(level, file, line, func, buffer)) < 0) { + k = write_to_kmsg(level, file, line, func, buffer); + if (k < 0) { log_close_kmsg(); log_open_console(); } else if (k > 0) r++; } - if (k <= 0 && - (k = write_to_console(level, file, line, func, buffer)) < 0) - return k; + if (k <= 0) { + k = write_to_console(level, file, line, func, buffer); + if (k < 0) + return k; + } buffer = e; } while (buffer); @@ -57,6 +57,7 @@ int log_get_max_level(void); int log_open(void); void log_close(void); +void log_forget_fds(void); void log_close_syslog(void); void log_close_kmsg(void); diff --git a/src/logind-session.c b/src/logind-session.c index b0a09e3d37..63ee75808b 100644 --- a/src/logind-session.c +++ b/src/logind-session.c @@ -536,7 +536,7 @@ int session_start(Session *s) { if (r < 0) return r; - log_full(s->display || s->tty ? LOG_INFO : LOG_DEBUG, + log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, "New session %s of user %s.", s->id, s->user->name); /* Create cgroup */ @@ -659,7 +659,7 @@ int session_stop(Session *s) { assert(s); if (s->started) - log_full(s->display || s->tty ? LOG_INFO : LOG_DEBUG, + log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, "Removed session %s.", s->id); /* Kill cgroup */ diff --git a/src/manager.c b/src/manager.c index f8cbcfcc98..9957bbf12a 100644 --- a/src/manager.c +++ b/src/manager.c @@ -231,7 +231,7 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { dual_timestamp_get(&m->startup_timestamp); m->running_as = running_as; - m->name_data_slot = m->subscribed_data_slot = -1; + m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1; m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->pin_cgroupfs_fd = -1; diff --git a/src/manager.h b/src/manager.h index 5deb5696b7..6e7558e175 100644 --- a/src/manager.h +++ b/src/manager.h @@ -179,6 +179,7 @@ struct Manager { Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */ int32_t name_data_slot; + int32_t conn_data_slot; int32_t subscribed_data_slot; uint32_t current_job_id; diff --git a/src/mount.c b/src/mount.c index f9cfe910a0..47422ccf8b 100644 --- a/src/mount.c +++ b/src/mount.c @@ -68,8 +68,10 @@ static void mount_init(Unit *u) { /* The stdio/kmsg bridge socket is on /, in order to avoid a * dep loop, don't use kmsg logging for -.mount */ - if (!unit_has_name(u, "-.mount")) - m->exec_context.std_output = EXEC_OUTPUT_KMSG; + if (!unit_has_name(u, "-.mount")) { + m->exec_context.std_output = u->meta.manager->default_std_output; + m->exec_context.std_error = u->meta.manager->default_std_error; + } /* We need to make sure that /bin/mount is always called in * the same process group as us, so that the autofs kernel diff --git a/src/pam-module.c b/src/pam-module.c index 78f9b30d5b..14e706b374 100644 --- a/src/pam-module.c +++ b/src/pam-module.c @@ -445,6 +445,10 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (isempty(display)) display = tty; tty = ""; + } else if (streq(tty, "cron")) { + /* cron has been setting PAM_TTY to "cron" for a very long time + * and it cannot stop doing that for compatibility reasons. */ + tty = ""; } if (!isempty(cvtnr)) @@ -454,7 +458,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( get_seat_from_display(display, &seat, &vtnr); type = !isempty(display) ? "x11" : - !isempty(tty) ? "tty" : "other"; + !isempty(tty) ? "tty" : "unspecified"; remote = !isempty(remote_host) && !streq(remote_host, "localhost") && !streq(remote_host, "localhost.localdomain"); @@ -499,6 +503,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( goto finish; } + if (debug) + pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: " + "uid=%u pid=%u service=%s type=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s", + uid, pid, service, type, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host); + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); if (!reply) { pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error)); @@ -519,6 +528,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( goto finish; } + if (debug) + pam_syslog(handle, LOG_DEBUG, "Reply from logind: " + "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u", + id, object_path, runtime_path, session_fd, seat, vtnr); + r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0); if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to set session id."); diff --git a/src/path.c b/src/path.c index f15c9214ef..3fee24760c 100644 --- a/src/path.c +++ b/src/path.c @@ -39,26 +39,205 @@ static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_FAILED] = UNIT_FAILED }; -static void path_init(Unit *u) { - Path *p = PATH(u); +int pathspec_watch(PathSpec *s, Unit *u) { + static const int flags_table[_PATH_TYPE_MAX] = { + [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, + [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, + [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO + }; + + bool exists = false; + char *k, *slash; + int r; assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(s); - p->directory_mode = 0755; + pathspec_unwatch(s, u); + + if (!(k = strdup(s->path))) + return -ENOMEM; + + if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { + r = -errno; + goto fail; + } + + if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) { + r = -errno; + goto fail; + } + + if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) + exists = true; + + do { + int flags; + + /* This assumes the path was passed through path_kill_slashes()! */ + if (!(slash = strrchr(k, '/'))) + break; + + /* Trim the path at the last slash. Keep the slash if it's the root dir. */ + slash[slash == k] = 0; + + flags = IN_MOVE_SELF; + if (!exists) + flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + + if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) + exists = true; + } while (slash != k); + + return 0; + +fail: + free(k); + + pathspec_unwatch(s, u); + return r; } -static void path_unwatch_one(Path *p, PathSpec *s) { +void pathspec_unwatch(PathSpec *s, Unit *u) { if (s->inotify_fd < 0) return; - unit_unwatch_fd(UNIT(p), &s->watch); + unit_unwatch_fd(u, &s->watch); close_nointr_nofail(s->inotify_fd); s->inotify_fd = -1; } +int pathspec_fd_event(PathSpec *s, uint32_t events) { + uint8_t *buf = NULL; + struct inotify_event *e; + ssize_t k; + int l; + int r = 0; + + if (events != EPOLLIN) { + log_error("Got Invalid poll event on inotify."); + r = -EINVAL; + goto out; + } + + if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) { + log_error("FIONREAD failed: %m"); + r = -errno; + goto out; + } + + assert(l > 0); + + if (!(buf = malloc(l))) { + log_error("Failed to allocate buffer: %m"); + r = -errno; + goto out; + } + + if ((k = read(s->inotify_fd, buf, l)) < 0) { + log_error("Failed to read inotify event: %m"); + r = -errno; + goto out; + } + + e = (struct inotify_event*) buf; + + while (k > 0) { + size_t step; + + if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) && + s->primary_wd == e->wd) + r = 1; + + step = sizeof(struct inotify_event) + e->len; + assert(step <= (size_t) k); + + e = (struct inotify_event*) ((uint8_t*) e + step); + k -= step; + } +out: + free(buf); + return r; +} + +static bool pathspec_check_good(PathSpec *s, bool initial) { + bool good = false; + + switch (s->type) { + + case PATH_EXISTS: + good = access(s->path, F_OK) >= 0; + break; + + case PATH_EXISTS_GLOB: + good = glob_exists(s->path) > 0; + break; + + case PATH_DIRECTORY_NOT_EMPTY: { + int k; + + k = dir_is_empty(s->path); + good = !(k == -ENOENT || k > 0); + break; + } + + case PATH_CHANGED: + case PATH_MODIFIED: { + bool b; + + b = access(s->path, F_OK) >= 0; + good = !initial && b != s->previous_exists; + s->previous_exists = b; + break; + } + + default: + ; + } + + return good; +} + +static bool pathspec_startswith(PathSpec *s, const char *what) { + return path_startswith(s->path, what); +} + +static void pathspec_mkdir(PathSpec *s, mode_t mode) { + int r; + + if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) + return; + + if ((r = mkdir_p(s->path, mode)) < 0) + log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); +} + +static void pathspec_dump(PathSpec *s, FILE *f, const char *prefix) { + fprintf(f, + "%s%s: %s\n", + prefix, + path_type_to_string(s->type), + s->path); +} + +void pathspec_done(PathSpec *s) { + assert(s->inotify_fd == -1); + free(s->path); +} + +static void path_init(Unit *u) { + Path *p = PATH(u); + + assert(u); + assert(u->meta.load_state == UNIT_STUB); + + p->directory_mode = 0755; +} + static void path_done(Unit *u) { Path *p = PATH(u); PathSpec *s; @@ -66,9 +245,9 @@ static void path_done(Unit *u) { assert(p); while ((s = p->specs)) { - path_unwatch_one(p, s); + pathspec_unwatch(s, u); LIST_REMOVE(PathSpec, spec, p->specs, s); - free(s->path); + pathspec_done(s); free(s); } } @@ -86,7 +265,7 @@ int path_add_one_mount_link(Path *p, Mount *m) { LIST_FOREACH(spec, s, p->specs) { - if (!path_startswith(s->path, m->where)) + if (!pathspec_startswith(s, m->where)) continue; if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) @@ -187,71 +366,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) { prefix, p->directory_mode); LIST_FOREACH(spec, s, p->specs) - fprintf(f, - "%s%s: %s\n", - prefix, - path_type_to_string(s->type), - s->path); -} - -static int path_watch_one(Path *p, PathSpec *s) { - static const int flags_table[_PATH_TYPE_MAX] = { - [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, - [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO - }; - - bool exists = false; - char *k, *slash; - int r; - - assert(p); - assert(s); - - path_unwatch_one(p, s); - - if (!(k = strdup(s->path))) - return -ENOMEM; - - if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { - r = -errno; - goto fail; - } - - if (unit_watch_fd(UNIT(p), s->inotify_fd, EPOLLIN, &s->watch) < 0) { - r = -errno; - goto fail; - } - - if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) - exists = true; - - do { - int flags; - - /* This assumes the path was passed through path_kill_slashes()! */ - if (!(slash = strrchr(k, '/'))) - break; - - /* Trim the path at the last slash. Keep the slash if it's the root dir. */ - slash[slash == k] = 0; - - flags = IN_MOVE_SELF; - if (!exists) - flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; - - if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) - exists = true; - } while (slash != k); - - return 0; - -fail: - free(k); - - path_unwatch_one(p, s); - return r; + pathspec_dump(s, f, prefix); } static void path_unwatch(Path *p) { @@ -260,7 +375,7 @@ static void path_unwatch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) - path_unwatch_one(p, s); + pathspec_unwatch(s, UNIT(p)); } static int path_watch(Path *p) { @@ -270,7 +385,7 @@ static int path_watch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) - if ((r = path_watch_one(p, s)) < 0) + if ((r = pathspec_watch(s, UNIT(p))) < 0) return r; return 0; @@ -361,37 +476,7 @@ static bool path_check_good(Path *p, bool initial) { assert(p); LIST_FOREACH(spec, s, p->specs) { - - switch (s->type) { - - case PATH_EXISTS: - good = access(s->path, F_OK) >= 0; - break; - - case PATH_EXISTS_GLOB: - good = glob_exists(s->path) > 0; - break; - - case PATH_DIRECTORY_NOT_EMPTY: { - int k; - - k = dir_is_empty(s->path); - good = !(k == -ENOENT || k > 0); - break; - } - - case PATH_CHANGED: { - bool b; - - b = access(s->path, F_OK) >= 0; - good = !initial && b != s->previous_exists; - s->previous_exists = b; - break; - } - - default: - ; - } + good = pathspec_check_good(s, initial); if (good) break; @@ -440,15 +525,8 @@ static void path_mkdir(Path *p) { if (!p->make_directory) return; - LIST_FOREACH(spec, s, p->specs) { - int r; - - if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) - continue; - - if ((r = mkdir_p(s->path, p->directory_mode)) < 0) - log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); - } + LIST_FOREACH(spec, s, p->specs) + pathspec_mkdir(s, p->directory_mode); } static int path_start(Unit *u) { @@ -525,12 +603,8 @@ static const char *path_sub_state_to_string(Unit *u) { static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { Path *p = PATH(u); - int l; - ssize_t k; - uint8_t *buf = NULL; - struct inotify_event *e; PathSpec *s; - bool changed; + int changed; assert(p); assert(fd >= 0); @@ -541,13 +615,8 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { /* log_debug("inotify wakeup on %s.", u->meta.id); */ - if (events != EPOLLIN) { - log_error("Got Invalid poll event on inotify."); - goto fail; - } - LIST_FOREACH(spec, s, p->specs) - if (s->inotify_fd == fd) + if (pathspec_owns_inotify_fd(s, fd)) break; if (!s) { @@ -555,55 +624,23 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { goto fail; } - if (ioctl(fd, FIONREAD, &l) < 0) { - log_error("FIONREAD failed: %s", strerror(errno)); + changed = pathspec_fd_event(s, events); + if (changed < 0) goto fail; - } - - assert(l > 0); - - if (!(buf = malloc(l))) { - log_error("Failed to allocate buffer: %s", strerror(ENOMEM)); - goto fail; - } - - if ((k = read(fd, buf, l)) < 0) { - log_error("Failed to read inotify event: %s", strerror(-errno)); - goto fail; - } /* If we are already running, then remember that one event was * dispatched so that we restart the service only if something * actually changed on disk */ p->inotify_triggered = true; - e = (struct inotify_event*) buf; - - changed = false; - while (k > 0) { - size_t step; - - if (s->type == PATH_CHANGED && s->primary_wd == e->wd) - changed = true; - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) k); - - e = (struct inotify_event*) ((uint8_t*) e + step); - k -= step; - } - if (changed) path_enter_running(p); else path_enter_waiting(p, false, true); - free(buf); - return; fail: - free(buf); path_enter_dead(p, false); } @@ -679,6 +716,7 @@ static const char* const path_type_table[_PATH_TYPE_MAX] = { [PATH_EXISTS] = "PathExists", [PATH_EXISTS_GLOB] = "PathExistsGlob", [PATH_CHANGED] = "PathChanged", + [PATH_MODIFIED] = "PathModified", [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty" }; diff --git a/src/path.h b/src/path.h index 116fc63ff7..1d78fe424a 100644 --- a/src/path.h +++ b/src/path.h @@ -41,6 +41,7 @@ typedef enum PathType { PATH_EXISTS_GLOB, PATH_DIRECTORY_NOT_EMPTY, PATH_CHANGED, + PATH_MODIFIED, _PATH_TYPE_MAX, _PATH_TYPE_INVALID = -1 } PathType; @@ -60,6 +61,14 @@ typedef struct PathSpec { } PathSpec; +int pathspec_watch(PathSpec *s, Unit *u); +void pathspec_unwatch(PathSpec *s, Unit *u); +int pathspec_fd_event(PathSpec *s, uint32_t events); +void pathspec_done(PathSpec *s); +static inline bool pathspec_owns_inotify_fd(PathSpec *s, int fd) { + return s->inotify_fd == fd; +} + struct Path { Meta meta; diff --git a/src/rc-local-generator.c b/src/rc-local-generator.c new file mode 100644 index 0000000000..ac6424a786 --- /dev/null +++ b/src/rc-local-generator.c @@ -0,0 +1,107 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2011 Michal Schmidt + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include "log.h" +#include "util.h" + +#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) +#define SCRIPT_PATH "/etc/rc.d/rc.local" +#elif defined(TARGET_SUSE) +#define SCRIPT_PATH "/etc/init.d/boot.local" +#endif + +const char *arg_dest = "/tmp"; + +static int add_symlink(const char *service) { + char *from = NULL, *to = NULL; + int r; + + assert(service); + + asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", service); + asprintf(&to, "%s/multi-user.target.wants/%s", arg_dest, service); + + if (!from || !to) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(to, 0755); + + r = symlink(from, to); + if (r < 0) { + if (errno == EEXIST) + r = 0; + else { + log_error("Failed to create symlink from %s to %s: %m", from, to); + r = -errno; + } + } + +finish: + + free(from); + free(to); + + return r; +} + +static bool file_is_executable(const char *f) { + struct stat st; + + if (stat(f, &st) < 0) + return false; + + return S_ISREG(st.st_mode) && (st.st_mode & 0111); +} + +int main(int argc, char *argv[]) { + + int r = EXIT_SUCCESS; + + if (argc > 2) { + log_error("This program takes one or no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (argc > 1) + arg_dest = argv[1]; + + if (file_is_executable(SCRIPT_PATH)) { + log_debug("Automatically adding rc-local.service."); + + if (add_symlink("rc-local.service") < 0) + r = EXIT_FAILURE; + + } + + return r; +} diff --git a/src/service.c b/src/service.c index eb475d9cc9..feecbbe2b6 100644 --- a/src/service.c +++ b/src/service.c @@ -841,7 +841,7 @@ static int service_load_sysv_path(Service *s, const char *path) { s->restart = SERVICE_RESTART_NO; if (s->meta.manager->sysv_console) - s->exec_context.std_output = EXEC_OUTPUT_TTY; + s->exec_context.std_output = EXEC_OUTPUT_SYSLOG_AND_CONSOLE; s->exec_context.kill_mode = KILL_PROCESS; @@ -1290,7 +1290,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { free(p2); } -static int service_load_pid_file(Service *s, bool warn_if_missing) { +static int service_load_pid_file(Service *s, bool may_warn) { char *k; int r; pid_t pid; @@ -1301,9 +1301,9 @@ static int service_load_pid_file(Service *s, bool warn_if_missing) { return -ENOENT; if ((r = read_one_line_file(s->pid_file, &k)) < 0) { - if (warn_if_missing) - log_warning("Failed to read PID file %s after %s. The service might be broken.", - s->pid_file, service_state_to_string(s->state)); + if (may_warn) + log_info("PID file %s not readable (yet?) after %s.", + s->pid_file, service_state_to_string(s->state)); return r; } @@ -1314,8 +1314,9 @@ static int service_load_pid_file(Service *s, bool warn_if_missing) { return r; if (kill(pid, 0) < 0 && errno != EPERM) { - log_warning("PID %lu read from file %s does not exist. Your service or init script might be broken.", - (unsigned long) pid, s->pid_file); + if (may_warn) + log_info("PID %lu read from file %s does not exist.", + (unsigned long) pid, s->pid_file); return -ESRCH; } @@ -1327,7 +1328,8 @@ static int service_load_pid_file(Service *s, bool warn_if_missing) { (unsigned long) s->main_pid, (unsigned long) pid); service_unwatch_main_pid(s); s->main_pid_known = false; - } + } else + log_debug("Main PID loaded: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1358,6 +1360,7 @@ static int service_search_main_pid(Service *s) { if ((pid = cgroup_bonding_search_main_pid_list(s->meta.cgroup_bondings)) <= 0) return -ENOENT; + log_debug("Main PID guessed: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1450,6 +1453,17 @@ static int service_notify_sockets_dead(Service *s) { return 0; } +static void service_unwatch_pid_file(Service *s) { + if (!s->pid_file_pathspec) + return; + + log_debug("Stopping watch for %s's PID file %s", s->meta.id, s->pid_file_pathspec->path); + pathspec_unwatch(s->pid_file_pathspec, UNIT(s)); + pathspec_done(s->pid_file_pathspec); + free(s->pid_file_pathspec); + s->pid_file_pathspec = NULL; +} + static void service_set_state(Service *s, ServiceState state) { ServiceState old_state; assert(s); @@ -1457,6 +1471,8 @@ static void service_set_state(Service *s, ServiceState state) { old_state = s->state; s->state = state; + service_unwatch_pid_file(s); + if (state != SERVICE_START_PRE && state != SERVICE_START && state != SERVICE_START_POST && @@ -2601,6 +2617,95 @@ static bool service_check_snapshot(Unit *u) { return !s->got_socket_fd; } +static int service_retry_pid_file(Service *s) { + int r; + + assert(s->pid_file); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + + r = service_load_pid_file(s, false); + if (r < 0) + return r; + + service_unwatch_pid_file(s); + + service_enter_running(s, true); + return 0; +} + +static int service_watch_pid_file(Service *s) { + int r; + + log_debug("Setting watch for %s's PID file %s", s->meta.id, s->pid_file_pathspec->path); + r = pathspec_watch(s->pid_file_pathspec, UNIT(s)); + if (r < 0) + goto fail; + + /* the pidfile might have appeared just before we set the watch */ + service_retry_pid_file(s); + + return 0; +fail: + log_error("Failed to set a watch for %s's PID file %s: %s", + s->meta.id, s->pid_file_pathspec->path, strerror(-r)); + service_unwatch_pid_file(s); + return r; +} + +static int service_demand_pid_file(Service *s) { + PathSpec *ps; + + assert(s->pid_file); + assert(!s->pid_file_pathspec); + + ps = new0(PathSpec, 1); + if (!ps) + return -ENOMEM; + + ps->path = strdup(s->pid_file); + if (!ps->path) { + free(ps); + return -ENOMEM; + } + + path_kill_slashes(ps->path); + + /* PATH_CHANGED would not be enough. There are daemons (sendmail) that + * keep their PID file open all the time. */ + ps->type = PATH_MODIFIED; + ps->inotify_fd = -1; + + s->pid_file_pathspec = ps; + + return service_watch_pid_file(s); +} + +static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { + Service *s = SERVICE(u); + + assert(s); + assert(fd >= 0); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + assert(s->pid_file_pathspec); + assert(pathspec_owns_inotify_fd(s->pid_file_pathspec, fd)); + + log_debug("inotify event for %s", u->meta.id); + + if (pathspec_fd_event(s->pid_file_pathspec, events) < 0) + goto fail; + + if (service_retry_pid_file(s) == 0) + return; + + if (service_watch_pid_file(s) < 0) + goto fail; + + return; +fail: + service_unwatch_pid_file(s); + service_enter_signal(s, SERVICE_STOP_SIGTERM, false); +} + static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { Service *s = SERVICE(u); bool success; @@ -2706,7 +2811,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { success = true; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, + log_full(success ? LOG_DEBUG : LOG_NOTICE, "%s: control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); s->failure = s->failure || !success; @@ -2741,30 +2846,46 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_START: assert(s->type == SERVICE_FORKING); - /* Let's try to load the pid - * file here if we can. We - * ignore the return value, - * since the PID file might - * actually be created by a - * START_POST script */ - - if (success) { - service_load_pid_file(s, !s->exec_command[SERVICE_EXEC_START_POST]); - service_search_main_pid(s); + if (!success) { + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + break; + } - service_enter_start_post(s); + if (s->pid_file) { + /* Let's try to load the pid file here if we can. + * The PID file might actually be created by a START_POST + * script. In that case don't worry if the loading fails. */ + bool has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; + int r = service_load_pid_file(s, !has_start_post); + if (!has_start_post && r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + break; + } } else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + service_search_main_pid(s); + service_enter_start_post(s); break; case SERVICE_START_POST: - if (success) { - service_load_pid_file(s, true); - service_search_main_pid(s); + if (!success) { + service_enter_stop(s, false); + break; } - s->reload_failure = !success; + if (s->pid_file) { + int r = service_load_pid_file(s, true); + if (r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_stop(s, false); + break; + } + } else + service_search_main_pid(s); + service_enter_running(s, true); break; @@ -2906,6 +3027,20 @@ static void service_cgroup_notify_event(Unit *u) { * except when we don't know pid which to expect the * SIGCHLD for. */ + case SERVICE_START: + case SERVICE_START_POST: + /* If we were hoping for the daemon to write its PID file, + * we can give up now. */ + if (s->pid_file_pathspec) { + log_warning("%s never wrote its PID file. Failing.", s->meta.id); + service_unwatch_pid_file(s); + if (s->state == SERVICE_START) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + else + service_enter_stop(s, false); + } + break; + case SERVICE_RUNNING: service_enter_running(s, true); break; @@ -3215,7 +3350,7 @@ static int service_enumerate(Manager *m) { r = 0; #ifdef TARGET_SUSE - sysv_facility_in_insserv_conf (m); + sysv_facility_in_insserv_conf (m); #endif finish: @@ -3510,6 +3645,7 @@ const UnitVTable service_vtable = { .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, + .fd_event = service_fd_event, .reset_failed = service_reset_failed, diff --git a/src/service.h b/src/service.h index e28f74b898..15d58cc8ca 100644 --- a/src/service.h +++ b/src/service.h @@ -86,6 +86,8 @@ typedef enum NotifyAccess { _NOTIFY_ACCESS_INVALID = -1 } NotifyAccess; +typedef struct PathSpec PathSpec; + struct Service { Meta meta; @@ -157,6 +159,7 @@ struct Service { Set *configured_sockets; Watch timer_watch; + PathSpec *pid_file_pathspec; NotifyAccess notify_access; }; diff --git a/src/shutdownd.c b/src/shutdownd.c index 0ffa8b2881..46856b01ad 100644 --- a/src/shutdownd.c +++ b/src/shutdownd.c @@ -173,7 +173,6 @@ int main(int argc, char *argv[]) { }; int r = EXIT_FAILURE, n_fds; - int one = 1; struct shutdownd_command c; struct pollfd pollfd[_FD_MAX]; bool exec_shutdown = false, unlink_nologin = false, failed = false; @@ -205,11 +204,6 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if (setsockopt(SD_LISTEN_FDS_START, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { - log_error("SO_PASSCRED failed: %m"); - return EXIT_FAILURE; - } - zero(c); zero(pollfd); diff --git a/src/socket.c b/src/socket.c index 7ddf326a22..0864cce86d 100644 --- a/src/socket.c +++ b/src/socket.c @@ -406,6 +406,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sFreeBind: %s\n" "%sTransparent: %s\n" "%sBroadcast: %s\n" + "%sPassCred: %s\n" "%sTCPCongestion: %s\n", prefix, socket_state_to_string(s->state), prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only), @@ -416,6 +417,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(s->free_bind), prefix, yes_no(s->transparent), prefix, yes_no(s->broadcast), + prefix, yes_no(s->pass_cred), prefix, strna(s->tcp_congestion)); if (s->control_pid > 0) @@ -657,6 +659,12 @@ static void socket_apply_socket_options(Socket *s, int fd) { log_warning("SO_BROADCAST failed: %m"); } + if (s->pass_cred) { + int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + log_warning("SO_PASSCRED failed: %m"); + } + if (s->priority >= 0) if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) log_warning("SO_PRIORITY failed: %m"); diff --git a/src/socket.h b/src/socket.h index fd13ac4e4c..fbd29dad72 100644 --- a/src/socket.h +++ b/src/socket.h @@ -118,6 +118,7 @@ struct Socket { bool free_bind; bool transparent; bool broadcast; + bool pass_cred; int priority; int mark; size_t receive_buffer; diff --git a/src/swap.c b/src/swap.c index 54a8640aa4..4fa30a3e32 100644 --- a/src/swap.c +++ b/src/swap.c @@ -74,7 +74,7 @@ static void swap_unset_proc_swaps(Swap *s) { s->parameters_proc_swaps.what = NULL; } - static void swap_init(Unit *u) { +static void swap_init(Unit *u) { Swap *s = SWAP(u); assert(s); @@ -83,7 +83,8 @@ static void swap_unset_proc_swaps(Swap *s) { s->timeout_usec = DEFAULT_TIMEOUT_USEC; exec_context_init(&s->exec_context); - s->exec_context.std_output = EXEC_OUTPUT_KMSG; + s->exec_context.std_output = u->meta.manager->default_std_output; + s->exec_context.std_error = u->meta.manager->default_std_error; s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; diff --git a/src/systemctl.c b/src/systemctl.c index 18074402e5..1142200e42 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -377,8 +377,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { n_shown++; - if (!streq(u->load_state, "loaded") && - !streq(u->load_state, "banned")) { + if (streq(u->load_state, "error")) { on_loaded = ansi_highlight(true); off_loaded = ansi_highlight(false); } else @@ -622,8 +621,6 @@ static int list_unit_files(DBusConnection *bus, char **args) { dbus_error_init(&error); - assert(bus); - pager_open_if_enabled(); if (avoid_bus()) { @@ -659,6 +656,8 @@ static int list_unit_files(DBusConnection *bus, char **args) { hashmap_free(h); } else { + assert(bus); + m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -2063,8 +2062,7 @@ static void print_status_info(UnitStatusInfo *i) { if (i->following) printf("\t Follow: unit currently follows state of %s\n", i->following); - if (streq_ptr(i->load_state, "failed") || - streq_ptr(i->load_state, "banned")) { + if (streq_ptr(i->load_state, "error")) { on = ansi_highlight(true); off = ansi_highlight(false); } else @@ -5000,7 +4998,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError * enable/disable */ if (!streq(verbs[i].verb, "enable") && !streq(verbs[i].verb, "disable") && - !streq(verbs[i].verb, "is-enable") && + !streq(verbs[i].verb, "is-enabled") && + !streq(verbs[i].verb, "list-unit-files") && !streq(verbs[i].verb, "reenable") && !streq(verbs[i].verb, "preset") && !streq(verbs[i].verb, "mask") && @@ -5156,7 +5155,7 @@ static int halt_main(DBusConnection *bus) { if (!arg_no_wtmp) { if (sd_booted() > 0) log_debug("Not writing utmp record, assuming that systemd-update-utmp is used."); - else if ((r = utmp_put_shutdown(0)) < 0) + else if ((r = utmp_put_shutdown()) < 0) log_warning("Failed to write utmp record: %s", strerror(-r)); } diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 21bf44d3a4..19a7c08c40 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -50,7 +50,7 @@ * properly owned directories beneath /tmp, /var/tmp, /run, which are * volatile and hence need to be recreated on bootup. */ -enum { +typedef enum ItemType { /* These ones take file names */ CREATE_FILE = 'f', TRUNCATE_FILE = 'F', @@ -61,11 +61,13 @@ enum { /* These ones take globs */ IGNORE_PATH = 'x', REMOVE_PATH = 'r', - RECURSIVE_REMOVE_PATH = 'R' -}; + RECURSIVE_REMOVE_PATH = 'R', + RELABEL_PATH = 'z', + RECURSIVE_RELABEL_PATH = 'Z' +} ItemType; typedef struct Item { - char type; + ItemType type; char *path; uid_t uid; @@ -90,8 +92,8 @@ static const char *arg_prefix = NULL; #define MAX_DEPTH 256 -static bool needs_glob(int t) { - return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH; +static bool needs_glob(ItemType t) { + return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH; } static struct Item* find_glob(Hashmap *h, const char *match) { @@ -405,8 +407,145 @@ finish: return r; } +static int item_set_perms(Item *i, const char *path) { + /* not using i->path directly because it may be a glob */ + if (i->mode_set) + if (chmod(path, i->mode) < 0) { + log_error("chmod(%s) failed: %m", path); + return -errno; + } + + if (i->uid_set || i->gid_set) + if (chown(path, + i->uid_set ? i->uid : (uid_t) -1, + i->gid_set ? i->gid : (gid_t) -1) < 0) { + + log_error("chown(%s) failed: %m", path); + return -errno; + } + + return label_fix(path, false); +} + +static int recursive_relabel_children(Item *i, const char *path) { + DIR *d; + int ret = 0; + + /* This returns the first error we run into, but nevertheless + * tries to go on */ + + d = opendir(path); + if (!d) + return errno == ENOENT ? 0 : -errno; + + for (;;) { + struct dirent buf, *de; + bool is_dir; + int r; + char *entry_path; + + r = readdir_r(d, &buf, &de); + if (r != 0) { + if (ret == 0) + ret = -r; + break; + } + + if (!de) + break; + + if (streq(de->d_name, ".") || streq(de->d_name, "..")) + continue; + + if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) { + if (ret == 0) + ret = -ENOMEM; + continue; + } + + if (de->d_type == DT_UNKNOWN) { + struct stat st; + + if (lstat(entry_path, &st) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + free(entry_path); + continue; + } + + is_dir = S_ISDIR(st.st_mode); + + } else + is_dir = de->d_type == DT_DIR; + + r = item_set_perms(i, entry_path); + if (r < 0) { + if (ret == 0 && r != -ENOENT) + ret = r; + free(entry_path); + continue; + } + + if (is_dir) { + r = recursive_relabel_children(i, entry_path); + if (r < 0 && ret == 0) + ret = r; + } + + free(entry_path); + } + + closedir(d); + + return ret; +} + +static int recursive_relabel(Item *i, const char *path) { + int r; + struct stat st; + + r = item_set_perms(i, path); + if (r < 0) + return r; + + if (lstat(path, &st) < 0) + return -errno; + + if (S_ISDIR(st.st_mode)) + r = recursive_relabel_children(i, path); + + return r; +} + +static int glob_item(Item *i, int (*action)(Item *, const char *)) { + int r = 0, k; + glob_t g; + char **fn; + + zero(g); + + errno = 0; + if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) { + + if (k != GLOB_NOMATCH) { + if (errno != 0) + errno = EIO; + + log_error("glob(%s) failed: %m", i->path); + return -errno; + } + } + + STRV_FOREACH(fn, g.gl_pathv) + if ((k = action(i, *fn)) < 0) + r = k; + + globfree(&g); + return r; +} + static int create_item(Item *i) { - int fd = -1, r; + int r; mode_t u; struct stat st; @@ -420,7 +559,8 @@ static int create_item(Item *i) { return 0; case CREATE_FILE: - case TRUNCATE_FILE: + case TRUNCATE_FILE: { + int fd; u = umask(0); fd = open(i->path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW| @@ -429,39 +569,27 @@ static int create_item(Item *i) { if (fd < 0) { log_error("Failed to create file %s: %m", i->path); - r = -errno; - goto finish; + return -errno; } - if (fstat(fd, &st) < 0) { + close_nointr_nofail(fd); + + if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (!S_ISREG(st.st_mode)) { log_error("%s is not a file.", i->path); - r = -EEXIST; - goto finish; + return -EEXIST; } - if (i->mode_set) - if (fchmod(fd, i->mode) < 0) { - log_error("chmod(%s) failed: %m", i->path); - r = -errno; - goto finish; - } - - if (i->uid_set || i->gid_set) - if (fchown(fd, - i->uid_set ? i->uid : (uid_t) -1, - i->gid_set ? i->gid : (gid_t) -1) < 0) { - log_error("chown(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + r = item_set_perms(i, i->path); + if (r < 0) + return r; break; + } case TRUNCATE_DIRECTORY: case CREATE_DIRECTORY: @@ -473,38 +601,22 @@ static int create_item(Item *i) { if (r < 0 && errno != EEXIST) { log_error("Failed to create directory %s: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (!S_ISDIR(st.st_mode)) { log_error("%s is not a directory.", i->path); - r = -EEXIST; - goto finish; + return -EEXIST; } - if (i->mode_set) - if (chmod(i->path, i->mode) < 0) { - log_error("chmod(%s) failed: %m", i->path); - r = -errno; - goto finish; - } - - if (i->uid_set || i->gid_set) - if (chown(i->path, - i->uid_set ? i->uid : (uid_t) -1, - i->gid_set ? i->gid : (gid_t) -1) < 0) { - - log_error("chown(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + r = item_set_perms(i, i->path); + if (r < 0) + return r; break; @@ -516,54 +628,45 @@ static int create_item(Item *i) { if (r < 0 && errno != EEXIST) { log_error("Failed to create fifo %s: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); - r = -errno; - goto finish; + return -errno; } if (!S_ISFIFO(st.st_mode)) { log_error("%s is not a fifo.", i->path); - r = -EEXIST; - goto finish; + return -EEXIST; } - if (i->mode_set) - if (chmod(i->path, i->mode) < 0) { - log_error("chmod(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + r = item_set_perms(i, i->path); + if (r < 0) + return r; - if (i->uid_set || i->gid_set) - if (chown(i->path, - i->uid_set ? i->uid : (uid_t) -1, - i->gid_set ? i->gid : (gid_t) -1) < 0) { - log_error("chown(%s) failed: %m", i->path); - r = -errno; - goto finish; - } + break; + case RELABEL_PATH: + + r = glob_item(i, item_set_perms); + if (r < 0) + return 0; break; - } - if ((r = label_fix(i->path, false)) < 0) - goto finish; + case RECURSIVE_RELABEL_PATH: - log_debug("%s created successfully.", i->path); + r = glob_item(i, recursive_relabel); + if (r < 0) + return r; + } -finish: - if (fd >= 0) - close_nointr_nofail(fd); + log_debug("%s created successfully.", i->path); - return r; + return 0; } -static int remove_item(Item *i, const char *instance) { +static int remove_item_instance(Item *i, const char *instance) { int r; assert(i); @@ -575,6 +678,8 @@ static int remove_item(Item *i, const char *instance) { case CREATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: break; case REMOVE_PATH: @@ -599,7 +704,9 @@ static int remove_item(Item *i, const char *instance) { return 0; } -static int remove_item_glob(Item *i) { +static int remove_item(Item *i) { + int r = 0; + assert(i); switch (i->type) { @@ -609,39 +716,18 @@ static int remove_item_glob(Item *i) { case CREATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: break; case REMOVE_PATH: case TRUNCATE_DIRECTORY: - case RECURSIVE_REMOVE_PATH: { - int r = 0, k; - glob_t g; - char **fn; - - zero(g); - - errno = 0; - if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) { - - if (k != GLOB_NOMATCH) { - if (errno != 0) - errno = EIO; - - log_error("glob(%s) failed: %m", i->path); - return -errno; - } - } - - STRV_FOREACH(fn, g.gl_pathv) - if ((k = remove_item(i, *fn)) < 0) - r = k; - - globfree(&g); - return r; - } + case RECURSIVE_REMOVE_PATH: + r = glob_item(i, remove_item_instance); + break; } - return 0; + return r; } static int process_item(Item *i) { @@ -650,7 +736,7 @@ static int process_item(Item *i) { assert(i); r = arg_create ? create_item(i) : 0; - q = arg_remove ? remove_item_glob(i) : 0; + q = arg_remove ? remove_item(i) : 0; p = arg_clean ? clean_item(i) : 0; if (r < 0) @@ -701,6 +787,7 @@ static bool item_equal(Item *a, Item *b) { static int parse_line(const char *fname, unsigned line, const char *buffer) { Item *i, *existing; char *mode = NULL, *user = NULL, *group = NULL, *age = NULL; + char type; Hashmap *h; int r; @@ -720,7 +807,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { "%ms " "%ms " "%ms", - &i->type, + &type, &i->path, &mode, &user, @@ -731,18 +818,24 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { goto finish; } - if (i->type != CREATE_FILE && - i->type != TRUNCATE_FILE && - i->type != CREATE_DIRECTORY && - i->type != TRUNCATE_DIRECTORY && - i->type != CREATE_FIFO && - i->type != IGNORE_PATH && - i->type != REMOVE_PATH && - i->type != RECURSIVE_REMOVE_PATH) { - log_error("[%s:%u] Unknown file type '%c'.", fname, line, i->type); + switch(type) { + case CREATE_FILE: + case TRUNCATE_FILE: + case CREATE_DIRECTORY: + case TRUNCATE_DIRECTORY: + case CREATE_FIFO: + case IGNORE_PATH: + case REMOVE_PATH: + case RECURSIVE_REMOVE_PATH: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: + break; + default: + log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); r = -EBADMSG; goto finish; } + i->type = type; if (!path_is_absolute(i->path)) { log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path); diff --git a/src/unit.c b/src/unit.c index 903a8e4da4..03c90f5874 100644 --- a/src/unit.c +++ b/src/unit.c @@ -564,8 +564,8 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE && c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE && c->std_error != EXEC_OUTPUT_KMSG && - c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_KMSG && + c->std_error != EXEC_OUTPUT_SYSLOG && + c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE && c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE) return 0; @@ -858,6 +858,7 @@ fail: u->meta.load_state = UNIT_ERROR; u->meta.load_error = r; unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); log_debug("Failed to load configuration for %s: %s", u->meta.id, strerror(-r)); @@ -924,7 +925,7 @@ int unit_start(Unit *u) { unit_add_to_dbus_queue(u); - unit_status_printf(u, "Starting %s...\n", unit_description(u)); + unit_status_printf(u, NULL, "Starting %s...", unit_description(u)); return UNIT_VTABLE(u)->start(u); } @@ -966,7 +967,7 @@ int unit_stop(Unit *u) { unit_add_to_dbus_queue(u); - unit_status_printf(u, "Stopping %s...\n", unit_description(u)); + unit_status_printf(u, NULL, "Stopping %s...", unit_description(u)); return UNIT_VTABLE(u)->stop(u); } @@ -1031,19 +1032,19 @@ static void unit_check_unneeded(Unit *u) { return; SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + if (unit_pending_active(other)) return; SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + if (unit_pending_active(other)) return; SET_FOREACH(other, u->meta.dependencies[UNIT_WANTED_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + if (unit_pending_active(other)) return; SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + if (unit_pending_active(other)) return; log_info("Service %s is not needed anymore. Stopping.", u->meta.id); @@ -1104,6 +1105,14 @@ static void retroactively_stop_dependencies(Unit *u) { SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); +} + +static void check_unneeded_dependencies(Unit *u) { + Iterator i; + Unit *other; + + assert(u); + assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); /* Garbage collect services that might not be needed anymore, if enabled */ SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i) @@ -1262,6 +1271,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su retroactively_stop_dependencies(u); } + /* stop unneeded units regardless if going down was expected or not */ + if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) + check_unneeded_dependencies(u); + if (ns != os && ns == UNIT_FAILED) { log_notice("Unit %s entered failed state.", u->meta.id); unit_trigger_on_failure(u); @@ -2426,8 +2439,11 @@ int unit_coldplug(Unit *u) { return 0; } -void unit_status_printf(Unit *u, const char *format, ...) { +void unit_status_printf(Unit *u, const char *status, const char *format, ...) { va_list ap; + char *s, *e; + int err; + const unsigned emax = status ? 80 - (sizeof("[ OK ]")-1) : 80; assert(u); assert(format); @@ -2442,8 +2458,21 @@ void unit_status_printf(Unit *u, const char *format, ...) { return; va_start(ap, format); - status_vprintf(format, ap); + err = vasprintf(&s, format, ap); va_end(ap); + if (err < 0) + return; + + e = ellipsize(s, emax, 100); + free(s); + if (!e) + return; + + if (status) + status_printf("%s%*s[%s]\n", e, emax - strlen(e), "", status); + else + status_printf("%s\n", e); + free(e); } bool unit_need_daemon_reload(Unit *u) { @@ -2501,7 +2530,7 @@ bool unit_pending_inactive(Unit *u) { bool unit_pending_active(Unit *u) { assert(u); - /* Returns true if the unit is inactive or going down */ + /* Returns true if the unit is active or going up */ if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) return true; diff --git a/src/unit.h b/src/unit.h index 7da572350e..b32c1a702e 100644 --- a/src/unit.h +++ b/src/unit.h @@ -512,7 +512,7 @@ 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, ...); +void unit_status_printf(Unit *u, const char *status, const char *format, ...); bool unit_need_daemon_reload(Unit *u); diff --git a/src/update-utmp.c b/src/update-utmp.c index 12e4d11042..073f28e254 100644 --- a/src/update-utmp.c +++ b/src/update-utmp.c @@ -284,7 +284,7 @@ static int on_shutdown(Context *c) { } #endif - if ((q = utmp_put_shutdown(0)) < 0) { + if ((q = utmp_put_shutdown()) < 0) { log_error("Failed to write utmp record: %s", strerror(-q)); r = q; } @@ -339,7 +339,7 @@ static int on_runlevel(Context *c) { } #endif - if ((q = utmp_put_runlevel(0, runlevel, previous)) < 0) { + if ((q = utmp_put_runlevel(runlevel, previous)) < 0) { log_error("Failed to write utmp record: %s", strerror(-q)); r = q; } diff --git a/src/util.c b/src/util.c index c07c569c26..7191750c52 100644 --- a/src/util.c +++ b/src/util.c @@ -517,7 +517,7 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { return -errno; if (!(fgets(line, sizeof(line), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; fclose(f); return r; } @@ -562,7 +562,7 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) { return -errno; if (!(fgets(line, sizeof(line), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; fclose(f); return r; } @@ -709,7 +709,7 @@ int read_one_line_file(const char *fn, char **line) { return -errno; if (!(fgets(t, sizeof(t), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; goto finish; } @@ -3296,7 +3296,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d) { return -errno; if (!fgets(line, sizeof(line), f)) { - k = -errno; + k = feof(f) ? -EIO : -errno; fclose(f); return k; } diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c index b03a3e70af..217ae1e2c7 100644 --- a/src/utmp-wtmp.c +++ b/src/utmp-wtmp.c @@ -155,11 +155,11 @@ static int write_entry_wtmp(const struct utmpx *store) { return -errno; } -static int write_entry_both(const struct utmpx *store) { +static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) { int r, s; - r = write_entry_utmp(store); - s = write_entry_wtmp(store); + r = write_entry_utmp(store_utmp); + s = write_entry_wtmp(store_wtmp); if (r >= 0) r = s; @@ -172,10 +172,14 @@ static int write_entry_both(const struct utmpx *store) { return r; } -int utmp_put_shutdown(usec_t t) { +static int write_entry_both(const struct utmpx *store) { + return write_utmp_wtmp(store, store); +} + +int utmp_put_shutdown(void) { struct utmpx store; - init_entry(&store, t); + init_entry(&store, 0); store.ut_type = RUN_LVL; strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); @@ -206,12 +210,12 @@ static const char *sanitize_id(const char *id) { return id + l - sizeof(((struct utmpx*) NULL)->ut_id); } -int utmp_put_init_process(usec_t t, const char *id, pid_t pid, pid_t sid, const char *line) { +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { struct utmpx store; assert(id); - init_timestamp(&store, t); + init_timestamp(&store, 0); store.ut_type = INIT_PROCESS; store.ut_pid = pid; @@ -226,7 +230,7 @@ int utmp_put_init_process(usec_t t, const char *id, pid_t pid, pid_t sid, const } int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { - struct utmpx lookup, store, *found; + struct utmpx lookup, store, store_wtmp, *found; assert(id); @@ -242,9 +246,7 @@ int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { if (found->ut_pid != pid) return 0; - zero(store); - - memcpy(&store, &lookup, sizeof(store)); + memcpy(&store, found, sizeof(store)); store.ut_type = DEAD_PROCESS; store.ut_exit.e_termination = code; store.ut_exit.e_exit = status; @@ -253,11 +255,15 @@ int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { zero(store.ut_host); zero(store.ut_tv); - return write_entry_both(&store); + memcpy(&store_wtmp, &store, sizeof(store_wtmp)); + /* wtmp wants the current time */ + init_timestamp(&store_wtmp, 0); + + return write_utmp_wtmp(&store, &store_wtmp); } -int utmp_put_runlevel(usec_t t, int runlevel, int previous) { +int utmp_put_runlevel(int runlevel, int previous) { struct utmpx store; int r; @@ -277,7 +283,7 @@ int utmp_put_runlevel(usec_t t, int runlevel, int previous) { if (previous == runlevel) return 0; - init_entry(&store, t); + init_entry(&store, 0); store.ut_type = RUN_LVL; store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); diff --git a/src/utmp-wtmp.h b/src/utmp-wtmp.h index 4054aff7ea..a5998ebb21 100644 --- a/src/utmp-wtmp.h +++ b/src/utmp-wtmp.h @@ -26,12 +26,12 @@ int utmp_get_runlevel(int *runlevel, int *previous); -int utmp_put_shutdown(usec_t timestamp); +int utmp_put_shutdown(void); int utmp_put_reboot(usec_t timestamp); -int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous); +int utmp_put_runlevel(int runlevel, int previous); int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(usec_t timestamp, const char *id, pid_t pid, pid_t sid, const char *line); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); int utmp_wall(const char *message, bool (*match_tty)(const char *tty)); |