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