http://apple.doit.wisc.edu/mirrors/amug/linux/linuxppc/sources/tarballs/xscreensaver...
[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) + 6);
173   memcpy (command2, "exec ", 5);
174   memcpy (command2 + 5, command, strlen (command) + 1);
175
176   /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
177   av [ac++] = (char *) shell;
178   av [ac++] = "-c";
179   av [ac++] = command2;
180   av [ac]   = 0;
181
182   execvp (av[0], av);                   /* shouldn't return. */
183
184   {
185     char buf [512];
186     sprintf (buf, "%s: execvp(\"%s\") failed", blurb(), av[0]);
187     perror (buf);
188     fflush(stderr);
189     fflush(stdout);
190     exit (1);   /* Note that this only exits a child fork.  */
191   }
192 }
193
194 #else  /* VMS */
195
196 static void
197 exec_vms_command (const char *command)
198 {
199   system (command);
200   fflush (stderr);
201   fflush (stdout);
202   exit (1);     /* Note that this only exits a child fork.  */
203 }
204
205 #endif /* !VMS */
206
207
208 static void
209 exec_screenhack (saver_info *si, const char *command)
210 {
211   /* I don't believe what a sorry excuse for an operating system UNIX is!
212
213      - I want to spawn a process.
214      - I want to know it's pid so that I can kill it.
215      - I would like to receive a message when it dies of natural causes.
216      - I want the spawned process to have user-specified arguments.
217
218      If shell metacharacters are present (wildcards, backquotes, etc), the
219      only way to parse those arguments is to run a shell to do the parsing
220      for you.
221
222      And the only way to know the pid of the process is to fork() and exec()
223      it in the spawned side of the fork.
224
225      But if you're running a shell to parse your arguments, this gives you
226      the pid of the *shell*, not the pid of the *process* that you're
227      actually interested in, which is an *inferior* of the shell.  This also
228      means that the SIGCHLD you get applies to the shell, not its inferior.
229      (Why isn't that sufficient?  I don't remember any more, but it turns
230      out that it isn't.)
231
232      So, the only solution, when metacharacters are present, is to force the
233      shell to exec() its inferior.  What a fucking hack!  We prepend "exec "
234      to the command string, and hope it doesn't contain unquoted semicolons
235      or ampersands (we don't search for them, because we don't want to
236      prohibit their use in quoted strings (messages, for example) and parsing
237      out the various quote characters is too much of a pain.)
238
239      (Actually, Clint Wong <clint@jts.com> points out that process groups
240      might be used to take care of this problem; this may be worth considering
241      some day, except that, 1: this code works now, so why fix it, and 2: from
242      what I've seen in Emacs, dealing with process groups isn't especially
243      portable.)
244    */
245   saver_preferences *p = &si->prefs;
246
247 #ifndef VMS
248   Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
249   /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
250
251   if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0)
252     {
253       /* If you're thinking of commenting this out, think again.
254          If you do so, you will open a security hole.  Mail jwz
255          so that he may enlighten you as to the error of your ways.
256        */
257       fprintf (stderr, "%s: we're still running as root!  Disaster!\n",
258                blurb());
259       saver_exit (si, 1, 0);
260     }
261
262   if (p->verbose_p)
263     fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
264              blurb(), command, (unsigned long) getpid (),
265              (hairy_p ? " (via shell)" : ""));
266
267   if (hairy_p)
268     /* If it contains any shell metacharacters, do it the hard way,
269        and fork a shell to parse the arguments for us. */
270     exec_complex_command (p->shell, command);
271   else
272     /* Otherwise, we can just exec the program directly. */
273     exec_simple_command (command);
274
275 #else /* VMS */
276   if (p->verbose_p)
277     fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
278              blurb(), command, getpid());
279   exec_vms_command (command);
280 #endif /* VMS */
281
282   abort();      /* that shouldn't have returned. */
283 }
284
285
286 \f
287 /* Management of child processes, and de-zombification.
288  */
289
290 enum job_status {
291   job_running,  /* the process is still alive */
292   job_stopped,  /* we have sent it a STOP signal */
293   job_killed,   /* we have sent it a TERM signal */
294   job_dead      /* we have wait()ed for it, and it's dead -- this state only
295                    occurs so that we can avoid calling free() from a signal
296                    handler.  Shortly after going into this state, the list
297                    element will be removed. */
298 };
299
300 struct screenhack_job {
301   char *name;
302   pid_t pid;
303   enum job_status status;
304   struct screenhack_job *next;
305 };
306
307 static struct screenhack_job *jobs = 0;
308
309 /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
310 void
311 show_job_list (void)
312 {
313   struct screenhack_job *job;
314   fprintf(stderr, "%s: job list:\n", blurb());
315   for (job = jobs; job; job = job->next)
316     fprintf (stderr, "  %5ld: (%s) %s\n",
317              (long) job->pid,
318              (job->status == job_running ? "running" :
319               job->status == job_stopped ? "stopped" :
320               job->status == job_killed  ? " killed" :
321               job->status == job_dead    ? "   dead" : "    ???"),
322              job->name);
323   fprintf (stderr, "\n");
324 }
325
326
327 static void clean_job_list (void);
328
329 static struct screenhack_job *
330 make_job (pid_t pid, const char *cmd)
331 {
332   struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
333
334   static char name [1024];
335   const char *in = cmd;
336   char *out = name;
337
338   clean_job_list();
339
340   while (isspace(*in)) in++;            /* skip whitespace */
341   while (!isspace(*in) && *in != ':')
342     *out++ = *in++;                     /* snarf first token */
343   while (isspace(*in)) in++;            /* skip whitespace */
344   if (*in == ':')                       /* token was a visual name; skip it. */
345     {
346       in++;
347       out = name;
348       while (isspace(*in)) in++;                /* skip whitespace */
349       while (!isspace(*in)) *out++ = *in++;     /* snarf first token */
350     }
351   *out = 0;
352
353   job->name = strdup(name);
354   job->pid = pid;
355   job->status = job_running;
356   job->next = jobs;
357   jobs = job;
358
359   return jobs;
360 }
361
362
363 static void
364 free_job (struct screenhack_job *job)
365 {
366   if (!job)
367     return;
368   else if (job == jobs)
369     jobs = jobs->next;
370   else
371     {
372       struct screenhack_job *job2, *prev;
373       for (prev = 0, job2 = jobs;
374            job2;
375            prev = job2, job2 = job2->next)
376         if (job2 == job)
377           {
378             prev->next = job->next;
379             break;
380           }
381     }
382   free(job->name);
383   free(job);
384 }
385
386
387 /* Cleans out dead jobs from the jobs list -- this must only be called
388    from the main thread, not from a signal handler. 
389  */
390 static void
391 clean_job_list (void)
392 {
393   struct screenhack_job *job, *prev, *next;
394   for (prev = 0, job = jobs, next = (job ? job->next : 0);
395        job;
396        prev = job, job = next, next = (job ? job->next : 0))
397     {
398       if (job->status == job_dead)
399         {
400           if (prev)
401             prev->next = next;
402           free_job (job);
403           job = prev;
404         }
405     }
406 }
407
408
409 static struct screenhack_job *
410 find_job (pid_t pid)
411 {
412   struct screenhack_job *job;
413   for (job = jobs; job; job = job->next)
414     if (job->pid == pid)
415       return job;
416   return 0;
417 }
418
419 static void await_dying_children (saver_info *si);
420 #ifndef VMS
421 static void describe_dead_child (saver_info *, pid_t, int wait_status);
422 #endif
423
424
425 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
426    Don't alter this directly -- use block_sigchld() / unblock_sigchld().
427  */
428 static int block_sigchld_handler = 0;
429
430
431 static void
432 block_sigchld (void)
433 {
434 #ifdef HAVE_SIGACTION
435   sigset_t child_set;
436   sigemptyset (&child_set);
437   sigaddset (&child_set, SIGCHLD);
438   sigprocmask (SIG_BLOCK, &child_set, 0);
439 #endif /* HAVE_SIGACTION */
440
441   block_sigchld_handler++;
442 }
443
444 static void
445 unblock_sigchld (void)
446 {
447 #ifdef HAVE_SIGACTION
448   sigset_t child_set;
449   sigemptyset(&child_set);
450   sigaddset(&child_set, SIGCHLD);
451   sigprocmask(SIG_UNBLOCK, &child_set, 0);
452 #endif /* HAVE_SIGACTION */
453
454   block_sigchld_handler--;
455 }
456
457 static int
458 kill_job (saver_info *si, pid_t pid, int signal)
459 {
460   saver_preferences *p = &si->prefs;
461   struct screenhack_job *job;
462   int status = -1;
463
464   clean_job_list();
465
466   if (block_sigchld_handler)
467     /* This function should not be called from the signal handler. */
468     abort();
469
470   block_sigchld();                      /* we control the horizontal... */
471
472   job = find_job (pid);
473   if (!job ||
474       !job->pid ||
475       job->status == job_killed)
476     {
477       if (p->verbose_p)
478         fprintf (stderr, "%s: no child %ld to signal!\n",
479                  blurb(), (long) pid);
480       goto DONE;
481     }
482
483   switch (signal) {
484   case SIGTERM: job->status = job_killed;  break;
485 #ifdef SIGSTOP
486     /* #### there must be a way to do this on VMS... */
487   case SIGSTOP: job->status = job_stopped; break;
488   case SIGCONT: job->status = job_running; break;
489 #endif /* SIGSTOP */
490   default: abort();
491   }
492
493 #ifdef SIGSTOP
494   if (p->verbose_p)
495     fprintf (stderr, "%s: %s pid %lu.\n", blurb(),
496              (signal == SIGTERM ? "killing" :
497               signal == SIGSTOP ? "suspending" :
498               signal == SIGCONT ? "resuming" : "signalling"),
499              (unsigned long) job->pid);
500 #else  /* !SIGSTOP */
501   if (p->verbose_p)
502     fprintf (stderr, "%s: %s pid %lu.\n", blurb(), "killing",
503              (unsigned long) job->pid);
504 #endif /* !SIGSTOP */
505
506   status = kill (job->pid, signal);
507
508   if (p->verbose_p && status < 0)
509     {
510       if (errno == ESRCH)
511         fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
512                  blurb(), job->pid, job->name);
513       else
514         {
515           char buf [1024];
516           sprintf (buf, "%s: couldn't kill child process %lu (%s)",
517                    blurb(), job->pid, job->name);
518           perror (buf);
519         }
520     }
521
522   await_dying_children (si);
523
524  DONE:
525   unblock_sigchld();
526   if (block_sigchld_handler < 0)
527     abort();
528
529   clean_job_list();
530   return status;
531 }
532
533
534 #ifdef SIGCHLD
535 static RETSIGTYPE
536 sigchld_handler (int sig)
537 {
538   saver_info *si = global_si_kludge;    /* I hate C so much... */
539
540   if (si->prefs.debug_p)
541     fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
542             (block_sigchld_handler ? " (blocked)" : ""));
543
544   if (block_sigchld_handler < 0)
545     abort();
546   else if (block_sigchld_handler == 0)
547     {
548       block_sigchld();
549       await_dying_children (si);
550       unblock_sigchld();
551     }
552
553   init_sigchld();
554 }
555 #endif /* SIGCHLD */
556
557
558 #ifndef VMS
559 static void
560 await_dying_children (saver_info *si)
561 {
562   while (1)
563     {
564       int wait_status = 0;
565       pid_t kid;
566
567       errno = 0;
568       kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
569
570       if (si->prefs.debug_p)
571         {
572           if (kid < 0 && errno)
573             fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
574                      (long) kid, errno);
575           else
576             fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
577                      (long) kid);
578         }
579
580       /* 0 means no more children to reap.
581          -1 means error -- except "interrupted system call" isn't a "real"
582          error, so if we get that, we should just try again. */
583       if (kid == 0 ||
584           (kid < 0 && errno != EINTR))
585         break;
586
587       describe_dead_child (si, kid, wait_status);
588     }
589 }
590
591
592 static void
593 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
594 {
595   int i;
596   saver_preferences *p = &si->prefs;
597   struct screenhack_job *job = find_job (kid);
598   const char *name = job ? job->name : "<unknown>";
599
600   if (WIFEXITED (wait_status))
601     {
602       int exit_status = WEXITSTATUS (wait_status);
603
604       /* Treat exit code as a signed 8-bit quantity. */
605       if (exit_status & 0x80) exit_status |= ~0xFF;
606
607       /* One might assume that exiting with non-0 means something went wrong.
608          But that loser xswarm exits with the code that it was killed with, so
609          it *always* exits abnormally.  Treat abnormal exits as "normal" (don't
610          mention them) if we've just killed the subprocess.  But mention them
611          if they happen on their own.
612        */
613       if (!job ||
614           (exit_status != 0 &&
615            (p->verbose_p || job->status != job_killed)))
616         fprintf (stderr,
617                  "%s: child pid %lu (%s) exited abnormally (code %d).\n",
618                  blurb(), (unsigned long) kid, name, exit_status);
619       else if (p->verbose_p)
620         fprintf (stderr, "%s: child pid %lu (%s) exited normally.\n",
621                  blurb(), (unsigned long) kid, name);
622
623       if (job)
624         job->status = job_dead;
625     }
626   else if (WIFSIGNALED (wait_status))
627     {
628       if (p->verbose_p ||
629           !job ||
630           job->status != job_killed ||
631           WTERMSIG (wait_status) != SIGTERM)
632         fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
633                  blurb(), (unsigned long) kid, name,
634                  signal_name (WTERMSIG(wait_status)));
635
636       if (job)
637         job->status = job_dead;
638     }
639   else if (WIFSTOPPED (wait_status))
640     {
641       if (p->verbose_p)
642         fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
643                  blurb(), (unsigned long) kid, name,
644                  signal_name (WSTOPSIG (wait_status)));
645
646       if (job)
647         job->status = job_stopped;
648     }
649   else
650     {
651       fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
652                blurb(), (unsigned long) kid, name);
653       if (job)
654         job->status = job_dead;
655     }
656
657   /* Clear out the pid so that screenhack_running_p() knows it's dead.
658    */
659   if (!job || job->status == job_dead)
660     for (i = 0; i < si->nscreens; i++)
661       {
662         saver_screen_info *ssi = &si->screens[i];
663         if (kid == ssi->pid)
664           ssi->pid = 0;
665       }
666 }
667
668 #else  /* VMS */
669 static void await_dying_children (saver_info *si) { return; }
670 #endif /* VMS */
671
672
673 void
674 init_sigchld (void)
675 {
676 #ifdef SIGCHLD
677
678 # ifdef HAVE_SIGACTION  /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
679
680   static Bool sigchld_initialized_p = 0;
681   if (!sigchld_initialized_p)
682     {
683       struct sigaction action, old;
684
685       action.sa_handler = sigchld_handler;
686       sigemptyset(&action.sa_mask);
687       action.sa_flags = 0;
688
689       if (sigaction(SIGCHLD, &action, &old) < 0)
690         {
691           char buf [255];
692           sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
693           perror (buf);
694         }
695       sigchld_initialized_p = True;
696     }
697
698 # else  /* !HAVE_SIGACTION */
699
700   if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
701     {
702       char buf [255];
703       sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
704       perror (buf);
705     }
706 # endif /* !HAVE_SIGACTION */
707 #endif /* SIGCHLD */
708 }
709
710
711
712 \f
713
714 static Bool
715 hack_enabled_p (const char *hack)
716 {
717   const char *s = hack;
718   while (isspace(*s)) s++;
719   return (*s != '-');
720 }
721
722 static Bool
723 select_visual_of_hack (saver_screen_info *ssi, const char *hack)
724 {
725   saver_info *si = ssi->global;
726   saver_preferences *p = &si->prefs;
727   Bool selected;
728   static char vis [1024];
729   const char *in = hack;
730   char *out = vis;
731   while (isspace(*in)) in++;            /* skip whitespace */
732   if (*in == '-') in++;                 /* skip optional "-" */
733   while (isspace(*in)) in++;            /* skip whitespace */
734
735   while (!isspace(*in) && *in != ':')
736     *out++ = *in++;                     /* snarf first token */
737   while (isspace(*in)) in++;            /* skip whitespace */
738   *out = 0;
739
740   if (*in == ':')
741     selected = select_visual(ssi, vis);
742   else
743     selected = select_visual(ssi, 0);
744
745   if (!selected && (p->verbose_p || si->demoing_p))
746     {
747       if (*in == ':') in++;
748       while (isspace(*in)) in++;
749       fprintf (stderr,
750                (si->demoing_p
751                 ? "%s: warning, no \"%s\" visual for \"%s\".\n"
752                 : "%s: no \"%s\" visual; skipping \"%s\".\n"),
753                blurb(), (*vis ? vis : "???"), in);
754     }
755
756   return selected;
757 }
758
759
760 static void
761 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
762 {
763   saver_info *si = ssi->global;
764   saver_preferences *p = &si->prefs;
765   raise_window (si, first_time_p, True, False);
766   XFlush (si->dpy);
767
768   if (p->screenhacks_count)
769     {
770       char *hack;
771       pid_t forked;
772       char buf [255];
773       int new_hack;
774       int retry_count = 0;
775       Bool force = False;
776
777     AGAIN:
778
779       if (p->screenhacks_count == 1)
780         /* If there is only one hack in the list, there is no choice. */
781         new_hack = 0;
782
783       else if (si->selection_mode == -1)
784         /* Select the next hack, wrapping. */
785         new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
786
787       else if (si->selection_mode == -2)
788         /* Select the previous hack, wrapping. */
789         new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
790                     % p->screenhacks_count);
791
792       else if (si->selection_mode > 0)
793         /* Select a specific hack, by number.  No negotiation. */
794         {
795           new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
796           force = True;
797         }
798       else
799         {
800           /* Select a random hack (but not the one we just ran.) */
801           while ((new_hack = random () % p->screenhacks_count)
802                  == ssi->current_hack)
803             ;
804         }
805
806       ssi->current_hack = new_hack;
807       hack = p->screenhacks[ssi->current_hack];
808
809       /* If the hack is disabled, or there is no visual for this hack,
810          then try again (move forward, or backward, or re-randomize.)
811          Unless this hack was specified explicitly, in which case,
812          use it regardless.
813        */
814       if (force)
815         select_visual_of_hack (ssi, hack);
816         
817       if (!force &&
818           (!hack_enabled_p (hack) ||
819            !select_visual_of_hack (ssi, hack)))
820         {
821           if (++retry_count > (p->screenhacks_count*4))
822             {
823               /* Uh, oops.  Odds are, there are no suitable visuals,
824                  and we're looping.  Give up.  (This is totally lame,
825                  what we should do is make a list of suitable hacks at
826                  the beginning, then only loop over them.)
827               */
828               if (p->verbose_p)
829                 fprintf(stderr,
830                         "%s: no suitable visuals for these programs.\n",
831                         blurb());
832               return;
833             }
834           else
835             goto AGAIN;
836         }
837
838       /* Turn off "next" and "prev" modes now, but "demo" mode is only
839          turned off by explicit action.
840        */
841       if (si->selection_mode < 0)
842         si->selection_mode = 0;
843
844
845       /* If there's a visual description on the front of the command, nuke it.
846        */
847       {
848         char *in = hack;
849         while (isspace(*in)) in++;                      /* skip whitespace */
850         if (*in == '-') in++;                           /* skip optional "-" */
851         while (isspace(*in)) in++;                      /* skip whitespace */
852         hack = in;
853         while (!isspace(*in) && *in != ':') in++;       /* snarf first token */
854         while (isspace(*in)) in++;                      /* skip whitespace */
855         if (*in == ':')
856           {
857             in++;
858             while (isspace(*in)) in++;
859             hack = in;
860           }
861       }
862
863       switch ((int) (forked = fork ()))
864         {
865         case -1:
866           sprintf (buf, "%s: couldn't fork", blurb());
867           perror (buf);
868           restore_real_vroot (si);
869           saver_exit (si, 1, 0);
870
871         case 0:
872           close (ConnectionNumber (si->dpy));   /* close display fd */
873           nice_subproc (p->nice_inferior);      /* change process priority */
874           hack_subproc_environment (ssi);       /* set $DISPLAY */
875           exec_screenhack (si, hack);           /* this does not return */
876           abort();
877           break;
878
879         default:
880           ssi->pid = forked;
881           (void) make_job (forked, hack);
882           break;
883         }
884     }
885 }
886
887
888 void
889 spawn_screenhack (saver_info *si, Bool first_time_p)
890 {
891   int i;
892
893   if (!monitor_powered_on_p (si))
894     {
895       if (si->prefs.verbose_p)
896         fprintf (stderr,
897                  "%s: server reports that monitor has powered down; "
898                  "not launching a new hack.\n", blurb());
899       return;
900     }
901
902   for (i = 0; i < si->nscreens; i++)
903     {
904       saver_screen_info *ssi = &si->screens[i];
905       spawn_screenhack_1 (ssi, first_time_p);
906     }
907 }
908
909
910 void
911 kill_screenhack (saver_info *si)
912 {
913   int i;
914   for (i = 0; i < si->nscreens; i++)
915     {
916       saver_screen_info *ssi = &si->screens[i];
917       if (ssi->pid)
918         kill_job (si, ssi->pid, SIGTERM);
919       ssi->pid = 0;
920     }
921 }
922
923
924 void
925 suspend_screenhack (saver_info *si, Bool suspend_p)
926 {
927 #ifdef SIGSTOP  /* older VMS doesn't have it... */
928   int i;
929   for (i = 0; i < si->nscreens; i++)
930     {
931       saver_screen_info *ssi = &si->screens[i];
932       if (ssi->pid)
933         kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
934     }
935 #endif /* SIGSTOP */
936 }
937
938
939 /* Called when we're exiting abnormally, to kill off the subproc. */
940 void
941 emergency_kill_subproc (saver_info *si)
942 {
943   int i;
944 #ifdef SIGCHLD
945   signal (SIGCHLD, SIG_IGN);
946 #endif /* SIGCHLD */
947
948   for (i = 0; i < si->nscreens; i++)
949     {
950       saver_screen_info *ssi = &si->screens[i];
951       if (ssi->pid)
952         {
953           kill_job (si, ssi->pid, SIGTERM);
954           ssi->pid = 0;
955         }
956     }
957 }
958
959 Bool
960 screenhack_running_p (saver_info *si)
961 {
962   Bool result = True;
963   int i;
964   for (i = 0; i < si->nscreens; i++)
965     {
966       saver_screen_info *ssi = &si->screens[i];
967       if (!ssi->pid)
968         result = False;
969     }
970   return result;
971 }
972
973 \f
974 /* Environment variables. */
975
976
977 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
978    is defined, the xscreensaver daemon will search that directory for hacks.
979  */
980 void
981 hack_environment (saver_info *si)
982 {
983 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
984   static const char *def_path = DEFAULT_PATH_PREFIX;
985   if (def_path && *def_path)
986     {
987       const char *opath = getenv("PATH");
988       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
989       strcpy (npath, "PATH=");
990       strcat (npath, def_path);
991       strcat (npath, ":");
992       strcat (npath, opath);
993
994       if (putenv (npath))
995         abort ();
996     }
997 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
998 }
999
1000
1001 void
1002 hack_subproc_environment (saver_screen_info *ssi)
1003 {
1004   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
1005      the spawned processes inherit is correct.  First, it must be on the same
1006      host and display as the value of -display passed in on our command line
1007      (which is not necessarily the same as what our $DISPLAY variable is.)
1008      Second, the screen number in the $DISPLAY passed to the subprocess should
1009      be the screen on which this particular hack is running -- not the display
1010      specification which the driver itself is using, since the driver ignores
1011      its screen number and manages all existing screens.
1012    */
1013   saver_info *si = ssi->global;
1014   const char *odpy = DisplayString (si->dpy);
1015   char *ndpy = (char *) malloc(strlen(odpy) + 20);
1016   int screen_number;
1017   char *s;
1018
1019   for (screen_number = 0; screen_number < si->nscreens; screen_number++)
1020     if (ssi == &si->screens[screen_number])
1021       break;
1022
1023   strcpy (ndpy, "DISPLAY=");
1024   s = ndpy + strlen(ndpy);
1025   strcpy (s, odpy);
1026
1027   while (*s && *s != ':') s++;                  /* skip to colon */
1028   while (*s == ':') s++;                        /* skip over colons */
1029   while (isdigit(*s)) s++;                      /* skip over dpy number */
1030   while (*s == '.') s++;                        /* skip over dot */
1031   if (s[-1] != '.') *s++ = '.';                 /* put on a dot */
1032   sprintf(s, "%d", screen_number);              /* put on screen number */
1033
1034   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1035      any more, right?  It's not Posix, but everyone seems to have it. */
1036 #ifdef HAVE_PUTENV
1037   if (putenv (ndpy))
1038     abort ();
1039 #endif /* HAVE_PUTENV */
1040 }
1041
1042 \f
1043 /* Restarting the xscreensaver process from scratch. */
1044
1045 static char **saved_argv;
1046
1047 void
1048 save_argv (int argc, char **argv)
1049 {
1050   saved_argv = (char **) calloc (argc+2, sizeof (char *));
1051   saved_argv [argc] = 0;
1052   while (argc--)
1053     {
1054       int i = strlen (argv [argc]) + 1;
1055       saved_argv [argc] = (char *) malloc (i);
1056       memcpy (saved_argv [argc], argv [argc], i);
1057     }
1058 }
1059
1060
1061 /* Re-execs the process with the arguments in saved_argv.
1062    Does not return unless there was an error.
1063  */
1064 void
1065 restart_process (saver_info *si)
1066 {
1067   if (si->prefs.verbose_p)
1068     {
1069       int i;
1070       fprintf (real_stderr, "%s: re-executing", blurb());
1071       for (i = 0; saved_argv[i]; i++)
1072         fprintf (real_stderr, " %s", saved_argv[i]);
1073       fprintf (real_stderr, "\n");
1074     }
1075   describe_uids (si, real_stderr);
1076   fprintf (real_stderr, "\n");
1077
1078   fflush (real_stdout);
1079   fflush (real_stderr);
1080   execvp (saved_argv [0], saved_argv);  /* shouldn't return */
1081   {
1082     char buf [512];
1083     sprintf (buf, "%s: could not restart process", blurb());
1084     perror(buf);
1085     fflush(stderr);
1086   }
1087   XBell(si->dpy, 0);
1088 }