1 /* subprocs.c --- choosing, spawning, and killing screenhacks.
2 * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998
3 * Jamie Zawinski <jwz@jwz.org>
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation. No representations are made about the suitability of this
10 * software for any purpose. It is provided "as is" without express or
22 #include <X11/Xlib.h> /* not used for much... */
28 #include <sys/time.h> /* sys/resource.h needs this for timeval */
30 #ifdef HAVE_SYS_WAIT_H
31 # include <sys/wait.h> /* for waitpid() and associated macros */
34 #if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
35 # include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
39 # include <processes.h>
40 # include <unixio.h> /* for close */
41 # include <unixlib.h> /* for getpid */
46 #include <signal.h> /* for the signal names */
48 #if !defined(SIGCHLD) && defined(SIGCLD)
49 # define SIGCHLD SIGCLD
52 #if 0 /* putenv() is declared in stdlib.h on modern linux systems. */
54 extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */
58 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
60 /* This file doesn't need the Xt headers, so stub these types out... */
62 #define XtAppContext void*
63 #define XrmDatabase void*
64 #define XtIntervalId void*
65 #define XtPointer void*
68 #include "xscreensaver.h"
72 extern saver_info *global_si_kludge; /* I hate C so much... */
75 nice_subproc (int nice_level)
80 #if defined(HAVE_NICE)
82 int old_nice = nice (0);
83 int n = nice_level - old_nice;
85 if (nice (n) == -1 && errno != 0)
88 sprintf (buf, "%s: nice(%d) failed", blurb(), n);
92 #elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
93 if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
96 sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
97 blurb(), (unsigned long) getpid(), nice_level);
102 "%s: don't know how to change process priority on this system.\n",
112 exec_simple_command (const char *command)
116 char *token = strtok (strdup(command), " \t");
120 token = strtok(0, " \t");
124 execvp (av[0], av); /* shouldn't return. */
128 sprintf (buf, "%s: could not execute \"%s\"", blurb(), av[0]);
131 if (errno == ENOENT &&
132 (token = getenv("PATH")))
136 # define PATH_MAX MAXPATHLEN
138 # define PATH_MAX 2048
142 fprintf (stderr, "\n");
144 # if defined(HAVE_GETCWD)
145 getcwd (path, sizeof(path));
146 # elif defined(HAVE_GETWD)
150 fprintf (stderr, " Current directory is: %s\n", path);
151 fprintf (stderr, " PATH is:\n");
152 token = strtok (strdup(token), ":");
155 fprintf (stderr, " %s\n", token);
156 token = strtok(0, ":");
158 fprintf (stderr, "\n");
163 exit (1); /* Note that this only exits a child fork. */
168 exec_complex_command (const char *shell, const char *command)
172 char *command2 = (char *) malloc (strlen (command) + 6);
173 memcpy (command2, "exec ", 5);
174 memcpy (command2 + 5, command, strlen (command) + 1);
176 /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
177 av [ac++] = (char *) shell;
179 av [ac++] = command2;
182 execvp (av[0], av); /* shouldn't return. */
186 sprintf (buf, "%s: execvp(\"%s\") failed", blurb(), av[0]);
190 exit (1); /* Note that this only exits a child fork. */
197 exec_vms_command (const char *command)
202 exit (1); /* Note that this only exits a child fork. */
209 exec_screenhack (saver_info *si, const char *command)
211 /* I don't believe what a sorry excuse for an operating system UNIX is!
213 - I want to spawn a process.
214 - I want to know it's pid so that I can kill it.
215 - I would like to receive a message when it dies of natural causes.
216 - I want the spawned process to have user-specified arguments.
218 If shell metacharacters are present (wildcards, backquotes, etc), the
219 only way to parse those arguments is to run a shell to do the parsing
222 And the only way to know the pid of the process is to fork() and exec()
223 it in the spawned side of the fork.
225 But if you're running a shell to parse your arguments, this gives you
226 the pid of the *shell*, not the pid of the *process* that you're
227 actually interested in, which is an *inferior* of the shell. This also
228 means that the SIGCHLD you get applies to the shell, not its inferior.
229 (Why isn't that sufficient? I don't remember any more, but it turns
232 So, the only solution, when metacharacters are present, is to force the
233 shell to exec() its inferior. What a fucking hack! We prepend "exec "
234 to the command string, and hope it doesn't contain unquoted semicolons
235 or ampersands (we don't search for them, because we don't want to
236 prohibit their use in quoted strings (messages, for example) and parsing
237 out the various quote characters is too much of a pain.)
239 (Actually, Clint Wong <clint@jts.com> points out that process groups
240 might be used to take care of this problem; this may be worth considering
241 some day, except that, 1: this code works now, so why fix it, and 2: from
242 what I've seen in Emacs, dealing with process groups isn't especially
245 saver_preferences *p = &si->prefs;
248 Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
249 /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
252 fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
253 blurb(), command, (unsigned long) getpid (),
254 (hairy_p ? " (via shell)" : ""));
257 /* If it contains any shell metacharacters, do it the hard way,
258 and fork a shell to parse the arguments for us. */
259 exec_complex_command (p->shell, command);
261 /* Otherwise, we can just exec the program directly. */
262 exec_simple_command (command);
266 fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
267 blurb(), command, getpid());
268 exec_vms_command (command);
271 abort(); /* that shouldn't have returned. */
276 /* Management of child processes, and de-zombification.
280 job_running, /* the process is still alive */
281 job_stopped, /* we have sent it a STOP signal */
282 job_killed, /* we have sent it a TERM signal */
283 job_dead /* we have wait()ed for it, and it's dead -- this state only
284 occurs so that we can avoid calling free() from a signal
285 handler. Shortly after going into this state, the list
286 element will be removed. */
289 struct screenhack_job {
292 enum job_status status;
293 struct screenhack_job *next;
296 static struct screenhack_job *jobs = 0;
298 /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
302 struct screenhack_job *job;
303 fprintf(stderr, "%s: job list:\n", blurb());
304 for (job = jobs; job; job = job->next)
305 fprintf (stderr, " %5ld: (%s) %s\n",
307 (job->status == job_running ? "running" :
308 job->status == job_stopped ? "stopped" :
309 job->status == job_killed ? " killed" :
310 job->status == job_dead ? " dead" : " ???"),
312 fprintf (stderr, "\n");
316 static void clean_job_list (void);
318 static struct screenhack_job *
319 make_job (pid_t pid, const char *cmd)
321 struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
323 static char name [1024];
324 const char *in = cmd;
329 while (isspace(*in)) in++; /* skip whitespace */
330 while (!isspace(*in) && *in != ':')
331 *out++ = *in++; /* snarf first token */
332 while (isspace(*in)) in++; /* skip whitespace */
333 if (*in == ':') /* token was a visual name; skip it. */
337 while (isspace(*in)) in++; /* skip whitespace */
338 while (!isspace(*in)) *out++ = *in++; /* snarf first token */
342 job->name = strdup(name);
344 job->status = job_running;
353 free_job (struct screenhack_job *job)
357 else if (job == jobs)
361 struct screenhack_job *job2, *prev;
362 for (prev = 0, job2 = jobs;
364 prev = job2, job2 = job2->next)
367 prev->next = job->next;
376 /* Cleans out dead jobs from the jobs list -- this must only be called
377 from the main thread, not from a signal handler.
380 clean_job_list (void)
382 struct screenhack_job *job, *prev, *next;
383 for (prev = 0, job = jobs, next = (job ? job->next : 0);
385 prev = job, job = next, next = (job ? job->next : 0))
387 if (job->status == job_dead)
398 static struct screenhack_job *
401 struct screenhack_job *job;
402 for (job = jobs; job; job = job->next)
408 static void await_dying_children (saver_info *si);
410 static void describe_dead_child (saver_info *, pid_t, int wait_status);
414 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
415 Don't alter this directly -- use block_sigchld() / unblock_sigchld().
417 static int block_sigchld_handler = 0;
423 #ifdef HAVE_SIGACTION
425 sigemptyset (&child_set);
426 sigaddset (&child_set, SIGCHLD);
427 sigprocmask (SIG_BLOCK, &child_set, 0);
428 #endif /* HAVE_SIGACTION */
430 block_sigchld_handler++;
434 unblock_sigchld (void)
436 #ifdef HAVE_SIGACTION
438 sigemptyset(&child_set);
439 sigaddset(&child_set, SIGCHLD);
440 sigprocmask(SIG_UNBLOCK, &child_set, 0);
441 #endif /* HAVE_SIGACTION */
443 block_sigchld_handler--;
447 kill_job (saver_info *si, pid_t pid, int signal)
449 saver_preferences *p = &si->prefs;
450 struct screenhack_job *job;
455 if (block_sigchld_handler)
456 /* This function should not be called from the signal handler. */
459 block_sigchld(); /* we control the horizontal... */
461 job = find_job (pid);
464 job->status == job_killed)
467 fprintf (stderr, "%s: no child %ld to signal!\n",
468 blurb(), (long) pid);
473 case SIGTERM: job->status = job_killed; break;
475 /* #### there must be a way to do this on VMS... */
476 case SIGSTOP: job->status = job_stopped; break;
477 case SIGCONT: job->status = job_running; break;
484 fprintf (stderr, "%s: %s pid %lu.\n", blurb(),
485 (signal == SIGTERM ? "killing" :
486 signal == SIGSTOP ? "suspending" :
487 signal == SIGCONT ? "resuming" : "signalling"),
488 (unsigned long) job->pid);
491 fprintf (stderr, "%s: %s pid %lu.\n", blurb(), "killing",
492 (unsigned long) job->pid);
493 #endif /* !SIGSTOP */
495 status = kill (job->pid, signal);
497 if (p->verbose_p && status < 0)
500 fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
501 blurb(), job->pid, job->name);
505 sprintf (buf, "%s: couldn't kill child process %lu (%s)",
506 blurb(), job->pid, job->name);
511 await_dying_children (si);
515 if (block_sigchld_handler < 0)
525 sigchld_handler (int sig)
527 saver_info *si = global_si_kludge; /* I hate C so much... */
529 if (si->prefs.debug_p)
530 fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
531 (block_sigchld_handler ? " (blocked)" : ""));
533 if (block_sigchld_handler < 0)
535 else if (block_sigchld_handler == 0)
538 await_dying_children (si);
549 await_dying_children (saver_info *si)
557 kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
559 if (si->prefs.debug_p)
561 if (kid < 0 && errno)
562 fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
565 fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
569 /* 0 means no more children to reap.
570 -1 means error -- except "interrupted system call" isn't a "real"
571 error, so if we get that, we should just try again. */
573 (kid < 0 && errno != EINTR))
576 describe_dead_child (si, kid, wait_status);
582 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
585 saver_preferences *p = &si->prefs;
586 struct screenhack_job *job = find_job (kid);
587 const char *name = job ? job->name : "<unknown>";
589 if (WIFEXITED (wait_status))
591 int exit_status = WEXITSTATUS (wait_status);
593 /* Treat exit code as a signed 8-bit quantity. */
594 if (exit_status & 0x80) exit_status |= ~0xFF;
596 /* One might assume that exiting with non-0 means something went wrong.
597 But that loser xswarm exits with the code that it was killed with, so
598 it *always* exits abnormally. Treat abnormal exits as "normal" (don't
599 mention them) if we've just killed the subprocess. But mention them
600 if they happen on their own.
604 (p->verbose_p || job->status != job_killed)))
606 "%s: child pid %lu (%s) exited abnormally (code %d).\n",
607 blurb(), (unsigned long) kid, name, exit_status);
608 else if (p->verbose_p)
609 fprintf (stderr, "%s: child pid %lu (%s) exited normally.\n",
610 blurb(), (unsigned long) kid, name);
613 job->status = job_dead;
615 else if (WIFSIGNALED (wait_status))
619 job->status != job_killed ||
620 WTERMSIG (wait_status) != SIGTERM)
621 fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
622 blurb(), (unsigned long) kid, name,
623 signal_name (WTERMSIG(wait_status)));
626 job->status = job_dead;
628 else if (WIFSTOPPED (wait_status))
631 fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
632 blurb(), (unsigned long) kid, name,
633 signal_name (WSTOPSIG (wait_status)));
636 job->status = job_stopped;
640 fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
641 blurb(), (unsigned long) kid, name);
643 job->status = job_dead;
646 /* Clear out the pid so that screenhack_running_p() knows it's dead.
648 if (!job || job->status == job_dead)
649 for (i = 0; i < si->nscreens; i++)
651 saver_screen_info *ssi = &si->screens[i];
658 static void await_dying_children (saver_info *si) { return; }
667 # ifdef HAVE_SIGACTION /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
669 static Bool sigchld_initialized_p = 0;
670 if (!sigchld_initialized_p)
672 struct sigaction action, old;
674 action.sa_handler = sigchld_handler;
675 sigemptyset(&action.sa_mask);
678 if (sigaction(SIGCHLD, &action, &old) < 0)
681 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
684 sigchld_initialized_p = True;
687 # else /* !HAVE_SIGACTION */
689 if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
692 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
695 # endif /* !HAVE_SIGACTION */
704 hack_enabled_p (const char *hack)
706 const char *s = hack;
707 while (isspace(*s)) s++;
712 select_visual_of_hack (saver_screen_info *ssi, const char *hack)
714 saver_info *si = ssi->global;
715 saver_preferences *p = &si->prefs;
717 static char vis [1024];
718 const char *in = hack;
720 while (isspace(*in)) in++; /* skip whitespace */
721 if (*in == '-') in++; /* skip optional "-" */
722 while (isspace(*in)) in++; /* skip whitespace */
724 while (!isspace(*in) && *in != ':')
725 *out++ = *in++; /* snarf first token */
726 while (isspace(*in)) in++; /* skip whitespace */
730 selected = select_visual(ssi, vis);
732 selected = select_visual(ssi, 0);
734 if (!selected && (p->verbose_p || si->demoing_p))
736 if (*in == ':') in++;
737 while (isspace(*in)) in++;
740 ? "%s: warning, no \"%s\" visual for \"%s\".\n"
741 : "%s: no \"%s\" visual; skipping \"%s\".\n"),
742 blurb(), (vis ? vis : "???"), in);
750 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
752 saver_info *si = ssi->global;
753 saver_preferences *p = &si->prefs;
754 raise_window (si, first_time_p, True, False);
757 if (p->screenhacks_count)
768 if (p->screenhacks_count == 1)
769 /* If there is only one hack in the list, there is no choice. */
772 else if (si->selection_mode == -1)
773 /* Select the next hack, wrapping. */
774 new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
776 else if (si->selection_mode == -2)
777 /* Select the previous hack, wrapping. */
778 new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
779 % p->screenhacks_count);
781 else if (si->selection_mode > 0)
782 /* Select a specific hack, by number. No negotiation. */
784 new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
789 /* Select a random hack (but not the one we just ran.) */
790 while ((new_hack = random () % p->screenhacks_count)
791 == ssi->current_hack)
795 ssi->current_hack = new_hack;
796 hack = p->screenhacks[ssi->current_hack];
798 /* If the hack is disabled, or there is no visual for this hack,
799 then try again (move forward, or backward, or re-randomize.)
800 Unless this hack was specified explicitly, in which case,
804 (!hack_enabled_p (hack) ||
805 !select_visual_of_hack (ssi, hack)))
807 if (++retry_count > (p->screenhacks_count*4))
809 /* Uh, oops. Odds are, there are no suitable visuals,
810 and we're looping. Give up. (This is totally lame,
811 what we should do is make a list of suitable hacks at
812 the beginning, then only loop over them.)
816 "%s: no suitable visuals for these programs.\n",
824 /* Turn off "next" and "prev" modes now, but "demo" mode is only
825 turned off by explicit action.
827 if (si->selection_mode < 0)
828 si->selection_mode = 0;
831 /* If there's a visual description on the front of the command, nuke it.
835 while (isspace(*in)) in++; /* skip whitespace */
836 if (*in == '-') in++; /* skip optional "-" */
837 while (isspace(*in)) in++; /* skip whitespace */
839 while (!isspace(*in) && *in != ':') in++; /* snarf first token */
840 while (isspace(*in)) in++; /* skip whitespace */
844 while (isspace(*in)) in++;
849 switch ((int) (forked = fork ()))
852 sprintf (buf, "%s: couldn't fork", blurb());
854 restore_real_vroot (si);
855 saver_exit (si, 1, 0);
858 close (ConnectionNumber (si->dpy)); /* close display fd */
859 nice_subproc (p->nice_inferior); /* change process priority */
860 hack_subproc_environment (ssi); /* set $DISPLAY */
861 exec_screenhack (si, hack); /* this does not return */
867 (void) make_job (forked, hack);
875 spawn_screenhack (saver_info *si, Bool first_time_p)
879 if (!monitor_powered_on_p (si))
881 if (si->prefs.verbose_p)
883 "%s: server reports that monitor has powered down; "
884 "not launching a new hack.\n", blurb());
888 for (i = 0; i < si->nscreens; i++)
890 saver_screen_info *ssi = &si->screens[i];
891 spawn_screenhack_1 (ssi, first_time_p);
897 kill_screenhack (saver_info *si)
900 for (i = 0; i < si->nscreens; i++)
902 saver_screen_info *ssi = &si->screens[i];
904 kill_job (si, ssi->pid, SIGTERM);
911 suspend_screenhack (saver_info *si, Bool suspend_p)
913 #ifdef SIGSTOP /* older VMS doesn't have it... */
915 for (i = 0; i < si->nscreens; i++)
917 saver_screen_info *ssi = &si->screens[i];
919 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
925 /* Called when we're exiting abnormally, to kill off the subproc. */
927 emergency_kill_subproc (saver_info *si)
931 signal (SIGCHLD, SIG_IGN);
934 for (i = 0; i < si->nscreens; i++)
936 saver_screen_info *ssi = &si->screens[i];
939 kill_job (si, ssi->pid, SIGTERM);
946 screenhack_running_p (saver_info *si)
950 for (i = 0; i < si->nscreens; i++)
952 saver_screen_info *ssi = &si->screens[i];
960 /* Environment variables. */
963 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
964 is defined, the xscreensaver daemon will search that directory for hacks.
967 hack_environment (saver_info *si)
969 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
970 static const char *def_path = DEFAULT_PATH_PREFIX;
971 if (def_path && *def_path)
973 const char *opath = getenv("PATH");
974 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
975 strcpy (npath, "PATH=");
976 strcat (npath, def_path);
978 strcat (npath, opath);
983 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
988 hack_subproc_environment (saver_screen_info *ssi)
990 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
991 the spawned processes inherit is correct. First, it must be on the same
992 host and display as the value of -display passed in on our command line
993 (which is not necessarily the same as what our $DISPLAY variable is.)
994 Second, the screen number in the $DISPLAY passed to the subprocess should
995 be the screen on which this particular hack is running -- not the display
996 specification which the driver itself is using, since the driver ignores
997 its screen number and manages all existing screens.
999 saver_info *si = ssi->global;
1000 const char *odpy = DisplayString (si->dpy);
1001 char *ndpy = (char *) malloc(strlen(odpy) + 20);
1005 for (screen_number = 0; screen_number < si->nscreens; screen_number++)
1006 if (ssi == &si->screens[screen_number])
1009 strcpy (ndpy, "DISPLAY=");
1010 s = ndpy + strlen(ndpy);
1013 while (*s && *s != ':') s++; /* skip to colon */
1014 while (*s == ':') s++; /* skip over colons */
1015 while (isdigit(*s)) s++; /* skip over dpy number */
1016 while (*s == '.') s++; /* skip over dot */
1017 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
1018 sprintf(s, "%d", screen_number); /* put on screen number */
1020 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1021 any more, right? It's not Posix, but everyone seems to have it. */
1025 #endif /* HAVE_PUTENV */
1029 /* Restarting the xscreensaver process from scratch. */
1031 static char **saved_argv;
1034 save_argv (int argc, char **argv)
1036 saved_argv = (char **) calloc (argc+2, sizeof (char *));
1037 saved_argv [argc] = 0;
1040 int i = strlen (argv [argc]) + 1;
1041 saved_argv [argc] = (char *) malloc (i);
1042 memcpy (saved_argv [argc], argv [argc], i);
1047 /* Re-execs the process with the arguments in saved_argv.
1048 Does not return unless there was an error.
1051 restart_process (saver_info *si)
1053 if (si->prefs.verbose_p)
1056 fprintf (real_stderr, "%s: re-executing", blurb());
1057 for (i = 0; saved_argv[i]; i++)
1058 fprintf (real_stderr, " %s", saved_argv[i]);
1059 fprintf (real_stderr, "\n");
1061 describe_uids (si, real_stderr);
1062 fprintf (real_stderr, "\n");
1064 fflush (real_stdout);
1065 fflush (real_stderr);
1066 execvp (saved_argv [0], saved_argv); /* shouldn't return */
1069 sprintf (buf, "%s: could not restart process", blurb());