diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-10-04 17:36:19 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-10-06 11:52:48 +0200 |
commit | 8dd4c05b5495c7ffe0f12ace87e71abe17bd0a0e (patch) | |
tree | 30a788ca911facca15b4f1fa8641e78753caa574 | |
parent | 79c7626d1f239e02152ad698298a1b5d0e9fbacf (diff) |
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.
-rw-r--r-- | Makefile-man.am | 5 | ||||
-rw-r--r-- | man/sd_listen_fds.xml | 133 | ||||
-rw-r--r-- | man/sd_notify.xml | 31 | ||||
-rw-r--r-- | man/systemd-activate.xml | 13 | ||||
-rw-r--r-- | man/systemd.socket.xml | 23 | ||||
-rw-r--r-- | man/systemd.xml | 1 | ||||
-rw-r--r-- | src/activate/activate.c | 61 | ||||
-rw-r--r-- | src/basic/strv.c | 47 | ||||
-rw-r--r-- | src/basic/strv.h | 2 | ||||
-rw-r--r-- | src/basic/util.c | 25 | ||||
-rw-r--r-- | src/basic/util.h | 2 | ||||
-rw-r--r-- | src/core/dbus-socket.c | 20 | ||||
-rw-r--r-- | src/core/execute.c | 81 | ||||
-rw-r--r-- | src/core/execute.h | 13 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 1 | ||||
-rw-r--r-- | src/core/load-fragment.c | 48 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/manager.c | 1 | ||||
-rw-r--r-- | src/core/service.c | 181 | ||||
-rw-r--r-- | src/core/service.h | 1 | ||||
-rw-r--r-- | src/core/socket.c | 15 | ||||
-rw-r--r-- | src/core/socket.h | 8 | ||||
-rw-r--r-- | src/libsystemd/sd-daemon/sd-daemon.c | 79 | ||||
-rw-r--r-- | src/systemd/sd-daemon.h | 2 | ||||
-rw-r--r-- | src/test/test-daemon.c | 17 | ||||
-rw-r--r-- | src/test/test-strv.c | 36 |
26 files changed, 677 insertions, 170 deletions
diff --git a/Makefile-man.am b/Makefile-man.am index 14c2b7d57e..1ff85d7d2c 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -355,6 +355,7 @@ MANPAGES_ALIAS += \ man/sd_journal_set_data_threshold.3 \ man/sd_journal_test_cursor.3 \ man/sd_journal_wait.3 \ + man/sd_listen_fds_with_names.3 \ man/sd_machine_get_ifindices.3 \ man/sd_notifyf.3 \ man/sd_pid_notify.3 \ @@ -643,6 +644,7 @@ man/sd_journal_sendv.3: man/sd_journal_print.3 man/sd_journal_set_data_threshold.3: man/sd_journal_get_data.3 man/sd_journal_test_cursor.3: man/sd_journal_get_cursor.3 man/sd_journal_wait.3: man/sd_journal_get_fd.3 +man/sd_listen_fds_with_names.3: man/sd_listen_fds.3 man/sd_machine_get_ifindices.3: man/sd_machine_get_class.3 man/sd_notifyf.3: man/sd_notify.3 man/sd_pid_notify.3: man/sd_notify.3 @@ -1319,6 +1321,9 @@ man/sd_journal_test_cursor.html: man/sd_journal_get_cursor.html man/sd_journal_wait.html: man/sd_journal_get_fd.html $(html-alias) +man/sd_listen_fds_with_names.html: man/sd_listen_fds.html + $(html-alias) + man/sd_machine_get_ifindices.html: man/sd_machine_get_class.html $(html-alias) diff --git a/man/sd_listen_fds.xml b/man/sd_listen_fds.xml index 9b9705eb2e..ea55671e4f 100644 --- a/man/sd_listen_fds.xml +++ b/man/sd_listen_fds.xml @@ -1,4 +1,4 @@ -<?xml version='1.0'?> <!--*-nxml-*--> +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> @@ -45,6 +45,7 @@ <refnamediv> <refname>sd_listen_fds</refname> + <refname>sd_listen_fds_with_names</refname> <refname>SD_LISTEN_FDS_START</refname> <refpurpose>Check for file descriptors passed by the system manager</refpurpose> </refnamediv> @@ -59,23 +60,26 @@ <funcdef>int <function>sd_listen_fds</function></funcdef> <paramdef>int <parameter>unset_environment</parameter></paramdef> </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_listen_fds_with_names</function></funcdef> + <paramdef>int <parameter>unset_environment</parameter></paramdef> + <paramdef>char*** <parameter>names</parameter></paramdef> + </funcprototype> </funcsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> - <para><function>sd_listen_fds()</function> shall be called by a - daemon to check for file descriptors passed by the init system as - part of the socket-based activation logic.</para> - - <para>If the <parameter>unset_environment</parameter> parameter is - non-zero, <function>sd_listen_fds()</function> will unset the - <varname>$LISTEN_FDS</varname> and <varname>$LISTEN_PID</varname> - environment variables before returning (regardless of whether the - function call itself succeeded or not). Further calls to - <function>sd_listen_fds()</function> will then fail, but the - variables are no longer inherited by child processes.</para> + <para><function>sd_listen_fds()</function> may be invoked by a + daemon to check for file descriptors passed by the service manager as + part of the socket-based activation logic. It returns the number + of received file descriptors. If no file descriptors have been + received zero is returned. The first file descriptor may be found + at file descriptor number 3 + (i.e. <constant>SD_LISTEN_FDS_START</constant>), the remaining + descriptors follow at 4, 5, 6, ..., if any.</para> <para>If a daemon receives more than one file descriptor, they will be passed in the same order as configured in the systemd @@ -108,12 +112,86 @@ <literal>FDSTORE=1</literal> messages, these file descriptors are passed last, in arbitrary order, and with duplicates removed.</para> + + <para>If the <parameter>unset_environment</parameter> parameter is + non-zero, <function>sd_listen_fds()</function> will unset the + <varname>$LISTEN_FDS</varname>, <varname>$LISTEN_PID</varname> and + <varname>$LISTEN_FDNAMES</varname> environment variables before + returning (regardless of whether the function call itself + succeeded or not). Further calls to + <function>sd_listen_fds()</function> will then return zero, but the + variables are no longer inherited by child processes.</para> + + <para><function>sd_listen_fds_with_names()</function> is like + <function>sd_listen_fds()</function> but optionally also returns + an array of strings with identification names for the passed file + descriptors, if that is available, and the + <parameter>names</parameter> parameter is non-NULL. This + information is read from the <varname>$LISTEN_FDNAMES</varname> + variable, which may contain a colon-separated list of names. For + socket-activated services, these names may be configured with the + <varname>FileDescriptorName=</varname> setting in socket unit + files, see + <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for details. For file descriptors pushed into the file descriptor + store (see above) the name is set via the + <varname>FDNAME=</varname> field transmitted via + <function>sd_pid_notify_with_fds()</function>. The primary usecase + for these names are services which accept a variety of file + descriptors which are not recognizable with functions like + <function>sd_is_socket()</function> alone, and thus require + identification via a name. It is recommended to rely on named file + descriptors only if identification via + <function>sd_is_socket()</function> and related calls is not + sufficient. Note that the names used are not unique in any + way. The returned array of strings has as many entries as file + descriptors has been received, plus a final NULL pointer + terminating the array. The caller needs to free the array itself + and each of its elements with libc's <varname>free()</varname> + call after use. If the <parameter>names</parameter> parameter is + NULL the call is entirely equivalent to + <function>sd_listen_fds()</function>.</para> + + <para>Under specific conditions the following automatic file + descriptor names are returned: + + <table> + <title> + <command>Special names</command> + </title> + + <tgroup cols='2'> + <thead> + <row> + <entry>Name</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry><literal>unknown</literal></entry> + <entry>The process received no name for the specific file descriptor from the service manager.</entry> + </row> + + <row> + <entry><literal>stored</literal></entry> + <entry>The file descriptor originates in the service manager's per-service file descriptor store, and the <varname>FDNAME=</varname> field was absent when the file descriptor was submitted to the service manager.</entry> + </row> + + <row> + <entry><literal>connection</literal></entry> + <entry>The service was activated in per-connection style using <varname>Accept=yes</varname> in the socket unit file, and the file descriptor is the connection socket.</entry> + </row> + </tbody> + </tgroup> + </table> + </para> </refsect1> <refsect1> <title>Return Value</title> - <para>On failure, this call returns a negative errno-style error + <para>On failure, these calls returns a negative errno-style error code. If <varname>$LISTEN_FDS</varname>/<varname>$LISTEN_PID</varname> was not set or was not correctly set for this daemon and hence no file @@ -128,13 +206,16 @@ <xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/> - <para>Internally, this function checks whether the - <varname>$LISTEN_PID</varname> environment variable equals the - daemon PID. If not, it returns immediately. Otherwise, it parses - the number passed in the <varname>$LISTEN_FDS</varname> + <para>Internally, <function>sd_listen_fds()</function> checks + whether the <varname>$LISTEN_PID</varname> environment variable + equals the daemon PID. If not, it returns immediately. Otherwise, + it parses the number passed in the <varname>$LISTEN_FDS</varname> environment variable, then sets the FD_CLOEXEC flag for the parsed number of file descriptors starting from SD_LISTEN_FDS_START. - Finally, it returns the parsed number.</para> + Finally, it returns the parsed + number. <function>sd_listen_fds_with_names()</function> does the + same but also parses <varname>$LISTEN_FDNAMES</varname> if + set.</para> </refsect1> <refsect1> @@ -144,15 +225,14 @@ <varlistentry> <term><varname>$LISTEN_PID</varname></term> <term><varname>$LISTEN_FDS</varname></term> + <term><varname>$LISTEN_FDNAMES</varname></term> - <listitem><para>Set by the init system - for supervised processes that use - socket-based activation. This - environment variable specifies the - data - <function>sd_listen_fds()</function> - parses. See above for - details.</para></listitem> + <listitem><para>Set by the service manager for supervised + processes that use socket-based activation. This environment + variable specifies the data + <function>sd_listen_fds()</function> and + <function>sd_listen_fds_with_names()</function> parses. See + above for details.</para></listitem> </varlistentry> </variablelist> </refsect1> @@ -167,6 +247,7 @@ <citerefentry><refentrytitle>sd_is_socket</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_is_socket_inet</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_is_socket_unix</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_pid_notify_with_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> diff --git a/man/sd_notify.xml b/man/sd_notify.xml index 14030f56b1..2d73c27f62 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -1,4 +1,4 @@ -<?xml version='1.0'?> <!--*-nxml-*--> +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> @@ -229,6 +229,27 @@ below.</para></listitem> </varlistentry> + <varlistentry> + <term>FDNAME=...</term> + + <listitem><para>When used in combination with + <varname>FDSTORE=1</varname> specifies a name for the + submitted file descriptors. This name is passed to the service + during activation, and may be queried using + <citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>. File + descriptors submitted without this field set, will implicitly + get the name <literal>stored</literal> assigned. Note that if + multiple file descriptors are submitted at once the specified + name will be assigned to all of them. In order to assign + different names to submitted file descriptors, submit them in + seperate invocations of + <function>sd_pid_notify_with_fds()</function>. The name may + consist of any ASCII characters, but must not contain control + characters or <literal>:</literal>. It may not be longer than + 255 characters. If a submitted name does not follow these + restrictions it is ignored.</para></listitem> + </varlistentry> + </variablelist> <para>It is recommended to prefix variable names that are not @@ -358,7 +379,7 @@ in order to continue operation after a service restart without losing state use <literal>FDSTORE=1</literal>:</para> - <programlisting>sd_pid_notify_with_fds(0, 0, "FDSTORE=1", &fd, 1);</programlisting> + <programlisting>sd_pid_notify_with_fds(0, 0, "FDSTORE=1\nFDNAME=foobar", &fd, 1);</programlisting> </example> </refsect1> @@ -367,9 +388,11 @@ <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>, - <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>, - <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry> + <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/systemd-activate.xml b/man/systemd-activate.xml index 3b854fd8ec..90e974c991 100644 --- a/man/systemd-activate.xml +++ b/man/systemd-activate.xml @@ -115,6 +115,16 @@ </para></listitem> </varlistentry> + <varlistentry> + <term><option>--fdname=</option><replaceable>NAME</replaceable></term> + + <listitem><para>Specify a name for the activation file + descriptors. This is equivalent to setting + <varname>FileDescriptorName=</varname> in socket unit files, and + enables use of + <citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem> + </varlistentry> + <xi:include href="standard-options.xml" xpointer="help" /> <xi:include href="standard-options.xml" xpointer="version" /> </variablelist> @@ -126,6 +136,7 @@ <varlistentry> <term><varname>$LISTEN_FDS</varname></term> <term><varname>$LISTEN_PID</varname></term> + <term><varname>$LISTEN_FDNAMES</varname></term> <listitem><para>See <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem> @@ -165,6 +176,8 @@ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>cat</refentrytitle><manvolnum>1</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 212764075d..46a47b2d95 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -1,4 +1,4 @@ -<?xml version='1.0'?> <!--*-nxml-*--> +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> @@ -748,6 +748,22 @@ list.</para></listitem> </varlistentry> + <varlistentry> + <term><varname>FileDescriptorName=</varname></term> + <listitem><para>Assigns a name to all file descriptors this + socket unit encapsulates. This is useful to help activated + services to identify specific file descriptors, if multiple + are passed. Services may use the + <citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call to acquire the names configured for the received file + descriptors. Names may contain any ASCII character, but must + exclude control characters or <literal>:</literal>, and must + be at most 255 characters in length. If this setting is not + used the file descriptor name defaults to the name of the + socket unit, including its <filename>.socket</filename> + suffix.</para></listitem> + </varlistentry> + </variablelist> <para>Check @@ -768,9 +784,10 @@ <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>, - <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry> + <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry> </para> - <para> For more extensive descriptions see the "systemd for Developers" series: <ulink url="http://0pointer.de/blog/projects/socket-activation.html">Socket Activation</ulink>, diff --git a/man/systemd.xml b/man/systemd.xml index 9e927c3204..391333bfb4 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -835,6 +835,7 @@ <varlistentry> <term><varname>$LISTEN_PID</varname></term> <term><varname>$LISTEN_FDS</varname></term> + <term><varname>$LISTEN_FDNAMES</varname></term> <listitem><para>Set by systemd for supervised processes during socket-based activation. See diff --git a/src/activate/activate.c b/src/activate/activate.c index 2dfc4a0bc4..6a8432314e 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -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; diff --git a/src/basic/strv.c b/src/basic/strv.c index 9524e80a6f..90f0b8c741 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -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; +} diff --git a/src/basic/strv.h b/src/basic/strv.h index 4c4b6526de..7c1f80230a 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -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); diff --git a/src/basic/util.c b/src/basic/util.c index c63ec0ceb0..054d45092e 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -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; +} diff --git a/src/basic/util.h b/src/basic/util.h index a4e3672130..034410b8a8 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -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); diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 4611ad5f86..7444649f8b 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -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), diff --git a/src/core/execute.c b/src/core/execute.c index 137a176c18..4664af873f 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -21,18 +21,18 @@ #include <errno.h> #include <fcntl.h> -#include <unistd.h> -#include <string.h> +#include <glob.h> +#include <grp.h> +#include <poll.h> #include <signal.h> -#include <sys/socket.h> -#include <sys/un.h> +#include <string.h> +#include <sys/personality.h> #include <sys/prctl.h> +#include <sys/socket.h> #include <sys/stat.h> -#include <grp.h> -#include <poll.h> -#include <glob.h> +#include <sys/un.h> +#include <unistd.h> #include <utmpx.h> -#include <sys/personality.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 "af-list.h" +#include "async.h" +#include "barrier.h" +#include "bus-endpoint.h" +#include "cap-list.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 "bus-endpoint.h" -#include "cap-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; diff --git a/src/core/execute.h b/src/core/execute.h index 2c93044748..f1c37116fd 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -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; }; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 2333926e7d..89e624b557 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -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) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index fc2755cb92..b1d4c6b57d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1475,10 +1475,10 @@ int config_parse_socket_service( void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *p = NULL; Socket *s = data; - int r; Unit *x; - _cleanup_free_ char *p = NULL; + 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, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 6ee7c71bc4..8661cbfedc 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -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); diff --git a/src/core/manager.c b/src/core/manager.c index 9de9691a47..526d4d1cef 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -495,6 +495,7 @@ static void manager_clean_environment(Manager *m) { "MANAGERPID", "LISTEN_PID", "LISTEN_FDS", + "LISTEN_FDNAMES", "WATCHDOG_PID", "WATCHDOG_USEC", NULL); diff --git a/src/core/service.c b/src/core/service.c index 2c78cb96c2..c941689dd7 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -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; + + rfd_names = strv_new("connection", NULL); + if (!rfd_names) + return -ENOMEM; - sock = SOCKET(u); + rn_fds = 1; + } else { + Iterator i; + Unit *u; - cn_fds = socket_collect_fds(sock, &cfds); - if (cn_fds < 0) - return cn_fds; + /* Pass all our configured sockets for singleton services */ - if (cn_fds <= 0) - continue; + SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { + _cleanup_free_ int *cfds = NULL; + Socket *sock; + int cn_fds; - if (!rfds) { - rfds = cfds; - rn_fds = cn_fds; + if (u->type != UNIT_SOCKET) + continue; - cfds = NULL; - } else { - int *t; + sock = SOCKET(u); - t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int)); - if (!t) - return -ENOMEM; + cn_fds = socket_collect_fds(sock, &cfds); + if (cn_fds < 0) + return cn_fds; + + if (cn_fds <= 0) + continue; + + if (!rfds) { + rfds = cfds; + rn_fds = cn_fds; - memcpy(t + rn_fds, cfds, cn_fds * sizeof(int)); + cfds = NULL; + } else { + int *t; - rfds = t; - rn_fds += cn_fds; + 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) diff --git a/src/core/service.h b/src/core/service.h index a8d42706bd..61bb44fbcf 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -97,6 +97,7 @@ struct ServiceFDStore { Service *service; int fd; + char *fdname; sd_event_source *event_source; LIST_FIELDS(ServiceFDStore, fd_store); diff --git a/src/core/socket.c b/src/core/socket.c index 4462fbd72d..6300b20f3e 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -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", diff --git a/src/core/socket.h b/src/core/socket.h index 96091b0609..94cda8a90d 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -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); diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 5fb0b73d02..437518119b 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -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) { diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h index 861dc8f1f4..214e77cab1 100644 --- a/src/systemd/sd-daemon.h +++ b/src/systemd/sd-daemon.h @@ -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 diff --git a/src/test/test-daemon.c b/src/test/test-daemon.c index 7e0ac754d1..45fb554445 100644 --- a/src/test/test-daemon.c +++ b/src/test/test-daemon.c @@ -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; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index cc47fd4d34..64801f8e01 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -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; } |