While debugging an ugly nbdkit / OCaml crash[1] which only happened
when nbdkit was run as a subprocess of libnbd, I needed a way to start
gdb attached to nbdkit. It's not possible to do this by running
"gdb --args nbdkit -s ..." as a command for various reasons.
My solution to this was (error checking removed for clarity):
nbd_connect_command (nbd, { "nbdkit", "-s" /* ... */});
char pid[100];
snprintf (pid, sizeof pid, "%d", nbd->pid);
if (fork () > 0) {
execl ("/usr/bin/gdb", "gdb", "../server/nbdkit", pid,
NULL);
_exit (1);
}
while (access ("/tmp/continue", F_OK) != 0) {
sleep (1);
}
/* Test case continues after the file is created. */
In this case I was fishing directly into the private nbd_handle struct
to get the pid field. However this seems like a genuine (if rare)
case for being able to get the process ID directly.
The documentation is written to be clear that this is only useful for
debugging cases, only works on some platforms, and shouldn't be relied
on more generally.
[1]
https://discuss.ocaml.org/t/free-uninitalized-data-when-calling-caml-c-th...
---
.gitignore | 1 +
generator/API.ml | 25 ++++++++++++++--
lib/handle.c | 14 +++++++++
tests/Makefile.am | 5 ++++
tests/get-subprocess-pid.c | 60 ++++++++++++++++++++++++++++++++++++++
5 files changed, 103 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
index b0ad1c65d..0d4bef92c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -258,6 +258,7 @@ Makefile.in
/tests/export-name
/tests/functions.sh
/tests/get-size
+/tests/get-subprocess-pid
/tests/get-version
/tests/is-not-rotational-flag
/tests/is-rotational-flag
diff --git a/generator/API.ml b/generator/API.ml
index 2f25dc4ec..386b64ef6 100644
--- a/generator/API.ml
+++ b/generator/API.ml
@@ -2239,7 +2239,9 @@ "connect_command", {
" ^ blocking_connect_call_description;
see_also = [Link "aio_connect_command";
Link "connect_systemd_socket_activation";
- Link "kill_subprocess"; Link "set_opt_mode"];
+ Link "kill_subprocess";
+ Link "get_subprocess_pid";
+ Link "set_opt_mode"];
example = Some "examples/connect-command.c";
};
@@ -2290,6 +2292,7 @@ "connect_systemd_socket_activation", {
" ^ blocking_connect_call_description;
see_also = [Link "aio_connect_systemd_socket_activation";
Link "connect_command"; Link "kill_subprocess";
+ Link "get_subprocess_pid";
Link "set_opt_mode";
Link "set_socket_activation_name";
Link "get_socket_activation_name";
@@ -4209,7 +4212,24 @@ "kill_subprocess", {
The C<signum> parameter is the optional signal number to send
(see L<signal(7)>). If C<signum> is C<0> then C<SIGTERM> is
sent.";
- see_also = [ExternalLink ("signal", 7); Link "connect_command"];
+ see_also = [ExternalLink ("signal", 7); Link "connect_command";
+ Link "get_subprocess_pid"];
+ };
+
+ "get_subprocess_pid", {
+ default_call with
+ args = []; ret = RInt64;
+ shortdesc = "get the process ID of the subprocess";
+ longdesc = "\
+For connections which create a subprocess such as
+L<nbd_connect_command(3)>, this returns the process ID (PID)
+of the subprocess. This is only supported on some platforms.
+
+This is mainly useful in debugging cases. For example we used
+this to attach L<gdb(1)> to an nbdkit subprocess that was crashing
+inside a plugin.";
+ see_also = [ExternalLink ("fork", 2); Link "connect_command";
+ Link "kill_subprocess"];
};
"supports_tls", {
@@ -4470,6 +4490,7 @@ let first_version =
"set_tls_hostname", (1, 22);
"get_tls_hostname", (1, 22);
"is_uri", (1, 22);
+ "get_subprocess_pid", (1, 22);
(* 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 196018b52..a263cc4c6 100644
--- a/lib/handle.c
+++ b/lib/handle.c
@@ -590,6 +590,20 @@ nbd_unlocked_kill_subprocess (struct nbd_handle *h, int signum)
return 0;
}
+int64_t
+nbd_unlocked_get_subprocess_pid (struct nbd_handle *h)
+{
+ if (h->pid == -1) {
+ set_error (ESRCH, "no subprocess exists");
+ return -1;
+ }
+
+ /* Probably this is always > 0, but we make no guarantees as this
+ * function is only useful for debugging.
+ */
+ return h->pid;
+}
+
/* NB: is_locked = false, may_set_error = false. */
int
nbd_unlocked_supports_tls (struct nbd_handle *h)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cb5ba2d91..8aca4c7de 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -187,6 +187,7 @@ check_PROGRAMS += \
errors-server-unknown-flags \
errors-client-zerosize \
errors-server-zerosize \
+ get-subprocess-pid \
server-death \
shutdown-flags \
shutdown-opt-mode \
@@ -262,6 +263,7 @@ TESTS += \
errors-server-unknown-flags \
errors-client-zerosize \
errors-server-zerosize \
+ get-subprocess-pid \
server-death \
shutdown-flags \
shutdown-opt-mode \
@@ -397,6 +399,9 @@ errors_client_zerosize_LDADD = $(top_builddir)/lib/libnbd.la
errors_server_zerosize_SOURCES = errors-server-zerosize.c
errors_server_zerosize_LDADD = $(top_builddir)/lib/libnbd.la
+get_subprocess_pid_SOURCES = get-subprocess-pid.c
+get_subprocess_pid_LDADD = $(top_builddir)/lib/libnbd.la
+
server_death_SOURCES = server-death.c
server_death_LDADD = $(top_builddir)/lib/libnbd.la
diff --git a/tests/get-subprocess-pid.c b/tests/get-subprocess-pid.c
new file mode 100644
index 000000000..73e2a656b
--- /dev/null
+++ b/tests/get-subprocess-pid.c
@@ -0,0 +1,60 @@
+/* NBD client library in userspace
+ * Copyright Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Check nbd_get_subprocess_pid looks sensible. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <libnbd.h>
+
+int
+main (int argc, char *argv[])
+{
+ struct nbd_handle *nbd;
+ int64_t pid;
+ const char *cmd[] = { NBDKIT, "-s", "--exit-with-parent",
+ "memory", "size=1m", NULL };
+
+ nbd = nbd_create ();
+ if (nbd == NULL) {
+ fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+
+ if (nbd_connect_command (nbd, (char **)cmd) == -1) {
+ fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+
+ pid = nbd_get_subprocess_pid (nbd);
+ if (pid == -1) {
+ fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+
+ printf ("pid = %" PRIi64 "\n", pid);
+
+ nbd_close (nbd);
+ exit (EXIT_SUCCESS);
+}
--
2.46.0