ftp://netsw.org/x11/tools/desktop/xscreensaver-4.07.tar.gz
[xscreensaver] / driver / subprocs.c
1 /* subprocs.c --- choosing, spawning, and killing screenhacks.
2  * xscreensaver, Copyright (c) 1991-2003 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, (unsigned long) 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, (unsigned long) 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 (p->screenhacks_count == 1)
665         {
666           /* Exactly one hack in the list */
667           new_hack = 0;
668         }
669       else if (si->selection_mode == -1)
670         {
671           /* Select the next hack, wrapping. */
672           new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
673         }
674       else if (si->selection_mode == -2)
675         {
676           /* Select the previous hack, wrapping. */
677           if (ssi->current_hack < 0)
678             new_hack = p->screenhacks_count - 1;
679           else
680             new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
681                         % p->screenhacks_count);
682         }
683       else if (si->selection_mode > 0)
684         {
685           /* Select a specific hack, by number (via the ACTIVATE command.) */
686           new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
687           force = True;
688         }
689       else if (p->mode == ONE_HACK &&
690                p->selected_hack >= 0)
691         {
692           /* Select a specific hack, by number (via "One Saver" mode.) */
693           new_hack = p->selected_hack;
694           force = True;
695         }
696       else if (p->mode == BLANK_ONLY || p->mode == DONT_BLANK)
697         {
698           new_hack = -1;
699         }
700       else  /* (p->mode == RANDOM_HACKS) */
701         {
702           /* Select a random hack (but not the one we just ran.) */
703           while ((new_hack = random () % p->screenhacks_count)
704                  == ssi->current_hack)
705             ;
706         }
707
708       if (new_hack < 0)   /* don't run a hack */
709         {
710           ssi->current_hack = -1;
711           if (si->selection_mode < 0)
712             si->selection_mode = 0;
713           return;
714         }
715
716       ssi->current_hack = new_hack;
717       hack = p->screenhacks[ssi->current_hack];
718
719       /* If the hack is disabled, or there is no visual for this hack,
720          then try again (move forward, or backward, or re-randomize.)
721          Unless this hack was specified explicitly, in which case,
722          use it regardless.
723        */
724       if (force)
725         select_visual_of_hack (ssi, hack);
726         
727       if (!force &&
728           (!hack->enabled_p ||
729            !select_visual_of_hack (ssi, hack)))
730         {
731           if (++retry_count > (p->screenhacks_count*4))
732             {
733               /* Uh, oops.  Odds are, there are no suitable visuals,
734                  and we're looping.  Give up.  (This is totally lame,
735                  what we should do is make a list of suitable hacks at
736                  the beginning, then only loop over them.)
737               */
738               if (p->verbose_p)
739                 fprintf(stderr,
740                       "%s: %d: no programs enabled, or no suitable visuals.\n",
741                         blurb(), ssi->number);
742               return;
743             }
744           else
745             goto AGAIN;
746         }
747
748       /* Turn off "next" and "prev" modes now, but "demo" mode is only
749          turned off by explicit action.
750        */
751       if (si->selection_mode < 0)
752         si->selection_mode = 0;
753
754       switch ((int) (forked = fork ()))
755         {
756         case -1:
757           sprintf (buf, "%s: couldn't fork", blurb());
758           perror (buf);
759           restore_real_vroot (si);
760           saver_exit (si, 1, 0);
761
762         case 0:
763           close (ConnectionNumber (si->dpy));   /* close display fd */
764           limit_subproc_memory (p->inferior_memory_limit, p->verbose_p);
765           hack_subproc_environment (ssi);       /* set $DISPLAY */
766
767           if (p->verbose_p)
768             fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
769                      blurb(), ssi->number, hack->command,
770                      (unsigned long) getpid ());
771
772           exec_command (p->shell, hack->command, p->nice_inferior);
773
774           /* If that returned, we were unable to exec the subprocess.
775              Print an error message, if desired.
776            */
777           if (! p->ignore_uninstalled_p)
778             print_path_error (hack->command);
779
780           exit (1);  /* exits child fork */
781           break;
782
783         default:
784           ssi->pid = forked;
785           (void) make_job (forked, ssi->number, hack->command);
786           break;
787         }
788     }
789 }
790
791
792 void
793 spawn_screenhack (saver_info *si, Bool first_time_p)
794 {
795   if (monitor_powered_on_p (si))
796     {
797       int i;
798       for (i = 0; i < si->nscreens; i++)
799         {
800           saver_screen_info *ssi = &si->screens[i];
801           spawn_screenhack_1 (ssi, first_time_p);
802         }
803     }
804   else if (si->prefs.verbose_p)
805     fprintf (stderr,
806              "%s: X says monitor has powered down; "
807              "not launching a hack.\n", blurb());
808
809   store_saver_status (si);  /* store current hack numbers */
810 }
811
812
813 void
814 kill_screenhack (saver_info *si)
815 {
816   int i;
817   for (i = 0; i < si->nscreens; i++)
818     {
819       saver_screen_info *ssi = &si->screens[i];
820       if (ssi->pid)
821         kill_job (si, ssi->pid, SIGTERM);
822       ssi->pid = 0;
823     }
824 }
825
826
827 void
828 suspend_screenhack (saver_info *si, Bool suspend_p)
829 {
830 #ifdef SIGSTOP  /* older VMS doesn't have it... */
831   int i;
832   for (i = 0; i < si->nscreens; i++)
833     {
834       saver_screen_info *ssi = &si->screens[i];
835       if (ssi->pid)
836         kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
837     }
838 #endif /* SIGSTOP */
839 }
840
841
842 /* Called when we're exiting abnormally, to kill off the subproc. */
843 void
844 emergency_kill_subproc (saver_info *si)
845 {
846   int i;
847 #ifdef SIGCHLD
848   signal (SIGCHLD, SIG_IGN);
849 #endif /* SIGCHLD */
850
851   for (i = 0; i < si->nscreens; i++)
852     {
853       saver_screen_info *ssi = &si->screens[i];
854       if (ssi->pid)
855         {
856           kill_job (si, ssi->pid, SIGTERM);
857           ssi->pid = 0;
858         }
859     }
860 }
861
862 Bool
863 screenhack_running_p (saver_info *si)
864 {
865   Bool any_running_p = False;
866   int i;
867   for (i = 0; i < si->nscreens; i++)
868     {
869       saver_screen_info *ssi = &si->screens[i];
870       if (ssi->pid) any_running_p = True;
871     }
872   return any_running_p;
873 }
874
875 \f
876 /* Environment variables. */
877
878
879 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
880    is defined, the xscreensaver daemon will search that directory for hacks.
881  */
882 void
883 hack_environment (saver_info *si)
884 {
885 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
886   static const char *def_path = DEFAULT_PATH_PREFIX;
887   if (def_path && *def_path)
888     {
889       const char *opath = getenv("PATH");
890       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
891       strcpy (npath, "PATH=");
892       strcat (npath, def_path);
893       strcat (npath, ":");
894       strcat (npath, opath);
895
896       if (putenv (npath))
897         abort ();
898
899       /* don't free (npath) -- some implementations of putenv (BSD 4.4,
900          glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
901          do not.  So we must leak it (and/or the previous setting). Yay.
902        */
903     }
904 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
905 }
906
907
908 void
909 hack_subproc_environment (saver_screen_info *ssi)
910 {
911   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
912      the spawned processes inherit is correct.  First, it must be on the same
913      host and display as the value of -display passed in on our command line
914      (which is not necessarily the same as what our $DISPLAY variable is.)
915      Second, the screen number in the $DISPLAY passed to the subprocess should
916      be the screen on which this particular hack is running -- not the display
917      specification which the driver itself is using, since the driver ignores
918      its screen number and manages all existing screens.
919    */
920   saver_info *si = ssi->global;
921   const char *odpy = DisplayString (si->dpy);
922   char *ndpy = (char *) malloc(strlen(odpy) + 20);
923   char *s;
924
925   strcpy (ndpy, "DISPLAY=");
926   s = ndpy + strlen(ndpy);
927   strcpy (s, odpy);
928
929   while (*s && *s != ':') s++;                  /* skip to colon */
930   while (*s == ':') s++;                        /* skip over colons */
931   while (isdigit(*s)) s++;                      /* skip over dpy number */
932   while (*s == '.') s++;                        /* skip over dot */
933   if (s[-1] != '.') *s++ = '.';                 /* put on a dot */
934   sprintf(s, "%d", ssi->number);                /* put on screen number */
935
936   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
937      any more, right?  It's not Posix, but everyone seems to have it. */
938 #ifdef HAVE_PUTENV
939   if (putenv (ndpy))
940     abort ();
941   /* do not free(ndpy) -- see above. */
942 #endif /* HAVE_PUTENV */
943 }
944
945 \f
946 /* GL crap */
947
948 Visual *
949 get_best_gl_visual (saver_screen_info *ssi)
950 {
951   saver_info *si = ssi->global;
952   pid_t forked;
953   int fds [2];
954   int in, out;
955   char buf[1024];
956
957   char *av[10];
958   int ac = 0;
959
960   av[ac++] = "xscreensaver-gl-helper";
961   av[ac] = 0;
962
963   if (pipe (fds))
964     {
965       perror ("error creating pipe:");
966       return 0;
967     }
968
969   in = fds [0];
970   out = fds [1];
971
972   switch ((int) (forked = fork ()))
973     {
974     case -1:
975       {
976         sprintf (buf, "%s: couldn't fork", blurb());
977         perror (buf);
978         saver_exit (si, 1, 0);
979       }
980     case 0:
981       {
982         int stdout_fd = 1;
983
984         close (in);  /* don't need this one */
985         close (ConnectionNumber (si->dpy));     /* close display fd */
986
987         if (dup2 (out, stdout_fd) < 0)          /* pipe stdout */
988           {
989             perror ("could not dup() a new stdout:");
990             return 0;
991           }
992         hack_subproc_environment (ssi);         /* set $DISPLAY */
993
994         execvp (av[0], av);                     /* shouldn't return. */
995
996         if (errno != ENOENT || si->prefs.verbose_p)
997           {
998             /* Ignore "no such file or directory" errors, unless verbose.
999                Issue all other exec errors, though. */
1000             sprintf (buf, "%s: running %s", blurb(), av[0]);
1001             perror (buf);
1002           }
1003         exit (1);                               /* exits fork */
1004         break;
1005       }
1006     default:
1007       {
1008         int result = 0;
1009         int wait_status = 0;
1010
1011         FILE *f = fdopen (in, "r");
1012         unsigned long v = 0;
1013         char c;
1014
1015         close (out);  /* don't need this one */
1016
1017         *buf = 0;
1018         fgets (buf, sizeof(buf)-1, f);
1019         fclose (f);
1020
1021         /* Wait for the child to die. */
1022         waitpid (-1, &wait_status, 0);
1023
1024         if (1 == sscanf (buf, "0x%lx %c", &v, &c))
1025           result = (int) v;
1026
1027         if (result == 0)
1028           {
1029             if (si->prefs.verbose_p)
1030               {
1031                 int L = strlen(buf);
1032                 fprintf (stderr, "%s: %s did not report a GL visual!\n",
1033                          blurb(), av[0]);
1034
1035                 if (L && buf[L-1] == '\n')
1036                   buf[--L] = 0;
1037                 if (*buf)
1038                   fprintf (stderr, "%s: %s said: \"%s\"\n",
1039                            blurb(), av[0], buf);
1040               }
1041             return 0;
1042           }
1043         else
1044           {
1045             Visual *v = id_to_visual (ssi->screen, result);
1046             if (si->prefs.verbose_p)
1047               fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n",
1048                        blurb(), ssi->number,
1049                        av[0], result,
1050                        (v == ssi->default_visual ? " (default)" : ""));
1051             return v;
1052           }
1053       }
1054     }
1055
1056   abort();
1057 }
1058
1059
1060 \f
1061 /* Restarting the xscreensaver process from scratch. */
1062
1063 static char **saved_argv;
1064
1065 void
1066 save_argv (int argc, char **argv)
1067 {
1068   saved_argv = (char **) calloc (argc+2, sizeof (char *));
1069   saved_argv [argc] = 0;
1070   while (argc--)
1071     {
1072       int i = strlen (argv [argc]) + 1;
1073       saved_argv [argc] = (char *) malloc (i);
1074       memcpy (saved_argv [argc], argv [argc], i);
1075     }
1076 }
1077
1078
1079 /* Re-execs the process with the arguments in saved_argv.  Does not return.
1080  */
1081 void
1082 restart_process (saver_info *si)
1083 {
1084   fflush (stdout);
1085   fflush (stderr);
1086   shutdown_stderr (si);
1087   if (si->prefs.verbose_p)
1088     {
1089       int i;
1090       fprintf (stderr, "%s: re-executing", blurb());
1091       for (i = 0; saved_argv[i]; i++)
1092         fprintf (stderr, " %s", saved_argv[i]);
1093       fprintf (stderr, "\n");
1094     }
1095   describe_uids (si, stderr);
1096   fprintf (stderr, "\n");
1097
1098   fflush (stdout);
1099   fflush (stderr);
1100   execvp (saved_argv [0], saved_argv);  /* shouldn't return */
1101   {
1102     char buf [512];
1103     sprintf (buf, "%s: could not restart process", blurb());
1104     perror(buf);
1105     fflush(stderr);
1106     abort();
1107   }
1108 }