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