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