]> granicus.if.org Git - strace/commitdiff
Introduce an API for handling spawned auxiliary children
authorEugene Syromiatnikov <esyr@redhat.com>
Wed, 13 Dec 2017 12:53:17 +0000 (13:53 +0100)
committerEugene Syromyatnikov <evgsyr@gmail.com>
Wed, 27 Feb 2019 16:30:51 +0000 (17:30 +0100)
One of gdbserver backend features is to spawn a GDB server. The handling
of this child process is more or less similar to strace_popen'ed child,
so let's generalise it.

* aux_children.c: New file.
* aux_children.h: Likewise.
* Makefile.am (strace_SOURCES): Add them.

Makefile.am
aux_children.c [new file with mode: 0644]
aux_children.h [new file with mode: 0644]

index f1344e8dc70062f6339ca08dae9a0611f1e558e7..3cf76f8567b456d9d8b333de907afb0fd08b9290 100644 (file)
@@ -73,6 +73,8 @@ strace_SOURCES =      \
        aio.c           \
        alpha.c         \
        arch_defs.h     \
+       aux_children.c  \
+       aux_children.h  \
        basic_filters.c \
        bind.c          \
        bjm.c           \
diff --git a/aux_children.c b/aux_children.c
new file mode 100644 (file)
index 0000000..f3b25c3
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Auxiliary children support implementation.
+ *
+ * Copyright (c) 2017 The strace developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "defs.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "list.h"
+
+#include "aux_children.h"
+
+/* We store children as a linked list since there is not a lot of them and in
+ * order to ease removal */
+
+struct aux_child {
+       pid_t pid;
+       const struct aux_child_handlers *handlers;
+       void *signal_fn_data;
+       void *exit_notify_fn_data;
+       void *exit_wait_fn_data;
+       struct list_item list;
+};
+
+static EMPTY_LIST(children);
+
+
+void
+register_aux_child_ex(pid_t pid, const struct aux_child_handlers *h,
+                     void *signal_fn_data, void *exit_notify_fn_data,
+                      void *exit_wait_fn_data)
+{
+       static const struct aux_child_handlers default_handlers;
+
+       struct aux_child *child;
+
+       list_foreach(child, &children, list) {
+               if (child->pid == pid)
+                       error_func_msg_and_die("Duplicate auxiliary child pid");
+       }
+
+       child = xcalloc(1, sizeof(*child));
+
+       child->pid = pid;
+       child->handlers = h ?: &default_handlers;
+       child->signal_fn_data = signal_fn_data;
+       child->exit_notify_fn_data = exit_notify_fn_data;
+       child->exit_wait_fn_data = exit_wait_fn_data;
+
+       list_append(&children, &child->list);
+}
+
+void
+remove_aux_child(pid_t pid)
+{
+       struct aux_child *child;
+
+       list_foreach(child, &children, list) {
+               if (child->pid == pid) {
+                       list_remove(&child->list);
+                       free(child);
+               }
+       }
+}
+
+bool
+have_aux_children(void)
+{
+       return !list_is_empty(&children);
+}
+
+enum aux_child_sig
+aux_children_signal(pid_t pid, int status)
+{
+       struct aux_child *child;
+
+       list_foreach(child, &children, list) {
+               if (child->pid == pid) {
+                       aux_child_signal_fn sig_handler =
+                               child->handlers->signal_fn;
+
+                       if (!sig_handler)
+                               sig_handler = aux_child_sig_handler;
+
+                       return sig_handler(pid, status, child->signal_fn_data);
+               }
+       }
+
+       return ACS_NONE;
+}
+
+void
+aux_children_exit_notify(int exit_code)
+{
+       struct aux_child *cur;
+       struct aux_child *tmp;
+
+       list_foreach_safe(cur, &children, list, tmp) {
+               if (cur->handlers->exit_notify_fn)
+                       cur->handlers->exit_notify_fn(cur->pid, exit_code,
+                                                     cur->exit_notify_fn_data);
+       }
+}
+
+int
+aux_children_exit_wait(int exit_code)
+{
+       int cnt = 0;
+       struct aux_child *cur;
+       struct aux_child *tmp;
+
+       list_foreach_safe(cur, &children, list, tmp) {
+               aux_child_exit_fn wait_handler = cur->handlers->exit_wait_fn;
+
+
+               if (!wait_handler)
+                       wait_handler = aux_child_wait_handler;
+
+               if (wait_handler(cur->pid, exit_code, cur->exit_wait_fn_data)
+                   == ACR_REMOVE_ME) {
+                       list_remove(&cur->list);
+                       free(cur);
+               } else {
+                       cnt++;
+               }
+       }
+
+       return cnt;
+}
+
+enum aux_child_sig
+aux_child_sig_handler(pid_t pid, int status, void *data)
+{
+       if (!WIFSTOPPED(status))
+               remove_aux_child(pid);
+
+       return ACS_CONTINUE;
+}
+
+enum aux_child_ret
+aux_child_wait_handler(pid_t pid, int exit_code, void *data)
+{
+       while (waitpid(pid, NULL, 0) < 0 && errno == EINTR)
+               ;
+
+       return ACR_REMOVE_ME;
+}
diff --git a/aux_children.h b/aux_children.h
new file mode 100644 (file)
index 0000000..cab8a4b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Auxiliary children infrastructure support header.
+ *
+ * Copyright (c) 2017-2019 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef STRACE_AUX_CHILDREN_H
+#define STRACE_AUX_CHILDREN_H
+
+#include <stdbool.h>
+
+enum aux_child_ret {
+       ACR_NO_ACTION,
+       ACR_REMOVE_ME,
+};
+
+enum aux_child_sig {
+       ACS_NONE,
+       ACS_CONTINUE,
+       ACS_TERMINATE,
+};
+
+typedef enum aux_child_sig (*aux_child_signal_fn)(pid_t pid, int status,
+                                                 void *data);
+typedef enum aux_child_ret (*aux_child_exit_fn)(pid_t pid, int exit_code,
+                                               void *data);
+
+/**
+ * Structure containing handler functions that are called for an auxiliary
+ * child process.
+ */
+struct aux_child_handlers {
+       /**
+        * Callback that is called when an auxiliary child is signalled.
+        *
+        * It accepts pid of the auxiliary child that was signalled, status,
+        * as returned by the wait4() function, and data that is supplied in
+        * signal_fn_data argument of register_aux_child_ex function (or NULL,
+        * if the auxiliary child was registered with register_aux_child.
+        * It has to return one of the entities defined in enum aux_child_sig
+        * enumeration:
+        *  - ACR_NO_ACTION, if no action has to be performed.
+        *  - ACR_REMOVE_ME, if the auxiliary child has to be removed
+        *                   from the auxiliary children list.
+        *
+        * The default callback (that is, the function which is called when this
+        * field is set to NULL) is aux_child_sig_handler.
+        */
+       aux_child_signal_fn signal_fn;
+       /**
+        * Callback that is called when an auxiliary child has to be notified
+        * about strace's termination.
+        */
+       aux_child_exit_fn exit_notify_fn;
+       /**
+        * A callback that is used for waiting for an auxiliary child.
+        */
+       aux_child_exit_fn exit_wait_fn;
+};
+
+
+/**
+ * Register an auxiliary child process (that is, not a tracee).
+ */
+extern void register_aux_child_ex(pid_t pid, const struct aux_child_handlers *h,
+                                 void *signal_fn_data,
+                                 void *exit_notify_fn_data,
+                                 void *exit_wait_fn_data);
+/* Do not remove other children from the aux_child handlers.  */
+extern void remove_aux_child(pid_t pid);
+
+/**
+ * A shorthand for registering an aux child with default handlers.
+ *
+ * @param pid Child's pid.
+ */
+static inline void
+register_aux_child(pid_t pid)
+{
+       register_aux_child_ex(pid, NULL, NULL, NULL, NULL);
+}
+
+extern bool have_aux_children(void);
+
+extern enum aux_child_sig aux_children_signal(pid_t pid, int status);
+extern void aux_children_exit_notify(int exit_code);
+extern int aux_children_exit_wait(int exit_code);
+
+/** Default signal handler, removes child from list */
+extern enum aux_child_sig aux_child_sig_handler(pid_t pid, int status,
+                                               void *data);
+/** Default wait handler, waits for the child and then returns ACR_REMOVE_ME */
+extern enum aux_child_ret aux_child_wait_handler(pid_t pid, int exit_code,
+                                                void *data);
+
+#endif /* !STRACE_AUX_CHILDREN_H */