Laszlo Ersek
2023-Mar-25 11:39 UTC
[Libguestfs] [libnbd PATCH v5 0/4] pass LISTEN_FDNAMES with systemd socket activation
V4 was here (incorrectly versioned on the mailing list as v3): <http://mid.mail-archive.com/20230323121016.1442655-1-lersek at redhat.com>. See the Notes section on each patch for the v5 updates. Laszlo Ersek (2): socket activation: generalize environment construction socket activation: set LISTEN_FDNAMES Richard W.M. Jones (2): common/include: Copy ascii-ctype functions from nbdkit generator: Add APIs to get/set the socket activation socket name .gitignore | 1 + common/include/Makefile.am | 6 + common/include/ascii-ctype.h | 75 +++++++++ common/include/test-ascii-ctype.c | 88 ++++++++++ generator/API.ml | 50 ++++++ generator/states-connect-socket-activation.c | 170 ++++++++++++++++---- lib/handle.c | 56 +++++++ lib/internal.h | 1 + 8 files changed, 412 insertions(+), 35 deletions(-) create mode 100644 common/include/ascii-ctype.h create mode 100644 common/include/test-ascii-ctype.c base-commit: a48a1142bc54b09dbd9ed45cc9f9f4945f8174ef
Laszlo Ersek
2023-Mar-25 11:39 UTC
[Libguestfs] [libnbd PATCH v5 1/4] socket activation: generalize environment construction
In the future, we'd like to add systemd socket activation environment
variables such that:
- their values may not be constants (not even for pre-allocating space),
- they may be optional,
- regardless of some optional variables being added or not, the positions
of those that we do add can be captured, for later reference.
Generalize prepare_socket_activation_environment() accordingly. Write the
child PID to "LISTEN_PID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" with the
new
"capturing" interface.
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
Reviewed-by: Richard W.M. Jones <rjones at redhat.com>
Reviewed-by: Eric Blake <eblake at redhat.com>
---
Notes:
v5:
- pick up Eric's R-b
v4:
- pick up Rich's R-b
- resolve rebase conflicts (a) inside
prepare_socket_activation_environment(), (b) near the
prepare_socket_activation_environment() call site -- due to keeping
set_error() internal to prepare_socket_activation_environment(), in
patch "socket activation: clean up responsibilities of
prep.sock.act.env.()"
context:-U6
generator/states-connect-socket-activation.c | 160 +++++++++++++++-----
1 file changed, 125 insertions(+), 35 deletions(-)
diff --git a/generator/states-connect-socket-activation.c
b/generator/states-connect-socket-activation.c
index a214ffd5a6e4..10ccc3119299 100644
--- a/generator/states-connect-socket-activation.c
+++ b/generator/states-connect-socket-activation.c
@@ -27,85 +27,169 @@
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "internal.h"
+#include "compiler-macros.h"
+#include "unique-name.h"
+#include "array-size.h"
+#include "checked-overflow.h"
/* This is baked into the systemd socket activation API. */
#define FIRST_SOCKET_ACTIVATION_FD 3
-/* == strlen ("LISTEN_PID=") | strlen ("LISTEN_FDS=") */
-#define PREFIX_LENGTH 11
+/* Describes a systemd socket activation environment variable. */
+struct sact_var {
+ const char *prefix; /* variable name and equal sign */
+ size_t prefix_len;
+ const char *value;
+ size_t value_len;
+};
+
+/* Determine the length of a string, using "sizeof" whenever
possible.
+ *
+ * Do not use this macro on an argument that has side effects, as no guarantees
+ * are given regarding the number of times the argument may be evaluated.
+ * TYPE_IS_ARRAY(s) itself may contribute a different number of evaluations
+ * dependent on whether "s" has variably modified type, and then the
conditional
+ * operator either evaluates "sizeof s" (which contributes 0 or 1
evaluations,
+ * dependent on whether "s" has variably modified type) or strlen(s)
(which
+ * contributes 1 evaluation). Also note that the argument of the
"sizeof"
+ * operator is *only* parenthesized because "s" is a macro parameter
here.
+*/
+#define STRLEN1(s) ((TYPE_IS_ARRAY (s) ? sizeof (s) - 1 : strlen (s)))
+
+/* Push a new element to an array of "sact_var" structures.
+ *
+ * "vars" is the array to extend. "num_vars" (of type
(size_t *)) points to the
+ * number of elements that the array, on input, contains; (*num_vars) is
+ * increased by one on output. "prefix" and "value" serve
as the values for
+ * setting the fields in the new element. "ofs" (of type (size_t *))
may be
+ * NULL; if it isn't, then on output, (*ofs) is set to the input value of
+ * (*num_vars): the offset of the just-pushed element.
+ *
+ * Avoid arguments with side-effects here as well.
+ */
+#define SACT_VAR_PUSH(vars, num_vars, prefix, value, ofs) \
+ SACT_VAR_PUSH1 ((vars), (num_vars), (prefix), (value), (ofs), \
+ NBDKIT_UNIQUE_NAME (_ofs))
+#define SACT_VAR_PUSH1(vars, num_vars, prefix, value, ofs, ofs1) \
+ do { \
+ size_t *ofs1; \
+ \
+ assert (*(num_vars) < ARRAY_SIZE (vars));
\
+ ofs1 = (ofs); \
+ if (ofs1 != NULL) \
+ *ofs1 = *(num_vars); \
+ (vars)[(*(num_vars))++] = (struct sact_var){ (prefix), STRLEN1 (prefix), \
+ (value), STRLEN1 (value) }; \
+ } while (0)
extern char **environ;
-/* Prepare environment for calling execvp when doing systemd socket
- * activation. Takes the current environment and copies it. Removes
- * any existing LISTEN_PID or LISTEN_FDS and replaces them with new
- * variables. env[0] is "LISTEN_PID=..." which is filled in by
- * CONNECT_SA.START, and env[1] is "LISTEN_FDS=1".
+/* Prepare environment for calling execvp when doing systemd socket activation.
+ * Takes the current environment and copies it. Removes any existing socket
+ * activation variables and replaces them with new ones. Variables in
"sact_var"
+ * will be placed at the front of "env", preserving the order from
"sact_var".
*/
static int
-prepare_socket_activation_environment (string_vector *env)
+prepare_socket_activation_environment (string_vector *env,
+ const struct sact_var *sact_var,
+ size_t num_vars)
{
- char *p;
+ const struct sact_var *var_end;
+ char *new_var;
+ const struct sact_var *var;
size_t i;
*env = (string_vector)empty_vector;
- /* Reserve slots env[0] and env[1]. */
- p = strdup ("LISTEN_PID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
- if (p == NULL)
- goto err;
- if (string_vector_append (env, p) == -1) {
- free (p);
- goto err;
- }
- p = strdup ("LISTEN_FDS=1");
- if (p == NULL)
- goto err;
- if (string_vector_append (env, p) == -1) {
- free (p);
- goto err;
+ /* Set the exclusive limit for loops over "sact_var". */
+ var_end = sact_var + num_vars;
+
+ /* New environment variable being constructed for "env". */
+ new_var = NULL;
+
+ /* Copy "sact_var" to the front of "env". */
+ for (var = sact_var; var < var_end; ++var) {
+ size_t new_var_size;
+ char *p;
+
+ /* Calculate the size of the "NAME=value" string. */
+ if (ADD_OVERFLOW (var->prefix_len, var->value_len, &new_var_size)
||
+ ADD_OVERFLOW (new_var_size, 1u, &new_var_size)) {
+ errno = EOVERFLOW;
+ goto err;
+ }
+
+ /* Allocate and format "NAME=value". */
+ new_var = malloc (new_var_size);
+ if (new_var == NULL)
+ goto err;
+ p = new_var;
+
+ memcpy (p, var->prefix, var->prefix_len);
+ p += var->prefix_len;
+
+ memcpy (p, var->value, var->value_len);
+ p += var->value_len;
+
+ *p++ = '\0';
+
+ /* Push "NAME=value" to the vector. */
+ if (string_vector_append (env, new_var) == -1)
+ goto err;
+ /* Ownership transferred. */
+ new_var = NULL;
}
- /* Append the current environment, but remove LISTEN_PID, LISTEN_FDS. */
+ /* Append the current environment to "env", but remove
"sact_var". */
for (i = 0; environ[i] != NULL; ++i) {
- if (strncmp (environ[i], "LISTEN_PID=", PREFIX_LENGTH) != 0
&&
- strncmp (environ[i], "LISTEN_FDS=", PREFIX_LENGTH) != 0) {
- char *copy = strdup (environ[i]);
- if (copy == NULL)
- goto err;
- if (string_vector_append (env, copy) == -1) {
- free (copy);
- goto err;
- }
+ for (var = sact_var; var < var_end; ++var) {
+ if (strncmp (environ[i], var->prefix, var->prefix_len) == 0)
+ break;
}
+ /* Drop known socket activation variable from the current environment. */
+ if (var < var_end)
+ continue;
+
+ new_var = strdup (environ[i]);
+ if (new_var == NULL)
+ goto err;
+
+ if (string_vector_append (env, new_var) == -1)
+ goto err;
+ /* Ownership transferred. */
+ new_var = NULL;
}
/* The environ must be NULL-terminated. */
if (string_vector_append (env, NULL) == -1)
goto err;
return 0;
err:
set_error (errno, "malloc");
+ free (new_var);
string_vector_empty (env);
return -1;
}
STATE_MACHINE {
CONNECT_SA.START:
enum state next;
char *tmpdir;
char *sockpath;
int s;
struct sockaddr_un addr;
struct execvpe execvpe_ctx;
+ size_t num_vars;
+ struct sact_var sact_var[2];
+ size_t pid_ofs;
string_vector env;
pid_t pid;
assert (!h->sock);
assert (h->argv.ptr);
assert (h->argv.ptr[0]);
@@ -153,13 +237,18 @@ CONNECT_SA.START:
if (nbd_internal_execvpe_init (&execvpe_ctx, h->argv.ptr[0],
h->argv.len) = -1) {
set_error (errno, "nbd_internal_execvpe_init");
goto unlink_sockpath;
}
- if (prepare_socket_activation_environment (&env) == -1)
+ num_vars = 0;
+ SACT_VAR_PUSH (sact_var, &num_vars,
+ "LISTEN_PID=",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", &pid_ofs);
+ SACT_VAR_PUSH (sact_var, &num_vars,
+ "LISTEN_FDS=", "1", NULL);
+ if (prepare_socket_activation_environment (&env, sact_var, num_vars) ==
-1)
/* prepare_socket_activation_environment() calls set_error() internally */
goto uninit_execvpe;
pid = fork ();
if (pid == -1) {
set_error (errno, "fork");
@@ -193,13 +282,14 @@ CONNECT_SA.START:
}
}
char buf[32];
const char *v nbd_internal_fork_safe_itoa ((long) getpid (), buf,
sizeof buf);
- strcpy (&env.ptr[0][PREFIX_LENGTH], v);
+ NBD_INTERNAL_FORK_SAFE_ASSERT (strlen (v) <=
sact_var[pid_ofs].value_len);
+ strcpy (env.ptr[pid_ofs] + sact_var[pid_ofs].prefix_len, v);
/* Restore SIGPIPE back to SIG_DFL. */
if (signal (SIGPIPE, SIG_DFL) == SIG_ERR) {
nbd_internal_fork_safe_perror ("signal");
_exit (126);
}
Laszlo Ersek
2023-Mar-25 11:39 UTC
[Libguestfs] [libnbd PATCH v5 2/4] common/include: Copy ascii-ctype functions from nbdkit
From: "Richard W.M. Jones" <rjones at redhat.com>
[Laszlo's note: "ascii-ctype.h" and "test-ascii-ctype.c"
match those of
nbdkit at commit 45b72f5bd8fc. Originally posted by Rich at
<https://listman.redhat.com/archives/libguestfs/2023-January/030559.html>.]
Message-Id: <20230130225521.1771496-3-rjones at redhat.com>
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
Reviewed-by: Richard W.M. Jones <rjones at redhat.com>
---
Notes:
v5:
- no change
v4:
- pick up Rich's R-b
- refresh patch and commit message against nbdkit 45b72f5bd8fc
common/include/ascii-ctype.h | 75 +++++++++++++++++
common/include/Makefile.am | 6 ++
common/include/test-ascii-ctype.c | 88 ++++++++++++++++++++
.gitignore | 1 +
4 files changed, 170 insertions(+)
diff --git a/common/include/ascii-ctype.h b/common/include/ascii-ctype.h
new file mode 100644
index 000000000000..191a0f27e19d
--- /dev/null
+++ b/common/include/ascii-ctype.h
@@ -0,0 +1,75 @@
+/* nbdkit
+ * Copyright Red Hat
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS
IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Normal ctype functions are affected by the current locale. For
+ * example isupper() might recognize ? in some but not all locales.
+ * These functions match only 7 bit ASCII characters.
+ */
+
+#ifndef NBDKIT_ASCII_CTYPE_H
+#define NBDKIT_ASCII_CTYPE_H
+
+#define ascii_isalnum(c) (ascii_isalpha (c) || ascii_isdigit (c))
+
+#define ascii_isalpha(c) \
+ (((c) >= 'a' && (c) <= 'z') || ((c) >=
'A' && (c) <= 'Z'))
+
+#define ascii_isdigit(c) \
+ ((c) >= '0' && (c) <= '9')
+
+#define ascii_isspace(c) \
+ ((c) == '\t' || (c) == '\n' || (c) == '\f' || (c) ==
'\r' || (c) == ' ')
+
+#define ascii_isupper(c) \
+ ((c) >= 'A' && (c) <= 'Z')
+
+#define ascii_islower(c) \
+ ((c) >= 'a' && (c) <= 'z')
+
+/* See also hexdigit.h */
+#define ascii_isxdigit(c) \
+ ((c) == '0' || (c) == '1' || (c) == '2' || (c) ==
'3' || (c) == '4' || \
+ (c) == '5' || (c) == '6' || (c) == '7' || (c) ==
'8' || (c) == '9' || \
+ (c) == 'a' || (c) == 'b' || (c) == 'c' ||
\
+ (c) == 'd' || (c) == 'e' || (c) == 'f' ||
\
+ (c) == 'A' || (c) == 'B' || (c) == 'C' ||
\
+ (c) == 'D' || (c) == 'E' || (c) == 'F')
+
+#define ascii_tolower(c) \
+ (ascii_isupper ((c)) ? (c) + 32 : (c))
+
+#define ascii_toupper(c) \
+ (ascii_islower ((c)) ? (c) - 32 : (c))
+
+#define ascii_isprint(c) ((c) >= 32 && (c) <= 126)
+
+#endif /* NBDKIT_ASCII_CTYPE_H */
diff --git a/common/include/Makefile.am b/common/include/Makefile.am
index 8c99f76b8e73..1acc9fa41311 100644
--- a/common/include/Makefile.am
+++ b/common/include/Makefile.am
@@ -20,6 +20,7 @@ include $(top_srcdir)/subdir-rules.mk
EXTRA_DIST = \
ansi-colours.h \
array-size.h \
+ ascii-ctype.h \
byte-swapping.h \
checked-overflow.h \
compiler-macros.h \
@@ -36,6 +37,7 @@ EXTRA_DIST = \
TESTS = \
test-array-size \
+ test-ascii-ctype \
test-byte-swapping \
test-checked-overflow \
test-isaligned \
@@ -49,6 +51,10 @@ test_array_size_SOURCES = test-array-size.c array-size.h
test_array_size_CPPFLAGS = -I$(srcdir)
test_array_size_CFLAGS = $(WARNINGS_CFLAGS)
+test_ascii_ctype_SOURCES = test-ascii-ctype.c ascii-ctype.h
+test_ascii_ctype_CPPFLAGS = -I$(srcdir)
+test_ascii_ctype_CFLAGS = $(WARNINGS_CFLAGS)
+
test_byte_swapping_SOURCES = test-byte-swapping.c byte-swapping.h
test_byte_swapping_CPPFLAGS = -I$(srcdir)
test_byte_swapping_CFLAGS = $(WARNINGS_CFLAGS)
diff --git a/common/include/test-ascii-ctype.c
b/common/include/test-ascii-ctype.c
new file mode 100644
index 000000000000..aefe1cc63640
--- /dev/null
+++ b/common/include/test-ascii-ctype.c
@@ -0,0 +1,88 @@
+/* nbdkit
+ * Copyright Red Hat
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS
IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#undef NDEBUG /* Keep test strong even for nbdkit built without assertions */
+#include <assert.h>
+
+#include "ascii-ctype.h"
+
+int
+main (void)
+{
+ assert (ascii_isspace (' '));
+ assert (ascii_isspace ('\t'));
+ assert (ascii_isspace ('\n'));
+ assert (! ascii_isspace ('a'));
+
+ assert (ascii_isalpha ('a'));
+ assert (ascii_isalpha ('Z'));
+ assert (ascii_isalpha ('z'));
+ assert (! ascii_isalpha (' '));
+ assert (! ascii_isalpha ('0'));
+ { const char *s = "?"; assert (! ascii_isalpha (s[0])); }
+ { const char *s = "?"; assert (! ascii_isalpha (s[0])); }
+
+ assert (ascii_isdigit ('0'));
+ assert (ascii_isdigit ('9'));
+ { const char *s = "?"; assert (! ascii_isdigit (s[0])); } /* U+00D8
*/
+ { const char *s = "?"; assert (! ascii_isdigit (s[0])); } /* U+FF19
*/
+
+ assert (ascii_islower ('a'));
+ assert (ascii_islower ('z'));
+ assert (! ascii_islower ('Z'));
+ { const char *s = "?"; assert (! ascii_islower (s[0])); }
+
+ assert (ascii_isupper ('A'));
+ assert (ascii_isupper ('Z'));
+ assert (! ascii_isupper ('z'));
+ { const char *s = "?"; assert (! ascii_isupper (s[0])); }
+
+ assert (ascii_tolower ('A') == 'a');
+ assert (ascii_tolower ('Z') == 'z');
+ assert (ascii_tolower ('a') == 'a');
+ assert (ascii_tolower ('z') == 'z');
+ assert (ascii_tolower ('0') == '0');
+ { const char *s = "?"; assert (ascii_tolower (s[0]) == s[0]); }
+
+ assert (ascii_toupper ('a') == 'A');
+ assert (ascii_toupper ('z') == 'Z');
+ assert (ascii_toupper ('A') == 'A');
+ assert (ascii_toupper ('Z') == 'Z');
+ assert (ascii_toupper ('0') == '0');
+ { const char *s = "?"; assert (ascii_toupper (s[0]) == s[0]); }
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/.gitignore b/.gitignore
index 9df5d9f9db4c..df4b6ca249c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,7 @@ Makefile.in
/bash-completion/nbdinfo
/bash-completion/nbdublk
/common/include/test-array-size
+/common/include/test-ascii-ctype
/common/include/test-byte-swapping
/common/include/test-checked-overflow
/common/include/test-isaligned
Laszlo Ersek
2023-Mar-25 11:39 UTC
[Libguestfs] [libnbd PATCH v5 3/4] generator: Add APIs to get/set the socket activation socket name
From: "Richard W.M. Jones" <rjones at redhat.com>
To allow us to name the socket passed down to the NBD server when
calling nbd_connect_systemd_socket_activation(3), we need to add the
field to the handle and add access functions.
[Laszlo's notes:
- Originally posted by Rich at
<https://listman.redhat.com/archives/libguestfs/2023-January/030557.html>
(Message-Id: <20230130225521.1771496-4-rjones at redhat.com>).
- I've renamed "sa_name" to "sact_name", due to
<signal.h> reserving
symbols with the "sa_" prefix. This corresponds to earlier patches
in
this series, such as 'socket activation: rename sa_(tmpdir|sockpath) to
sact_(tmpdir|sockpath)' and 'ocaml: rename "sa_u" to
"saddr_u"'.
- Restricted "set_socket_activation_name" to the "Created"
state, per
Eric's recommendation
<http://mid.mail-archive.com/n6lzl7ii5m4hu77n7rrzm3uih3tnx457e4y67rghki34b2ghh7
at 7vyini26mqko>.
- Documented that we are going to pass "unknown" if
"socket_name" is set
to the empty string
<http://mid.mail-archive.com/oqwjnjvq4phqr76yum6zo5erfrm3tvmyewr5nxru3oxklobpgp
at 4plkku7opujw>.]
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
Reviewed-by: Eric Blake <eblake at redhat.com>
---
Notes:
v5:
- restrict the set_socket_activation_name() API to the Created state
[Eric]
- Explain that not setting (or unsetting) the socket activation name
results (per update to the next patch) in the child NBD server seeing
"unknown" [Eric, Laszlo]
- update the commit message accordingly (as the commit message is still
based on Rich's original); also move the Message-Id tag (identifying
Rich's original patch) next to the mailing list archive URL
- drop Rich's R-b due to the above changes
- pick up Eric's R-b
v4:
- pick up Rich's R-b
lib/internal.h | 1 +
generator/API.ml | 50 +++++++++++++++++
lib/handle.c | 56 ++++++++++++++++++++
3 files changed, 107 insertions(+)
diff --git a/lib/internal.h b/lib/internal.h
index 35cb5e8994ee..2de8e4e5e043 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -101,6 +101,7 @@ struct nbd_handle {
_Atomic uintptr_t private_data;
char *export_name; /* Export name, never NULL. */
+ char *sact_name; /* Socket activation name, can be NULL. */
/* TLS settings. */
int tls; /* 0 = disable, 1 = enable, 2 = require */
diff --git a/generator/API.ml b/generator/API.ml
index 91e57a4c7c4f..bbb0b8a6e1ee 100644
--- a/generator/API.ml
+++ b/generator/API.ml
@@ -2036,15 +2036,63 @@ "connect_systemd_socket_activation", {
When the NBD handle is closed the server subprocess
is killed.
+
+=head3 Socket name
+
+The socket activation protocol lets you optionally give
+the socket a name. If used, the name is passed to the
+NBD server using the C<LISTEN_FDNAMES> environment
+variable. To provide a socket name, call
+L<nbd_set_socket_activation_name(3)> before calling
+the connect function.
" ^ blocking_connect_call_description;
see_also = [Link "aio_connect_systemd_socket_activation";
Link "connect_command"; Link
"kill_subprocess";
Link "set_opt_mode";
+ Link "set_socket_activation_name";
+ Link "get_socket_activation_name";
ExternalLink ("qemu-nbd", 1);
URLLink
"http://0pointer.de/blog/projects/socket-activation.html"];
example = Some "examples/open-qcow2.c";
};
+ "set_socket_activation_name", {
+ default_call with
+ args = [ String "socket_name" ]; ret = RErr;
+ permitted_states = [ Created ];
+ shortdesc = "set the socket activation name";
+ longdesc = "\
+When running an NBD server using
+L<nbd_connect_systemd_socket_activation(3)> you can optionally
+name the socket. Call this function before connecting to the
+server.
+
+Some servers such as L<qemu-storage-daemon(1)>
+can use this information to associate the socket with a name
+used on the command line, but most servers will ignore it.
+The name is passed through the C<LISTEN_FDNAMES> environment
+variable.
+
+The parameter C<socket_name> can be a short alphanumeric string.
+If it is set to the empty string (also the default when the handle
+is created) then the name C<unknown> will be seen by the server.";
+ see_also = [Link "connect_systemd_socket_activation";
+ Link "get_socket_activation_name"];
+ };
+
+ "get_socket_activation_name", {
+ default_call with
+ args = []; ret = RString;
+ shortdesc = "get the socket activation name";
+ longdesc = "\
+Return the socket name used when you call
+L<nbd_connect_systemd_socket_activation(3)> on the same
+handle. By default this will return the empty string
+meaning that no name is passed to the server.";
+ see_also = [Link "connect_systemd_socket_activation";
+ Link "set_socket_activation_name"];
+ };
+
"is_read_only", {
default_call with
args = []; ret = RBool;
@@ -3844,6 +3892,8 @@ "get_uri", {
"aio_opt_structured_reply", (1, 16);
"opt_starttls", (1, 16);
"aio_opt_starttls", (1, 16);
+ "set_socket_activation_name", (1, 16);
+ "get_socket_activation_name", (1, 16);
(* These calls are proposed for a future version of libnbd, but
* have not been added to any released version so far.
diff --git a/lib/handle.c b/lib/handle.c
index 8468b964240b..0f11bee56221 100644
--- a/lib/handle.c
+++ b/lib/handle.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/wait.h>
+#include "ascii-ctype.h"
#include "internal.h"
static void
@@ -159,6 +160,7 @@ nbd_close (struct nbd_handle *h)
waitpid (h->pid, NULL, 0);
free (h->export_name);
+ free (h->sact_name);
free (h->tls_certificates);
free (h->tls_username);
free (h->tls_psk_file);
@@ -197,6 +199,60 @@ nbd_unlocked_get_handle_name (struct nbd_handle *h)
return copy;
}
+int
+nbd_unlocked_set_socket_activation_name (struct nbd_handle *h,
+ const char *name)
+{
+ size_t i, len;
+ char *new_name;
+
+ len = strlen (name);
+
+ /* Setting it to empty string stores NULL in the handle. */
+ if (len == 0) {
+ free (h->sact_name);
+ h->sact_name = NULL;
+ return 0;
+ }
+
+ /* Check the proposed name is short and alphanumeric. */
+ if (len > 32) {
+ set_error (ENAMETOOLONG, "socket activation name should be "
+ "<= 32 characters");
+ return -1;
+ }
+ for (i = 0; i < len; ++i) {
+ if (! ascii_isalnum (name[i])) {
+ set_error (EINVAL, "socket activation name should contain "
+ "only alphanumeric ASCII characters");
+ return -1;
+ }
+ }
+
+ new_name = strdup (name);
+ if (!new_name) {
+ set_error (errno, "strdup");
+ return -1;
+ }
+
+ free (h->sact_name);
+ h->sact_name = new_name;
+ return 0;
+}
+
+char *
+nbd_unlocked_get_socket_activation_name (struct nbd_handle *h)
+{
+ char *copy = strdup (h->sact_name ? h->sact_name : "");
+
+ if (!copy) {
+ set_error (errno, "strdup");
+ return NULL;
+ }
+
+ return copy;
+}
+
uintptr_t
nbd_unlocked_set_private_data (struct nbd_handle *h, uintptr_t data)
{
Laszlo Ersek
2023-Mar-25 11:39 UTC
[Libguestfs] [libnbd PATCH v5 4/4] socket activation: set LISTEN_FDNAMES
Add LISTEN_FDNAMES to the child process's environment unconditionally.
- If the user calls nbd_set_socket_activation_name() with a non-empty
string before calling nbd_connect_systemd_socket_activation(), pass the
name down to the server through LISTEN_FDNAMES.
This is a feature addition.
- If the user doesn't call nbd_set_socket_activation_name(), or calls it
with the empty string, add "LISTEN_FDNAMES=unknown" to the
environment,
overwriting any potentially inherited LISTEN_FDNAMES variable.
This is a bugfix: otherwise we could leak a confusing LISTEN_FDNAMES to
the child process, in case the libnbd client app were itself
socket-activated. Given "LISTEN_FDS=1", the setting
"LISTEN_FDNAMES=unknown" has the same effect on the child as
removing
LISTEN_FDNAMES from the environment:
<https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html>.
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
Notes:
v5:
- push LISTEN_FDNAMES unconditionally, with value "unknown" if the
socket activation name is not set by the libnbd client application --
rewrite patch and commit message [Eric, Laszlo]
- drop Rich's R-b due to the above changes
v4:
- pick up Rich's R-b
- resolve rebase conflict near the
prepare_socket_activation_environment() call site, due to keeping
set_error() internal to that callee, in patch "socket activation:
clean up responsibilities of prep.sock.act.env.()"
generator/states-connect-socket-activation.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/generator/states-connect-socket-activation.c
b/generator/states-connect-socket-activation.c
index 10ccc3119299..b57d5d0075b0 100644
--- a/generator/states-connect-socket-activation.c
+++ b/generator/states-connect-socket-activation.c
@@ -185,7 +185,7 @@ CONNECT_SA.START:
struct sockaddr_un addr;
struct execvpe execvpe_ctx;
size_t num_vars;
- struct sact_var sact_var[2];
+ struct sact_var sact_var[3];
size_t pid_ofs;
string_vector env;
pid_t pid;
@@ -245,6 +245,16 @@ CONNECT_SA.START:
"LISTEN_PID=",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", &pid_ofs);
SACT_VAR_PUSH (sact_var, &num_vars,
"LISTEN_FDS=", "1", NULL);
+ /* Push LISTEN_FDNAMES unconditionally. This ensures we overwrite any
+ * inherited LISTEN_FDNAMES. If "h->sact_name" is NULL, then
push
+ * "LISTEN_FDNAMES=unknown"; it will have the same effect on the
child process
+ * as unsetting LISTEN_FDNAMES would (with LISTEN_FDS being set to 1):
+ *
<https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html>.
+ */
+ SACT_VAR_PUSH (sact_var, &num_vars,
+ "LISTEN_FDNAMES=",
+ h->sact_name == NULL ? "unknown" :
h->sact_name,
+ NULL);
if (prepare_socket_activation_environment (&env, sact_var, num_vars) ==
-1)
/* prepare_socket_activation_environment() calls set_error() internally */
goto uninit_execvpe;
Possibly Parallel Threads
- [libnbd PATCH v3 00/19] pass LISTEN_FDNAMES with systemd socket activation
- [libnbd PATCH v3 19/19] socket activation: set LISTEN_FDNAMES
- [libnbd PATCH v3 19/19] socket activation: set LISTEN_FDNAMES
- [libnbd PATCH v3 19/19] socket activation: set LISTEN_FDNAMES
- [PATCH libnbd v2 0/4] Pass LISTEN_FDNAMES with systemd socket activation