about summary refs log tree commit diff
path: root/sysdeps/posix/system.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/posix/system.c')
-rw-r--r--sysdeps/posix/system.c72
1 files changed, 57 insertions, 15 deletions
diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c
index 6df7984889..0881a3a431 100644
--- a/sysdeps/posix/system.c
+++ b/sysdeps/posix/system.c
@@ -16,13 +16,14 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <errno.h>
+#include <signal.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
 #include <sys/types.h>
-#include <errno.h>
+#include <sys/wait.h>
+#include <bits/libc-lock.h>
 
 
 #ifndef	HAVE_GNU_LD
@@ -32,13 +33,36 @@
 #define	SHELL_PATH	"/bin/sh"	/* Path of the shell.  */
 #define	SHELL_NAME	"sh"		/* Name to give it.  */
 
+
+#ifdef _LIBC_REENTRANT
+static struct sigaction intr, quit;
+static int sa_refcntr;
+__libc_lock_define_initialized (static, lock);
+
+# define DO_LOCK() __libc_lock_lock (lock)
+# define DO_UNLOCK() __libc_lock_unlock (lock)
+# define INIT_LOCK() ({ __libc_lock_init (lock); sa_refcntr = 0; })
+# define ADD_REF() sa_refcntr++
+# define SUB_REF() --sa_refcntr
+#else
+# define DO_LOCK()
+# define DO_UNLOCK()
+# define INIT_LOCK()
+# define ADD_REF() (void) 0
+# define SUB_REF() 0
+#endif
+
+
 /* Execute LINE as a shell command, returning its status.  */
 static int
 do_system (const char *line)
 {
   int status, save;
   pid_t pid;
-  struct sigaction sa, intr, quit;
+  struct sigaction sa;
+#ifndef _LIBC_REENTRANT
+  struct sigaction intr, quit;
+#endif
 #ifndef WAITPID_CANNOT_BLOCK_SIGCHLD
   sigset_t block, omask;
 #endif
@@ -47,13 +71,22 @@ do_system (const char *line)
   sa.sa_flags = 0;
   __sigemptyset (&sa.sa_mask);
 
-  if (__sigaction (SIGINT, &sa, &intr) < 0)
-    return -1;
-  if (__sigaction (SIGQUIT, &sa, &quit) < 0)
+  DO_LOCK ();
+  if (ADD_REF () == 0)
     {
-      save = errno;
-      goto out_restore_sigint;
+      if (__sigaction (SIGINT, &sa, &intr) < 0)
+	{
+	  SUB_REF ();
+	  DO_UNLOCK ();
+	  return -1;
+	}
+      if (__sigaction (SIGQUIT, &sa, &quit) < 0)
+	{
+	  save = errno;
+	  goto out_restore_sigint;
+	}
     }
+  DO_UNLOCK ();
 
   __sigemptyset (&block);
   __sigaddset (&block, SIGCHLD);
@@ -65,9 +98,14 @@ do_system (const char *line)
       else
 	{
 	  save = errno;
-	  (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
+	  DO_LOCK ();
+	  if (SUB_REF () == 0)
+	    {
+	      (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
 	out_restore_sigint:
-	  (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
+	      (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
+	    }
+	  DO_UNLOCK ();
 	  __set_errno (save);
 	  return -1;
 	}
@@ -87,6 +125,7 @@ do_system (const char *line)
       (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
       (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
       (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
+      INIT_LOCK ();
 
       /* Exec the shell.  */
       (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ);
@@ -119,9 +158,11 @@ do_system (const char *line)
     }
 
   save = errno;
-  if ((__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
-       | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)
-       | __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL)) != 0)
+  DO_LOCK ();
+  if ((SUB_REF () == 0
+       && (__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
+	   | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)
+      || __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)
     {
 #ifndef _LIBC
       /* glibc cannot be used on systems without waitpid.  */
@@ -129,8 +170,9 @@ do_system (const char *line)
 	__set_errno (save);
       else
 #endif
-	return -1;
+	status = -1;
     }
+  DO_UNLOCK ();
 
   return status;
 }