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