$OpenBSD: patch-glib_gspawn_c,v 1.4 2019/11/19 21:06:48 ajacoutot Exp $

From 2d37239510ed6f2e005a1bd980ff6155438995a0 Mon Sep 17 00:00:00 2001
From: Sebastian Schwarz <seschwar@gmail.com>
Date: Sun, 15 Sep 2019 12:53:38 +0800
Subject: [PATCH] gspawn: Optimize fd closing on AIX and BSDs

From 8af823c8e5f5c164a3b0e5ac3ccdafd647c1ba2b Mon Sep 17 00:00:00 2001
From: Ting-Wei Lan <lantw@src.gnome.org>
Date: Fri, 20 Sep 2019 22:36:30 +0800
Subject: [PATCH] gspawn: Use fdwalk provided by system only when it is known to be safe

From eae72c3597276bf7cda994d5262ec0421fd4c4f1 Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Thu, 26 Sep 2019 14:10:36 +0100
Subject: [PATCH] gspawn: Rewrite some retry loops to use `while` rather than `goto`

From 1097b50c1c46c43b6b6af9aeefed447c6eb23b06 Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Thu, 26 Sep 2019 14:13:01 +0100
Subject: [PATCH] gspawn: Retry on EBUSY errors from dup2()

Index: glib/gspawn.c
--- glib/gspawn.c.orig
+++ glib/gspawn.c
@@ -47,7 +47,7 @@
 #include <sys/resource.h>
 #endif /* HAVE_SYS_RESOURCE_H */
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__DragonFly__)
 #include <sys/syscall.h>  /* for syscall and SYS_getdents64 */
 #endif
 
@@ -1125,7 +1125,27 @@ set_cloexec (void *data, gint fd)
   return 0;
 }
 
-#ifndef HAVE_FDWALK
+static gint
+sane_close (gint fd)
+{
+  gint ret;
+
+  do
+    ret = close (fd);
+  while (ret < 0 && errno == EINTR);
+
+  return ret;
+}
+
+G_GNUC_UNUSED static int
+close_func (void *data, int fd)
+{
+  if (fd >= GPOINTER_TO_INT (data))
+    (void) sane_close (fd);
+
+  return 0;
+}
+
 #ifdef __linux__
 struct linux_dirent64
 {
@@ -1165,8 +1185,21 @@ filename_to_fd (const char *p)
 #endif
 
 static int
-fdwalk (int (*cb)(void *data, int fd), void *data)
+safe_fdwalk (int (*cb)(void *data, int fd), void *data)
 {
+#if 0
+  /* Use fdwalk function provided by the system if it is known to be
+   * async-signal safe.
+   *
+   * Currently there are no operating systems known to provide a safe
+   * implementation, so this section is not used for now.
+   */
+  return fdwalk (cb, data);
+#else
+  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
+   * may be slow on non-Linux operating systems, especially on systems allowing
+   * very high number of open file descriptors.
+   */
   gint open_max;
   gint fd;
   gint res = 0;
@@ -1199,7 +1232,7 @@ fdwalk (int (*cb)(void *data, int fd), void *data)
             }
         }
 
-      close (dir_fd);
+      sane_close (dir_fd);
       return res;
     }
 
@@ -1221,18 +1254,49 @@ fdwalk (int (*cb)(void *data, int fd), void *data)
           break;
 
   return res;
+#endif
 }
+
+static void
+safe_closefrom (int lowfd)
+{
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+  /* Use closefrom function provided by the system if it is known to be
+   * async-signal safe.
+   *
+   * FreeBSD: closefrom is included in the list of async-signal safe functions
+   * found in https://man.freebsd.org/sigaction(2).
+   *
+   * OpenBSD: closefrom is not included in the list, but a direct system call
+   * should be safe to use.
+   */
+  (void) closefrom (lowfd);
+#elif defined(__DragonFly__)
+  /* It is unclear whether closefrom function included in DragonFlyBSD libc_r
+   * is safe to use because it calls a lot of library functions. It is also
+   * unclear whether libc_r itself is still being used. Therefore, we do a
+   * direct system call here ourselves to avoid possible issues.
+   */
+  (void) syscall (SYS_closefrom, lowfd);
+#elif defined(F_CLOSEM)
+  /* NetBSD and AIX have a special fcntl command which does the same thing as
+   * closefrom. NetBSD also includes closefrom function, which seems to be a
+   * simple wrapper of the fcntl command.
+   */
+  (void) fcntl (lowfd, F_CLOSEM);
+#else
+  (void) safe_fdwalk (close_func, GINT_TO_POINTER (lowfd));
 #endif
+}
 
 static gint
 sane_dup2 (gint fd1, gint fd2)
 {
   gint ret;
 
- retry:
-  ret = dup2 (fd1, fd2);
-  if (ret < 0 && errno == EINTR)
-    goto retry;
+  do
+    ret = dup2 (fd1, fd2);
+  while (ret < 0 && (errno == EINTR || errno == EBUSY));
 
   return ret;
 }
@@ -1242,10 +1306,9 @@ sane_open (const char *path, gint mode)
 {
   gint ret;
 
- retry:
-  ret = open (path, mode);
-  if (ret < 0 && errno == EINTR)
-    goto retry;
+  do
+    ret = open (path, mode);
+  while (ret < 0 && errno == EINTR);
 
   return ret;
 }
@@ -1279,21 +1342,6 @@ do_exec (gint                  child_err_report_fd,
   if (working_directory && chdir (working_directory) < 0)
     write_err_and_exit (child_err_report_fd,
                         CHILD_CHDIR_FAILED);
-
-  /* Close all file descriptors but stdin stdout and stderr as
-   * soon as we exec. Note that this includes
-   * child_err_report_fd, which keeps the parent from blocking
-   * forever on the other end of that pipe.
-   */
-  if (close_descriptors)
-    {
-      fdwalk (set_cloexec, GINT_TO_POINTER(3));
-    }
-  else
-    {
-      /* We need to do child_err_report_fd anyway */
-      set_cloexec (GINT_TO_POINTER(0), child_err_report_fd);
-    }
   
   /* Redirect pipes as required */
   
@@ -1349,6 +1397,31 @@ do_exec (gint                  child_err_report_fd,
       gint write_null = sane_open ("/dev/null", O_WRONLY);
       sane_dup2 (write_null, 2);
       close_and_invalidate (&write_null);
+    }
+
+  /* Close all file descriptors but stdin, stdout and stderr
+   * before we exec. Note that this includes
+   * child_err_report_fd, which keeps the parent from blocking
+   * forever on the other end of that pipe.
+   */
+  if (close_descriptors)
+    {
+      if (child_setup == NULL)
+        {
+          sane_dup2 (child_err_report_fd, 3);
+          set_cloexec (GINT_TO_POINTER (0), 3);
+          safe_closefrom (4);
+          child_err_report_fd = 3;
+        }
+      else
+        {
+          safe_fdwalk (set_cloexec, GINT_TO_POINTER (3));
+        }
+    }
+  else
+    {
+      /* We need to do child_err_report_fd anyway */
+      set_cloexec (GINT_TO_POINTER (0), child_err_report_fd);
     }
   
   /* Call user function just before we exec */
