http://ftp.x.org/contrib/applications/xscreensaver-3.20.tar.gz
[xscreensaver] / driver / subprocs.c
1 /* subprocs.c --- choosing, spawning, and killing screenhacks.
2  * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998
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
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   if (first && *in == ':')              /* token was a visual name; skip it. */
399     {
400       out = name;
401       first = 0;
402       goto AGAIN;
403     }
404   *out = 0;
405
406   job->name = strdup(name);
407   job->pid = pid;
408   job->status = job_running;
409   job->next = jobs;
410   jobs = job;
411
412   return jobs;
413 }
414
415
416 static void
417 free_job (struct screenhack_job *job)
418 {
419   if (!job)
420     return;
421   else if (job == jobs)
422     jobs = jobs->next;
423   else
424     {
425       struct screenhack_job *job2, *prev;
426       for (prev = 0, job2 = jobs;
427            job2;
428            prev = job2, job2 = job2->next)
429         if (job2 == job)
430           {
431             prev->next = job->next;
432             break;
433           }
434     }
435   free(job->name);
436   free(job);
437 }
438
439
440 /* Cleans out dead jobs from the jobs list -- this must only be called
441    from the main thread, not from a signal handler. 
442  */
443 static void
444 clean_job_list (void)
445 {
446   struct screenhack_job *job, *prev, *next;
447   for (prev = 0, job = jobs, next = (job ? job->next : 0);
448        job;
449        prev = job, job = next, next = (job ? job->next : 0))
450     {
451       if (job->status == job_dead)
452         {
453           if (prev)
454             prev->next = next;
455           free_job (job);
456           job = prev;
457         }
458     }
459 }
460
461
462 static struct screenhack_job *
463 find_job (pid_t pid)
464 {
465   struct screenhack_job *job;
466   for (job = jobs; job; job = job->next)
467     if (job->pid == pid)
468       return job;
469   return 0;
470 }
471
472 static void await_dying_children (saver_info *si);
473 #ifndef VMS
474 static void describe_dead_child (saver_info *, pid_t, int wait_status);
475 #endif
476
477
478 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
479    Don't alter this directly -- use block_sigchld() / unblock_sigchld().
480  */
481 static int block_sigchld_handler = 0;
482
483
484 static void
485 block_sigchld (void)
486 {
487 #ifdef HAVE_SIGACTION
488   sigset_t child_set;
489   sigemptyset (&child_set);
490   sigaddset (&child_set, SIGCHLD);
491   sigprocmask (SIG_BLOCK, &child_set, 0);
492 #endif /* HAVE_SIGACTION */
493
494   block_sigchld_handler++;
495 }
496
497 static void
498 unblock_sigchld (void)
499 {
500 #ifdef HAVE_SIGACTION
501   sigset_t child_set;
502   sigemptyset(&child_set);
503   sigaddset(&child_set, SIGCHLD);
504   sigprocmask(SIG_UNBLOCK, &child_set, 0);
505 #endif /* HAVE_SIGACTION */
506
507   block_sigchld_handler--;
508 }
509
510 static int
511 kill_job (saver_info *si, pid_t pid, int signal)
512 {
513   saver_preferences *p = &si->prefs;
514   struct screenhack_job *job;
515   int status = -1;
516
517   clean_job_list();
518
519   if (block_sigchld_handler)
520     /* This function should not be called from the signal handler. */
521     abort();
522
523   block_sigchld();                      /* we control the horizontal... */
524
525   job = find_job (pid);
526   if (!job ||
527       !job->pid ||
528       job->status == job_killed)
529     {
530       if (p->verbose_p)
531         fprintf (stderr, "%s: no child %ld to signal!\n",
532                  blurb(), (long) pid);
533       goto DONE;
534     }
535
536   switch (signal) {
537   case SIGTERM: job->status = job_killed;  break;
538 #ifdef SIGSTOP
539     /* #### there must be a way to do this on VMS... */
540   case SIGSTOP: job->status = job_stopped; break;
541   case SIGCONT: job->status = job_running; break;
542 #endif /* SIGSTOP */
543   default: abort();
544   }
545
546 #ifdef SIGSTOP
547   if (p->verbose_p)
548     fprintf (stderr, "%s: %s pid %lu.\n", blurb(),
549              (signal == SIGTERM ? "killing" :
550               signal == SIGSTOP ? "suspending" :
551               signal == SIGCONT ? "resuming" : "signalling"),
552              (unsigned long) job->pid);
553 #else  /* !SIGSTOP */
554   if (p->verbose_p)
555     fprintf (stderr, "%s: %s pid %lu.\n", blurb(), "killing",
556              (unsigned long) job->pid);
557 #endif /* !SIGSTOP */
558
559   status = kill (job->pid, signal);
560
561   if (p->verbose_p && status < 0)
562     {
563       if (errno == ESRCH)
564         fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
565                  blurb(), job->pid, job->name);
566       else
567         {
568           char buf [1024];
569           sprintf (buf, "%s: couldn't kill child process %lu (%s)",
570                    blurb(), job->pid, job->name);
571           perror (buf);
572         }
573     }
574
575   await_dying_children (si);
576
577  DONE:
578   unblock_sigchld();
579   if (block_sigchld_handler < 0)
580     abort();
581
582   clean_job_list();
583   return status;
584 }
585
586
587 #ifdef SIGCHLD
588 static RETSIGTYPE
589 sigchld_handler (int sig)
590 {
591   saver_info *si = global_si_kludge;    /* I hate C so much... */
592
593   if (si->prefs.debug_p)
594     fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
595             (block_sigchld_handler ? " (blocked)" : ""));
596
597   if (block_sigchld_handler < 0)
598     abort();
599   else if (block_sigchld_handler == 0)
600     {
601       block_sigchld();
602       await_dying_children (si);
603       unblock_sigchld();
604     }
605
606   init_sigchld();
607 }
608 #endif /* SIGCHLD */
609
610
611 #ifndef VMS
612 static void
613 await_dying_children (saver_info *si)
614 {
615   while (1)
616     {
617       int wait_status = 0;
618       pid_t kid;
619
620       errno = 0;
621       kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
622
623       if (si->prefs.debug_p)
624         {
625           if (kid < 0 && errno)
626             fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
627                      (long) kid, errno);
628           else
629             fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
630                      (long) kid);
631         }
632
633       /* 0 means no more children to reap.
634          -1 means error -- except "interrupted system call" isn't a "real"
635          error, so if we get that, we should just try again. */
636       if (kid == 0 ||
637           (kid < 0 && errno != EINTR))
638         break;
639
640       describe_dead_child (si, kid, wait_status);
641     }
642 }
643
644
645 static void
646 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
647 {
648   int i;
649   saver_preferences *p = &si->prefs;
650   struct screenhack_job *job = find_job (kid);
651   const char *name = job ? job->name : "<unknown>";
652
653   if (WIFEXITED (wait_status))
654     {
655       int exit_status = WEXITSTATUS (wait_status);
656
657       /* Treat exit code as a signed 8-bit quantity. */
658       if (exit_status & 0x80) exit_status |= ~0xFF;
659
660       /* One might assume that exiting with non-0 means something went wrong.
661          But that loser xswarm exits with the code that it was killed with, so
662          it *always* exits abnormally.  Treat abnormal exits as "normal" (don't
663          mention them) if we've just killed the subprocess.  But mention them
664          if they happen on their own.
665        */
666       if (!job ||
667           (exit_status != 0 &&
668            (p->verbose_p || job->status != job_killed)))
669         fprintf (stderr,
670                  "%s: child pid %lu (%s) exited abnormally (code %d).\n",
671                  blurb(), (unsigned long) kid, name, exit_status);
672       else if (p->verbose_p)
673         fprintf (stderr, "%s: child pid %lu (%s) exited normally.\n",
674                  blurb(), (unsigned long) kid, name);
675
676       if (job)
677         job->status = job_dead;
678     }
679   else if (WIFSIGNALED (wait_status))
680     {
681       if (p->verbose_p ||
682           !job ||
683           job->status != job_killed ||
684           WTERMSIG (wait_status) != SIGTERM)
685         fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
686                  blurb(), (unsigned long) kid, name,
687                  signal_name (WTERMSIG(wait_status)));
688
689       if (job)
690         job->status = job_dead;
691     }
692   else if (WIFSTOPPED (wait_status))
693     {
694       if (p->verbose_p)
695         fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
696                  blurb(), (unsigned long) kid, name,
697                  signal_name (WSTOPSIG (wait_status)));
698
699       if (job)
700         job->status = job_stopped;
701     }
702   else
703     {
704       fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
705                blurb(), (unsigned long) kid, name);
706       if (job)
707         job->status = job_dead;
708     }
709
710   /* Clear out the pid so that screenhack_running_p() knows it's dead.
711    */
712   if (!job || job->status == job_dead)
713     for (i = 0; i < si->nscreens; i++)
714       {
715         saver_screen_info *ssi = &si->screens[i];
716         if (kid == ssi->pid)
717           ssi->pid = 0;
718       }
719 }
720
721 #else  /* VMS */
722 static void await_dying_children (saver_info *si) { return; }
723 #endif /* VMS */
724
725
726 void
727 init_sigchld (void)
728 {
729 #ifdef SIGCHLD
730
731 # ifdef HAVE_SIGACTION  /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
732
733   static Bool sigchld_initialized_p = 0;
734   if (!sigchld_initialized_p)
735     {
736       struct sigaction action, old;
737
738       action.sa_handler = sigchld_handler;
739       sigemptyset(&action.sa_mask);
740       action.sa_flags = 0;
741
742       if (sigaction(SIGCHLD, &action, &old) < 0)
743         {
744           char buf [255];
745           sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
746           perror (buf);
747         }
748       sigchld_initialized_p = True;
749     }
750
751 # else  /* !HAVE_SIGACTION */
752
753   if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
754     {
755       char buf [255];
756       sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
757       perror (buf);
758     }
759 # endif /* !HAVE_SIGACTION */
760 #endif /* SIGCHLD */
761 }
762
763
764
765 \f
766
767 static Bool
768 select_visual_of_hack (saver_screen_info *ssi, screenhack *hack)
769 {
770   saver_info *si = ssi->global;
771   saver_preferences *p = &si->prefs;
772   Bool selected;
773
774   if (hack->visual && *hack->visual == ':')
775     selected = select_visual(ssi, hack->visual);
776   else
777     selected = select_visual(ssi, 0);
778
779   if (!selected && (p->verbose_p || si->demoing_p))
780     fprintf (stderr,
781              (si->demoing_p
782               ? "%s: warning, no \"%s\" visual for \"%s\".\n"
783               : "%s: no \"%s\" visual; skipping \"%s\".\n"),
784              blurb(),
785              (hack->visual && *hack->visual ? hack->visual : "???"),
786              hack->command);
787
788   return selected;
789 }
790
791
792 static void
793 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
794 {
795   saver_info *si = ssi->global;
796   saver_preferences *p = &si->prefs;
797   raise_window (si, first_time_p, True, False);
798   XFlush (si->dpy);
799
800   if (p->screenhacks_count)
801     {
802       screenhack *hack;
803       pid_t forked;
804       char buf [255];
805       int new_hack;
806       int retry_count = 0;
807       Bool force = False;
808
809     AGAIN:
810
811       if (p->screenhacks_count == 1)
812         /* If there is only one hack in the list, there is no choice. */
813         new_hack = 0;
814
815       else if (si->selection_mode == -1)
816         /* Select the next hack, wrapping. */
817         new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
818
819       else if (si->selection_mode == -2)
820         /* Select the previous hack, wrapping. */
821         new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
822                     % p->screenhacks_count);
823
824       else if (si->selection_mode > 0)
825         /* Select a specific hack, by number.  No negotiation. */
826         {
827           new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
828           force = True;
829         }
830       else
831         {
832           /* Select a random hack (but not the one we just ran.) */
833           while ((new_hack = random () % p->screenhacks_count)
834                  == ssi->current_hack)
835             ;
836         }
837
838       ssi->current_hack = new_hack;
839       hack = p->screenhacks[ssi->current_hack];
840
841       /* If the hack is disabled, or there is no visual for this hack,
842          then try again (move forward, or backward, or re-randomize.)
843          Unless this hack was specified explicitly, in which case,
844          use it regardless.
845        */
846       if (force)
847         select_visual_of_hack (ssi, hack);
848         
849       if (!force &&
850           (!hack->enabled_p ||
851            !select_visual_of_hack (ssi, hack)))
852         {
853           if (++retry_count > (p->screenhacks_count*4))
854             {
855               /* Uh, oops.  Odds are, there are no suitable visuals,
856                  and we're looping.  Give up.  (This is totally lame,
857                  what we should do is make a list of suitable hacks at
858                  the beginning, then only loop over them.)
859               */
860               if (p->verbose_p)
861                 fprintf(stderr,
862                         "%s: no suitable visuals for these programs.\n",
863                         blurb());
864               return;
865             }
866           else
867             goto AGAIN;
868         }
869
870       /* Turn off "next" and "prev" modes now, but "demo" mode is only
871          turned off by explicit action.
872        */
873       if (si->selection_mode < 0)
874         si->selection_mode = 0;
875
876       switch ((int) (forked = fork ()))
877         {
878         case -1:
879           sprintf (buf, "%s: couldn't fork", blurb());
880           perror (buf);
881           restore_real_vroot (si);
882           saver_exit (si, 1, 0);
883
884         case 0:
885           close (ConnectionNumber (si->dpy));   /* close display fd */
886           nice_subproc (p->nice_inferior);      /* change process priority */
887           hack_subproc_environment (ssi);       /* set $DISPLAY */
888           exec_screenhack (si, hack->command);  /* this does not return */
889           abort();
890           break;
891
892         default:
893           ssi->pid = forked;
894           (void) make_job (forked, hack->command);
895           break;
896         }
897     }
898 }
899
900
901 void
902 spawn_screenhack (saver_info *si, Bool first_time_p)
903 {
904   if (monitor_powered_on_p (si))
905     {
906       int i;
907       for (i = 0; i < si->nscreens; i++)
908         {
909           saver_screen_info *ssi = &si->screens[i];
910           spawn_screenhack_1 (ssi, first_time_p);
911         }
912     }
913   else if (si->prefs.verbose_p)
914     fprintf (stderr,
915              "%s: server reports that monitor has powered down; "
916              "not launching a new hack.\n", blurb());
917
918   store_saver_status (si);  /* store current hack numbers */
919 }
920
921
922 void
923 kill_screenhack (saver_info *si)
924 {
925   int i;
926   for (i = 0; i < si->nscreens; i++)
927     {
928       saver_screen_info *ssi = &si->screens[i];
929       if (ssi->pid)
930         kill_job (si, ssi->pid, SIGTERM);
931       ssi->pid = 0;
932     }
933 }
934
935
936 void
937 suspend_screenhack (saver_info *si, Bool suspend_p)
938 {
939 #ifdef SIGSTOP  /* older VMS doesn't have it... */
940   int i;
941   for (i = 0; i < si->nscreens; i++)
942     {
943       saver_screen_info *ssi = &si->screens[i];
944       if (ssi->pid)
945         kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
946     }
947 #endif /* SIGSTOP */
948 }
949
950
951 /* Called when we're exiting abnormally, to kill off the subproc. */
952 void
953 emergency_kill_subproc (saver_info *si)
954 {
955   int i;
956 #ifdef SIGCHLD
957   signal (SIGCHLD, SIG_IGN);
958 #endif /* SIGCHLD */
959
960   for (i = 0; i < si->nscreens; i++)
961     {
962       saver_screen_info *ssi = &si->screens[i];
963       if (ssi->pid)
964         {
965           kill_job (si, ssi->pid, SIGTERM);
966           ssi->pid = 0;
967         }
968     }
969 }
970
971 Bool
972 screenhack_running_p (saver_info *si)
973 {
974   Bool result = True;
975   int i;
976   for (i = 0; i < si->nscreens; i++)
977     {
978       saver_screen_info *ssi = &si->screens[i];
979       if (!ssi->pid)
980         result = False;
981     }
982   return result;
983 }
984
985 \f
986 /* Environment variables. */
987
988
989 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
990    is defined, the xscreensaver daemon will search that directory for hacks.
991  */
992 void
993 hack_environment (saver_info *si)
994 {
995 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
996   static const char *def_path = DEFAULT_PATH_PREFIX;
997   if (def_path && *def_path)
998     {
999       const char *opath = getenv("PATH");
1000       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
1001       strcpy (npath, "PATH=");
1002       strcat (npath, def_path);
1003       strcat (npath, ":");
1004       strcat (npath, opath);
1005
1006       if (putenv (npath))
1007         abort ();
1008     }
1009 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
1010 }
1011
1012
1013 void
1014 hack_subproc_environment (saver_screen_info *ssi)
1015 {
1016   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
1017      the spawned processes inherit is correct.  First, it must be on the same
1018      host and display as the value of -display passed in on our command line
1019      (which is not necessarily the same as what our $DISPLAY variable is.)
1020      Second, the screen number in the $DISPLAY passed to the subprocess should
1021      be the screen on which this particular hack is running -- not the display
1022      specification which the driver itself is using, since the driver ignores
1023      its screen number and manages all existing screens.
1024    */
1025   saver_info *si = ssi->global;
1026   const char *odpy = DisplayString (si->dpy);
1027   char *ndpy = (char *) malloc(strlen(odpy) + 20);
1028   int screen_number;
1029   char *s;
1030
1031   for (screen_number = 0; screen_number < si->nscreens; screen_number++)
1032     if (ssi == &si->screens[screen_number])
1033       break;
1034
1035   strcpy (ndpy, "DISPLAY=");
1036   s = ndpy + strlen(ndpy);
1037   strcpy (s, odpy);
1038
1039   while (*s && *s != ':') s++;                  /* skip to colon */
1040   while (*s == ':') s++;                        /* skip over colons */
1041   while (isdigit(*s)) s++;                      /* skip over dpy number */
1042   while (*s == '.') s++;                        /* skip over dot */
1043   if (s[-1] != '.') *s++ = '.';                 /* put on a dot */
1044   sprintf(s, "%d", screen_number);              /* put on screen number */
1045
1046   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1047      any more, right?  It's not Posix, but everyone seems to have it. */
1048 #ifdef HAVE_PUTENV
1049   if (putenv (ndpy))
1050     abort ();
1051 #endif /* HAVE_PUTENV */
1052 }
1053
1054 \f
1055 /* Restarting the xscreensaver process from scratch. */
1056
1057 static char **saved_argv;
1058
1059 void
1060 save_argv (int argc, char **argv)
1061 {
1062   saved_argv = (char **) calloc (argc+2, sizeof (char *));
1063   saved_argv [argc] = 0;
1064   while (argc--)
1065     {
1066       int i = strlen (argv [argc]) + 1;
1067       saved_argv [argc] = (char *) malloc (i);
1068       memcpy (saved_argv [argc], argv [argc], i);
1069     }
1070 }
1071
1072
1073 /* Re-execs the process with the arguments in saved_argv.
1074    Does not return unless there was an error.
1075  */
1076 void
1077 restart_process (saver_info *si)
1078 {
1079   if (si->prefs.verbose_p)
1080     {
1081       int i;
1082       fprintf (real_stderr, "%s: re-executing", blurb());
1083       for (i = 0; saved_argv[i]; i++)
1084         fprintf (real_stderr, " %s", saved_argv[i]);
1085       fprintf (real_stderr, "\n");
1086     }
1087   describe_uids (si, real_stderr);
1088   fprintf (real_stderr, "\n");
1089
1090   fflush (real_stdout);
1091   fflush (real_stderr);
1092   execvp (saved_argv [0], saved_argv);  /* shouldn't return */
1093   {
1094     char buf [512];
1095     sprintf (buf, "%s: could not restart process", blurb());
1096     perror(buf);
1097     fflush(stderr);
1098   }
1099   XBell(si->dpy, 0);
1100 }