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