summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dbus-socket.c2
-rw-r--r--src/dbus.c382
-rw-r--r--src/execute.c152
-rw-r--r--src/exit-status.c3
-rw-r--r--src/exit-status.h3
-rw-r--r--src/fsck.c2
-rw-r--r--src/install.c5
-rw-r--r--src/job.c13
-rw-r--r--src/kmsg-syslogd.c3
-rw-r--r--src/label.c2
-rw-r--r--src/load-fragment-gperf.gperf.m42
-rw-r--r--src/log.c27
-rw-r--r--src/log.h1
-rw-r--r--src/logind-session.c4
-rw-r--r--src/manager.c2
-rw-r--r--src/manager.h1
-rw-r--r--src/mount.c6
-rw-r--r--src/pam-module.c16
-rw-r--r--src/path.c360
-rw-r--r--src/path.h9
-rw-r--r--src/rc-local-generator.c107
-rw-r--r--src/service.c188
-rw-r--r--src/service.h3
-rw-r--r--src/shutdownd.c6
-rw-r--r--src/socket.c8
-rw-r--r--src/socket.h1
-rw-r--r--src/swap.c5
-rw-r--r--src/systemctl.c15
-rw-r--r--src/tmpfiles.c333
-rw-r--r--src/unit.c51
-rw-r--r--src/unit.h2
-rw-r--r--src/update-utmp.c4
-rw-r--r--src/util.c8
-rw-r--r--src/utmp-wtmp.c34
-rw-r--r--src/utmp-wtmp.h6
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), &param) < 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);
diff --git a/src/job.c b/src/job.c
index 20971da852..1520d81ad9 100644
--- a/src/job.c
+++ b/src/job.c
@@ -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)
diff --git a/src/log.c b/src/log.c
index b8ce122f3d..4f57821da3 100644
--- a/src/log.c
+++ b/src/log.c
@@ -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);
diff --git a/src/log.h b/src/log.h
index c402afb8ea..9942e3e9a0 100644
--- a/src/log.h
+++ b/src/log.h
@@ -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));