ftp://ftp.swin.edu.au/slackware/slackware-9.1/source/xap/xscreensaver/xscreensaver...
[xscreensaver] / driver / subprocs.c
index 97904499981d1eeb69f1deebd416e0084144f52a..f7d8cc8f09aefdb6f72eb0ff579e62cecd2b2ba7 100644 (file)
@@ -1,6 +1,5 @@
 /* subprocs.c --- choosing, spawning, and killing screenhacks.
 /* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998
- *  Jamie Zawinski <jwz@netscape.com>
+ * xscreensaver, Copyright (c) 1991-2003 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
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 #include <X11/Xlib.h>          /* not used for much... */
 
 #ifndef ESRCH
 #include <X11/Xlib.h>          /* not used for much... */
 
 #ifndef ESRCH
-#include <errno.h>
+# include <errno.h>
 #endif
 
 #include <sys/time.h>          /* sys/resource.h needs this for timeval */
 
 #endif
 
 #include <sys/time.h>          /* sys/resource.h needs this for timeval */
 
-#ifndef VMS
-
-# include <sys/resource.h>     /* for setpriority() and PRIO_PROCESS */
+#ifdef HAVE_SYS_WAIT_H
 # include <sys/wait.h>         /* for waitpid() and associated macros */
 # include <sys/wait.h>         /* for waitpid() and associated macros */
+#endif
 
 
-#else  /* VMS */
-
-# if __DECC_VER >= 50200000
-#  include <sys/wait.h>
-# endif
+#ifdef HAVE_SETRLIMIT
+# include <sys/resource.h>     /* for setrlimit() and RLIMIT_AS */
+#endif
 
 
+#ifdef VMS
 # include <processes.h>
 # include <unixio.h>           /* for close */
 # include <unixlib.h>          /* for getpid */
 # include <processes.h>
 # include <unixio.h>           /* for close */
 # include <unixlib.h>          /* for getpid */
-# define pid_t    int
-# define fork     vfork
-
+# define pid_t int
+# define fork  vfork
 #endif /* VMS */
 
 #include <signal.h>            /* for the signal names */
 
 #endif /* VMS */
 
 #include <signal.h>            /* for the signal names */
 
-#ifndef NO_SETUID
-#include <pwd.h>               /* for getpwnam() and struct passwd */
-#include <grp.h>               /* for getgrgid() and struct group */
-#endif /* NO_SETUID */
-
 #if !defined(SIGCHLD) && defined(SIGCLD)
 #if !defined(SIGCHLD) && defined(SIGCLD)
-#define SIGCHLD SIGCLD
+# define SIGCHLD SIGCLD
 #endif
 
 #endif
 
+#if 0 /* putenv() is declared in stdlib.h on modern linux systems. */
 #ifdef HAVE_PUTENV
 extern int putenv (/* const char * */);        /* getenv() is in stdlib.h... */
 #endif
 #ifdef HAVE_PUTENV
 extern int putenv (/* const char * */);        /* getenv() is in stdlib.h... */
 #endif
+#endif
+
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
 /* This file doesn't need the Xt headers, so stub these types out... */
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
 /* This file doesn't need the Xt headers, so stub these types out... */
@@ -72,212 +66,79 @@ extern int kill (pid_t, int);              /* signal() is in sys/signal.h... */
 
 #include "xscreensaver.h"
 #include "yarandom.h"
 
 #include "xscreensaver.h"
 #include "yarandom.h"
-
+#include "visual.h"    /* for id_to_visual() */
 
 extern saver_info *global_si_kludge;   /* I hate C so much... */
 
 
 extern saver_info *global_si_kludge;   /* I hate C so much... */
 
-static void hack_environment (saver_screen_info *ssi);
 
 
+/* RLIMIT_AS (called RLIMIT_VMEM on some systems) controls the maximum size
+   of a process's address space, i.e., the maximal brk(2) and mmap(2) values.
+   Setting this lets you put a cap on how much memory a process can allocate.
+
+   Except the "and mmap()" part kinda makes this useless, since many GL
+   implementations end up using mmap() to pull the whole frame buffer into
+   memory (or something along those lines) making it appear processes are
+   using hundreds of megabytes when in fact they're using very little, and
+   we end up capping their mallocs prematurely.  YAY!
+ */
+#if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS)
+# define RLIMIT_AS RLIMIT_VMEM
+#endif
 
 static void
 
 static void
-nice_subproc (int nice_level)
+limit_subproc_memory (int address_space_limit, Bool verbose_p)
 {
 {
-  if (nice_level == 0)
+
+/* This has caused way more problems than it has solved...
+   Let's just completely ignore the "memoryLimit" option now.
+ */
+#undef HAVE_SETRLIMIT
+
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_AS)
+  struct rlimit r;
+
+  if (address_space_limit < 10 * 1024)  /* let's not be crazy */
     return;
 
     return;
 
-#if defined(HAVE_NICE)
-  {
-    int old_nice = nice (0);
-    int n = nice_level - old_nice;
-    errno = 0;
-    if (nice (n) == -1 && errno != 0)
-      {
-       char buf [512];
-       sprintf (buf, "%s: nice(%d) failed", progname, n);
-       perror (buf);
-    }
-  }
-#elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
-  if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
+  if (getrlimit (RLIMIT_AS, &r) != 0)
     {
       char buf [512];
     {
       char buf [512];
-      sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
-              progname, (unsigned long) getpid(), nice_level);
+      sprintf (buf, "%s: getrlimit(RLIMIT_AS) failed", blurb());
       perror (buf);
       perror (buf);
+      return;
     }
     }
-#else
-  fprintf (stderr,
-          "%s: don't know how to change process priority on this system.\n",
-          progname);
-
-#endif
-}
-
 
 
-#ifndef VMS
+  r.rlim_cur = address_space_limit;
 
 
-static void
-exec_simple_command (const char *command)
-{
-  char *av[1024];
-  int ac = 0;
-  char *token = strtok (strdup(command), " \t");
-  while (token)
+  if (setrlimit (RLIMIT_AS, &r) != 0)
     {
     {
-      av[ac++] = token;
-      token = strtok(0, " \t");
+      char buf [512];
+      sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed",
+               blurb(), r.rlim_cur, r.rlim_max);
+      perror (buf);
+      return;
     }
     }
-  av[ac] = 0;
-
-  execvp (av[0], av);                  /* shouldn't return. */
-
-  {
-    char buf [512];
-    sprintf (buf, "%s: could not execute \"%s\"", progname, av[0]);
-    perror (buf);
-
-    if (errno == ENOENT &&
-       (token = getenv("PATH")))
-      {
-# ifndef PATH_MAX
-#  ifdef MAXPATHLEN
-#   define PATH_MAX MAXPATHLEN
-#  else
-#   define PATH_MAX 2048
-#  endif
-# endif
-       char path[PATH_MAX];
-       fprintf (stderr, "\n");
-       *path = 0;
-# if defined(HAVE_GETCWD)
-       getcwd (path, sizeof(path));
-# elif defined(HAVE_GETWD)
-       getwd (path);
-# endif
-       if (*path)
-         fprintf (stderr, "    Current directory is: %s\n", path);
-       fprintf (stderr, "    PATH is:\n");
-       token = strtok (strdup(token), ":");
-       while (token)
-         {
-           fprintf (stderr, "        %s\n", token);
-           token = strtok(0, ":");
-         }
-       fprintf (stderr, "\n");
-      }
-  }
-  fflush(stderr);
-  fflush(stdout);
-  exit (1);    /* Note that this only exits a child fork.  */
-}
-
-
-static void
-exec_complex_command (const char *shell, const char *command)
-{
-  char *av[5];
-  int ac = 0;
-  char *command2 = (char *) malloc (strlen (command) + 6);
-  memcpy (command2, "exec ", 5);
-  memcpy (command2 + 5, command, strlen (command) + 1);
-
-  /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
-  av [ac++] = (char *) shell;
-  av [ac++] = "-c";
-  av [ac++] = command2;
-  av [ac]   = 0;
 
 
-  execvp (av[0], av);                  /* shouldn't return. */
-
-  {
-    char buf [512];
-    sprintf (buf, "%s: execvp(\"%s\") failed", progname, av[0]);
-    perror (buf);
-    fflush(stderr);
-    fflush(stdout);
-    exit (1);  /* Note that this only exits a child fork.  */
-  }
-}
-
-#else  /* VMS */
-
-static void
-exec_vms_command (const char *command)
-{
-  system (command);
-  fflush (stderr);
-  fflush (stdout);
-  exit (1);    /* Note that this only exits a child fork.  */
-}
-
-#endif /* !VMS */
-
-
-static void
-exec_screenhack (saver_info *si, const char *command)
-{
-  /* I don't believe what a sorry excuse for an operating system UNIX is!
-
-     - I want to spawn a process.
-     - I want to know it's pid so that I can kill it.
-     - I would like to receive a message when it dies of natural causes.
-     - I want the spawned process to have user-specified arguments.
-
-     If shell metacharacters are present (wildcards, backquotes, etc), the
-     only way to parse those arguments is to run a shell to do the parsing
-     for you.
-
-     And the only way to know the pid of the process is to fork() and exec()
-     it in the spawned side of the fork.
-
-     But if you're running a shell to parse your arguments, this gives you
-     the pid of the *shell*, not the pid of the *process* that you're
-     actually interested in, which is an *inferior* of the shell.  This also
-     means that the SIGCHLD you get applies to the shell, not its inferior.
-     (Why isn't that sufficient?  I don't remember any more, but it turns
-     out that it isn't.)
-
-     So, the only solution, when metacharacters are present, is to force the
-     shell to exec() its inferior.  What a fucking hack!  We prepend "exec "
-     to the command string, and hope it doesn't contain unquoted semicolons
-     or ampersands (we don't search for them, because we don't want to
-     prohibit their use in quoted strings (messages, for example) and parsing
-     out the various quote characters is too much of a pain.)
-
-     (Actually, Clint Wong <clint@jts.com> points out that process groups
-     might be used to take care of this problem; this may be worth considering
-     some day, except that, 1: this code works now, so why fix it, and 2: from
-     what I've seen in Emacs, dealing with process groups isn't especially
-     portable.)
-   */
-  saver_preferences *p = &si->prefs;
-
-#ifndef VMS
-  Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"");
-
-  if (p->verbose_p)
-    printf ("%s: spawning \"%s\" in pid %lu%s.\n",
-           progname, command, (unsigned long) getpid (),
-           (hairy_p ? " (via shell)" : ""));
-
-  if (hairy_p)
-    /* If it contains any shell metacharacters, do it the hard way,
-       and fork a shell to parse the arguments for us. */
-    exec_complex_command (p->shell, command);
-  else
-    /* Otherwise, we can just exec the program directly. */
-    exec_simple_command (command);
+  if (verbose_p)
+    {
+      int i = address_space_limit;
+      char buf[100];
+      if      (i >= (1<<30) && i == ((i >> 30) << 30))
+        sprintf(buf, "%dG", i >> 30);
+      else if (i >= (1<<20) && i == ((i >> 20) << 20))
+        sprintf(buf, "%dM", i >> 20);
+      else if (i >= (1<<10) && i == ((i >> 10) << 10))
+        sprintf(buf, "%dK", i >> 10);
+      else
+        sprintf(buf, "%d bytes", i);
 
 
-#else /* VMS */
-  if (p->verbose_p)
-    printf ("%s: spawning \"%s\" in pid %lu.\n", progname, command, getpid());
-  exec_vms_command (command);
-#endif /* VMS */
+      fprintf (stderr, "%s: limited pid %lu address space to %s.\n",
+               blurb(), (unsigned long) getpid (), buf);
+    }
 
 
-  abort();     /* that shouldn't have returned. */
+#endif /* HAVE_SETRLIMIT && RLIMIT_AS */
 }
 
 }
 
-
 \f
 /* Management of child processes, and de-zombification.
  */
 \f
 /* Management of child processes, and de-zombification.
  */
@@ -295,6 +156,7 @@ enum job_status {
 struct screenhack_job {
   char *name;
   pid_t pid;
 struct screenhack_job {
   char *name;
   pid_t pid;
+  int screen;
   enum job_status status;
   struct screenhack_job *next;
 };
   enum job_status status;
   struct screenhack_job *next;
 };
@@ -306,10 +168,11 @@ void
 show_job_list (void)
 {
   struct screenhack_job *job;
 show_job_list (void)
 {
   struct screenhack_job *job;
-  fprintf(stderr, "%s: job list:\n", progname);
+  fprintf(stderr, "%s: job list:\n", blurb());
   for (job = jobs; job; job = job->next)
   for (job = jobs; job; job = job->next)
-    fprintf (stderr, "  %5ld: (%s) %s\n",
+    fprintf (stderr, "  %5ld: %2d: (%s) %s\n",
             (long) job->pid,
             (long) job->pid,
+             job->screen,
             (job->status == job_running ? "running" :
              job->status == job_stopped ? "stopped" :
              job->status == job_killed  ? " killed" :
             (job->status == job_running ? "running" :
              job->status == job_stopped ? "stopped" :
              job->status == job_killed  ? " killed" :
@@ -322,31 +185,39 @@ show_job_list (void)
 static void clean_job_list (void);
 
 static struct screenhack_job *
 static void clean_job_list (void);
 
 static struct screenhack_job *
-make_job (pid_t pid, const char *cmd)
+make_job (pid_t pid, int screen, const char *cmd)
 {
   struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
 
   static char name [1024];
   const char *in = cmd;
   char *out = name;
 {
   struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
 
   static char name [1024];
   const char *in = cmd;
   char *out = name;
+  int got_eq = 0;
+  int first = 1;
 
   clean_job_list();
 
 
   clean_job_list();
 
+ AGAIN:
   while (isspace(*in)) in++;           /* skip whitespace */
   while (isspace(*in)) in++;           /* skip whitespace */
-  while (!isspace(*in) && *in != ':')
+  while (!isspace(*in) && *in != ':') {
+    if (*in == '=') got_eq = 1;
     *out++ = *in++;                    /* snarf first token */
     *out++ = *in++;                    /* snarf first token */
-  while (isspace(*in)) in++;           /* skip whitespace */
-  if (*in == ':')                      /* token was a visual name; skip it. */
-    {
-      in++;
+  }
+
+  if (got_eq)                          /* if the first token was FOO=bar */
+    {                                  /* then get the next token instead. */
+      got_eq = 0;
       out = name;
       out = name;
-      while (isspace(*in)) in++;               /* skip whitespace */
-      while (!isspace(*in)) *out++ = *in++;    /* snarf first token */
+      first = 0;
+      goto AGAIN;
     }
     }
+
+  while (isspace(*in)) in++;           /* skip whitespace */
   *out = 0;
 
   job->name = strdup(name);
   job->pid = pid;
   *out = 0;
 
   job->name = strdup(name);
   job->pid = pid;
+  job->screen = screen;
   job->status = job_running;
   job->next = jobs;
   jobs = job;
   job->status = job_running;
   job->next = jobs;
   jobs = job;
@@ -423,26 +294,38 @@ static void describe_dead_child (saver_info *, pid_t, int wait_status);
 static int block_sigchld_handler = 0;
 
 
 static int block_sigchld_handler = 0;
 
 
-static void
+#ifdef HAVE_SIGACTION
+ sigset_t
+#else  /* !HAVE_SIGACTION */
+ int
+#endif /* !HAVE_SIGACTION */
 block_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
   sigset_t child_set;
   sigemptyset (&child_set);
   sigaddset (&child_set, SIGCHLD);
 block_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
   sigset_t child_set;
   sigemptyset (&child_set);
   sigaddset (&child_set, SIGCHLD);
+  sigaddset (&child_set, SIGPIPE);
   sigprocmask (SIG_BLOCK, &child_set, 0);
 #endif /* HAVE_SIGACTION */
 
   block_sigchld_handler++;
   sigprocmask (SIG_BLOCK, &child_set, 0);
 #endif /* HAVE_SIGACTION */
 
   block_sigchld_handler++;
+
+#ifdef HAVE_SIGACTION
+  return child_set;
+#else  /* !HAVE_SIGACTION */
+  return 0;
+#endif /* !HAVE_SIGACTION */
 }
 
 }
 
-static void
+void
 unblock_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
   sigset_t child_set;
   sigemptyset(&child_set);
   sigaddset(&child_set, SIGCHLD);
 unblock_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
   sigset_t child_set;
   sigemptyset(&child_set);
   sigaddset(&child_set, SIGCHLD);
+  sigaddset(&child_set, SIGPIPE);
   sigprocmask(SIG_UNBLOCK, &child_set, 0);
 #endif /* HAVE_SIGACTION */
 
   sigprocmask(SIG_UNBLOCK, &child_set, 0);
 #endif /* HAVE_SIGACTION */
 
@@ -471,7 +354,7 @@ kill_job (saver_info *si, pid_t pid, int signal)
     {
       if (p->verbose_p)
        fprintf (stderr, "%s: no child %ld to signal!\n",
     {
       if (p->verbose_p)
        fprintf (stderr, "%s: no child %ld to signal!\n",
-                progname, (long) pid);
+                blurb(), (long) pid);
       goto DONE;
     }
 
       goto DONE;
     }
 
@@ -485,31 +368,27 @@ kill_job (saver_info *si, pid_t pid, int signal)
   default: abort();
   }
 
   default: abort();
   }
 
-#ifdef SIGSTOP
   if (p->verbose_p)
   if (p->verbose_p)
-    fprintf (stderr, "%s: %s pid %lu.\n", progname,
-            (signal == SIGTERM ? "killing" :
-             signal == SIGSTOP ? "suspending" :
-             signal == SIGCONT ? "resuming" : "signalling"),
-            (unsigned long) job->pid);
-#else  /* !SIGSTOP */
-  if (p->verbose_p)
-    fprintf (stderr, "%s: %s pid %lu.\n", progname, "killing",
-            (unsigned long) job->pid);
-#endif /* !SIGSTOP */
+    fprintf (stderr, "%s: %d: %s pid %lu (%s)\n",
+             blurb(), job->screen,
+             (job->status == job_killed  ? "killing" :
+              job->status == job_stopped ? "suspending" : "resuming"),
+             (unsigned long) job->pid,
+             job->name);
 
   status = kill (job->pid, signal);
 
   if (p->verbose_p && status < 0)
     {
       if (errno == ESRCH)
 
   status = kill (job->pid, signal);
 
   if (p->verbose_p && status < 0)
     {
       if (errno == ESRCH)
-       fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
-                progname, job->pid, job->name);
+       fprintf (stderr,
+                 "%s: %d: child process %lu (%s) was already dead.\n",
+                blurb(), job->screen, (unsigned long) job->pid, job->name);
       else
        {
          char buf [1024];
       else
        {
          char buf [1024];
-         sprintf (buf, "%s: couldn't kill child process %lu (%s)",
-                  progname, job->pid, job->name);
+         sprintf (buf, "%s: %d: couldn't kill child process %lu (%s)",
+                  blurb(), job->screen, (unsigned long) job->pid, job->name);
          perror (buf);
        }
     }
          perror (buf);
        }
     }
@@ -533,7 +412,7 @@ sigchld_handler (int sig)
   saver_info *si = global_si_kludge;   /* I hate C so much... */
 
   if (si->prefs.debug_p)
   saver_info *si = global_si_kludge;   /* I hate C so much... */
 
   if (si->prefs.debug_p)
-    fprintf(stderr, "%s: got SIGCHLD%s\n", progname,
+    fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
            (block_sigchld_handler ? " (blocked)" : ""));
 
   if (block_sigchld_handler < 0)
            (block_sigchld_handler ? " (blocked)" : ""));
 
   if (block_sigchld_handler < 0)
@@ -565,10 +444,10 @@ await_dying_children (saver_info *si)
       if (si->prefs.debug_p)
        {
          if (kid < 0 && errno)
       if (si->prefs.debug_p)
        {
          if (kid < 0 && errno)
-           fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", progname,
+           fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
                     (long) kid, errno);
          else
                     (long) kid, errno);
          else
-           fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", progname,
+           fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
                     (long) kid);
        }
 
                     (long) kid);
        }
 
@@ -591,6 +470,7 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
   saver_preferences *p = &si->prefs;
   struct screenhack_job *job = find_job (kid);
   const char *name = job ? job->name : "<unknown>";
   saver_preferences *p = &si->prefs;
   struct screenhack_job *job = find_job (kid);
   const char *name = job ? job->name : "<unknown>";
+  int screen_no = job ? job->screen : 0;
 
   if (WIFEXITED (wait_status))
     {
 
   if (WIFEXITED (wait_status))
     {
@@ -609,11 +489,11 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
          (exit_status != 0 &&
           (p->verbose_p || job->status != job_killed)))
        fprintf (stderr,
          (exit_status != 0 &&
           (p->verbose_p || job->status != job_killed)))
        fprintf (stderr,
-                "%s: child pid %lu (%s) exited abnormally (code %d).\n",
-                progname, (unsigned long) kid, name, exit_status);
+                "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n",
+                blurb(), screen_no, (unsigned long) kid, name, exit_status);
       else if (p->verbose_p)
       else if (p->verbose_p)
-       printf ("%s: child pid %lu (%s) exited normally.\n",
-               progname, (unsigned long) kid, name);
+       fprintf (stderr, "%s: %d: child pid %lu (%s) exited normally.\n",
+                blurb(), screen_no, (unsigned long) kid, name);
 
       if (job)
        job->status = job_dead;
 
       if (job)
        job->status = job_dead;
@@ -624,8 +504,8 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
          !job ||
          job->status != job_killed ||
          WTERMSIG (wait_status) != SIGTERM)
          !job ||
          job->status != job_killed ||
          WTERMSIG (wait_status) != SIGTERM)
-       fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
-                progname, (unsigned long) kid, name,
+       fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n",
+                blurb(), screen_no, (unsigned long) kid, name,
                 signal_name (WTERMSIG(wait_status)));
 
       if (job)
                 signal_name (WTERMSIG(wait_status)));
 
       if (job)
@@ -635,7 +515,7 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
     {
       if (p->verbose_p)
        fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
     {
       if (p->verbose_p)
        fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
-                progname, (unsigned long) kid, name,
+                blurb(), (unsigned long) kid, name,
                 signal_name (WSTOPSIG (wait_status)));
 
       if (job)
                 signal_name (WSTOPSIG (wait_status)));
 
       if (job)
@@ -644,7 +524,7 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
   else
     {
       fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
   else
     {
       fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
-              progname, (unsigned long) kid, name);
+              blurb(), (unsigned long) kid, name);
       if (job)
        job->status = job_dead;
     }
       if (job)
        job->status = job_dead;
     }
@@ -684,7 +564,7 @@ init_sigchld (void)
       if (sigaction(SIGCHLD, &action, &old) < 0)
        {
          char buf [255];
       if (sigaction(SIGCHLD, &action, &old) < 0)
        {
          char buf [255];
-         sprintf (buf, "%s: couldn't catch SIGCHLD", progname);
+         sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
          perror (buf);
        }
       sigchld_initialized_p = True;
          perror (buf);
        }
       sigchld_initialized_p = True;
@@ -695,7 +575,7 @@ init_sigchld (void)
   if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
     {
       char buf [255];
   if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
     {
       char buf [255];
-      sprintf (buf, "%s: couldn't catch SIGCHLD", progname);
+      sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
       perror (buf);
     }
 # endif /* !HAVE_SIGACTION */
       perror (buf);
     }
 # endif /* !HAVE_SIGACTION */
@@ -707,40 +587,74 @@ init_sigchld (void)
 \f
 
 static Bool
 \f
 
 static Bool
-select_visual_of_hack (saver_screen_info *ssi, const char *hack)
+select_visual_of_hack (saver_screen_info *ssi, screenhack *hack)
 {
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
   Bool selected;
 {
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
   Bool selected;
-  static char vis [1024];
-  const char *in = hack;
-  char *out = vis;
-  while (isspace(*in)) in++;           /* skip whitespace */
-  while (!isspace(*in) && *in != ':')
-    *out++ = *in++;                    /* snarf first token */
-  while (isspace(*in)) in++;           /* skip whitespace */
-  *out = 0;
 
 
-  if (*in == ':')
-    selected = select_visual(ssi, vis);
+  if (hack->visual && *hack->visual)
+    selected = select_visual(ssi, hack->visual);
   else
     selected = select_visual(ssi, 0);
 
   else
     selected = select_visual(ssi, 0);
 
-  if (!selected && (p->verbose_p || si->demo_mode_p))
-    {
-      if (*in == ':') in++;
-      while (isspace(*in)) in++;
-      fprintf (stderr,
-              (si->demo_mode_p
-               ? "%s: warning, no \"%s\" visual for \"%s\".\n"
-               : "%s: no \"%s\" visual; skipping \"%s\".\n"),
-              progname, (vis ? vis : "???"), in);
-    }
+  if (!selected && (p->verbose_p || si->demoing_p))
+    fprintf (stderr,
+             (si->demoing_p
+              ? "%s: warning, no \"%s\" visual for \"%s\".\n"
+              : "%s: no \"%s\" visual; skipping \"%s\".\n"),
+             blurb(),
+             (hack->visual && *hack->visual ? hack->visual : "???"),
+             hack->command);
 
   return selected;
 }
 
 
 
   return selected;
 }
 
 
+static void
+print_path_error (const char *program)
+{
+  char buf [512];
+  char *cmd = strdup (program);
+  char *token = strchr (cmd, ' ');
+
+  if (token) *token = 0;
+  sprintf (buf, "%s: could not execute \"%.100s\"", blurb(), cmd);
+  free (cmd);
+  perror (buf);
+
+  if (errno == ENOENT &&
+      (token = getenv("PATH")))
+    {
+# ifndef PATH_MAX
+#  ifdef MAXPATHLEN
+#   define PATH_MAX MAXPATHLEN
+#  else
+#   define PATH_MAX 2048
+#  endif
+# endif
+      char path[PATH_MAX];
+      fprintf (stderr, "\n");
+      *path = 0;
+# if defined(HAVE_GETCWD)
+      getcwd (path, sizeof(path));
+# elif defined(HAVE_GETWD)
+      getwd (path);
+# endif
+      if (*path)
+        fprintf (stderr, "    Current directory is: %s\n", path);
+      fprintf (stderr, "    PATH is:\n");
+      token = strtok (strdup(token), ":");
+      while (token)
+        {
+          fprintf (stderr, "        %s\n", token);
+          token = strtok(0, ":");
+        }
+      fprintf (stderr, "\n");
+    }
+}
+
+
 static void
 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
 {
 static void
 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
 {
@@ -749,96 +663,144 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
   raise_window (si, first_time_p, True, False);
   XFlush (si->dpy);
 
   raise_window (si, first_time_p, True, False);
   XFlush (si->dpy);
 
-  if (p->screenhacks_count || si->demo_mode_p)
+  if (p->screenhacks_count)
     {
     {
-      char *hack;
+      screenhack *hack;
       pid_t forked;
       char buf [255];
       int new_hack;
       pid_t forked;
       char buf [255];
       int new_hack;
-
-      if (si->demo_mode_p)
+      int retry_count = 0;
+      Bool force = False;
+
+    AGAIN:
+
+      if (p->screenhacks_count < 1)
+        {
+          /* No hacks at all */
+          new_hack = -1;
+        }
+      else if (p->screenhacks_count == 1)
+        {
+          /* Exactly one hack in the list */
+          new_hack = 0;
+        }
+      else if (si->selection_mode == -1)
+        {
+          /* Select the next hack, wrapping. */
+          new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
+        }
+      else if (si->selection_mode == -2)
+        {
+          /* Select the previous hack, wrapping. */
+          if (ssi->current_hack < 0)
+            new_hack = p->screenhacks_count - 1;
+          else
+            new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
+                        % p->screenhacks_count);
+        }
+      else if (si->selection_mode > 0)
        {
        {
-         hack = si->demo_hack;
-
-         /* Ignore visual-selection failure if in demo mode. */
-         (void) select_visual_of_hack (ssi, hack);
+          /* Select a specific hack, by number (via the ACTIVATE command.) */
+         new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
+         force = True;
        }
        }
-      else
+      else if (p->mode == ONE_HACK &&
+               p->selected_hack >= 0)
        {
        {
-         int retry_count = 0;
-
-       AGAIN:
-         if (p->screenhacks_count == 1)
-           new_hack = 0;
-         else if (si->next_mode_p == 1)
-           new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
-         else if (si->next_mode_p == 2)
-           new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
-                       % p->screenhacks_count);
-         else
-           while ((new_hack = random () % p->screenhacks_count)
-                  == ssi->current_hack)
-             ;
-         ssi->current_hack = new_hack;
-         hack = p->screenhacks[ssi->current_hack];
+          /* Select a specific hack, by number (via "One Saver" mode.) */
+          new_hack = p->selected_hack;
+         force = True;
+       }
+      else if (p->mode == BLANK_ONLY || p->mode == DONT_BLANK)
+        {
+          new_hack = -1;
+        }
+      else  /* (p->mode == RANDOM_HACKS) */
+       {
+         /* Select a random hack (but not the one we just ran.) */
+         while ((new_hack = random () % p->screenhacks_count)
+                == ssi->current_hack)
+           ;
+       }
 
 
-         if (!select_visual_of_hack (ssi, hack))
+      if (new_hack < 0)   /* don't run a hack */
+        {
+          ssi->current_hack = -1;
+          if (si->selection_mode < 0)
+            si->selection_mode = 0;
+          return;
+        }
+
+      ssi->current_hack = new_hack;
+      hack = p->screenhacks[ssi->current_hack];
+
+      /* If the hack is disabled, or there is no visual for this hack,
+        then try again (move forward, or backward, or re-randomize.)
+        Unless this hack was specified explicitly, in which case,
+        use it regardless.
+       */
+      if (force)
+        select_visual_of_hack (ssi, hack);
+        
+      if (!force &&
+         (!hack->enabled_p ||
+          !select_visual_of_hack (ssi, hack)))
+       {
+         if (++retry_count > (p->screenhacks_count*4))
            {
            {
-             if (++retry_count > (p->screenhacks_count*4))
-               {
-                 /* Uh, oops.  Odds are, there are no suitable visuals,
-                    and we're looping.  Give up.  (This is totally lame,
-                    what we should do is make a list of suitable hacks at
-                    the beginning, then only loop over them.)
-                 */
-                 if (p->verbose_p)
-                   fprintf(stderr,
-                           "%s: no suitable visuals for these programs.\n",
-                           progname);
-                 return;
-               }
-             else
-               goto AGAIN;
+             /* Uh, oops.  Odds are, there are no suitable visuals,
+                and we're looping.  Give up.  (This is totally lame,
+                what we should do is make a list of suitable hacks at
+                the beginning, then only loop over them.)
+             */
+             if (p->verbose_p)
+               fprintf(stderr,
+                     "%s: %d: no programs enabled, or no suitable visuals.\n",
+                       blurb(), ssi->number);
+             return;
            }
            }
+         else
+           goto AGAIN;
        }
        }
-      si->next_mode_p = 0;
-
 
 
-      /* If there's a visual description on the front of the command, nuke it.
+      /* Turn off "next" and "prev" modes now, but "demo" mode is only
+        turned off by explicit action.
        */
        */
-      {
-       char *in = hack;
-       while (isspace(*in)) in++;                      /* skip whitespace */
-       hack = in;
-       while (!isspace(*in) && *in != ':') in++;       /* snarf first token */
-       while (isspace(*in)) in++;                      /* skip whitespace */
-       if (*in == ':')
-         {
-           in++;
-           while (isspace(*in)) in++;
-           hack = in;
-         }
-      }
+      if (si->selection_mode < 0)
+       si->selection_mode = 0;
 
       switch ((int) (forked = fork ()))
        {
        case -1:
 
       switch ((int) (forked = fork ()))
        {
        case -1:
-         sprintf (buf, "%s: couldn't fork", progname);
+         sprintf (buf, "%s: couldn't fork", blurb());
          perror (buf);
          restore_real_vroot (si);
          perror (buf);
          restore_real_vroot (si);
-         saver_exit (si, 1);
+         saver_exit (si, 1, 0);
 
        case 0:
          close (ConnectionNumber (si->dpy));   /* close display fd */
 
        case 0:
          close (ConnectionNumber (si->dpy));   /* close display fd */
-         nice_subproc (p->nice_inferior);      /* change process priority */
-         hack_environment (ssi);               /* set $DISPLAY */
-         exec_screenhack (si, hack);           /* this does not return */
-         abort();
+         limit_subproc_memory (p->inferior_memory_limit, p->verbose_p);
+         hack_subproc_environment (ssi);       /* set $DISPLAY */
+
+          if (p->verbose_p)
+            fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
+                     blurb(), ssi->number, hack->command,
+                     (unsigned long) getpid ());
+
+         exec_command (p->shell, hack->command, p->nice_inferior);
+
+          /* If that returned, we were unable to exec the subprocess.
+             Print an error message, if desired.
+           */
+          if (! p->ignore_uninstalled_p)
+            print_path_error (hack->command);
+
+          exit (1);  /* exits child fork */
          break;
 
        default:
          ssi->pid = forked;
          break;
 
        default:
          ssi->pid = forked;
-         (void) make_job (forked, hack);
+         (void) make_job (forked, ssi->number, hack->command);
          break;
        }
     }
          break;
        }
     }
@@ -848,12 +810,21 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
 void
 spawn_screenhack (saver_info *si, Bool first_time_p)
 {
 void
 spawn_screenhack (saver_info *si, Bool first_time_p)
 {
-  int i;
-  for (i = 0; i < si->nscreens; i++)
+  if (monitor_powered_on_p (si))
     {
     {
-      saver_screen_info *ssi = &si->screens[i];
-      spawn_screenhack_1 (ssi, first_time_p);
+      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 */
 }
 
 
 }
 
 
@@ -909,66 +880,51 @@ emergency_kill_subproc (saver_info *si)
 Bool
 screenhack_running_p (saver_info *si)
 {
 Bool
 screenhack_running_p (saver_info *si)
 {
-  Bool result = True;
+  Bool any_running_p = False;
   int i;
   for (i = 0; i < si->nscreens; i++)
     {
       saver_screen_info *ssi = &si->screens[i];
   int i;
   for (i = 0; i < si->nscreens; i++)
     {
       saver_screen_info *ssi = &si->screens[i];
-      if (!ssi->pid)
-       result = False;
+      if (ssi->pid) any_running_p = True;
     }
     }
-  return result;
+  return any_running_p;
 }
 
 \f
 }
 
 \f
-/* Restarting the xscreensaver process from scratch. */
+/* Environment variables. */
 
 
-static char **saved_argv;
 
 
+/* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
+   is defined, the xscreensaver daemon will search that directory for hacks.
+ */
 void
 void
-save_argv (int argc, char **argv)
+hack_environment (saver_info *si)
 {
 {
-  saved_argv = (char **) malloc ((argc + 2) * sizeof (char *));
-  saved_argv [argc] = 0;
-  while (argc--)
+#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
+  static const char *def_path = DEFAULT_PATH_PREFIX;
+  if (def_path && *def_path)
     {
     {
-      int i = strlen (argv [argc]) + 1;
-      saved_argv [argc] = (char *) malloc (i);
-      memcpy (saved_argv [argc], argv [argc], i);
+      const char *opath = getenv("PATH");
+      char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
+      strcpy (npath, "PATH=");
+      strcat (npath, def_path);
+      strcat (npath, ":");
+      strcat (npath, opath);
+
+      if (putenv (npath))
+       abort ();
+
+      /* don't free (npath) -- some implementations of putenv (BSD 4.4,
+         glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
+         do not.  So we must leak it (and/or the previous setting). Yay.
+       */
     }
     }
+#endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
 }
 
 }
 
-void
-restart_process (saver_info *si)
-{
-  fflush (real_stdout);
-  fflush (real_stderr);
-  execvp (saved_argv [0], saved_argv); /* shouldn't return */
-  {
-    char buf [512];
-    sprintf (buf, "%s: could not restart process", progname);
-    perror(buf);
-    fflush(stderr);
-  }
-}
 
 
-/* Like restart_process(), but ensures that when it restarts,
-   it comes up in demo-mode. */
 void
 void
-demo_mode_restart_process (saver_info *si)
-{
-  int i;
-  for (i = 0; saved_argv [i]; i++);
-  /* add the -initial-demo-mode switch; save_argv() left room for this. */
-  saved_argv [i] = "-initial-demo-mode";
-  saved_argv [i+1] = 0;
-  restart_process (si);                /* shouldn't return */
-  saved_argv [i] = 0;
-  XBell(si->dpy, 0);
-}
-
-static void
-hack_environment (saver_screen_info *ssi)
+hack_subproc_environment (saver_screen_info *ssi)
 {
   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
      the spawned processes inherit is correct.  First, it must be on the same
 {
   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
      the spawned processes inherit is correct.  First, it must be on the same
@@ -978,18 +934,17 @@ hack_environment (saver_screen_info *ssi)
      be the screen on which this particular hack is running -- not the display
      specification which the driver itself is using, since the driver ignores
      its screen number and manages all existing screens.
      be the screen on which this particular hack is running -- not the display
      specification which the driver itself is using, since the driver ignores
      its screen number and manages all existing screens.
+
+     Likewise, store a window ID in $XSCREENSAVER_WINDOW -- this will allow
+     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);
    */
   saver_info *si = ssi->global;
   const char *odpy = DisplayString (si->dpy);
-  char *ndpy = (char *) malloc(strlen(odpy) + 20);
-  int screen_number;
+  char *ndpy = (char *) malloc (strlen(odpy) + 20);
+  char *nssw = (char *) malloc (40);
   char *s;
 
   char *s;
 
-  for (screen_number = 0; screen_number < si->nscreens; screen_number++)
-    if (ssi == &si->screens[screen_number])
-      break;
-  if (screen_number >= si->nscreens) abort();
-
   strcpy (ndpy, "DISPLAY=");
   s = ndpy + strlen(ndpy);
   strcpy (s, odpy);
   strcpy (ndpy, "DISPLAY=");
   s = ndpy + strlen(ndpy);
   strcpy (s, odpy);
@@ -999,140 +954,187 @@ hack_environment (saver_screen_info *ssi)
   while (isdigit(*s)) s++;                     /* skip over dpy number */
   while (*s == '.') s++;                       /* skip over dot */
   if (s[-1] != '.') *s++ = '.';                        /* put on a dot */
   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", screen_number);             /* put on screen number */
+  sprintf(s, "%d", ssi->real_screen_number);   /* put on screen number */
+
+  sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX",
+           (unsigned long) ssi->screensaver_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. */
 #ifdef HAVE_PUTENV
   if (putenv (ndpy))
     abort ();
 
   /* 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. */
 #ifdef HAVE_PUTENV
   if (putenv (ndpy))
     abort ();
+  if (putenv (nssw))
+    abort ();
+
+  /* don't free ndpy/nssw -- some implementations of putenv (BSD 4.4,
+     glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
+     do not.  So we must leak it (and/or the previous setting). Yay.
+   */
 #endif /* HAVE_PUTENV */
 }
 
 \f
 #endif /* HAVE_PUTENV */
 }
 
 \f
-/* Change the uid/gid of the screensaver process, so that it is safe for it
-   to run setuid root (which it needs to do on some systems to read the 
-   encrypted passwords from the passwd file.)
-
-   hack_uid() is run before opening the X connection, so that XAuth works.
-   hack_uid_warn() is called after the connection is opened and the command
-   line arguments are parsed, so that the messages from hack_uid() get 
-   printed after we know whether we're in `verbose' mode.
- */
+/* GL crap */
 
 
-#ifndef NO_SETUID
+Visual *
+get_best_gl_visual (saver_screen_info *ssi)
+{
+  saver_info *si = ssi->global;
+  pid_t forked;
+  int fds [2];
+  int in, out;
+  char buf[1024];
 
 
-static int hack_uid_errno;
-static char hack_uid_buf [255], *hack_uid_error;
+  char *av[10];
+  int ac = 0;
 
 
-void
-hack_uid (saver_info *si)
-{
+  av[ac++] = "xscreensaver-gl-helper";
+  av[ac] = 0;
 
 
-  /* If we've been run as setuid or setgid to someone else (most likely root)
-     turn off the extra permissions so that random user-specified programs
-     don't get special privileges.  (On some systems it might be necessary
-     to install this as setuid root in order to read the passwd file to
-     implement lock-mode...)
-  */
-  setgid (getgid ());
-  setuid (getuid ());
-
-  hack_uid_errno = 0;
-  hack_uid_error = 0;
-
-  /* If we're being run as root (as from xdm) then switch the user id
-     to something safe. */
-  if (getuid () == 0)
+  if (pipe (fds))
     {
     {
-      struct passwd *p;
-      /* Locking can't work when running as root, because we have no way of
-        knowing what the user id of the logged in user is (so we don't know
-        whose password to prompt for.)
-       */
-      si->locking_disabled_p = True;
-      si->nolock_reason = "running as root";
-      p = getpwnam ("nobody");
-      if (! p) p = getpwnam ("noaccess");
-      if (! p) p = getpwnam ("daemon");
-      if (! p) p = getpwnam ("bin");
-      if (! p) p = getpwnam ("sys");
-      if (! p)
-       {
-         hack_uid_error = "couldn't find safe uid; running as root.";
-         hack_uid_errno = -1;
-       }
-      else
-       {
-         struct group *g = getgrgid (p->pw_gid);
-         hack_uid_error = hack_uid_buf;
-         sprintf (hack_uid_error, "changing uid/gid to %s/%s (%ld/%ld).",
-                  p->pw_name, (g ? g->gr_name : "???"),
-                  (long) p->pw_uid, (long) p->pw_gid);
-
-         /* Change the gid to be a safe one.  If we can't do that, then
-            print a warning.  We change the gid before the uid so that we
-            change the gid while still root. */
-         if (setgid (p->pw_gid) != 0)
-           {
-             hack_uid_errno = errno;
-             sprintf (hack_uid_error, "couldn't set gid to %s (%ld)",
-                      (g ? g->gr_name : "???"), (long) p->pw_gid);
-           }
+      perror ("error creating pipe:");
+      return 0;
+    }
 
 
-         /* Now change the uid to be a safe one. */
-         if (setuid (p->pw_uid) != 0)
-           {
-             hack_uid_errno = errno;
-             sprintf (hack_uid_error, "couldn't set uid to %s (%ld)",
-                      p->pw_name, (long) p->pw_uid);
-           }
-       }
+  in = fds [0];
+  out = fds [1];
+
+  switch ((int) (forked = fork ()))
+    {
+    case -1:
+      {
+        sprintf (buf, "%s: couldn't fork", blurb());
+        perror (buf);
+        saver_exit (si, 1, 0);
+      }
+    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 */
+          {
+            perror ("could not dup() a new stdout:");
+            return 0;
+          }
+        hack_subproc_environment (ssi);                /* set $DISPLAY */
+
+        execvp (av[0], av);                    /* shouldn't return. */
+
+        if (errno != ENOENT || si->prefs.verbose_p)
+          {
+            /* Ignore "no such file or directory" errors, unless verbose.
+               Issue all other exec errors, though. */
+            sprintf (buf, "%s: running %s", blurb(), av[0]);
+            perror (buf);
+          }
+        exit (1);                               /* exits fork */
+        break;
+      }
+    default:
+      {
+        int result = 0;
+        int wait_status = 0;
+
+        FILE *f = fdopen (in, "r");
+        unsigned long v = 0;
+        char c;
+
+        close (out);  /* don't need this one */
+
+        *buf = 0;
+        fgets (buf, sizeof(buf)-1, f);
+        fclose (f);
+
+        /* Wait for the child to die. */
+        waitpid (-1, &wait_status, 0);
+
+        if (1 == sscanf (buf, "0x%lx %c", &v, &c))
+          result = (int) v;
+
+        if (result == 0)
+          {
+            if (si->prefs.verbose_p)
+              {
+                int L = strlen(buf);
+                fprintf (stderr, "%s: %s did not report a GL visual!\n",
+                         blurb(), av[0]);
+
+                if (L && buf[L-1] == '\n')
+                  buf[--L] = 0;
+                if (*buf)
+                  fprintf (stderr, "%s: %s said: \"%s\"\n",
+                           blurb(), av[0], buf);
+              }
+            return 0;
+          }
+        else
+          {
+            Visual *v = id_to_visual (ssi->screen, result);
+            if (si->prefs.verbose_p)
+              fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n",
+                       blurb(), ssi->number,
+                       av[0], result,
+                       (v == ssi->default_visual ? " (default)" : ""));
+            return v;
+          }
+      }
     }
     }
-# ifndef NO_LOCKING
- else  /* disable locking if already being run as "someone else" */
-   {
-     struct passwd *p = getpwuid (getuid ());
-     if (!p ||
-        !strcmp (p->pw_name, "root") ||
-        !strcmp (p->pw_name, "nobody") ||
-        !strcmp (p->pw_name, "noaccess") ||
-        !strcmp (p->pw_name, "daemon") ||
-        !strcmp (p->pw_name, "bin") ||
-        !strcmp (p->pw_name, "sys"))
-       {
-        si->locking_disabled_p = True;
-        si->nolock_reason = hack_uid_buf;
-        sprintf (si->nolock_reason, "running as %s", p->pw_name);
-       }
-   }
-# endif /* !NO_LOCKING */
+
+  abort();
 }
 
 }
 
+
+\f
+/* Restarting the xscreensaver process from scratch. */
+
+static char **saved_argv;
+
 void
 void
-hack_uid_warn (saver_info *si)
+save_argv (int argc, char **argv)
 {
 {
-  saver_preferences *p = &si->prefs;
-
-  if (! hack_uid_error)
-    ;
-  else if (hack_uid_errno == 0)
+  saved_argv = (char **) calloc (argc+2, sizeof (char *));
+  saved_argv [argc] = 0;
+  while (argc--)
     {
     {
-      if (p->verbose_p)
-       printf ("%s: %s\n", progname, hack_uid_error);
+      int i = strlen (argv [argc]) + 1;
+      saved_argv [argc] = (char *) malloc (i);
+      memcpy (saved_argv [argc], argv [argc], i);
     }
     }
-  else
+}
+
+
+/* Re-execs the process with the arguments in saved_argv.  Does not return.
+ */
+void
+restart_process (saver_info *si)
+{
+  fflush (stdout);
+  fflush (stderr);
+  shutdown_stderr (si);
+  if (si->prefs.verbose_p)
     {
     {
-      char buf [255];
-      sprintf (buf, "%s: %s", progname, hack_uid_error);
-      if (hack_uid_errno == -1)
-       fprintf (stderr, "%s\n", buf);
-      else
-       {
-         errno = hack_uid_errno;
-         perror (buf);
-       }
+      int i;
+      fprintf (stderr, "%s: re-executing", blurb());
+      for (i = 0; saved_argv[i]; i++)
+       fprintf (stderr, " %s", saved_argv[i]);
+      fprintf (stderr, "\n");
     }
     }
-}
+  describe_uids (si, stderr);
+  fprintf (stderr, "\n");
 
 
-#endif /* !NO_SETUID */
+  fflush (stdout);
+  fflush (stderr);
+  execvp (saved_argv [0], saved_argv); /* shouldn't return */
+  {
+    char buf [512];
+    sprintf (buf, "%s: could not restart process", blurb());
+    perror(buf);
+    fflush(stderr);
+    abort();
+  }
+}