<p>laforge has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/16619">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Introduce helper functions for safe fork+exec of processes<br><br>In some situations, we want to execute an external shell command<br>in a non-blocking way.  Similar to 'system', but without waiting for<br>the child to complete.  We also want to close all file descriptors<br>ahead of the exec() and filter + modify the environment.<br><br>Change-Id: Ib24ac8a083db32e55402ce496a5eabd8749cc888<br>Related: OS#4332<br>---<br>M src/Makefile.am<br>A src/exec.c<br>M tests/Makefile.am<br>A tests/exec/exec_test.c<br>A tests/exec/exec_test.err<br>A tests/exec/exec_test.ok<br>M tests/testsuite.at<br>7 files changed, 413 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/19/16619/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index 9943281..eeb3f7d 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -27,6 +27,7 @@</span><br><span>                      tdef.c \</span><br><span>                     sockaddr_str.c \</span><br><span>                     use_count.c \</span><br><span style="color: hsl(120, 100%, 40%);">+                         exec.c \</span><br><span>                     $(NULL)</span><br><span> </span><br><span> if HAVE_SSSE3</span><br><span>diff --git a/src/exec.c b/src/exec.c</span><br><span>new file mode 100644</span><br><span>index 0000000..e0d97c3</span><br><span>--- /dev/null</span><br><span>+++ b/src/exec.c</span><br><span>@@ -0,0 +1,198 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2019 by Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <dirent.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/types.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/exec.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* list of environment variables that we pass (if they exist) to the sub-process/script */</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *environment_whitelist[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ "USER", "LOGNAME", "HOME",</span><br><span style="color: hsl(120, 100%, 40%);">+      "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",</span><br><span style="color: hsl(120, 100%, 40%);">+    "PATH",</span><br><span style="color: hsl(120, 100%, 40%);">+     "PWD",</span><br><span style="color: hsl(120, 100%, 40%);">+      "SHELL",</span><br><span style="color: hsl(120, 100%, 40%);">+    "TERM",</span><br><span style="color: hsl(120, 100%, 40%);">+     "TMPDIR",</span><br><span style="color: hsl(120, 100%, 40%);">+   "LD_LIBRARY_PATH",</span><br><span style="color: hsl(120, 100%, 40%);">+  "LD_PRELOAD",</span><br><span style="color: hsl(120, 100%, 40%);">+       "POSIXLY_CORRECT",</span><br><span style="color: hsl(120, 100%, 40%);">+  "HOSTALIASES",</span><br><span style="color: hsl(120, 100%, 40%);">+      "TZ", "TZDIR",</span><br><span style="color: hsl(120, 100%, 40%);">+    "TERMCAP",</span><br><span style="color: hsl(120, 100%, 40%);">+  "COLUMNS", "LINES",</span><br><span style="color: hsl(120, 100%, 40%);">+       NULL</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static bool str_in_list(const char **list, const char *key)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const char **ent;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (ent = list; *ent; ent++) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!strcmp(*ent, key))</span><br><span style="color: hsl(120, 100%, 40%);">+                       return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! generate a filtered version of the process environment containing only entries of whitelisted keys.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \oaram[out] out caller-allocated array of pointers for the generated output</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] out_len size of out (number of pointers)</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] in input environment (NULL-terminated list of pointers like **environment)</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] whitelist whitelist of permitted keys in environment (like **environment)</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns number of entries filled in 'out'; negtive on error */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_environment_filter(char **out, size_t out_len, char **in, const char **whitelist)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        char tmp[256];</span><br><span style="color: hsl(120, 100%, 40%);">+        char **ent;</span><br><span style="color: hsl(120, 100%, 40%);">+   size_t out_used = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* invalid calls */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!out || out_len == 0 || !whitelist)</span><br><span style="color: hsl(120, 100%, 40%);">+               return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* legal, but unusual: no input to filter should generate empty, terminated out */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!in) {</span><br><span style="color: hsl(120, 100%, 40%);">+            out[0] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* iterate over input entries */</span><br><span style="color: hsl(120, 100%, 40%);">+      for (ent = in; *ent; ent++) {</span><br><span style="color: hsl(120, 100%, 40%);">+         char *eq = strchr(*ent, '=');</span><br><span style="color: hsl(120, 100%, 40%);">+         unsigned long eq_pos;</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!eq) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* no '=' in string, skip it */</span><br><span style="color: hsl(120, 100%, 40%);">+                       continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             eq_pos = eq - *ent;</span><br><span style="color: hsl(120, 100%, 40%);">+           if (eq_pos >= ARRAY_SIZE(tmp))</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             strncpy(tmp, *ent, eq_pos);</span><br><span style="color: hsl(120, 100%, 40%);">+           tmp[eq_pos] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+           if (str_in_list(whitelist, tmp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (out_used == out_len-1)</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        /* append to output */</span><br><span style="color: hsl(120, 100%, 40%);">+                        out[out_used++] = *ent;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(out_used < out_len);</span><br><span style="color: hsl(120, 100%, 40%);">+   out[out_used++] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       return out_used;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! append one environment to another; only copying pointers, not actual strings.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \oaram[out] out caller-allocated array of pointers for the generated output</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] out_len size of out (number of pointers)</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] in input environment (NULL-terminated list of pointers like **environment)</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns number of entries filled in 'out'; negative on error */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_environment_append(char **out, size_t out_len, char **in)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t out_used = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!out || out_len == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* seek to end of existing output */</span><br><span style="color: hsl(120, 100%, 40%);">+  for (out_used = 0; out[out_used]; out_used++) {}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!in) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (out_used == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                    out[out_used++] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+               return out_used;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (; *in && out_used < out_len-1; in++)</span><br><span style="color: hsl(120, 100%, 40%);">+          out[out_used++] = *in;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(out_used < out_len);</span><br><span style="color: hsl(120, 100%, 40%);">+   out[out_used++] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return out_used;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Iterate over files in /proc/self/fd and close all above lst_fd_to_keep */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_close_all_fds_above(int last_fd_to_keep)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct dirent *ent;</span><br><span style="color: hsl(120, 100%, 40%);">+   DIR *dir;</span><br><span style="color: hsl(120, 100%, 40%);">+     int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     dir = opendir("/proc/self/fd");</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!dir) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DLGLOBAL, LOGL_ERROR, "Cannot open /proc/self/fd: %s\n", strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+             return -ENODEV;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while ((ent = readdir(dir))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                int fd = atoi(ent->d_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (fd <= last_fd_to_keep)</span><br><span style="color: hsl(120, 100%, 40%);">+                 continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (fd == dirfd(dir))</span><br><span style="color: hsl(120, 100%, 40%);">+                 continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = close(fd);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+                       LOGP(DLGLOBAL, LOGL_ERROR, "Error closing fd=%d: %s\n", fd, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     closedir(dir);</span><br><span style="color: hsl(120, 100%, 40%);">+        return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Seems like POSIX has no header file for this? */</span><br><span style="color: hsl(120, 100%, 40%);">+extern char **environ;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* mimic the behavior of system(3), but don't wait for completion */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_system_nowait(const char *command, char **addl_env)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = fork();</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rc == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                /* we are in the child */</span><br><span style="color: hsl(120, 100%, 40%);">+             char *new_env[1024];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                /* close all file descriptors above stdio */</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_close_all_fds_above(2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                /* build the new environment */</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_environment_filter(new_env, ARRAY_SIZE(new_env), environ, environment_whitelist);</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_environment_append(new_env, ARRAY_SIZE(new_env), addl_env);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            /* if we want to behave like system(3), we must go via the shell */</span><br><span style="color: hsl(120, 100%, 40%);">+           execle("/bin/sh", "sh", "-c", command, (char *) NULL, new_env);</span><br><span style="color: hsl(120, 100%, 40%);">+         /* only reached in case of error */</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DLGLOBAL, LOGL_ERROR, "Error executing command '%s' after fork: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 command, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+            return -EIO;</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* we are in the parent */</span><br><span style="color: hsl(120, 100%, 40%);">+            return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index 3a3ea37..498f6a8 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -34,6 +34,7 @@</span><br><span>             use_count/use_count_test                               \</span><br><span>             context/context_test                                   \</span><br><span>                  gsm0502/gsm0502_test                                      \</span><br><span style="color: hsl(120, 100%, 40%);">+                 exec/exec_test                                              \</span><br><span>             $(NULL)</span><br><span> </span><br><span> if ENABLE_MSGFILE</span><br><span>@@ -259,6 +260,9 @@</span><br><span> context_context_test_SOURCES = context/context_test.c</span><br><span> context_context_test_LDADD = $(LDADD)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+exec_exec_test_SOURCES = exec/exec_test.c</span><br><span style="color: hsl(120, 100%, 40%);">+exec_exec_test_LDADD = $(LDADD)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # The `:;' works around a Bash 3.2 bug when the output is not writeable.</span><br><span> $(srcdir)/package.m4: $(top_srcdir)/configure.ac</span><br><span>      :;{ \</span><br><span>@@ -334,6 +338,7 @@</span><br><span>       use_count/use_count_test.ok use_count/use_count_test.err \</span><br><span>           context/context_test.ok \</span><br><span>            gsm0502/gsm0502_test.ok \</span><br><span style="color: hsl(120, 100%, 40%);">+             exec/exec_test.ok \</span><br><span>          $(NULL)</span><br><span> </span><br><span> DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c</span><br><span>diff --git a/tests/exec/exec_test.c b/tests/exec/exec_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..7559360</span><br><span>--- /dev/null</span><br><span>+++ b/tests/exec/exec_test.c</span><br><span>@@ -0,0 +1,155 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/exec.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/types.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/stat.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/wait.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void env_dump(char **env)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char **ent;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (ent = env; *ent; ent++)</span><br><span style="color: hsl(120, 100%, 40%);">+          printf("\t%s\n", *ent);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_env_filter(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  char *out[256];</span><br><span style="color: hsl(120, 100%, 40%);">+       char *env_in[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            "FOO=1",</span><br><span style="color: hsl(120, 100%, 40%);">+            "BAR=2",</span><br><span style="color: hsl(120, 100%, 40%);">+            "USER=mahlzeit",</span><br><span style="color: hsl(120, 100%, 40%);">+            "BAZ=3",</span><br><span style="color: hsl(120, 100%, 40%);">+            "SHELL=/bin/sh",</span><br><span style="color: hsl(120, 100%, 40%);">+            NULL</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *filter[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+              "SHELL",</span><br><span style="color: hsl(120, 100%, 40%);">+            "USER",</span><br><span style="color: hsl(120, 100%, 40%);">+             NULL</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("\n==== osmo_environment_filter ====\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  printf("Input Environment:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     env_dump(env_in);</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("Input Whitelist:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       env_dump((char **) filter);</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = osmo_environment_filter(out, ARRAY_SIZE(out), env_in, filter);</span><br><span style="color: hsl(120, 100%, 40%);">+   printf("Output Environment (%d):\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+   env_dump(out);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(rc == 3);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("Testing for NULL out\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = osmo_environment_filter(NULL, 123, env_in, filter);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(rc < 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("Testing for zero-length out\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    rc = osmo_environment_filter(out, 0, env_in, filter);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(rc < 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("Testing for one-length out\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = osmo_environment_filter(out, 1, env_in, filter);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(rc == 1 && out[0] == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("Testing for no filter\n");</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = osmo_environment_filter(out, ARRAY_SIZE(out), env_in, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(rc < 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("Testing for no input\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = osmo_environment_filter(out, ARRAY_SIZE(out), NULL, filter);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(rc == 1 && out[0] == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("Success!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_env_append(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    char *out[256] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            "FOO=a",</span><br><span style="color: hsl(120, 100%, 40%);">+            "BAR=b",</span><br><span style="color: hsl(120, 100%, 40%);">+            "BAZ=c",</span><br><span style="color: hsl(120, 100%, 40%);">+            NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    char *add[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+               "MAHL=zeit",</span><br><span style="color: hsl(120, 100%, 40%);">+                "GSM=global",</span><br><span style="color: hsl(120, 100%, 40%);">+               "UMTS=universal",</span><br><span style="color: hsl(120, 100%, 40%);">+           "LTE=evolved",</span><br><span style="color: hsl(120, 100%, 40%);">+              NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("\n==== osmo_environment_append ====\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  printf("Input Environment:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     env_dump(out);</span><br><span style="color: hsl(120, 100%, 40%);">+        printf("Input Addition:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        env_dump(add);</span><br><span style="color: hsl(120, 100%, 40%);">+        rc = osmo_environment_append(out, ARRAY_SIZE(out), add);</span><br><span style="color: hsl(120, 100%, 40%);">+      printf("Output Environment (%d)\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+    env_dump(out);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(rc == 8);</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("Success!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_close_fd(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct stat st;</span><br><span style="color: hsl(120, 100%, 40%);">+       int fds[2];</span><br><span style="color: hsl(120, 100%, 40%);">+   int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("\n==== osmo_close_all_fds_above ====\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* create some extra fds */</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(rc == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = fstat(fds[0], &st);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(rc == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_close_all_fds_above(2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        rc = fstat(fds[0], &st);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(rc == -1 && errno == EBADF);</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = fstat(fds[1], &st);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(rc == -1 && errno == EBADF);</span><br><span style="color: hsl(120, 100%, 40%);">+      printf("Success!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_system_nowait(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *addl_env[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+          "MAHLZEIT=spaet",</span><br><span style="color: hsl(120, 100%, 40%);">+           NULL</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc, pid, i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("\n==== osmo_system_nowait ====\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       pid = osmo_system_nowait("env | grep MAHLZEIT 1>&2", addl_env);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(pid > 0);</span><br><span style="color: hsl(120, 100%, 40%);">+      for (i = 0; i < 10; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+         sleep(1);</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = waitpid(pid, NULL, WNOHANG);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (rc == pid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      printf("Success!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                       return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("ERROR: child didn't terminate within 10s\n");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  test_env_filter();</span><br><span style="color: hsl(120, 100%, 40%);">+    test_env_append();</span><br><span style="color: hsl(120, 100%, 40%);">+    test_close_fd();</span><br><span style="color: hsl(120, 100%, 40%);">+      test_system_nowait();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/exec/exec_test.err b/tests/exec/exec_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..4edc61d</span><br><span>--- /dev/null</span><br><span>+++ b/tests/exec/exec_test.err</span><br><span>@@ -0,0 +1 @@</span><br><span style="color: hsl(120, 100%, 40%);">+MAHLZEIT=spaet</span><br><span>diff --git a/tests/exec/exec_test.ok b/tests/exec/exec_test.ok</span><br><span>new file mode 100644</span><br><span>index 0000000..45a20f0</span><br><span>--- /dev/null</span><br><span>+++ b/tests/exec/exec_test.ok</span><br><span>@@ -0,0 +1,46 @@</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+==== osmo_environment_filter ====</span><br><span style="color: hsl(120, 100%, 40%);">+Input Environment:</span><br><span style="color: hsl(120, 100%, 40%);">+  FOO=1</span><br><span style="color: hsl(120, 100%, 40%);">+ BAR=2</span><br><span style="color: hsl(120, 100%, 40%);">+ USER=mahlzeit</span><br><span style="color: hsl(120, 100%, 40%);">+ BAZ=3</span><br><span style="color: hsl(120, 100%, 40%);">+ SHELL=/bin/sh</span><br><span style="color: hsl(120, 100%, 40%);">+Input Whitelist:</span><br><span style="color: hsl(120, 100%, 40%);">+       SHELL</span><br><span style="color: hsl(120, 100%, 40%);">+ USER</span><br><span style="color: hsl(120, 100%, 40%);">+Output Environment (3):</span><br><span style="color: hsl(120, 100%, 40%);">+ USER=mahlzeit</span><br><span style="color: hsl(120, 100%, 40%);">+ SHELL=/bin/sh</span><br><span style="color: hsl(120, 100%, 40%);">+Testing for NULL out</span><br><span style="color: hsl(120, 100%, 40%);">+Testing for zero-length out</span><br><span style="color: hsl(120, 100%, 40%);">+Testing for one-length out</span><br><span style="color: hsl(120, 100%, 40%);">+Testing for no filter</span><br><span style="color: hsl(120, 100%, 40%);">+Testing for no input</span><br><span style="color: hsl(120, 100%, 40%);">+Success!</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+==== osmo_environment_append ====</span><br><span style="color: hsl(120, 100%, 40%);">+Input Environment:</span><br><span style="color: hsl(120, 100%, 40%);">+  FOO=a</span><br><span style="color: hsl(120, 100%, 40%);">+ BAR=b</span><br><span style="color: hsl(120, 100%, 40%);">+ BAZ=c</span><br><span style="color: hsl(120, 100%, 40%);">+Input Addition:</span><br><span style="color: hsl(120, 100%, 40%);">+        MAHL=zeit</span><br><span style="color: hsl(120, 100%, 40%);">+     GSM=global</span><br><span style="color: hsl(120, 100%, 40%);">+    UMTS=universal</span><br><span style="color: hsl(120, 100%, 40%);">+        LTE=evolved</span><br><span style="color: hsl(120, 100%, 40%);">+Output Environment (8)</span><br><span style="color: hsl(120, 100%, 40%);">+   FOO=a</span><br><span style="color: hsl(120, 100%, 40%);">+ BAR=b</span><br><span style="color: hsl(120, 100%, 40%);">+ BAZ=c</span><br><span style="color: hsl(120, 100%, 40%);">+ MAHL=zeit</span><br><span style="color: hsl(120, 100%, 40%);">+     GSM=global</span><br><span style="color: hsl(120, 100%, 40%);">+    UMTS=universal</span><br><span style="color: hsl(120, 100%, 40%);">+        LTE=evolved</span><br><span style="color: hsl(120, 100%, 40%);">+Success!</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+==== osmo_close_all_fds_above ====</span><br><span style="color: hsl(120, 100%, 40%);">+Success!</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+==== osmo_system_nowait ====</span><br><span style="color: hsl(120, 100%, 40%);">+Success!</span><br><span>diff --git a/tests/testsuite.at b/tests/testsuite.at</span><br><span>index c231b96..cb83ab9 100644</span><br><span>--- a/tests/testsuite.at</span><br><span>+++ b/tests/testsuite.at</span><br><span>@@ -362,3 +362,10 @@</span><br><span> cat $abs_srcdir/context/context_test.ok > expout</span><br><span> AT_CHECK([$abs_top_builddir/tests/context/context_test], [0], [expout], [ignore])</span><br><span> AT_CLEANUP</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AT_SETUP([exec])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_KEYWORDS([exec])</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/exec/exec_test.ok > expout</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/exec/exec_test.err > experr</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CHECK([$abs_top_builddir/tests/exec/exec_test], [0], [expout], [experr])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CLEANUP</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/16619">change 16619</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/16619"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ib24ac8a083db32e55402ce496a5eabd8749cc888 </div>
<div style="display:none"> Gerrit-Change-Number: 16619 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>