#include "service.h"
#include "signal-util.h"
#include "special.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
return r;
r = unit_watch_pid(UNIT(s), pid);
- if (r < 0) {
- /* FIXME: we need to do something here */
- log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
- return r;
- }
+ if (r < 0) /* FIXME: we need to do something here */
+ return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
return 0;
}
return 0;
}
-static int service_collect_fds(Service *s, int **fds, char ***fd_names) {
+static int service_collect_fds(Service *s, int **fds, char ***fd_names, unsigned *n_socket_fds) {
_cleanup_strv_free_ char **rfd_names = NULL;
_cleanup_free_ int *rfds = NULL;
- int rn_fds = 0, r;
+ unsigned rn_socket_fds = 0;
+ int rn_fds = 0, r;
assert(s);
assert(fds);
assert(fd_names);
+ assert(n_socket_fds);
if (s->socket_fd >= 0) {
}
}
+ rn_socket_fds = rn_fds;
+
if (s->n_fd_store > 0) {
ServiceFDStore *fs;
char **nl;
*fds = rfds;
*fd_names = rfd_names;
+ *n_socket_fds = rn_socket_fds;
rfds = NULL;
rfd_names = NULL;
_cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL;
_cleanup_free_ int *fds = NULL;
- unsigned n_fds = 0, n_env = 0;
+ unsigned n_fds = 0, n_socket_fds = 0, n_env = 0;
const char *path;
pid_t pid;
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
- r = service_collect_fds(s, &fds, &fd_names);
+ r = service_collect_fds(s, &fds, &fd_names, &n_socket_fds);
if (r < 0)
return r;
return r;
}
- if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
+ if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {
_cleanup_free_ char *addr = NULL;
char *t;
unsigned port;
exec_params.fds = fds;
exec_params.fd_names = fd_names;
exec_params.n_fds = n_fds;
+ exec_params.n_socket_fds = n_socket_fds;
exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager);
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = path;
return r;
r = unit_watch_pid(UNIT(s), pid);
- if (r < 0)
- /* FIXME: we need to do something here */
+ if (r < 0) /* FIXME: we need to do something here */
return r;
*_pid = pid;
return !!s->exec_command[SERVICE_EXEC_RELOAD];
}
+static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecCommand *current) {
+ Service *s = SERVICE(u);
+ unsigned idx = 0;
+ ExecCommand *first, *c;
+
+ assert(s);
+
+ first = s->exec_command[id];
+
+ /* Figure out where we are in the list by walking back to the beginning */
+ for (c = current; c != first; c = c->command_prev)
+ idx++;
+
+ return idx;
+}
+
+static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) {
+ Service *s = SERVICE(u);
+ ServiceExecCommand id;
+ unsigned idx;
+ const char *type;
+ char **arg;
+ _cleanup_free_ char *args = NULL, *p = NULL;
+ size_t allocated = 0, length = 0;
+
+ assert(s);
+ assert(f);
+
+ if (!command)
+ return 0;
+
+ if (command == s->control_command) {
+ type = "control";
+ id = s->control_command_id;
+ } else {
+ type = "main";
+ id = SERVICE_EXEC_START;
+ }
+
+ idx = service_exec_command_index(u, id, command);
+
+ STRV_FOREACH(arg, command->argv) {
+ size_t n;
+ _cleanup_free_ char *e = NULL;
+
+ e = xescape(*arg, WHITESPACE);
+ if (!e)
+ return -ENOMEM;
+
+ n = strlen(e);
+ if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
+ return -ENOMEM;
+
+ if (length > 0)
+ args[length++] = ' ';
+
+ memcpy(args + length, e, n);
+ length += n;
+ }
+
+ if (!GREEDY_REALLOC(args, allocated, length + 1))
+ return -ENOMEM;
+ args[length++] = 0;
+
+ p = xescape(command->path, WHITESPACE);
+ if (!p)
+ return -ENOMEM;
+
+ fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args);
+
+ return 0;
+}
+
static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
Service *s = SERVICE(u);
ServiceFDStore *fs;
if (r < 0)
return r;
- /* FIXME: There's a minor uncleanliness here: if there are
- * multiple commands attached here, we will start from the
- * first one again */
- if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
+ service_serialize_exec_command(u, f, s->control_command);
+ service_serialize_exec_command(u, f, s->main_command);
r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
if (r < 0)
return 0;
}
+static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) {
+ Service *s = SERVICE(u);
+ int r;
+ unsigned idx = 0, i;
+ bool control, found = false;
+ ServiceExecCommand id = _SERVICE_EXEC_COMMAND_INVALID;
+ ExecCommand *command = NULL;
+ _cleanup_free_ char *path = NULL;
+ _cleanup_strv_free_ char **argv = NULL;
+
+ enum ExecCommandState {
+ STATE_EXEC_COMMAND_TYPE,
+ STATE_EXEC_COMMAND_INDEX,
+ STATE_EXEC_COMMAND_PATH,
+ STATE_EXEC_COMMAND_ARGS,
+ _STATE_EXEC_COMMAND_MAX,
+ _STATE_EXEC_COMMAND_INVALID = -1,
+ } state;
+
+ assert(s);
+ assert(key);
+ assert(value);
+
+ control = streq(key, "control-command");
+
+ state = STATE_EXEC_COMMAND_TYPE;
+
+ for (;;) {
+ _cleanup_free_ char *arg = NULL;
+
+ r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
+ if (r == 0)
+ break;
+ else if (r < 0)
+ return r;
+
+ switch (state) {
+ case STATE_EXEC_COMMAND_TYPE:
+ id = service_exec_command_from_string(arg);
+ if (id < 0)
+ return -EINVAL;
+
+ state = STATE_EXEC_COMMAND_INDEX;
+ break;
+ case STATE_EXEC_COMMAND_INDEX:
+ r = safe_atou(arg, &idx);
+ if (r < 0)
+ return -EINVAL;
+
+ state = STATE_EXEC_COMMAND_PATH;
+ break;
+ case STATE_EXEC_COMMAND_PATH:
+ path = arg;
+ arg = NULL;
+ state = STATE_EXEC_COMMAND_ARGS;
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+ break;
+ case STATE_EXEC_COMMAND_ARGS:
+ r = strv_extend(&argv, arg);
+ if (r < 0)
+ return -ENOMEM;
+ break;
+ default:
+ assert_not_reached("Unknown error at deserialization of exec command");
+ break;
+ }
+ }
+
+ if (state != STATE_EXEC_COMMAND_ARGS)
+ return -EINVAL;
+
+ /* Let's check whether exec command on given offset matches data that we just deserialized */
+ for (command = s->exec_command[id], i = 0; command; command = command->command_next, i++) {
+ if (i != idx)
+ continue;
+
+ found = strv_equal(argv, command->argv) && streq(command->path, path);
+ break;
+ }
+
+ if (!found) {
+ /* Command at the index we serialized is different, let's look for command that exactly
+ * matches but is on different index. If there is no such command we will not resume execution. */
+ for (command = s->exec_command[id]; command; command = command->command_next)
+ if (strv_equal(command->argv, argv) && streq(command->path, path))
+ break;
+ }
+
+ if (command && control)
+ s->control_command = command;
+ else if (command)
+ s->main_command = command;
+ else
+ log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed.");
+
+ return 0;
+}
+
static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Service *s = SERVICE(u);
int r;
s->status_text = t;
}
- } else if (streq(key, "control-command")) {
- ServiceExecCommand id;
-
- id = service_exec_command_from_string(value);
- if (id < 0)
- log_unit_debug(u, "Failed to parse exec-command value: %s", value);
- else {
- s->control_command_id = id;
- s->control_command = s->exec_command[id];
- }
} else if (streq(key, "accept-socket")) {
Unit *socket;
s->watchdog_override_enable = true;
s->watchdog_override_usec = watchdog_override_usec;
}
+ } else if (STR_IN_SET(key, "main-command", "control-command")) {
+ r = service_deserialize_exec_command(u, key, value);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to parse serialized command \"%s\": %m", value);
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG :
(code == CLD_EXITED ? LOG_NOTICE : LOG_WARNING),
- LOG_UNIT_ID(u),
LOG_UNIT_MESSAGE(u, "Main process exited, code=%s, status=%i/%s",
sigchld_code_to_string(code), status,
strna(code == CLD_EXITED
: signal_to_string(status))),
"EXIT_CODE=%s", sigchld_code_to_string(code),
"EXIT_STATUS=%i", status,
+ LOG_UNIT_ID(u),
NULL);
if (s->result == SERVICE_SUCCESS)
/* If the PID set is empty now, then let's finish this off
(On unified we use proper notifications) */
- if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids))
+ if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids))
service_notify_cgroup_empty_event(u);
}
log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e);
else if (pid == s->control_pid)
log_unit_warning(u, "A control process cannot also be the main process");
+ else if (pid == getpid() || pid == 1)
+ log_unit_warning(u, "Service manager can't be main process, ignoring sd_notify() MAINPID= field");
else {
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);
if (r >= 0)
r = sd_bus_creds_get_pid(creds, &pid);
if (r >= 0) {
- log_unit_debug(u, "D-Bus name %s is now owned by process %u", name, (unsigned) pid);
+ log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, name, pid);
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);