core: add support for naming file descriptors passed using socket activation

This adds support for naming file descriptors passed using socket
activation. The names are passed in a new $LISTEN_FDNAMES= environment
variable, that matches the existign $LISTEN_FDS= one and contains a
colon-separated list of names.

This also adds support for naming fds submitted to the per-service fd
store using FDNAME= in the sd_notify() message.

This also adds a new FileDescriptorName= setting for socket unit files
to set the name for fds created by socket units.

This also adds a new call sd_listen_fds_with_names(), that is similar to
sd_listen_fds(), but also returns the names of the fds.

systemd-activate gained the new --fdname= switch to specify a name for
testing socket activation.

This is based on #1247 by Maciej Wereski.

Fixes #1247.
This commit is contained in:
Lennart Poettering
2015-10-06 11:52:48 +02:00
parent 79c7626d1f
commit 8dd4c05b54
26 changed files with 680 additions and 173 deletions
+45 -16
View File
@@ -26,7 +26,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include "systemd/sd-daemon.h"
#include "sd-daemon.h"
#include "log.h"
#include "macro.h"
@@ -38,6 +38,7 @@ static char** arg_listen = NULL;
static bool arg_accept = false;
static char** arg_args = NULL;
static char** arg_setenv = NULL;
static const char *arg_fdname = NULL;
static int add_epoll(int epoll_fd, int fd) {
struct epoll_event ev = {
@@ -136,8 +137,8 @@ static int launch(char* name, char **argv, char **env, int fds) {
length = strv_length(arg_setenv);
/* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
envp = new0(char *, length + 7);
/* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
envp = new0(char *, length + 8);
if (!envp)
return log_oom();
@@ -145,7 +146,9 @@ static int launch(char* name, char **argv, char **env, int fds) {
if (strchr(*s, '='))
envp[n_env++] = *s;
else {
_cleanup_free_ char *p = strappend(*s, "=");
_cleanup_free_ char *p;
p = strappend(*s, "=");
if (!p)
return log_oom();
envp[n_env] = strv_find_prefix(env, p);
@@ -164,15 +167,37 @@ static int launch(char* name, char **argv, char **env, int fds) {
(asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
return log_oom();
if (arg_fdname) {
char *e;
e = strappend("LISTEN_FDNAMES=", arg_fdname);
if (!e)
return log_oom();
for (i = 1; i < (unsigned) fds; i++) {
char *c;
c = strjoin(e, ":", arg_fdname, NULL);
if (!c) {
free(e);
return log_oom();
}
free(e);
e = c;
}
envp[n_env++] = e;
}
tmp = strv_join(argv, " ");
if (!tmp)
return log_oom();
log_info("Execing %s (%s)", name, tmp);
execvpe(name, argv, envp);
log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
return -errno;
return log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
}
static int launch1(const char* child, char** argv, char **env, int fd) {
@@ -289,6 +314,7 @@ static void help(void) {
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FDNAME,
};
static const struct option options[] = {
@@ -297,11 +323,12 @@ static int parse_argv(int argc, char *argv[]) {
{ "listen", required_argument, NULL, 'l' },
{ "accept", no_argument, NULL, 'a' },
{ "setenv", required_argument, NULL, 'E' },
{ "environment", required_argument, NULL, 'E' }, /* alias */
{ "environment", required_argument, NULL, 'E' }, /* legacy alias */
{ "fdname", required_argument, NULL, ARG_FDNAME },
{}
};
int c;
int c, r;
assert(argc >= 0);
assert(argv);
@@ -315,25 +342,27 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERSION:
return version();
case 'l': {
int r = strv_extend(&arg_listen, optarg);
case 'l':
r = strv_extend(&arg_listen, optarg);
if (r < 0)
return r;
return log_oom();
break;
}
case 'a':
arg_accept = true;
break;
case 'E': {
int r = strv_extend(&arg_setenv, optarg);
case 'E':
r = strv_extend(&arg_setenv, optarg);
if (r < 0)
return r;
return log_oom();
break;
}
case ARG_FDNAME:
arg_fdname = optarg;
break;
case '?':
return -EINVAL;
+44 -3
View File
@@ -277,8 +277,8 @@ char **strv_split_newlines(const char *s) {
}
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
size_t n = 0, allocated = 0;
int r;
assert(t);
@@ -302,13 +302,16 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
l[n] = NULL;
}
if (!l)
if (!l) {
l = new0(char*, 1);
if (!l)
return -ENOMEM;
}
*t = l;
l = NULL;
return 0;
return (int) n;
}
char *strv_join(char **l, const char *separator) {
@@ -745,3 +748,41 @@ char **strv_skip(char **l, size_t n) {
return l;
}
int strv_extend_n(char ***l, const char *value, size_t n) {
size_t i, j, k;
char **nl;
assert(l);
if (!value)
return 0;
if (n == 0)
return 0;
/* Adds the value value n times to l */
k = strv_length(*l);
nl = realloc(*l, sizeof(char*) * (k + n + 1));
if (!nl)
return -ENOMEM;
*l = nl;
for (i = k; i < k + n; i++) {
nl[i] = strdup(value);
if (!nl[i])
goto rollback;
}
nl[i] = NULL;
return 0;
rollback:
for (j = k; j < i; i++)
free(nl[j]);
nl[k] = NULL;
return NULL;
}
+2
View File
@@ -158,3 +158,5 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
char ***strv_free_free(char ***l);
char **strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);
+25
View File
@@ -6842,3 +6842,28 @@ int version(void) {
SYSTEMD_FEATURES);
return 0;
}
bool fdname_is_valid(const char *s) {
const char *p;
/* Validates a name for $LISTEN_NAMES. We basically allow
* everything ASCII that's not a control character. Also, as
* special exception the ":" character is not allowed, as we
* use that as field separator in $LISTEN_NAMES.
*
* Note that the empty string is explicitly allowed here.*/
if (!s)
return false;
for (p = s; *p; p++) {
if (*p < ' ')
return false;
if (*p >= 127)
return false;
if (*p == ':')
return false;
}
return p - s < 256;
}
+2
View File
@@ -941,3 +941,5 @@ int receive_one_fd(int transport_fd, int flags);
void nop_signal_handler(int sig);
int version(void);
bool fdname_is_valid(const char *s);
+20
View File
@@ -84,6 +84,25 @@ static int property_get_listen(
return sd_bus_message_close_container(reply);
}
static int property_get_fdname(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Socket *s = SOCKET(userdata);
assert(bus);
assert(reply);
assert(s);
return sd_bus_message_append(reply, "s", socket_fdname(s));
}
const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -128,6 +147,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Socket, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0),
SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0),
SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+49 -36
View File
@@ -21,18 +21,18 @@
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <glob.h>
#include <grp.h>
#include <poll.h>
#include <glob.h>
#include <utmpx.h>
#include <signal.h>
#include <string.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <utmpx.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
@@ -50,37 +50,38 @@
#include <sys/apparmor.h>
#endif
#include "barrier.h"
#include "sd-messages.h"
#include "rm-rf.h"
#include "strv.h"
#include "macro.h"
#include "capability.h"
#include "util.h"
#include "log.h"
#include "ioprio.h"
#include "securebits.h"
#include "namespace.h"
#include "exit-status.h"
#include "missing.h"
#include "utmp-wtmp.h"
#include "def.h"
#include "path-util.h"
#include "env-util.h"
#include "fileio.h"
#include "unit.h"
#include "async.h"
#include "selinux-util.h"
#include "errno-list.h"
#include "af-list.h"
#include "mkdir.h"
#include "smack-util.h"
#include "async.h"
#include "barrier.h"
#include "bus-endpoint.h"
#include "cap-list.h"
#include "capability.h"
#include "def.h"
#include "env-util.h"
#include "errno-list.h"
#include "exit-status.h"
#include "fileio.h"
#include "formats-util.h"
#include "ioprio.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
#include "mkdir.h"
#include "namespace.h"
#include "path-util.h"
#include "process-util.h"
#include "terminal-util.h"
#include "rm-rf.h"
#include "securebits.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "smack-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "unit.h"
#include "util.h"
#include "utmp-wtmp.h"
#ifdef HAVE_APPARMOR
#include "apparmor-util.h"
@@ -1198,6 +1199,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
static int build_environment(
const ExecContext *c,
unsigned n_fds,
char ** fd_names,
usec_t watchdog_usec,
const char *home,
const char *username,
@@ -1211,11 +1213,13 @@ static int build_environment(
assert(c);
assert(ret);
our_env = new0(char*, 10);
our_env = new0(char*, 11);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
_cleanup_free_ char *joined = NULL;
if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
@@ -1223,6 +1227,15 @@ static int build_environment(
if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)
return -ENOMEM;
our_env[n_env++] = x;
joined = strv_join(fd_names, ":");
if (!joined)
return -ENOMEM;
x = strjoin("LISTEN_FDNAMES=", joined, NULL);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (watchdog_usec > 0) {
@@ -1273,7 +1286,7 @@ static int build_environment(
}
our_env[n_env++] = NULL;
assert(n_env <= 10);
assert(n_env <= 11);
*ret = our_env;
our_env = NULL;
@@ -1850,7 +1863,7 @@ static int exec_child(
#endif
}
r = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env);
r = build_environment(context, n_fds, params->fd_names, params->watchdog_usec, home, username, shell, &our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
+12 -1
View File
@@ -208,19 +208,30 @@ struct ExecContext {
struct ExecParameters {
char **argv;
int *fds; unsigned n_fds;
int *fds;
char **fd_names;
unsigned n_fds;
char **environment;
bool apply_permissions;
bool apply_chroot;
bool apply_tty_stdin;
bool confirm_spawn;
bool selinux_context_net;
CGroupMask cgroup_supported;
const char *cgroup_path;
bool cgroup_delegate;
const char *runtime_prefix;
usec_t watchdog_usec;
int *idle_pipe;
char *bus_endpoint_path;
int bus_endpoint_fd;
};
+1
View File
@@ -287,6 +287,7 @@ Socket.MessageQueueMaxMessages, config_parse_long, 0,
Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize)
Socket.RemoveOnStop, config_parse_bool, 0, offsetof(Socket, remove_on_stop)
Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks)
Socket.FileDescriptorName, config_parse_fdname, 0, 0
Socket.Service, config_parse_socket_service, 0, 0
m4_ifdef(`HAVE_SMACK',
`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack)
+47 -3
View File
@@ -1475,10 +1475,10 @@ int config_parse_socket_service(
void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
Socket *s = data;
int r;
Unit *x;
_cleanup_free_ char *p = NULL;
Socket *s = data;
Unit *x;
int r;
assert(filename);
assert(lvalue);
@@ -1507,6 +1507,50 @@ int config_parse_socket_service(
return 0;
}
int config_parse_fdname(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_free_ char *p = NULL;
Socket *s = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (isempty(rvalue)) {
s->fdname = mfree(s->fdname);
return 0;
}
r = unit_name_printf(UNIT(s), rvalue, &p);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
return 0;
}
if (!fdname_is_valid(p)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
return 0;
}
free(s->fdname);
s->fdname = p;
p = NULL;
return 0;
}
int config_parse_service_sockets(
const char *unit,
const char *filename,
+1
View File
@@ -107,6 +107,7 @@ int config_parse_protect_system(const char* unit, const char *filename, unsigned
int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_working_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_fdname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
+1
View File
@@ -495,6 +495,7 @@ static void manager_clean_environment(Manager *m) {
"MANAGERPID",
"LISTEN_PID",
"LISTEN_FDS",
"LISTEN_FDNAMES",
"WATCHDOG_PID",
"WATCHDOG_USEC",
NULL);
+123 -58
View File
@@ -261,6 +261,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) {
sd_event_source_unref(fs->event_source);
}
free(fs->fdname);
safe_close(fs->fd);
free(fs);
}
@@ -334,7 +335,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us
return 0;
}
static int service_add_fd_store(Service *s, int fd) {
static int service_add_fd_store(Service *s, int fd, const char *name) {
ServiceFDStore *fs;
int r;
@@ -361,9 +362,13 @@ static int service_add_fd_store(Service *s, int fd) {
fs->fd = fd;
fs->service = s;
fs->fdname = strdup(name ?: "stored");
if (!fs->fdname)
return -ENOMEM;
r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs);
if (r < 0) {
free(fs->fdname);
free(fs);
return r;
}
@@ -376,7 +381,7 @@ static int service_add_fd_store(Service *s, int fd) {
return 1;
}
static int service_add_fd_store_set(Service *s, FDSet *fds) {
static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
int r;
assert(s);
@@ -391,7 +396,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
if (fd < 0)
break;
r = service_add_fd_store(s, fd);
r = service_add_fd_store(s, fd, name);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m");
if (r > 0) {
@@ -957,56 +962,79 @@ static int service_coldplug(Unit *u) {
return 0;
}
static int service_collect_fds(Service *s, int **fds) {
static int service_collect_fds(Service *s, int **fds, char ***fd_names) {
_cleanup_strv_free_ char **rfd_names = NULL;
_cleanup_free_ int *rfds = NULL;
int rn_fds = 0;
Iterator i;
Unit *u;
int rn_fds = 0, r;
assert(s);
assert(fds);
assert(fd_names);
if (s->socket_fd >= 0)
return -EINVAL;
if (s->socket_fd >= 0) {
SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
_cleanup_free_ int *cfds = NULL;
Socket *sock;
int cn_fds;
/* Pass the per-connection socket */
if (u->type != UNIT_SOCKET)
continue;
rfds = new(int, 1);
if (!rfds)
return -ENOMEM;
rfds[0] = s->socket_fd;
sock = SOCKET(u);
rfd_names = strv_new("connection", NULL);
if (!rfd_names)
return -ENOMEM;
cn_fds = socket_collect_fds(sock, &cfds);
if (cn_fds < 0)
return cn_fds;
rn_fds = 1;
} else {
Iterator i;
Unit *u;
if (cn_fds <= 0)
continue;
/* Pass all our configured sockets for singleton services */
if (!rfds) {
rfds = cfds;
rn_fds = cn_fds;
SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
_cleanup_free_ int *cfds = NULL;
Socket *sock;
int cn_fds;
cfds = NULL;
} else {
int *t;
if (u->type != UNIT_SOCKET)
continue;
t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
if (!t)
return -ENOMEM;
sock = SOCKET(u);
memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
cn_fds = socket_collect_fds(sock, &cfds);
if (cn_fds < 0)
return cn_fds;
rfds = t;
rn_fds += cn_fds;
if (cn_fds <= 0)
continue;
if (!rfds) {
rfds = cfds;
rn_fds = cn_fds;
cfds = NULL;
} else {
int *t;
t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
if (!t)
return -ENOMEM;
memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
rfds = t;
rn_fds += cn_fds;
}
r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds);
if (r < 0)
return r;
}
}
if (s->n_fd_store > 0) {
ServiceFDStore *fs;
char **nl;
int *t;
t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int));
@@ -1014,12 +1042,30 @@ static int service_collect_fds(Service *s, int **fds) {
return -ENOMEM;
rfds = t;
LIST_FOREACH(fd_store, fs, s->fd_store)
rfds[rn_fds++] = fs->fd;
nl = realloc(rfd_names, (rn_fds + s->n_fd_store + 1) * sizeof(char*));
if (!nl)
return -ENOMEM;
rfd_names = nl;
LIST_FOREACH(fd_store, fs, s->fd_store) {
rfds[rn_fds] = fs->fd;
rfd_names[rn_fds] = strdup(strempty(fs->fdname));
if (!rfd_names[rn_fds])
return -ENOMEM;
rn_fds++;
}
rfd_names[rn_fds] = NULL;
}
*fds = rfds;
*fd_names = rfd_names;
rfds = NULL;
rfd_names = NULL;
return rn_fds;
}
@@ -1035,15 +1081,13 @@ static int service_spawn(
bool is_control,
pid_t *_pid) {
pid_t pid;
int r;
int *fds = NULL;
_cleanup_free_ int *fdsbuf = NULL;
unsigned n_fds = 0, n_env = 0;
_cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL;
_cleanup_free_ char *bus_endpoint_path = NULL;
_cleanup_strv_free_ char
**argv = NULL, **final_env = NULL, **our_env = NULL;
_cleanup_free_ int *fds = NULL;
unsigned n_fds = 0, n_env = 0;
const char *path;
pid_t pid;
ExecParameters exec_params = {
.apply_permissions = apply_permissions,
.apply_chroot = apply_chroot,
@@ -1052,6 +1096,8 @@ static int service_spawn(
.selinux_context_net = s->socket_fd_selinux_context_net
};
int r;
assert(s);
assert(c);
assert(_pid);
@@ -1071,17 +1117,11 @@ static int service_spawn(
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
if (s->socket_fd >= 0) {
fds = &s->socket_fd;
n_fds = 1;
} else {
r = service_collect_fds(s, &fdsbuf);
if (r < 0)
goto fail;
r = service_collect_fds(s, &fds, &fd_names);
if (r < 0)
goto fail;
fds = fdsbuf;
n_fds = r;
}
n_fds = r;
}
if (timeout > 0) {
@@ -1119,7 +1159,7 @@ static int service_spawn(
goto fail;
}
if (UNIT_DEREF(s->accept_socket)) {
if (s->socket_fd >= 0) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
@@ -1185,6 +1225,7 @@ static int service_spawn(
exec_params.argv = argv;
exec_params.fds = fds;
exec_params.fd_names = fd_names;
exec_params.n_fds = n_fds;
exec_params.environment = final_env;
exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
@@ -2047,13 +2088,16 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
}
LIST_FOREACH(fd_store, fs, s->fd_store) {
_cleanup_free_ char *c = NULL;
int copy;
copy = fdset_put_dup(fds, fs->fd);
if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "fd-store-fd", "%i", copy);
c = cescape(fs->fdname);
unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c));
}
if (s->main_exec_status.pid > 0) {
@@ -2183,12 +2227,24 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
s->bus_endpoint_fd = fdset_remove(fds, fd);
}
} else if (streq(key, "fd-store-fd")) {
const char *fdv;
size_t pf;
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
pf = strcspn(value, WHITESPACE);
fdv = strndupa(value, pf);
if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value);
else {
r = service_add_fd_store(s, fd);
_cleanup_free_ char *t = NULL;
const char *fdn;
fdn = value + pf;
fdn += strspn(fdn, WHITESPACE);
(void) cunescape(fdn, 0, &t);
r = service_add_fd_store(s, fd, t);
if (r < 0)
log_unit_error_errno(u, r, "Failed to add fd to store: %m");
else if (r > 0)
@@ -2942,8 +2998,17 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
if (strv_find(tags, "WATCHDOG=1"))
service_reset_watchdog(s);
if (strv_find(tags, "FDSTORE=1"))
service_add_fd_store_set(s, fds);
if (strv_find(tags, "FDSTORE=1")) {
const char *name;
name = strv_find_startswith(tags, "FDNAME=");
if (name && !fdname_is_valid(name)) {
log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring.");
name = NULL;
}
service_add_fd_store_set(s, fds, name);
}
/* Notify clients about changed status or main pid */
if (notify_dbus)
+1
View File
@@ -97,6 +97,7 @@ struct ServiceFDStore {
Service *service;
int fd;
char *fdname;
sd_event_source *event_source;
LIST_FIELDS(ServiceFDStore, fd_store);
+15
View File
@@ -508,6 +508,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sTCPCongestion: %s\n"
"%sRemoveOnStop: %s\n"
"%sWritable: %s\n"
"%sFDName: %s\n"
"%sSELinuxContextFromNet: %s\n",
prefix, socket_state_to_string(s->state),
prefix, socket_result_to_string(s->result),
@@ -525,6 +526,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, strna(s->tcp_congestion),
prefix, yes_no(s->remove_on_stop),
prefix, yes_no(s->writable),
prefix, socket_fdname(s),
prefix, yes_no(s->selinux_context_from_net));
if (s->control_pid > 0)
@@ -2760,6 +2762,19 @@ static int socket_get_timeout(Unit *u, uint64_t *timeout) {
return 1;
}
char *socket_fdname(Socket *s) {
assert(s);
/* Returns the name to use for $LISTEN_NAMES. If the user
* didn't specify anything specifically, use the socket unit's
* name as fallback. */
if (s->fdname)
return s->fdname;
return UNIT(s)->id;
}
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
[SOCKET_EXEC_START_CHOWN] = "StartChown",
+6 -2
View File
@@ -154,6 +154,8 @@ struct Socket {
char *user, *group;
bool reset_cpu_usage:1;
char *fdname;
};
/* Called from the service code when collecting fds */
@@ -164,6 +166,10 @@ void socket_connection_unref(Socket *s);
void socket_free_ports(Socket *s);
int socket_instantiate_service(Socket *s);
char *socket_fdname(Socket *s);
extern const UnitVTable socket_vtable;
const char* socket_exec_command_to_string(SocketExecCommand i) _const_;
@@ -173,5 +179,3 @@ const char* socket_result_to_string(SocketResult i) _const_;
SocketResult socket_result_from_string(const char *s) _pure_;
const char* socket_port_type_to_string(SocketPort *p) _pure_;
int socket_instantiate_service(Socket *s);
+64 -15
View File
@@ -19,25 +19,37 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stddef.h>
#include <limits.h>
#include <mqueue.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include "util.h"
#include "path-util.h"
#include "socket-util.h"
#include "strv.h"
#include "util.h"
#include "sd-daemon.h"
static void unsetenv_all(bool unset_environment) {
if (!unset_environment)
return;
unsetenv("LISTEN_PID");
unsetenv("LISTEN_FDS");
unsetenv("LISTEN_FDNAMES");
}
_public_ int sd_listen_fds(int unset_environment) {
const char *e;
unsigned n;
@@ -79,12 +91,49 @@ _public_ int sd_listen_fds(int unset_environment) {
r = (int) n;
finish:
if (unset_environment) {
unsetenv("LISTEN_PID");
unsetenv("LISTEN_FDS");
unsetenv_all(unset_environment);
return r;
}
_public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
_cleanup_strv_free_ char **l = NULL;
bool have_names;
int n_names = 0, n_fds;
const char *e;
int r;
if (!names)
return sd_listen_fds(unset_environment);
e = getenv("LISTEN_FDNAMES");
if (e) {
n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (n_names < 0) {
unsetenv_all(unset_environment);
return n_names;
}
have_names = true;
} else
have_names = false;
n_fds = sd_listen_fds(unset_environment);
if (n_fds <= 0)
return n_fds;
if (have_names) {
if (n_names != n_fds)
return -EINVAL;
} else {
r = strv_extend_n(&l, "unknown", n_fds);
if (r < 0)
return r;
}
return r;
*names = l;
l = NULL;
return n_fds;
}
_public_ int sd_is_fifo(int fd, const char *path) {
+2
View File
@@ -76,6 +76,8 @@ _SD_BEGIN_DECLARATIONS;
*/
int sd_listen_fds(int unset_environment);
int sd_listen_fds_with_names(int unset_environment, char ***names);
/*
Helper call for identifying a passed file descriptor. Returns 1 if
the file descriptor is a FIFO in the file system stored under the
+15 -2
View File
@@ -21,9 +21,22 @@
#include <unistd.h>
#include "systemd/sd-daemon.h"
#include "sd-daemon.h"
#include "strv.h"
int main(int argc, char*argv[]) {
_cleanup_strv_free_ char **l = NULL;
int n, i;
n = sd_listen_fds_with_names(false, &l);
if (n < 0) {
log_error_errno(n, "Failed to get listening fds: %m");
return EXIT_FAILURE;
}
for (i = 0; i < n; i++)
log_info("fd=%i name=%s\n", SD_LISTEN_FDS_START + i, l[i]);
sd_notify(0,
"STATUS=Starting up");
@@ -49,5 +62,5 @@ int main(int argc, char*argv[]) {
"STOPPING=1");
sleep(5);
return 0;
return EXIT_SUCCESS;
}
+32 -4
View File
@@ -155,7 +155,7 @@ static void test_strv_join(void) {
static void test_strv_quote_unquote(const char* const *split, const char *quoted) {
_cleanup_free_ char *p;
_cleanup_strv_free_ char **s;
_cleanup_strv_free_ char **s = NULL;
char **t;
int r;
@@ -166,7 +166,7 @@ static void test_strv_quote_unquote(const char* const *split, const char *quoted
assert_se(streq(p, quoted));
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
assert_se(r == 0);
assert_se(r == (int) strv_length(s));
assert_se(s);
STRV_FOREACH(t, s) {
assert_se(*t);
@@ -183,7 +183,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
int r;
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
assert_se(r == 0);
assert_se(r == (int) strv_length(list));
assert_se(s);
j = strv_join(s, " | ");
assert_se(j);
@@ -225,7 +225,7 @@ static void test_strv_split_extract(void) {
int r;
r = strv_split_extract(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
assert_se(r == 0);
assert_se(r == (int) strv_length(l));
assert_se(streq_ptr(l[0], ""));
assert_se(streq_ptr(l[1], "foo:bar"));
assert_se(streq_ptr(l[2], ""));
@@ -591,6 +591,33 @@ static void test_strv_skip(void) {
test_strv_skip_one(STRV_MAKE(NULL), 55, STRV_MAKE(NULL));
}
static void test_strv_extend_n(void) {
_cleanup_strv_free_ char **v = NULL;
v = strv_new("foo", "bar", NULL);
assert_se(v);
assert_se(strv_extend_n(&v, "waldo", 3) >= 0);
assert_se(strv_extend_n(&v, "piep", 2) >= 0);
assert_se(streq(v[0], "foo"));
assert_se(streq(v[1], "bar"));
assert_se(streq(v[2], "waldo"));
assert_se(streq(v[3], "waldo"));
assert_se(streq(v[4], "waldo"));
assert_se(streq(v[5], "piep"));
assert_se(streq(v[6], "piep"));
assert_se(v[7] == NULL);
v = strv_free(v);
assert_se(strv_extend_n(&v, "foo", 1) >= 0);
assert_se(strv_extend_n(&v, "bar", 0) >= 0);
assert_se(streq(v[0], "foo"));
assert_se(v[1] == NULL);
}
int main(int argc, char *argv[]) {
test_specifier_printf();
test_strv_foreach();
@@ -650,6 +677,7 @@ int main(int argc, char *argv[]) {
test_strv_reverse();
test_strv_shell_escape();
test_strv_skip();
test_strv_extend_n();
return 0;
}