gnokii-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 7/8] Use posix_spawn to run external scripts


From: Ladislav Michl
Subject: [PATCH v2 7/8] Use posix_spawn to run external scripts
Date: Tue, 4 Dec 2018 22:27:46 +0100
User-agent: Mutt/1.10.1 (2018-07-13)

posix_spawn specification dates back to last century and its
implementation is mature enough in all systems we do support.
Thus use it instead of current fork and exec in hope it will
save us some resources.
---
 CHANGES:
 -v2: Drop unneeded changes, split patch

 common/Makefile.am        |   7 ++-
 common/device.c           |  59 +--------------------
 common/posixscript.c      | 108 ++++++++++++++++++++++++++++++++++++++
 configure.ac              |   2 +
 include/gnokii-internal.h |   9 ++++
 5 files changed, 127 insertions(+), 58 deletions(-)
 create mode 100644 common/posixscript.c

diff --git a/common/Makefile.am b/common/Makefile.am
index 5e6be928..21dee3fd 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -6,6 +6,10 @@ else
 DATA_DIR = data
 endif
 
+if HAVE_POSIX_SPAWN
+DEVICE_SCRIPT = posixscript.c
+endif
+
 lib_LTLIBRARIES = libgnokii.la
 
 SUBDIRS = phones \
@@ -51,7 +55,8 @@ libgnokii_la_SOURCES = \
        snprintf.c \
        localcharset.c \
        map.c \
-       gsm-auth.c
+       gsm-auth.c \
+       $(DEVICE_SCRIPT)
 
 libgnokii_la_LIBADD = \
        $(top_builddir)/common/phones/libPHONES.la \
diff --git a/common/device.c b/common/device.c
index 7b6a901c..a74b9f95 100644
--- a/common/device.c
+++ b/common/device.c
@@ -27,66 +27,11 @@
 #include "devices/tcp.h"
 #include "devices/tekram.h"
 
-#include <errno.h>
-#include <sys/wait.h>
-
 GNOKII_API int device_getfd(struct gn_statemachine *state)
 {
        return state->device.fd;
 }
 
-/* Script handling: */
-static void device_script_cfgfunc(const char *section, const char *key, const 
char *value)
-{
-       setenv(key, value, 1); /* errors ignored */
-}
-
-int device_script(int fd, const char *section, struct gn_statemachine *state)
-{
-       pid_t pid;
-       const char *scriptname;
-       int status;
-
-       if (!strcmp(section, "connect_script"))
-               scriptname = state->config.connect_script;
-       else
-               scriptname = state->config.disconnect_script;
-       if (scriptname[0] == '\0')
-               return 0;
-
-       errno = 0;
-       switch ((pid = fork())) {
-       case -1:
-               fprintf(stderr, _("device_script(\"%s\"): fork() failure: 
%s!\n"), scriptname, strerror(errno));
-               return -1;
-
-       case 0: /* child */
-               cfg_foreach(section, device_script_cfgfunc);
-               errno = 0;
-               if (dup2(fd, 0) != 0 || dup2(fd, 1) != 1 || close(fd)) {
-                       fprintf(stderr, _("device_script(\"%s\"): file 
descriptor preparation failure: %s\n"), scriptname, strerror(errno));
-                       _exit(-1);
-               }
-               /* FIXME: close all open descriptors - how to track them?
-                */
-               execl("/bin/sh", "sh", "-c", scriptname, NULL);
-               fprintf(stderr, _("device_script(\"%s\"): script execution 
failure: %s\n"), scriptname, strerror(errno));
-               _exit(-1);
-               /* NOTREACHED */
-
-       default:
-               if (pid == waitpid(pid, &status, 0 /* options */) && 
WIFEXITED(status) && !WEXITSTATUS(status))
-                       return 0;
-               fprintf(stderr, _("device_script(\"%s\"): child script 
execution failure: %s, exit code=%d\n"), scriptname,
-                       (WIFEXITED(status) ? _("normal exit") : _("abnormal 
exit")),
-                       (WIFEXITED(status) ? WEXITSTATUS(status) : -1));
-               errno = EIO;
-               return -1;
-
-       }
-       /* NOTREACHED */
-}
-
 int device_open(const char *file, int with_odd_parity, int with_async,
                int with_hw_handshake, gn_connection_type device_type,
                struct gn_statemachine *state)
@@ -131,7 +76,7 @@ int device_open(const char *file, int with_odd_parity, int 
with_async,
        /*
         * handle config file connect_script:
         */
-       if (device_script(state->device.fd, "connect_script", state) == -1) {
+       if (device_script(state->device.fd, 1, state)) {
                dprintf("gnokii open device: connect_script failure\n");
                device_close(state);
                return 0;
@@ -147,7 +92,7 @@ void device_close(struct gn_statemachine *state)
        /*
         * handle config file disconnect_script:
         */
-       if (device_script(state->device.fd, "disconnect_script", state) == -1)
+       if (device_script(state->device.fd, 0, state))
                dprintf("gnokii device close: disconnect_script failure\n");
 
        switch (state->device.type) {
diff --git a/common/posixscript.c b/common/posixscript.c
new file mode 100644
index 00000000..272eab42
--- /dev/null
+++ b/common/posixscript.c
@@ -0,0 +1,108 @@
+/*
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for the mobile phones.
+
+  This file is part of gnokii.
+
+  Copyright (C) 2018       Ladislav Michl
+
+*/
+
+#include "config.h"
+#include "compat.h"
+#include "cfgreader.h"
+#include "misc.h"
+#include "gnokii.h"
+#include "gnokii-internal.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define cfg_foreach_entry(section, header, entry) \
+       for (h = gn_cfg_info; header != NULL; header = header->next) \
+               if (strcmp(section, h->section) == 0) \
+                       for (entry = h->entries; entry != NULL; entry = 
entry->next)
+
+int device_script(int fd, int connect, struct gn_statemachine *state)
+{
+       const char *scriptname, *section;
+       char **envp, *env;
+       struct gn_cfg_entry *e;
+       struct gn_cfg_header *h;
+       posix_spawn_file_actions_t fa;
+       pid_t pid;
+       int cnt, ret, status;
+       size_t len;
+
+       if (connect) {
+               scriptname = state->config.connect_script;
+               section = "connect_script";
+       } else {
+               scriptname = state->config.disconnect_script;
+               section = "disconnect_script";
+       }
+       if (scriptname[0] == '\0')
+               return 0;
+
+       cnt = 0; len = 0; ret = -1;
+       h = gn_cfg_info;
+       cfg_foreach_entry(section, h, e) {
+               len += strlen(e->key);
+               len += strlen(e->value);
+               len += 1 + 1 + sizeof(char *);
+               cnt++;
+       }
+
+       env = malloc(len + sizeof(char *));
+       if (!env) {
+               fprintf(stderr, _("device_script(\"%s\"): out of memory\n"), 
scriptname);
+               goto out;
+       }
+
+       envp = &env;
+       env += sizeof(char *) * (cnt + 1);
+       h = gn_cfg_info;
+       cfg_foreach_entry(section, h, e) {
+               *envp++ = &env;
+               env += snprintf(env, "%s=%s", e->key, e->value);
+               env++;
+       }
+       envp = NULL;
+
+       if (posix_spawn_file_actions_init(&fa)) {
+               fprintf(stderr, _("device_script(\"%s\"): file descriptor 
preparation failure: %s\n"),
+                       scriptname, strerror(errno));
+               goto out_free;
+       }
+       if (posix_spawn_file_actions_adddup2(&fa, fd, STDIN_FILENO) ||
+           posix_spawn_file_actions_adddup2(&fa, fd, STDOUT_FILENO) ||
+           posix_spawn_file_actions_addclose(&fa, fd)) {
+               fprintf(stderr, _("device_script(\"%s\"): file descriptor 
preparation failure: %s\n"),
+                       scriptname, strerror(errno));
+               goto out_destroy;
+       }
+       if (posix_spawn(&pid, scriptname, &fa, NULL, NULL, envp)) {
+               fprintf(stderr, _("device_script(\"%s\"): script execution 
failure: %s\n"),
+                       scriptname, strerror(errno));
+               goto out_destroy;
+       }
+
+       if (pid != waitpid(pid, &status, 0) && WIFEXITED(status) && 
!WEXITSTATUS(status)) {
+               fprintf(stderr, _("device_script(\"%s\"): child script 
execution failure: %s, exit code=%d\n"), scriptname,
+                       (WIFEXITED(status) ? _("normal exit") : _("abnormal 
exit")),
+                       (WIFEXITED(status) ? WEXITSTATUS(status) : -1));
+       } else {
+               ret = 0;
+       }
+
+out_destroy:
+       posix_spawn_file_actions_destroy(&fa);
+out_free:
+       free(env);
+out:
+       return ret;
+}
diff --git a/configure.ac b/configure.ac
index b427c393..49c21fd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -927,6 +927,8 @@ AC_CHECK_FUNCS(mktime timegm gettimeofday select poll 
wcrtomb)
 AC_CHECK_FUNCS(strchr strdup strndup strstr strtol strtok strsep)
 AC_CHECK_FUNCS(asprintf vasprintf snprintf vsnprintf getpass setenv)
 AC_CHECK_FUNCS(getaddrinfo)
+AC_CHECK_FUNCS(posix_spawn)
+AM_CONDITIONAL([HAVE_POSIX_SPAWN], [test "x$ac_cv_func_posix_spawn" = xyes])
 AC_CACHE_CHECK(for ISO C99 compliant snprintf,ac_cv_func_snprintf_c99,
        [AC_TRY_RUN([
 #include <stdio.h>
diff --git a/include/gnokii-internal.h b/include/gnokii-internal.h
index c4f6d625..e02e44ea 100644
--- a/include/gnokii-internal.h
+++ b/include/gnokii-internal.h
@@ -179,4 +179,13 @@ int strip_slashes(char *dest, const char *src, int maxlen, 
int len);
 /* authentication for at driver */
 gn_error do_auth(gn_auth_type auth_type, struct gn_statemachine *state);
 
+#ifdef HAVE_POSIX_SPAWN
+int device_script(int fd, int connect, struct gn_statemachine *state);
+#else
+static int device_script(int fd, int connect, struct gn_statemachine *state)
+{
+       return 0;
+}
+#endif
+
 #endif /* _gnokii_internal_h */
-- 
2.20.0.rc2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]