This file is part of systemd.
Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
s->guess_main_pid = true;
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+
+ s->exec_context.keyring_mode = MANAGER_IS_SYSTEM(u->manager) ?
+ EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT;
}
static void service_unwatch_control_pid(Service *s) {
s->pid_file = mfree(s->pid_file);
s->status_text = mfree(s->status_text);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime);
+ s->exec_runtime = exec_runtime_unref(s->exec_runtime, false);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
s->main_command = NULL;
s->bus_name_owner = mfree(s->bus_name_owner);
+ s->usb_function_descriptors = mfree(s->usb_function_descriptors);
+ s->usb_function_strings = mfree(s->usb_function_strings);
+
service_close_socket_fd(s);
s->peer = socket_peer_unref(s->peer);
if (r < 0)
return r;
- if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE)
- s->notify_access = NOTIFY_MAIN;
-
- if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE)
+ /* If the service needs the notify socket, let's enable it automatically. */
+ if (s->notify_access == NOTIFY_NONE &&
+ (s->type == SERVICE_NOTIFY || s->watchdog_usec > 0 || s->n_fd_store_max > 0))
s->notify_access = NOTIFY_MAIN;
r = service_add_default_dependencies(s);
}
static void service_dump(Unit *u, FILE *f, const char *prefix) {
- char buf_restart[FORMAT_TIMESPAN_MAX] = {}, buf_start[FORMAT_TIMESPAN_MAX] = {}, buf_stop[FORMAT_TIMESPAN_MAX] = {};
- char buf_runtime[FORMAT_TIMESPAN_MAX] = {}, buf_watchdog[FORMAT_TIMESPAN_MAX] = {};
+ char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX];
+ char buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX];
ServiceExecCommand c;
Service *s = SERVICE(u);
const char *prefix2;
if (s->n_fd_store_max > 0)
fprintf(f,
"%sFile Descriptor Store Max: %u\n"
- "%sFile Descriptor Store Current: %u\n",
+ "%sFile Descriptor Store Current: %zu\n",
prefix, s->n_fd_store_max,
prefix, s->n_fd_store);
static int service_load_pid_file(Service *s, bool may_warn) {
char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ bool questionable_pid_file = false;
_cleanup_free_ char *k = NULL;
_cleanup_close_ int fd = -1;
int r, prio;
prio = may_warn ? LOG_INFO : LOG_DEBUG;
fd = chase_symlinks(s->pid_file, NULL, CHASE_OPEN|CHASE_SAFE, NULL);
- if (fd == -EPERM)
- return log_unit_full(UNIT(s), prio, fd, "Permission denied while opening PID file or unsafe symlink chain: %s", s->pid_file);
+ if (fd == -EPERM) {
+ log_unit_full(UNIT(s), LOG_DEBUG, fd, "Permission denied while opening PID file or potentially unsafe symlink chain, will now retry with relaxed checks: %s", s->pid_file);
+
+ questionable_pid_file = true;
+
+ fd = chase_symlinks(s->pid_file, NULL, CHASE_OPEN, NULL);
+ }
if (fd < 0)
return log_unit_full(UNIT(s), prio, fd, "Can't open PID file %s (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state));
if (r == 0) {
struct stat st;
+ if (questionable_pid_file) {
+ log_unit_error(UNIT(s), "Refusing to accept PID outside of service control group, acquired through unsafe symlink chain: %s", s->pid_file);
+ return -EPERM;
+ }
+
/* Hmm, it's not clear if the new main PID is safe. Let's allow this if the PID file is owned by root */
if (fstat(fd, &st) < 0)
if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
service_start_watchdog(s);
- if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
+ if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) {
(void) unit_setup_dynamic_creds(u);
+ (void) unit_setup_exec_runtime(u);
+ }
if (UNIT_ISSET(s->accept_socket)) {
Socket* socket = SOCKET(UNIT_DEREF(s->accept_socket));
continue;
if (!rfds) {
- rfds = cfds;
+ rfds = TAKE_PTR(cfds);
rn_socket_fds = cn_fds;
-
- cfds = NULL;
} else {
int *t;
- t = realloc(rfds, (rn_socket_fds + cn_fds) * sizeof(int));
+ t = reallocarray(rfds, rn_socket_fds + cn_fds, sizeof(int));
if (!t)
return -ENOMEM;
char **nl;
int *t;
- t = realloc(rfds, (rn_socket_fds + s->n_fd_store) * sizeof(int));
+ t = reallocarray(rfds, rn_socket_fds + s->n_fd_store, sizeof(int));
if (!t)
return -ENOMEM;
rfds = t;
- nl = realloc(rfd_names, (rn_socket_fds + s->n_fd_store + 1) * sizeof(char*));
+ nl = reallocarray(rfd_names, rn_socket_fds + s->n_fd_store + 1, sizeof(char *));
if (!nl)
return -ENOMEM;
rfd_names[n_fds] = NULL;
}
- *fds = rfds;
- *fd_names = rfd_names;
+ *fds = TAKE_PTR(rfds);
+ *fd_names = TAKE_PTR(rfd_names);
*n_socket_fds = rn_socket_fds;
*n_storage_fds = rn_storage_fds;
- rfds = NULL;
- rfd_names = NULL;
-
return 0;
}
}
}
- manager_set_exec_params(UNIT(s)->manager, &exec_params);
unit_set_exec_params(UNIT(s), &exec_params);
final_env = strv_env_merge(2, exec_params.environment, our_env, NULL);
s->forbid_restart = false;
/* We want fresh tmpdirs in case service is started again immediately */
- exec_runtime_destroy(s->exec_runtime);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime);
+ s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
if (s->exec_context.runtime_directory_preserve_mode == EXEC_PRESERVE_NO ||
(s->exec_context.runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART && !service_will_restart(UNIT(s))))
service_unwatch_control_pid(s);
- if (service_good(s)) {
+ if (s->result != SERVICE_SUCCESS)
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
+ else if (service_good(s)) {
- /* If there are any queued up sd_notify()
- * notifications, process them now */
+ /* If there are any queued up sd_notify() notifications, process them now */
if (s->notify_state == NOTIFY_RELOADING)
service_enter_reload_by_notify(s);
else if (s->notify_state == NOTIFY_STOPPING)
service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec));
}
- } else if (f != SERVICE_SUCCESS)
- service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
- else if (s->remain_after_exit)
+ } else if (s->remain_after_exit)
service_set_state(s, SERVICE_EXITED);
else
service_enter_stop(s, SERVICE_SUCCESS);
LOG_UNIT_ID(UNIT(s)),
LOG_UNIT_INVOCATION_ID(UNIT(s)),
LOG_UNIT_MESSAGE(UNIT(s), "Scheduled restart job, restart counter is at %u.", s->n_restarts),
- "N_RESTARTS=%u", s->n_restarts,
- NULL);
+ "N_RESTARTS=%u", s->n_restarts);
/* Notify clients about changed restart counter */
unit_add_to_dbus_queue(UNIT(s));
state = STATE_EXEC_COMMAND_PATH;
break;
case STATE_EXEC_COMMAND_PATH:
- path = arg;
- arg = NULL;
+ path = TAKE_PTR(arg);
state = STATE_EXEC_COMMAND_ARGS;
if (!path_is_absolute(path))
if (r < 0)
log_unit_debug_errno(u, r, "Failed to load accept-socket unit: %s", value);
else {
- unit_ref_set(&s->accept_socket, socket);
+ unit_ref_set(&s->accept_socket, u, socket);
SOCKET(socket)->n_connections++;
}
return service_state_to_string(SERVICE(u)->state);
}
-static bool service_check_gc(Unit *u) {
+static bool service_may_gc(Unit *u) {
Service *s = SERVICE(u);
assert(s);
/* Never clean up services that still have a process around, even if the service is formally dead. Note that
- * unit_check_gc() already checked our cgroup for us, we just check our two additional PIDs, too, in case they
+ * unit_may_gc() already checked our cgroup for us, we just check our two additional PIDs, too, in case they
* have moved outside of the cgroup. */
if (main_pid_good(s) > 0 ||
control_pid_good(s) > 0)
- return true;
+ return false;
- return false;
+ return true;
}
static int service_retry_pid_file(Service *s) {
return -ENOMEM;
}
- path_kill_slashes(ps->path);
+ path_simplify(ps->path, false);
/* PATH_CHANGED would not be enough. There are daemons (sendmail) that
* keep their PID file open all the time. */
"EXIT_CODE=%s", sigchld_code_to_string(code),
"EXIT_STATUS=%i", status,
LOG_UNIT_ID(u),
- LOG_UNIT_INVOCATION_ID(u),
- NULL);
+ LOG_UNIT_INVOCATION_ID(u));
if (s->result == SERVICE_SUCCESS)
s->result = f;
break;
case SERVICE_AUTO_RESTART:
- log_unit_info(UNIT(s),
- s->restart_usec > 0 ?
- "Service hold-off time over, scheduling restart." :
- "Service has no hold-off time, scheduling restart.");
+ if (s->restart_usec > 0) {
+ char buf_restart[FORMAT_TIMESPAN_MAX];
+ log_unit_info(UNIT(s),
+ "Service RestartSec=%s expired, scheduling restart.",
+ format_timespan(buf_restart, sizeof buf_restart, s->restart_usec, USEC_PER_SEC));
+ } else
+ log_unit_info(UNIT(s),
+ "Service has no hold-off time (RestartSec=0), scheduling restart.");
+
service_enter_restart(s);
break;
s->socket_fd = fd;
s->socket_fd_selinux_context_net = selinux_context_net;
- unit_ref_set(&s->accept_socket, UNIT(sock));
+ unit_ref_set(&s->accept_socket, UNIT(s), UNIT(sock));
return 0;
}
"Install\0",
.private_section = "Service",
+ .can_transient = true,
+ .can_delegate = true,
+
.init = service_init,
.done = service_done,
.load = service_load,
.will_restart = service_will_restart,
- .check_gc = service_check_gc,
+ .may_gc = service_may_gc,
.sigchld_event = service_sigchld_event,
.get_timeout = service_get_timeout,
.needs_console = service_needs_console,
- .can_transient = true,
.status_message_formats = {
.starting_stopping = {