http://ftp.aanet.ru/pub/Linux/X11/apps/xscreensaver-2.31.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 #ifndef VMS
31
32 # include <sys/resource.h>      /* for setpriority() and PRIO_PROCESS */
33 # include <sys/wait.h>          /* for waitpid() and associated macros */
34
35 #else  /* VMS */
36
37 # if __DECC_VER >= 50200000
38 #  include <sys/wait.h>
39 # endif
40
41 # include <processes.h>
42 # include <unixio.h>            /* for close */
43 # include <unixlib.h>           /* for getpid */
44 # define pid_t    int
45 # define fork     vfork
46
47 #endif /* VMS */
48
49 #include <signal.h>             /* for the signal names */
50
51 #if !defined(SIGCHLD) && defined(SIGCLD)
52 #define SIGCHLD SIGCLD
53 #endif
54
55 #ifdef HAVE_PUTENV
56 extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */
57 #endif
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->next_mode_p == 1)
770             new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
771           else if (si->next_mode_p == 2)
772             new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
773                         % p->screenhacks_count);
774           else
775             while ((new_hack = random () % p->screenhacks_count)
776                    == ssi->current_hack)
777               ;
778           ssi->current_hack = new_hack;
779           hack = p->screenhacks[ssi->current_hack];
780
781           if (!select_visual_of_hack (ssi, hack))
782             {
783               if (++retry_count > (p->screenhacks_count*4))
784                 {
785                   /* Uh, oops.  Odds are, there are no suitable visuals,
786                      and we're looping.  Give up.  (This is totally lame,
787                      what we should do is make a list of suitable hacks at
788                      the beginning, then only loop over them.)
789                   */
790                   if (p->verbose_p)
791                     fprintf(stderr,
792                             "%s: no suitable visuals for these programs.\n",
793                             blurb());
794                   return;
795                 }
796               else
797                 goto AGAIN;
798             }
799         }
800       si->next_mode_p = 0;
801
802
803       /* If there's a visual description on the front of the command, nuke it.
804        */
805       {
806         char *in = hack;
807         while (isspace(*in)) in++;                      /* skip whitespace */
808         hack = in;
809         while (!isspace(*in) && *in != ':') in++;       /* snarf first token */
810         while (isspace(*in)) in++;                      /* skip whitespace */
811         if (*in == ':')
812           {
813             in++;
814             while (isspace(*in)) in++;
815             hack = in;
816           }
817       }
818
819       switch ((int) (forked = fork ()))
820         {
821         case -1:
822           sprintf (buf, "%s: couldn't fork", blurb());
823           perror (buf);
824           restore_real_vroot (si);
825           saver_exit (si, 1, 0);
826
827         case 0:
828           close (ConnectionNumber (si->dpy));   /* close display fd */
829           nice_subproc (p->nice_inferior);      /* change process priority */
830           hack_subproc_environment (ssi);       /* set $DISPLAY */
831           exec_screenhack (si, hack);           /* this does not return */
832           abort();
833           break;
834
835         default:
836           ssi->pid = forked;
837           (void) make_job (forked, hack);
838           break;
839         }
840     }
841 }
842
843
844 void
845 spawn_screenhack (saver_info *si, Bool first_time_p)
846 {
847   int i;
848
849   if (!monitor_powered_on_p (si))
850     {
851       if (si->prefs.verbose_p)
852         fprintf (stderr,
853                  "%s: server reports that monitor has powered down; "
854                  "not launching a new hack.\n", blurb());
855       return;
856     }
857
858   for (i = 0; i < si->nscreens; i++)
859     {
860       saver_screen_info *ssi = &si->screens[i];
861       spawn_screenhack_1 (ssi, first_time_p);
862     }
863 }
864
865
866 void
867 kill_screenhack (saver_info *si)
868 {
869   int i;
870   for (i = 0; i < si->nscreens; i++)
871     {
872       saver_screen_info *ssi = &si->screens[i];
873       if (ssi->pid)
874         kill_job (si, ssi->pid, SIGTERM);
875       ssi->pid = 0;
876     }
877 }
878
879
880 void
881 suspend_screenhack (saver_info *si, Bool suspend_p)
882 {
883 #ifdef SIGSTOP  /* older VMS doesn't have it... */
884   int i;
885   for (i = 0; i < si->nscreens; i++)
886     {
887       saver_screen_info *ssi = &si->screens[i];
888       if (ssi->pid)
889         kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
890     }
891 #endif /* SIGSTOP */
892 }
893
894
895 /* Called when we're exiting abnormally, to kill off the subproc. */
896 void
897 emergency_kill_subproc (saver_info *si)
898 {
899   int i;
900 #ifdef SIGCHLD
901   signal (SIGCHLD, SIG_IGN);
902 #endif /* SIGCHLD */
903
904   for (i = 0; i < si->nscreens; i++)
905     {
906       saver_screen_info *ssi = &si->screens[i];
907       if (ssi->pid)
908         {
909           kill_job (si, ssi->pid, SIGTERM);
910           ssi->pid = 0;
911         }
912     }
913 }
914
915 Bool
916 screenhack_running_p (saver_info *si)
917 {
918   Bool result = True;
919   int i;
920   for (i = 0; i < si->nscreens; i++)
921     {
922       saver_screen_info *ssi = &si->screens[i];
923       if (!ssi->pid)
924         result = False;
925     }
926   return result;
927 }
928
929 \f
930 /* Restarting the xscreensaver process from scratch. */
931
932 static char **saved_argv;
933
934 void
935 save_argv (int argc, char **argv)
936 {
937   saved_argv = (char **) malloc ((argc + 2) * sizeof (char *));
938   saved_argv [argc] = 0;
939   while (argc--)
940     {
941       int i = strlen (argv [argc]) + 1;
942       saved_argv [argc] = (char *) malloc (i);
943       memcpy (saved_argv [argc], argv [argc], i);
944     }
945 }
946
947 void
948 restart_process (saver_info *si)
949 {
950   fflush (real_stdout);
951   fflush (real_stderr);
952   execvp (saved_argv [0], saved_argv);  /* shouldn't return */
953   {
954     char buf [512];
955     sprintf (buf, "%s: could not restart process", blurb());
956     perror(buf);
957     fflush(stderr);
958   }
959 }
960
961 /* Like restart_process(), but ensures that when it restarts,
962    it comes up in demo-mode. */
963 void
964 demo_mode_restart_process (saver_info *si)
965 {
966   int i;
967   for (i = 0; saved_argv [i]; i++);
968   /* add the -initial-demo-mode switch; save_argv() left room for this. */
969   saved_argv [i] = "-initial-demo-mode";
970   saved_argv [i+1] = 0;
971   restart_process (si);         /* shouldn't return */
972   saved_argv [i] = 0;
973   XBell(si->dpy, 0);
974 }
975
976 static void
977 hack_subproc_environment (saver_screen_info *ssi)
978 {
979   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
980      the spawned processes inherit is correct.  First, it must be on the same
981      host and display as the value of -display passed in on our command line
982      (which is not necessarily the same as what our $DISPLAY variable is.)
983      Second, the screen number in the $DISPLAY passed to the subprocess should
984      be the screen on which this particular hack is running -- not the display
985      specification which the driver itself is using, since the driver ignores
986      its screen number and manages all existing screens.
987    */
988   saver_info *si = ssi->global;
989   const char *odpy = DisplayString (si->dpy);
990   char *ndpy = (char *) malloc(strlen(odpy) + 20);
991   int screen_number;
992   char *s;
993
994   for (screen_number = 0; screen_number < si->nscreens; screen_number++)
995     if (ssi == &si->screens[screen_number])
996       break;
997   if (screen_number >= si->nscreens) abort();
998
999   strcpy (ndpy, "DISPLAY=");
1000   s = ndpy + strlen(ndpy);
1001   strcpy (s, odpy);
1002
1003   while (*s && *s != ':') s++;                  /* skip to colon */
1004   while (*s == ':') s++;                        /* skip over colons */
1005   while (isdigit(*s)) s++;                      /* skip over dpy number */
1006   while (*s == '.') s++;                        /* skip over dot */
1007   if (s[-1] != '.') *s++ = '.';                 /* put on a dot */
1008   sprintf(s, "%d", screen_number);              /* put on screen number */
1009
1010   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1011      any more, right?  It's not Posix, but everyone seems to have it. */
1012 #ifdef HAVE_PUTENV
1013   if (putenv (ndpy))
1014     abort ();
1015 #endif /* HAVE_PUTENV */
1016 }
1017
1018
1019 void
1020 hack_environment (saver_info *si)
1021 {
1022 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
1023   static const char *def_path = DEFAULT_PATH_PREFIX;
1024   if (def_path && *def_path)
1025     {
1026       const char *opath = getenv("PATH");
1027       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
1028       strcpy (npath, "PATH=");
1029       strcat (npath, def_path);
1030       strcat (npath, ":");
1031       strcat (npath, opath);
1032
1033       if (putenv (npath))
1034         abort ();
1035     }
1036 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
1037 }