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