http://ftp.x.org/contrib/applications/xscreensaver-3.26.tar.gz
[xscreensaver] / driver / subprocs.c
1 /* subprocs.c --- choosing, spawning, and killing screenhacks.
2  * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998, 1999, 2000
3  *  Jamie Zawinski <jwz@jwz.org>
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  */
13
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <string.h>
21
22 #include <X11/Xlib.h>           /* not used for much... */
23
24 #ifndef ESRCH
25 # include <errno.h>
26 #endif
27
28 #include <sys/time.h>           /* sys/resource.h needs this for timeval */
29
30 #ifdef HAVE_SYS_WAIT_H
31 # include <sys/wait.h>          /* for waitpid() and associated macros */
32 #endif
33
34 #if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
35 # include <sys/resource.h>      /* for setpriority() and PRIO_PROCESS */
36 #endif
37
38 #ifdef VMS
39 # include <processes.h>
40 # include <unixio.h>            /* for close */
41 # include <unixlib.h>           /* for getpid */
42 # define pid_t int
43 # define fork  vfork
44 #endif /* VMS */
45
46 #include <signal.h>             /* for the signal names */
47
48 #if !defined(SIGCHLD) && defined(SIGCLD)
49 # define SIGCHLD SIGCLD
50 #endif
51
52 #if 0 /* putenv() is declared in stdlib.h on modern linux systems. */
53 #ifdef HAVE_PUTENV
54 extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */
55 #endif
56 #endif
57
58 extern int kill (pid_t, int);           /* signal() is in sys/signal.h... */
59
60 /* This file doesn't need the Xt headers, so stub these types out... */
61 #undef XtPointer
62 #define XtAppContext void*
63 #define XrmDatabase  void*
64 #define XtIntervalId void*
65 #define XtPointer    void*
66 #define Widget       void*
67
68 #include "xscreensaver.h"
69 #include "yarandom.h"
70 #include "visual.h"    /* for id_to_visual() */
71
72 extern saver_info *global_si_kludge;    /* I hate C so much... */
73
74 static void
75 nice_subproc (int nice_level)
76 {
77   if (nice_level == 0)
78     return;
79
80 #if defined(HAVE_NICE)
81   {
82     int old_nice = nice (0);
83     int n = nice_level - old_nice;
84     errno = 0;
85     if (nice (n) == -1 && errno != 0)
86       {
87         char buf [512];
88         sprintf (buf, "%s: nice(%d) failed", blurb(), n);
89         perror (buf);
90     }
91   }
92 #elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
93   if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
94     {
95       char buf [512];
96       sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
97                blurb(), (unsigned long) getpid(), nice_level);
98       perror (buf);
99     }
100 #else
101   fprintf (stderr,
102            "%s: don't know how to change process priority on this system.\n",
103            blurb());
104
105 #endif
106 }
107
108
109 #ifndef VMS
110
111 static void
112 exec_simple_command (const char *command)
113 {
114   char *av[1024];
115   int ac = 0;
116   char *token = strtok (strdup(command), " \t");
117   while (token)
118     {
119       av[ac++] = token;
120       token = strtok(0, " \t");
121     }
122   av[ac] = 0;
123
124   execvp (av[0], av);                   /* shouldn't return. */
125
126   {
127     char buf [512];
128     sprintf (buf, "%s: could not execute \"%s\"", blurb(), av[0]);
129     perror (buf);
130
131     if (errno == ENOENT &&
132         (token = getenv("PATH")))
133       {
134 # ifndef PATH_MAX
135 #  ifdef MAXPATHLEN
136 #   define PATH_MAX MAXPATHLEN
137 #  else
138 #   define PATH_MAX 2048
139 #  endif
140 # endif
141         char path[PATH_MAX];
142         fprintf (stderr, "\n");
143         *path = 0;
144 # if defined(HAVE_GETCWD)
145         getcwd (path, sizeof(path));
146 # elif defined(HAVE_GETWD)
147         getwd (path);
148 # endif
149         if (*path)
150           fprintf (stderr, "    Current directory is: %s\n", path);
151         fprintf (stderr, "    PATH is:\n");
152         token = strtok (strdup(token), ":");
153         while (token)
154           {
155             fprintf (stderr, "        %s\n", token);
156             token = strtok(0, ":");
157           }
158         fprintf (stderr, "\n");
159       }
160   }
161   fflush(stderr);
162   fflush(stdout);
163   exit (1);     /* Note that this only exits a child fork.  */
164 }
165
166
167 static void
168 exec_complex_command (const char *shell, const char *command)
169 {
170   char *av[5];
171   int ac = 0;
172   char *command2 = (char *) malloc (strlen (command) + 10);
173   const char *s;
174   int got_eq = 0;
175   const char *after_vars;
176
177   /* Skip leading whitespace.
178    */
179   while (*command == ' ' || *command == '\t')
180     command++;
181
182   /* If the string has a series of tokens with "=" in them at them, set
183      `after_vars' to point into the string after those tokens and any
184      trailing whitespace.  Otherwise, after_vars == command.
185    */
186   after_vars = command;
187   for (s = command; *s; s++)
188     {
189       if (*s == '=') got_eq = 1;
190       else if (*s == ' ')
191         {
192           if (got_eq)
193             {
194               while (*s == ' ' || *s == '\t')
195                 s++;
196               after_vars = s;
197               got_eq = 0;
198             }
199           else
200             break;
201         }
202     }
203
204   *command2 = 0;
205   strncat (command2, command, after_vars - command);
206   strcat (command2, "exec ");
207   strcat (command2, after_vars);
208
209   /* We have now done these transformations:
210      "foo -x -y"               ==>  "exec foo -x -y"
211      "BLAT=foop      foo -x"   ==>  "BLAT=foop      exec foo -x"
212      "BLAT=foop A=b  foo -x"   ==>  "BLAT=foop A=b  exec foo -x"
213    */
214
215
216   /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
217   av [ac++] = (char *) shell;
218   av [ac++] = "-c";
219   av [ac++] = command2;
220   av [ac]   = 0;
221
222   execvp (av[0], av);                   /* shouldn't return. */
223
224   {
225     char buf [512];
226     sprintf (buf, "%s: execvp(\"%s\") failed", blurb(), av[0]);
227     perror (buf);
228     fflush(stderr);
229     fflush(stdout);
230     exit (1);   /* Note that this only exits a child fork.  */
231   }
232 }
233
234 #else  /* VMS */
235
236 static void
237 exec_vms_command (const char *command)
238 {
239   system (command);
240   fflush (stderr);
241   fflush (stdout);
242   exit (1);     /* Note that this only exits a child fork.  */
243 }
244
245 #endif /* !VMS */
246
247
248 static void
249 exec_screenhack (saver_info *si, const char *command)
250 {
251   /* I don't believe what a sorry excuse for an operating system UNIX is!
252
253      - I want to spawn a process.
254      - I want to know it's pid so that I can kill it.
255      - I would like to receive a message when it dies of natural causes.
256      - I want the spawned process to have user-specified arguments.
257
258      If shell metacharacters are present (wildcards, backquotes, etc), the
259      only way to parse those arguments is to run a shell to do the parsing
260      for you.
261
262      And the only way to know the pid of the process is to fork() and exec()
263      it in the spawned side of the fork.
264
265      But if you're running a shell to parse your arguments, this gives you
266      the pid of the *shell*, not the pid of the *process* that you're
267      actually interested in, which is an *inferior* of the shell.  This also
268      means that the SIGCHLD you get applies to the shell, not its inferior.
269      (Why isn't that sufficient?  I don't remember any more, but it turns
270      out that it isn't.)
271
272      So, the only solution, when metacharacters are present, is to force the
273      shell to exec() its inferior.  What a fucking hack!  We prepend "exec "
274      to the command string, and hope it doesn't contain unquoted semicolons
275      or ampersands (we don't search for them, because we don't want to
276      prohibit their use in quoted strings (messages, for example) and parsing
277      out the various quote characters is too much of a pain.)
278
279      (Actually, Clint Wong <clint@jts.com> points out that process groups
280      might be used to take care of this problem; this may be worth considering
281      some day, except that, 1: this code works now, so why fix it, and 2: from
282      what I've seen in Emacs, dealing with process groups isn't especially
283      portable.)
284    */
285   saver_preferences *p = &si->prefs;
286
287 #ifndef VMS
288   Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
289   /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
290
291   if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0)
292     {
293       /* If you're thinking of commenting this out, think again.
294          If you do so, you will open a security hole.  Mail jwz
295          so that he may enlighten you as to the error of your ways.
296        */
297       fprintf (stderr, "%s: we're still running as root!  Disaster!\n",
298                blurb());
299       saver_exit (si, 1, 0);
300     }
301
302   if (p->verbose_p)
303     fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
304              blurb(), command, (unsigned long) getpid (),
305              (hairy_p ? " (via shell)" : ""));
306
307   if (hairy_p)
308     /* If it contains any shell metacharacters, do it the hard way,
309        and fork a shell to parse the arguments for us. */
310     exec_complex_command (p->shell, command);
311   else
312     /* Otherwise, we can just exec the program directly. */
313     exec_simple_command (command);
314
315 #else /* VMS */
316   if (p->verbose_p)
317     fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
318              blurb(), command, getpid());
319   exec_vms_command (command);
320 #endif /* VMS */
321
322   abort();      /* that shouldn't have returned. */
323 }
324
325
326 \f
327 /* Management of child processes, and de-zombification.
328  */
329
330 enum job_status {
331   job_running,  /* the process is still alive */
332   job_stopped,  /* we have sent it a STOP signal */
333   job_killed,   /* we have sent it a TERM signal */
334   job_dead      /* we have wait()ed for it, and it's dead -- this state only
335                    occurs so that we can avoid calling free() from a signal
336                    handler.  Shortly after going into this state, the list
337                    element will be removed. */
338 };
339
340 struct screenhack_job {
341   char *name;
342   pid_t pid;
343   enum job_status status;
344   struct screenhack_job *next;
345 };
346
347 static struct screenhack_job *jobs = 0;
348
349 /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
350 void
351 show_job_list (void)
352 {
353   struct screenhack_job *job;
354   fprintf(stderr, "%s: job list:\n", blurb());
355   for (job = jobs; job; job = job->next)
356     fprintf (stderr, "  %5ld: (%s) %s\n",
357              (long) job->pid,
358              (job->status == job_running ? "running" :
359               job->status == job_stopped ? "stopped" :
360               job->status == job_killed  ? " killed" :
361               job->status == job_dead    ? "   dead" : "    ???"),
362              job->name);
363   fprintf (stderr, "\n");
364 }
365
366
367 static void clean_job_list (void);
368
369 static struct screenhack_job *
370 make_job (pid_t pid, const char *cmd)
371 {
372   struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
373
374   static char name [1024];
375   const char *in = cmd;
376   char *out = name;
377   int got_eq = 0;
378   int first = 1;
379
380   clean_job_list();
381
382  AGAIN:
383   while (isspace(*in)) in++;            /* skip whitespace */
384   while (!isspace(*in) && *in != ':') {
385     if (*in == '=') got_eq = 1;
386     *out++ = *in++;                     /* snarf first token */
387   }
388
389   if (got_eq)                           /* if the first token was FOO=bar */
390     {                                   /* then get the next token instead. */
391       got_eq = 0;
392       out = name;
393       first = 0;
394       goto AGAIN;
395     }
396
397   while (isspace(*in)) in++;            /* skip whitespace */
398   *out = 0;
399
400   job->name = strdup(name);
401   job->pid = pid;
402   job->status = job_running;
403   job->next = jobs;
404   jobs = job;
405
406   return jobs;
407 }
408
409
410 static void
411 free_job (struct screenhack_job *job)
412 {
413   if (!job)
414     return;
415   else if (job == jobs)
416     jobs = jobs->next;
417   else
418     {
419       struct screenhack_job *job2, *prev;
420       for (prev = 0, job2 = jobs;
421            job2;
422            prev = job2, job2 = job2->next)
423         if (job2 == job)
424           {
425             prev->next = job->next;
426             break;
427           }
428     }
429   free(job->name);
430   free(job);
431 }
432
433
434 /* Cleans out dead jobs from the jobs list -- this must only be called
435    from the main thread, not from a signal handler. 
436  */
437 static void
438 clean_job_list (void)
439 {
440   struct screenhack_job *job, *prev, *next;
441   for (prev = 0, job = jobs, next = (job ? job->next : 0);
442        job;
443        prev = job, job = next, next = (job ? job->next : 0))
444     {
445       if (job->status == job_dead)
446         {
447           if (prev)
448             prev->next = next;
449           free_job (job);
450           job = prev;
451         }
452     }
453 }
454
455
456 static struct screenhack_job *
457 find_job (pid_t pid)
458 {
459   struct screenhack_job *job;
460   for (job = jobs; job; job = job->next)
461     if (job->pid == pid)
462       return job;
463   return 0;
464 }
465
466 static void await_dying_children (saver_info *si);
467 #ifndef VMS
468 static void describe_dead_child (saver_info *, pid_t, int wait_status);
469 #endif
470
471
472 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
473    Don't alter this directly -- use block_sigchld() / unblock_sigchld().
474  */
475 static int block_sigchld_handler = 0;
476
477
478 static void
479 block_sigchld (void)
480 {
481 #ifdef HAVE_SIGACTION
482   sigset_t child_set;
483   sigemptyset (&child_set);
484   sigaddset (&child_set, SIGCHLD);
485   sigprocmask (SIG_BLOCK, &child_set, 0);
486 #endif /* HAVE_SIGACTION */
487
488   block_sigchld_handler++;
489 }
490
491 static void
492 unblock_sigchld (void)
493 {
494 #ifdef HAVE_SIGACTION
495   sigset_t child_set;
496   sigemptyset(&child_set);
497   sigaddset(&child_set, SIGCHLD);
498   sigprocmask(SIG_UNBLOCK, &child_set, 0);
499 #endif /* HAVE_SIGACTION */
500
501   block_sigchld_handler--;
502 }
503
504 static int
505 kill_job (saver_info *si, pid_t pid, int signal)
506 {
507   saver_preferences *p = &si->prefs;
508   struct screenhack_job *job;
509   int status = -1;
510
511   clean_job_list();
512
513   if (block_sigchld_handler)
514     /* This function should not be called from the signal handler. */
515     abort();
516
517   block_sigchld();                      /* we control the horizontal... */
518
519   job = find_job (pid);
520   if (!job ||
521       !job->pid ||
522       job->status == job_killed)
523     {
524       if (p->verbose_p)
525         fprintf (stderr, "%s: no child %ld to signal!\n",
526                  blurb(), (long) pid);
527       goto DONE;
528     }
529
530   switch (signal) {
531   case SIGTERM: job->status = job_killed;  break;
532 #ifdef SIGSTOP
533     /* #### there must be a way to do this on VMS... */
534   case SIGSTOP: job->status = job_stopped; break;
535   case SIGCONT: job->status = job_running; break;
536 #endif /* SIGSTOP */
537   default: abort();
538   }
539
540 #ifdef SIGSTOP
541   if (p->verbose_p)
542     fprintf (stderr, "%s: %s pid %lu.\n", blurb(),
543              (signal == SIGTERM ? "killing" :
544               signal == SIGSTOP ? "suspending" :
545               signal == SIGCONT ? "resuming" : "signalling"),
546              (unsigned long) job->pid);
547 #else  /* !SIGSTOP */
548   if (p->verbose_p)
549     fprintf (stderr, "%s: %s pid %lu.\n", blurb(), "killing",
550              (unsigned long) job->pid);
551 #endif /* !SIGSTOP */
552
553   status = kill (job->pid, signal);
554
555   if (p->verbose_p && status < 0)
556     {
557       if (errno == ESRCH)
558         fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
559                  blurb(), job->pid, job->name);
560       else
561         {
562           char buf [1024];
563           sprintf (buf, "%s: couldn't kill child process %lu (%s)",
564                    blurb(), job->pid, job->name);
565           perror (buf);
566         }
567     }
568
569   await_dying_children (si);
570
571  DONE:
572   unblock_sigchld();
573   if (block_sigchld_handler < 0)
574     abort();
575
576   clean_job_list();
577   return status;
578 }
579
580
581 #ifdef SIGCHLD
582 static RETSIGTYPE
583 sigchld_handler (int sig)
584 {
585   saver_info *si = global_si_kludge;    /* I hate C so much... */
586
587   if (si->prefs.debug_p)
588     fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
589             (block_sigchld_handler ? " (blocked)" : ""));
590
591   if (block_sigchld_handler < 0)
592     abort();
593   else if (block_sigchld_handler == 0)
594     {
595       block_sigchld();
596       await_dying_children (si);
597       unblock_sigchld();
598     }
599
600   init_sigchld();
601 }
602 #endif /* SIGCHLD */
603
604
605 #ifndef VMS
606 static void
607 await_dying_children (saver_info *si)
608 {
609   while (1)
610     {
611       int wait_status = 0;
612       pid_t kid;
613
614       errno = 0;
615       kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
616
617       if (si->prefs.debug_p)
618         {
619           if (kid < 0 && errno)
620             fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
621                      (long) kid, errno);
622           else
623             fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
624                      (long) kid);
625         }
626
627       /* 0 means no more children to reap.
628          -1 means error -- except "interrupted system call" isn't a "real"
629          error, so if we get that, we should just try again. */
630       if (kid == 0 ||
631           (kid < 0 && errno != EINTR))
632         break;
633
634       describe_dead_child (si, kid, wait_status);
635     }
636 }
637
638
639 static void
640 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
641 {
642   int i;
643   saver_preferences *p = &si->prefs;
644   struct screenhack_job *job = find_job (kid);
645   const char *name = job ? job->name : "<unknown>";
646
647   if (WIFEXITED (wait_status))
648     {
649       int exit_status = WEXITSTATUS (wait_status);
650
651       /* Treat exit code as a signed 8-bit quantity. */
652       if (exit_status & 0x80) exit_status |= ~0xFF;
653
654       /* One might assume that exiting with non-0 means something went wrong.
655          But that loser xswarm exits with the code that it was killed with, so
656          it *always* exits abnormally.  Treat abnormal exits as "normal" (don't
657          mention them) if we've just killed the subprocess.  But mention them
658          if they happen on their own.
659        */
660       if (!job ||
661           (exit_status != 0 &&
662            (p->verbose_p || job->status != job_killed)))
663         fprintf (stderr,
664                  "%s: child pid %lu (%s) exited abnormally (code %d).\n",
665                  blurb(), (unsigned long) kid, name, exit_status);
666       else if (p->verbose_p)
667         fprintf (stderr, "%s: child pid %lu (%s) exited normally.\n",
668                  blurb(), (unsigned long) kid, name);
669
670       if (job)
671         job->status = job_dead;
672     }
673   else if (WIFSIGNALED (wait_status))
674     {
675       if (p->verbose_p ||
676           !job ||
677           job->status != job_killed ||
678           WTERMSIG (wait_status) != SIGTERM)
679         fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
680                  blurb(), (unsigned long) kid, name,
681                  signal_name (WTERMSIG(wait_status)));
682
683       if (job)
684         job->status = job_dead;
685     }
686   else if (WIFSTOPPED (wait_status))
687     {
688       if (p->verbose_p)
689         fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
690                  blurb(), (unsigned long) kid, name,
691                  signal_name (WSTOPSIG (wait_status)));
692
693       if (job)
694         job->status = job_stopped;
695     }
696   else
697     {
698       fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
699                blurb(), (unsigned long) kid, name);
700       if (job)
701         job->status = job_dead;
702     }
703
704   /* Clear out the pid so that screenhack_running_p() knows it's dead.
705    */
706   if (!job || job->status == job_dead)
707     for (i = 0; i < si->nscreens; i++)
708       {
709         saver_screen_info *ssi = &si->screens[i];
710         if (kid == ssi->pid)
711           ssi->pid = 0;
712       }
713 }
714
715 #else  /* VMS */
716 static void await_dying_children (saver_info *si) { return; }
717 #endif /* VMS */
718
719
720 void
721 init_sigchld (void)
722 {
723 #ifdef SIGCHLD
724
725 # ifdef HAVE_SIGACTION  /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
726
727   static Bool sigchld_initialized_p = 0;
728   if (!sigchld_initialized_p)
729     {
730       struct sigaction action, old;
731
732       action.sa_handler = sigchld_handler;
733       sigemptyset(&action.sa_mask);
734       action.sa_flags = 0;
735
736       if (sigaction(SIGCHLD, &action, &old) < 0)
737         {
738           char buf [255];
739           sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
740           perror (buf);
741         }
742       sigchld_initialized_p = True;
743     }
744
745 # else  /* !HAVE_SIGACTION */
746
747   if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
748     {
749       char buf [255];
750       sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
751       perror (buf);
752     }
753 # endif /* !HAVE_SIGACTION */
754 #endif /* SIGCHLD */
755 }
756
757
758
759 \f
760
761 static Bool
762 select_visual_of_hack (saver_screen_info *ssi, screenhack *hack)
763 {
764   saver_info *si = ssi->global;
765   saver_preferences *p = &si->prefs;
766   Bool selected;
767
768   if (hack->visual && *hack->visual)
769     selected = select_visual(ssi, hack->visual);
770   else
771     selected = select_visual(ssi, 0);
772
773   if (!selected && (p->verbose_p || si->demoing_p))
774     fprintf (stderr,
775              (si->demoing_p
776               ? "%s: warning, no \"%s\" visual for \"%s\".\n"
777               : "%s: no \"%s\" visual; skipping \"%s\".\n"),
778              blurb(),
779              (hack->visual && *hack->visual ? hack->visual : "???"),
780              hack->command);
781
782   return selected;
783 }
784
785
786 static void
787 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
788 {
789   saver_info *si = ssi->global;
790   saver_preferences *p = &si->prefs;
791   raise_window (si, first_time_p, True, False);
792   XFlush (si->dpy);
793
794   if (p->screenhacks_count)
795     {
796       screenhack *hack;
797       pid_t forked;
798       char buf [255];
799       int new_hack;
800       int retry_count = 0;
801       Bool force = False;
802
803     AGAIN:
804
805       if (p->screenhacks_count == 1)
806         /* If there is only one hack in the list, there is no choice. */
807         new_hack = 0;
808
809       else if (si->selection_mode == -1)
810         /* Select the next hack, wrapping. */
811         new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
812
813       else if (si->selection_mode == -2)
814         /* Select the previous hack, wrapping. */
815         new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
816                     % p->screenhacks_count);
817
818       else if (si->selection_mode > 0)
819         /* Select a specific hack, by number.  No negotiation. */
820         {
821           new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
822           force = True;
823         }
824       else
825         {
826           /* Select a random hack (but not the one we just ran.) */
827           while ((new_hack = random () % p->screenhacks_count)
828                  == ssi->current_hack)
829             ;
830         }
831
832       ssi->current_hack = new_hack;
833       hack = p->screenhacks[ssi->current_hack];
834
835       /* If the hack is disabled, or there is no visual for this hack,
836          then try again (move forward, or backward, or re-randomize.)
837          Unless this hack was specified explicitly, in which case,
838          use it regardless.
839        */
840       if (force)
841         select_visual_of_hack (ssi, hack);
842         
843       if (!force &&
844           (!hack->enabled_p ||
845            !select_visual_of_hack (ssi, hack)))
846         {
847           if (++retry_count > (p->screenhacks_count*4))
848             {
849               /* Uh, oops.  Odds are, there are no suitable visuals,
850                  and we're looping.  Give up.  (This is totally lame,
851                  what we should do is make a list of suitable hacks at
852                  the beginning, then only loop over them.)
853               */
854               if (p->verbose_p)
855                 fprintf(stderr,
856                         "%s: no suitable visuals for these programs.\n",
857                         blurb());
858               return;
859             }
860           else
861             goto AGAIN;
862         }
863
864       /* Turn off "next" and "prev" modes now, but "demo" mode is only
865          turned off by explicit action.
866        */
867       if (si->selection_mode < 0)
868         si->selection_mode = 0;
869
870       switch ((int) (forked = fork ()))
871         {
872         case -1:
873           sprintf (buf, "%s: couldn't fork", blurb());
874           perror (buf);
875           restore_real_vroot (si);
876           saver_exit (si, 1, 0);
877
878         case 0:
879           close (ConnectionNumber (si->dpy));   /* close display fd */
880           nice_subproc (p->nice_inferior);      /* change process priority */
881           hack_subproc_environment (ssi);       /* set $DISPLAY */
882           exec_screenhack (si, hack->command);  /* this does not return */
883           abort();
884           break;
885
886         default:
887           ssi->pid = forked;
888           (void) make_job (forked, hack->command);
889           break;
890         }
891     }
892 }
893
894
895 void
896 spawn_screenhack (saver_info *si, Bool first_time_p)
897 {
898   if (monitor_powered_on_p (si))
899     {
900       int i;
901       for (i = 0; i < si->nscreens; i++)
902         {
903           saver_screen_info *ssi = &si->screens[i];
904           spawn_screenhack_1 (ssi, first_time_p);
905         }
906     }
907   else if (si->prefs.verbose_p)
908     fprintf (stderr,
909              "%s: X says monitor has powered down; "
910              "not launching a hack.\n", blurb());
911
912   store_saver_status (si);  /* store current hack numbers */
913 }
914
915
916 void
917 kill_screenhack (saver_info *si)
918 {
919   int i;
920   for (i = 0; i < si->nscreens; i++)
921     {
922       saver_screen_info *ssi = &si->screens[i];
923       if (ssi->pid)
924         kill_job (si, ssi->pid, SIGTERM);
925       ssi->pid = 0;
926     }
927 }
928
929
930 void
931 suspend_screenhack (saver_info *si, Bool suspend_p)
932 {
933 #ifdef SIGSTOP  /* older VMS doesn't have it... */
934   int i;
935   for (i = 0; i < si->nscreens; i++)
936     {
937       saver_screen_info *ssi = &si->screens[i];
938       if (ssi->pid)
939         kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
940     }
941 #endif /* SIGSTOP */
942 }
943
944
945 /* Called when we're exiting abnormally, to kill off the subproc. */
946 void
947 emergency_kill_subproc (saver_info *si)
948 {
949   int i;
950 #ifdef SIGCHLD
951   signal (SIGCHLD, SIG_IGN);
952 #endif /* SIGCHLD */
953
954   for (i = 0; i < si->nscreens; i++)
955     {
956       saver_screen_info *ssi = &si->screens[i];
957       if (ssi->pid)
958         {
959           kill_job (si, ssi->pid, SIGTERM);
960           ssi->pid = 0;
961         }
962     }
963 }
964
965 Bool
966 screenhack_running_p (saver_info *si)
967 {
968   Bool any_running_p = False;
969   int i;
970   for (i = 0; i < si->nscreens; i++)
971     {
972       saver_screen_info *ssi = &si->screens[i];
973       if (ssi->pid) any_running_p = True;
974     }
975   return any_running_p;
976 }
977
978 \f
979 /* Environment variables. */
980
981
982 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
983    is defined, the xscreensaver daemon will search that directory for hacks.
984  */
985 void
986 hack_environment (saver_info *si)
987 {
988 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
989   static const char *def_path = DEFAULT_PATH_PREFIX;
990   if (def_path && *def_path)
991     {
992       const char *opath = getenv("PATH");
993       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
994       strcpy (npath, "PATH=");
995       strcat (npath, def_path);
996       strcat (npath, ":");
997       strcat (npath, opath);
998
999       if (putenv (npath))
1000         abort ();
1001     }
1002 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
1003 }
1004
1005
1006 void
1007 hack_subproc_environment (saver_screen_info *ssi)
1008 {
1009   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
1010      the spawned processes inherit is correct.  First, it must be on the same
1011      host and display as the value of -display passed in on our command line
1012      (which is not necessarily the same as what our $DISPLAY variable is.)
1013      Second, the screen number in the $DISPLAY passed to the subprocess should
1014      be the screen on which this particular hack is running -- not the display
1015      specification which the driver itself is using, since the driver ignores
1016      its screen number and manages all existing screens.
1017    */
1018   saver_info *si = ssi->global;
1019   const char *odpy = DisplayString (si->dpy);
1020   char *ndpy = (char *) malloc(strlen(odpy) + 20);
1021   int screen_number;
1022   char *s;
1023
1024   for (screen_number = 0; screen_number < si->nscreens; screen_number++)
1025     if (ssi == &si->screens[screen_number])
1026       break;
1027
1028   strcpy (ndpy, "DISPLAY=");
1029   s = ndpy + strlen(ndpy);
1030   strcpy (s, odpy);
1031
1032   while (*s && *s != ':') s++;                  /* skip to colon */
1033   while (*s == ':') s++;                        /* skip over colons */
1034   while (isdigit(*s)) s++;                      /* skip over dpy number */
1035   while (*s == '.') s++;                        /* skip over dot */
1036   if (s[-1] != '.') *s++ = '.';                 /* put on a dot */
1037   sprintf(s, "%d", screen_number);              /* put on screen number */
1038
1039   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1040      any more, right?  It's not Posix, but everyone seems to have it. */
1041 #ifdef HAVE_PUTENV
1042   if (putenv (ndpy))
1043     abort ();
1044 #endif /* HAVE_PUTENV */
1045 }
1046
1047 \f
1048 /* GL crap */
1049
1050 Visual *
1051 get_best_gl_visual (saver_screen_info *ssi)
1052 {
1053   saver_info *si = ssi->global;
1054   pid_t forked;
1055   int fds [2];
1056   int in, out;
1057   char buf[1024];
1058
1059   char *av[10];
1060   int ac = 0;
1061
1062   av[ac++] = "xscreensaver-gl-helper";
1063   av[ac] = 0;
1064
1065   if (pipe (fds))
1066     {
1067       perror ("error creating pipe:");
1068       return 0;
1069     }
1070
1071   in = fds [0];
1072   out = fds [1];
1073
1074   switch ((int) (forked = fork ()))
1075     {
1076     case -1:
1077       {
1078         sprintf (buf, "%s: couldn't fork", blurb());
1079         perror (buf);
1080         saver_exit (si, 1, 0);
1081       }
1082     case 0:
1083       {
1084         int stdout_fd = 1;
1085
1086         close (in);  /* don't need this one */
1087         close (ConnectionNumber (si->dpy));     /* close display fd */
1088
1089         if (dup2 (out, stdout_fd) < 0)          /* pipe stdout */
1090           {
1091             perror ("could not dup() a new stdout:");
1092             return 0;
1093           }
1094         hack_subproc_environment (ssi);         /* set $DISPLAY */
1095
1096         execvp (av[0], av);                     /* shouldn't return. */
1097
1098         if (errno != ENOENT || si->prefs.verbose_p)
1099           {
1100             /* Ignore "no such file or directory" errors, unless verbose.
1101                Issue all other exec errors, though. */
1102             sprintf (buf, "%s: running %s", blurb(), av[0]);
1103             perror (buf);
1104           }
1105         exit (1);                               /* exits fork */
1106         break;
1107       }
1108     default:
1109       {
1110         int result = 0;
1111         int wait_status = 0;
1112
1113         FILE *f = fdopen (in, "r");
1114         unsigned long v = 0;
1115         char c;
1116
1117         close (out);  /* don't need this one */
1118
1119         *buf = 0;
1120         fgets (buf, sizeof(buf)-1, f);
1121         fclose (f);
1122
1123         /* Wait for the child to die. */
1124         waitpid (-1, &wait_status, 0);
1125
1126         if (1 == sscanf (buf, "0x%x %c", &v, &c))
1127           result = (int) v;
1128
1129         if (result == 0)
1130           {
1131             if (si->prefs.verbose_p)
1132               fprintf (stderr, "%s: %s did not report a GL visual!\n",
1133                        blurb(), av[0]);
1134             return 0;
1135           }
1136         else
1137           {
1138             Visual *v = id_to_visual (ssi->screen, result);
1139             if (si->prefs.verbose_p)
1140               fprintf (stderr, "%s: %s says the GL visual is 0x%X%s.\n",
1141                        blurb(), av[0], result,
1142                        (v == ssi->default_visual ? " (the default)" : ""));
1143             return v;
1144           }
1145       }
1146     }
1147
1148   abort();
1149 }
1150
1151
1152 \f
1153 /* Restarting the xscreensaver process from scratch. */
1154
1155 static char **saved_argv;
1156
1157 void
1158 save_argv (int argc, char **argv)
1159 {
1160   saved_argv = (char **) calloc (argc+2, sizeof (char *));
1161   saved_argv [argc] = 0;
1162   while (argc--)
1163     {
1164       int i = strlen (argv [argc]) + 1;
1165       saved_argv [argc] = (char *) malloc (i);
1166       memcpy (saved_argv [argc], argv [argc], i);
1167     }
1168 }
1169
1170
1171 /* Re-execs the process with the arguments in saved_argv.
1172    Does not return unless there was an error.
1173  */
1174 void
1175 restart_process (saver_info *si)
1176 {
1177   if (si->prefs.verbose_p)
1178     {
1179       int i;
1180       fprintf (real_stderr, "%s: re-executing", blurb());
1181       for (i = 0; saved_argv[i]; i++)
1182         fprintf (real_stderr, " %s", saved_argv[i]);
1183       fprintf (real_stderr, "\n");
1184     }
1185   describe_uids (si, real_stderr);
1186   fprintf (real_stderr, "\n");
1187
1188   fflush (real_stdout);
1189   fflush (real_stderr);
1190   execvp (saved_argv [0], saved_argv);  /* shouldn't return */
1191   {
1192     char buf [512];
1193     sprintf (buf, "%s: could not restart process", blurb());
1194     perror(buf);
1195     fflush(stderr);
1196   }
1197   XBell(si->dpy, 0);
1198 }