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