1 /* subprocs.c --- choosing, spawning, and killing screenhacks.
2 * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski <jwz@jwz.org>
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
21 #include <X11/Xlib.h> /* not used for much... */
27 #include <sys/time.h> /* sys/resource.h needs this for timeval */
29 #ifdef HAVE_SYS_WAIT_H
30 # include <sys/wait.h> /* for waitpid() and associated macros */
34 # include <sys/resource.h> /* for setrlimit() and RLIMIT_AS */
38 # include <processes.h>
39 # include <unixio.h> /* for close */
40 # include <unixlib.h> /* for getpid */
45 #include <signal.h> /* for the signal names */
47 #if !defined(SIGCHLD) && defined(SIGCLD)
48 # define SIGCHLD SIGCLD
51 #if 0 /* putenv() is declared in stdlib.h on modern linux systems. */
53 extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */
57 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
59 /* This file doesn't need the Xt headers, so stub these types out... */
61 #define XtAppContext void*
62 #define XrmDatabase void*
63 #define XtIntervalId void*
64 #define XtPointer void*
67 #include "xscreensaver.h"
69 #include "visual.h" /* for id_to_visual() */
71 extern saver_info *global_si_kludge; /* I hate C so much... */
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.
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!
84 #if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS)
85 # define RLIMIT_AS RLIMIT_VMEM
89 limit_subproc_memory (int address_space_limit, Bool verbose_p)
92 /* This has caused way more problems than it has solved...
93 Let's just completely ignore the "memoryLimit" option now.
97 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_AS)
100 if (address_space_limit < 10 * 1024) /* let's not be crazy */
103 if (getrlimit (RLIMIT_AS, &r) != 0)
106 sprintf (buf, "%s: getrlimit(RLIMIT_AS) failed", blurb());
111 r.rlim_cur = address_space_limit;
113 if (setrlimit (RLIMIT_AS, &r) != 0)
116 sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed",
117 blurb(), r.rlim_cur, r.rlim_max);
124 int i = address_space_limit;
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);
133 sprintf(buf, "%d bytes", i);
135 fprintf (stderr, "%s: limited pid %lu address space to %s.\n",
136 blurb(), (unsigned long) getpid (), buf);
139 #endif /* HAVE_SETRLIMIT && RLIMIT_AS */
143 /* Management of child processes, and de-zombification.
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. */
156 struct screenhack_job {
160 enum job_status status;
161 struct screenhack_job *next;
164 static struct screenhack_job *jobs = 0;
166 /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
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",
176 (job->status == job_running ? "running" :
177 job->status == job_stopped ? "stopped" :
178 job->status == job_killed ? " killed" :
179 job->status == job_dead ? " dead" : " ???"),
181 fprintf (stderr, "\n");
185 static void clean_job_list (void);
187 static struct screenhack_job *
188 make_job (pid_t pid, int screen, const char *cmd)
190 struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
192 static char name [1024];
193 const char *in = cmd;
201 while (isspace(*in)) in++; /* skip whitespace */
202 while (!isspace(*in) && *in != ':') {
203 if (*in == '=') got_eq = 1;
204 *out++ = *in++; /* snarf first token */
207 if (got_eq) /* if the first token was FOO=bar */
208 { /* then get the next token instead. */
215 while (isspace(*in)) in++; /* skip whitespace */
218 job->name = strdup(name);
220 job->screen = screen;
221 job->status = job_running;
230 free_job (struct screenhack_job *job)
234 else if (job == jobs)
238 struct screenhack_job *job2, *prev;
239 for (prev = 0, job2 = jobs;
241 prev = job2, job2 = job2->next)
244 prev->next = job->next;
253 /* Cleans out dead jobs from the jobs list -- this must only be called
254 from the main thread, not from a signal handler.
257 clean_job_list (void)
259 struct screenhack_job *job, *prev, *next;
260 for (prev = 0, job = jobs, next = (job ? job->next : 0);
262 prev = job, job = next, next = (job ? job->next : 0))
264 if (job->status == job_dead)
275 static struct screenhack_job *
278 struct screenhack_job *job;
279 for (job = jobs; job; job = job->next)
285 static void await_dying_children (saver_info *si);
287 static void describe_dead_child (saver_info *, pid_t, int wait_status);
291 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
292 Don't alter this directly -- use block_sigchld() / unblock_sigchld().
294 static int block_sigchld_handler = 0;
300 #ifdef HAVE_SIGACTION
302 sigemptyset (&child_set);
303 sigaddset (&child_set, SIGCHLD);
304 sigprocmask (SIG_BLOCK, &child_set, 0);
305 #endif /* HAVE_SIGACTION */
307 block_sigchld_handler++;
311 unblock_sigchld (void)
313 #ifdef HAVE_SIGACTION
315 sigemptyset(&child_set);
316 sigaddset(&child_set, SIGCHLD);
317 sigprocmask(SIG_UNBLOCK, &child_set, 0);
318 #endif /* HAVE_SIGACTION */
320 block_sigchld_handler--;
324 kill_job (saver_info *si, pid_t pid, int signal)
326 saver_preferences *p = &si->prefs;
327 struct screenhack_job *job;
332 if (block_sigchld_handler)
333 /* This function should not be called from the signal handler. */
336 block_sigchld(); /* we control the horizontal... */
338 job = find_job (pid);
341 job->status == job_killed)
344 fprintf (stderr, "%s: no child %ld to signal!\n",
345 blurb(), (long) pid);
350 case SIGTERM: job->status = job_killed; break;
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;
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,
367 status = kill (job->pid, signal);
369 if (p->verbose_p && status < 0)
373 "%s: %d: child process %lu (%s) was already dead.\n",
374 blurb(), job->screen, (unsigned long) job->pid, job->name);
378 sprintf (buf, "%s: %d: couldn't kill child process %lu (%s)",
379 blurb(), job->screen, (unsigned long) job->pid, job->name);
384 await_dying_children (si);
388 if (block_sigchld_handler < 0)
398 sigchld_handler (int sig)
400 saver_info *si = global_si_kludge; /* I hate C so much... */
402 if (si->prefs.debug_p)
403 fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
404 (block_sigchld_handler ? " (blocked)" : ""));
406 if (block_sigchld_handler < 0)
408 else if (block_sigchld_handler == 0)
411 await_dying_children (si);
422 await_dying_children (saver_info *si)
430 kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
432 if (si->prefs.debug_p)
434 if (kid < 0 && errno)
435 fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
438 fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
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. */
446 (kid < 0 && errno != EINTR))
449 describe_dead_child (si, kid, wait_status);
455 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
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;
463 if (WIFEXITED (wait_status))
465 int exit_status = WEXITSTATUS (wait_status);
467 /* Treat exit code as a signed 8-bit quantity. */
468 if (exit_status & 0x80) exit_status |= ~0xFF;
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.
478 (p->verbose_p || job->status != job_killed)))
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);
487 job->status = job_dead;
489 else if (WIFSIGNALED (wait_status))
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)));
500 job->status = job_dead;
502 else if (WIFSTOPPED (wait_status))
505 fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
506 blurb(), (unsigned long) kid, name,
507 signal_name (WSTOPSIG (wait_status)));
510 job->status = job_stopped;
514 fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
515 blurb(), (unsigned long) kid, name);
517 job->status = job_dead;
520 /* Clear out the pid so that screenhack_running_p() knows it's dead.
522 if (!job || job->status == job_dead)
523 for (i = 0; i < si->nscreens; i++)
525 saver_screen_info *ssi = &si->screens[i];
532 static void await_dying_children (saver_info *si) { return; }
541 # ifdef HAVE_SIGACTION /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
543 static Bool sigchld_initialized_p = 0;
544 if (!sigchld_initialized_p)
546 struct sigaction action, old;
548 action.sa_handler = sigchld_handler;
549 sigemptyset(&action.sa_mask);
552 if (sigaction(SIGCHLD, &action, &old) < 0)
555 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
558 sigchld_initialized_p = True;
561 # else /* !HAVE_SIGACTION */
563 if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
566 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
569 # endif /* !HAVE_SIGACTION */
578 select_visual_of_hack (saver_screen_info *ssi, screenhack *hack)
580 saver_info *si = ssi->global;
581 saver_preferences *p = &si->prefs;
584 if (hack->visual && *hack->visual)
585 selected = select_visual(ssi, hack->visual);
587 selected = select_visual(ssi, 0);
589 if (!selected && (p->verbose_p || si->demoing_p))
592 ? "%s: warning, no \"%s\" visual for \"%s\".\n"
593 : "%s: no \"%s\" visual; skipping \"%s\".\n"),
595 (hack->visual && *hack->visual ? hack->visual : "???"),
603 print_path_error (const char *program)
606 char *cmd = strdup (program);
607 char *token = strchr (cmd, ' ');
609 if (token) *token = 0;
610 sprintf (buf, "%s: could not execute \"%.100s\"", blurb(), cmd);
614 if (errno == ENOENT &&
615 (token = getenv("PATH")))
619 # define PATH_MAX MAXPATHLEN
621 # define PATH_MAX 2048
625 fprintf (stderr, "\n");
627 # if defined(HAVE_GETCWD)
628 getcwd (path, sizeof(path));
629 # elif defined(HAVE_GETWD)
633 fprintf (stderr, " Current directory is: %s\n", path);
634 fprintf (stderr, " PATH is:\n");
635 token = strtok (strdup(token), ":");
638 fprintf (stderr, " %s\n", token);
639 token = strtok(0, ":");
641 fprintf (stderr, "\n");
647 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
649 saver_info *si = ssi->global;
650 saver_preferences *p = &si->prefs;
651 raise_window (si, first_time_p, True, False);
654 if (p->screenhacks_count)
665 if (p->screenhacks_count < 1)
667 /* No hacks at all */
670 else if (p->screenhacks_count == 1)
672 /* Exactly one hack in the list */
675 else if (si->selection_mode == -1)
677 /* Select the next hack, wrapping. */
678 new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
680 else if (si->selection_mode == -2)
682 /* Select the previous hack, wrapping. */
683 if (ssi->current_hack < 0)
684 new_hack = p->screenhacks_count - 1;
686 new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
687 % p->screenhacks_count);
689 else if (si->selection_mode > 0)
691 /* Select a specific hack, by number (via the ACTIVATE command.) */
692 new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
695 else if (p->mode == ONE_HACK &&
696 p->selected_hack >= 0)
698 /* Select a specific hack, by number (via "One Saver" mode.) */
699 new_hack = p->selected_hack;
702 else if (p->mode == BLANK_ONLY || p->mode == DONT_BLANK)
706 else /* (p->mode == RANDOM_HACKS) */
708 /* Select a random hack (but not the one we just ran.) */
709 while ((new_hack = random () % p->screenhacks_count)
710 == ssi->current_hack)
714 if (new_hack < 0) /* don't run a hack */
716 ssi->current_hack = -1;
717 if (si->selection_mode < 0)
718 si->selection_mode = 0;
722 ssi->current_hack = new_hack;
723 hack = p->screenhacks[ssi->current_hack];
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,
731 select_visual_of_hack (ssi, hack);
735 !select_visual_of_hack (ssi, hack)))
737 if (++retry_count > (p->screenhacks_count*4))
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.)
746 "%s: %d: no programs enabled, or no suitable visuals.\n",
747 blurb(), ssi->number);
754 /* Turn off "next" and "prev" modes now, but "demo" mode is only
755 turned off by explicit action.
757 if (si->selection_mode < 0)
758 si->selection_mode = 0;
760 switch ((int) (forked = fork ()))
763 sprintf (buf, "%s: couldn't fork", blurb());
765 restore_real_vroot (si);
766 saver_exit (si, 1, 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 */
774 fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
775 blurb(), ssi->number, hack->command,
776 (unsigned long) getpid ());
778 exec_command (p->shell, hack->command, p->nice_inferior);
780 /* If that returned, we were unable to exec the subprocess.
781 Print an error message, if desired.
783 if (! p->ignore_uninstalled_p)
784 print_path_error (hack->command);
786 exit (1); /* exits child fork */
791 (void) make_job (forked, ssi->number, hack->command);
799 spawn_screenhack (saver_info *si, Bool first_time_p)
801 if (monitor_powered_on_p (si))
804 for (i = 0; i < si->nscreens; i++)
806 saver_screen_info *ssi = &si->screens[i];
807 spawn_screenhack_1 (ssi, first_time_p);
810 else if (si->prefs.verbose_p)
812 "%s: X says monitor has powered down; "
813 "not launching a hack.\n", blurb());
815 store_saver_status (si); /* store current hack numbers */
820 kill_screenhack (saver_info *si)
823 for (i = 0; i < si->nscreens; i++)
825 saver_screen_info *ssi = &si->screens[i];
827 kill_job (si, ssi->pid, SIGTERM);
834 suspend_screenhack (saver_info *si, Bool suspend_p)
836 #ifdef SIGSTOP /* older VMS doesn't have it... */
838 for (i = 0; i < si->nscreens; i++)
840 saver_screen_info *ssi = &si->screens[i];
842 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
848 /* Called when we're exiting abnormally, to kill off the subproc. */
850 emergency_kill_subproc (saver_info *si)
854 signal (SIGCHLD, SIG_IGN);
857 for (i = 0; i < si->nscreens; i++)
859 saver_screen_info *ssi = &si->screens[i];
862 kill_job (si, ssi->pid, SIGTERM);
869 screenhack_running_p (saver_info *si)
871 Bool any_running_p = False;
873 for (i = 0; i < si->nscreens; i++)
875 saver_screen_info *ssi = &si->screens[i];
876 if (ssi->pid) any_running_p = True;
878 return any_running_p;
882 /* Environment variables. */
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.
889 hack_environment (saver_info *si)
891 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
892 static const char *def_path = DEFAULT_PATH_PREFIX;
893 if (def_path && *def_path)
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);
900 strcat (npath, opath);
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.
910 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
915 hack_subproc_environment (saver_screen_info *ssi)
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.
926 saver_info *si = ssi->global;
927 const char *odpy = DisplayString (si->dpy);
928 char *ndpy = (char *) malloc(strlen(odpy) + 20);
931 strcpy (ndpy, "DISPLAY=");
932 s = ndpy + strlen(ndpy);
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 */
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. */
947 /* do not free(ndpy) -- see above. */
948 #endif /* HAVE_PUTENV */
955 get_best_gl_visual (saver_screen_info *ssi)
957 saver_info *si = ssi->global;
966 av[ac++] = "xscreensaver-gl-helper";
971 perror ("error creating pipe:");
978 switch ((int) (forked = fork ()))
982 sprintf (buf, "%s: couldn't fork", blurb());
984 saver_exit (si, 1, 0);
990 close (in); /* don't need this one */
991 close (ConnectionNumber (si->dpy)); /* close display fd */
993 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
995 perror ("could not dup() a new stdout:");
998 hack_subproc_environment (ssi); /* set $DISPLAY */
1000 execvp (av[0], av); /* shouldn't return. */
1002 if (errno != ENOENT || si->prefs.verbose_p)
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]);
1009 exit (1); /* exits fork */
1015 int wait_status = 0;
1017 FILE *f = fdopen (in, "r");
1018 unsigned long v = 0;
1021 close (out); /* don't need this one */
1024 fgets (buf, sizeof(buf)-1, f);
1027 /* Wait for the child to die. */
1028 waitpid (-1, &wait_status, 0);
1030 if (1 == sscanf (buf, "0x%lx %c", &v, &c))
1035 if (si->prefs.verbose_p)
1037 int L = strlen(buf);
1038 fprintf (stderr, "%s: %s did not report a GL visual!\n",
1041 if (L && buf[L-1] == '\n')
1044 fprintf (stderr, "%s: %s said: \"%s\"\n",
1045 blurb(), av[0], buf);
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,
1056 (v == ssi->default_visual ? " (default)" : ""));
1067 /* Restarting the xscreensaver process from scratch. */
1069 static char **saved_argv;
1072 save_argv (int argc, char **argv)
1074 saved_argv = (char **) calloc (argc+2, sizeof (char *));
1075 saved_argv [argc] = 0;
1078 int i = strlen (argv [argc]) + 1;
1079 saved_argv [argc] = (char *) malloc (i);
1080 memcpy (saved_argv [argc], argv [argc], i);
1085 /* Re-execs the process with the arguments in saved_argv. Does not return.
1088 restart_process (saver_info *si)
1092 shutdown_stderr (si);
1093 if (si->prefs.verbose_p)
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");
1101 describe_uids (si, stderr);
1102 fprintf (stderr, "\n");
1106 execvp (saved_argv [0], saved_argv); /* shouldn't return */
1109 sprintf (buf, "%s: could not restart process", blurb());