From http://www.jwz.org/xscreensaver/xscreensaver-5.27.tar.gz
[xscreensaver] / driver / subprocs.c
index 643179829d4ad3db0fcaa64a3d2a07f964ffdf6b..65b0079abd86941009eb0cc74662ecaa441838cd 100644 (file)
@@ -1,5 +1,5 @@
 /* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991-2005 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1991-2014 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -25,6 +25,7 @@
 #endif
 
 #include <sys/time.h>          /* sys/resource.h needs this for timeval */
+#include <sys/param.h>         /* for PATH_MAX */
 
 #ifdef HAVE_SYS_WAIT_H
 # include <sys/wait.h>         /* for waitpid() and associated macros */
@@ -65,6 +66,7 @@ extern int kill (pid_t, int);         /* signal() is in sys/signal.h... */
 #define Widget       void*
 
 #include "xscreensaver.h"
+#include "exec.h"
 #include "yarandom.h"
 #include "visual.h"    /* for id_to_visual() */
 
@@ -91,7 +93,7 @@ no_malloc_number_to_string (long num)
       num = -num;
     }
 
-  while ((num > 0) && (num_digits < sizeof(string - 1)))
+  while ((num > 0) && (num_digits < sizeof(string) - 1))
     {
       int digit;
       digit = (int) num % 10;
@@ -216,7 +218,10 @@ struct screenhack_job {
 
 static struct screenhack_job *jobs = 0;
 
-/* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
+/* for debugging -- nothing calls this, but it's useful to invoke from gdb.
+ */
+void show_job_list (void);
+
 void
 show_job_list (void)
 {
@@ -355,12 +360,20 @@ static int block_sigchld_handler = 0;
 block_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
+  struct sigaction sa;
   sigset_t child_set;
+
+  memset (&sa, 0, sizeof (sa));
+  sa.sa_handler = SIG_IGN;
+  sigaction (SIGPIPE, &sa, NULL);
+
   sigemptyset (&child_set);
   sigaddset (&child_set, SIGCHLD);
-  sigaddset (&child_set, SIGPIPE);
   sigprocmask (SIG_BLOCK, &child_set, 0);
-#endif /* HAVE_SIGACTION */
+
+#else  /* !HAVE_SIGACTION */
+  signal (SIGPIPE, SIG_IGN);
+#endif /* !HAVE_SIGACTION */
 
   block_sigchld_handler++;
 
@@ -375,12 +388,20 @@ void
 unblock_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
+  struct sigaction sa;
   sigset_t child_set;
+
+  memset(&sa, 0, sizeof (sa));
+  sa.sa_handler = SIG_DFL;
+  sigaction(SIGPIPE, &sa, NULL);
+
   sigemptyset(&child_set);
   sigaddset(&child_set, SIGCHLD);
-  sigaddset(&child_set, SIGPIPE);
   sigprocmask(SIG_UNBLOCK, &child_set, 0);
-#endif /* HAVE_SIGACTION */
+
+#else /* !HAVE_SIGACTION */
+  signal(SIGPIPE, SIG_DFL);
+#endif /* !HAVE_SIGACTION */
 
   block_sigchld_handler--;
 }
@@ -463,6 +484,7 @@ static RETSIGTYPE
 sigchld_handler (int sig)
 {
   saver_info *si = global_si_kludge;   /* I hate C so much... */
+  in_signal_handler_p++;
 
   if (si->prefs.debug_p)
     {
@@ -489,6 +511,7 @@ sigchld_handler (int sig)
     }
 
   init_sigchld();
+  in_signal_handler_p--;
 }
 #endif /* SIGCHLD */
 
@@ -830,7 +853,7 @@ fork_and_exec (saver_screen_info *ssi, const char *command)
     case 0:
       close (ConnectionNumber (si->dpy));      /* close display fd */
       limit_subproc_memory (p->inferior_memory_limit, p->verbose_p);
-      hack_subproc_environment (ssi);          /* set $DISPLAY */
+      hack_subproc_environment (ssi->screen, ssi->screensaver_window);
 
       if (p->verbose_p)
         fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
@@ -857,14 +880,22 @@ fork_and_exec (saver_screen_info *ssi, const char *command)
 }
 
 
-static void
-spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
+void
+spawn_screenhack (saver_screen_info *ssi)
 {
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
-  raise_window (si, first_time_p, True, False);
   XFlush (si->dpy);
 
+  if (!monitor_powered_on_p (si))
+    {
+      if (si->prefs.verbose_p)
+        fprintf (stderr,
+                 "%s: %d: X says monitor has powered down; "
+                 "not launching a hack.\n", blurb(), ssi->number);
+      return;
+    }
+
   if (p->screenhacks_count)
     {
       screenhack *hack;
@@ -996,55 +1027,28 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
          break;
        }
     }
-}
 
-
-void
-spawn_screenhack (saver_info *si, Bool first_time_p)
-{
-  if (monitor_powered_on_p (si))
-    {
-      int i;
-      for (i = 0; i < si->nscreens; i++)
-        {
-          saver_screen_info *ssi = &si->screens[i];
-          spawn_screenhack_1 (ssi, first_time_p);
-        }
-    }
-  else if (si->prefs.verbose_p)
-    fprintf (stderr,
-             "%s: X says monitor has powered down; "
-             "not launching a hack.\n", blurb());
-
-  store_saver_status (si);  /* store current hack numbers */
+  store_saver_status (si);  /* store current hack number */
 }
 
 
 void
-kill_screenhack (saver_info *si)
+kill_screenhack (saver_screen_info *ssi)
 {
-  int i;
-  for (i = 0; i < si->nscreens; i++)
-    {
-      saver_screen_info *ssi = &si->screens[i];
-      if (ssi->pid)
-       kill_job (si, ssi->pid, SIGTERM);
-      ssi->pid = 0;
-    }
+  saver_info *si = ssi->global;
+  if (ssi->pid)
+    kill_job (si, ssi->pid, SIGTERM);
+  ssi->pid = 0;
 }
 
 
 void
-suspend_screenhack (saver_info *si, Bool suspend_p)
+suspend_screenhack (saver_screen_info *ssi, Bool suspend_p)
 {
 #ifdef SIGSTOP /* older VMS doesn't have it... */
-  int i;
-  for (i = 0; i < si->nscreens; i++)
-    {
-      saver_screen_info *ssi = &si->screens[i];
-      if (ssi->pid)
-       kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
-    }
+  saver_info *si = ssi->global;
+  if (ssi->pid)
+    kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
 #endif /* SIGSTOP */
 }
 
@@ -1097,6 +1101,7 @@ hack_environment (saver_info *si)
   if (def_path && *def_path)
     {
       const char *opath = getenv("PATH");
+      if (! opath) opath = "/bin:/usr/bin";  /* WTF */
       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
       strcpy (npath, "PATH=");
       strcat (npath, def_path);
@@ -1116,7 +1121,7 @@ hack_environment (saver_info *si)
 
 
 void
-hack_subproc_environment (saver_screen_info *ssi)
+hack_subproc_environment (Screen *screen, Window saver_window)
 {
   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
      the spawned processes inherit is correct.  First, it must be on the same
@@ -1131,25 +1136,27 @@ hack_subproc_environment (saver_screen_info *ssi)
      us to (eventually) run multiple hacks in Xinerama mode, where each hack
      has the same $DISPLAY but a different piece of glass.
    */
-  saver_info *si = ssi->global;
-  const char *odpy = DisplayString (si->dpy);
+  Display *dpy = DisplayOfScreen (screen);
+  const char *odpy = DisplayString (dpy);
   char *ndpy = (char *) malloc (strlen(odpy) + 20);
   char *nssw = (char *) malloc (40);
-  char *s;
+  char *s, *c;
 
   strcpy (ndpy, "DISPLAY=");
   s = ndpy + strlen(ndpy);
   strcpy (s, odpy);
 
-  while (*s && *s != ':') s++;                 /* skip to colon */
-  while (*s == ':') s++;                       /* skip over colons */
+  /* We have to find the last colon since it is the boundary between
+     hostname & screen - IPv6 numeric format addresses may have many
+     colons before that point, and DECnet addresses always have two colons */
+  c = strrchr(s,':');                          /* skip to last colon */
+  if (c != NULL) s = c+1;
   while (isdigit(*s)) s++;                     /* skip over dpy number */
   while (*s == '.') s++;                       /* skip over dot */
   if (s[-1] != '.') *s++ = '.';                        /* put on a dot */
-  sprintf(s, "%d", ssi->real_screen_number);   /* put on screen number */
+  sprintf(s, "%d", screen_number (screen));    /* put on screen number */
 
-  sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX",
-           (unsigned long) ssi->screensaver_window);
+  sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", (unsigned long) saver_window);
 
   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
      any more, right?  It's not Posix, but everyone seems to have it. */
@@ -1170,12 +1177,13 @@ hack_subproc_environment (saver_screen_info *ssi)
 /* GL crap */
 
 Visual *
-get_best_gl_visual (saver_screen_info *ssi)
+get_best_gl_visual (saver_info *si, Screen *screen)
 {
-  saver_info *si = ssi->global;
   pid_t forked;
   int fds [2];
   int in, out;
+  int errfds[2];
+  int errin = -1, errout = -1;
   char buf[1024];
 
   char *av[10];
@@ -1193,6 +1201,23 @@ get_best_gl_visual (saver_screen_info *ssi)
   in = fds [0];
   out = fds [1];
 
+  if (!si->prefs.verbose_p)
+    {
+      if (pipe (errfds))
+        {
+          perror ("error creating pipe:");
+          return 0;
+        }
+
+      errin = errfds [0];
+      errout = errfds [1];
+    }
+
+  block_sigchld();   /* This blocks it in the parent and child, to avoid
+                        racing.  It is never unblocked in the child before
+                        the child exits, but that doesn't matter.
+                      */
+
   switch ((int) (forked = fork ()))
     {
     case -1:
@@ -1203,23 +1228,32 @@ get_best_gl_visual (saver_screen_info *ssi)
       }
     case 0:
       {
-        int stdout_fd = 1;
-
         close (in);  /* don't need this one */
         close (ConnectionNumber (si->dpy));    /* close display fd */
 
-        if (dup2 (out, stdout_fd) < 0)         /* pipe stdout */
+        if (dup2 (out, STDOUT_FILENO) < 0)     /* pipe stdout */
           {
             perror ("could not dup() a new stdout:");
             return 0;
           }
-        hack_subproc_environment (ssi);                /* set $DISPLAY */
+
+        if (! si->prefs.verbose_p)
+          {
+            close(errin);
+            if (dup2 (errout, STDERR_FILENO) < 0)
+              {
+                perror ("could not dup() a new stderr:");
+                return 0;
+              }
+          }
+
+        hack_subproc_environment (screen, 0);  /* set $DISPLAY */
 
         execvp (av[0], av);                    /* shouldn't return. */
 
-        if (errno != ENOENT || si->prefs.verbose_p)
+        if (errno != ENOENT /* || si->prefs.verbose_p */ )
           {
-            /* Ignore "no such file or directory" errors, unless verbose.
+            /* Ignore "no such file or directory" errors.
                Issue all other exec errors, though. */
             sprintf (buf, "%s: running %s", blurb(), av[0]);
             perror (buf);
@@ -1243,9 +1277,17 @@ get_best_gl_visual (saver_screen_info *ssi)
           *buf = 0;
         fclose (f);
 
+        if (! si->prefs.verbose_p)
+          {
+            close (errout);
+            close (errin);
+          }
+
         /* Wait for the child to die. */
         waitpid (-1, &wait_status, 0);
 
+        unblock_sigchld();   /* child is dead and waited, unblock now. */
+
         if (1 == sscanf (buf, "0x%lx %c", &v, &c))
           result = (int) v;
 
@@ -1267,12 +1309,13 @@ get_best_gl_visual (saver_screen_info *ssi)
           }
         else
           {
-            Visual *v = id_to_visual (ssi->screen, result);
+            Visual *v = id_to_visual (screen, result);
             if (si->prefs.verbose_p)
               fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n",
-                       blurb(), ssi->number,
+                       blurb(), screen_number (screen),
                        av[0], result,
-                       (v == ssi->default_visual ? " (default)" : ""));
+                       (v == DefaultVisualOfScreen (screen)
+                        ? " (default)" : ""));
             return v;
           }
       }