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 select_visual_of_hack (ssi, hack);
807 (!hack_enabled_p (hack) ||
808 !select_visual_of_hack (ssi, hack)))
810 if (++retry_count > (p->screenhacks_count*4))
812 /* Uh, oops. Odds are, there are no suitable visuals,
813 and we're looping. Give up. (This is totally lame,
814 what we should do is make a list of suitable hacks at
815 the beginning, then only loop over them.)
819 "%s: no suitable visuals for these programs.\n",
827 /* Turn off "next" and "prev" modes now, but "demo" mode is only
828 turned off by explicit action.
830 if (si->selection_mode < 0)
831 si->selection_mode = 0;
834 /* If there's a visual description on the front of the command, nuke it.
838 while (isspace(*in)) in++; /* skip whitespace */
839 if (*in == '-') in++; /* skip optional "-" */
840 while (isspace(*in)) in++; /* skip whitespace */
842 while (!isspace(*in) && *in != ':') in++; /* snarf first token */
843 while (isspace(*in)) in++; /* skip whitespace */
847 while (isspace(*in)) in++;
852 switch ((int) (forked = fork ()))
855 sprintf (buf, "%s: couldn't fork", blurb());
857 restore_real_vroot (si);
858 saver_exit (si, 1, 0);
861 close (ConnectionNumber (si->dpy)); /* close display fd */
862 nice_subproc (p->nice_inferior); /* change process priority */
863 hack_subproc_environment (ssi); /* set $DISPLAY */
864 exec_screenhack (si, hack); /* this does not return */
870 (void) make_job (forked, hack);
878 spawn_screenhack (saver_info *si, Bool first_time_p)
882 if (!monitor_powered_on_p (si))
884 if (si->prefs.verbose_p)
886 "%s: server reports that monitor has powered down; "
887 "not launching a new hack.\n", blurb());
891 for (i = 0; i < si->nscreens; i++)
893 saver_screen_info *ssi = &si->screens[i];
894 spawn_screenhack_1 (ssi, first_time_p);
900 kill_screenhack (saver_info *si)
903 for (i = 0; i < si->nscreens; i++)
905 saver_screen_info *ssi = &si->screens[i];
907 kill_job (si, ssi->pid, SIGTERM);
914 suspend_screenhack (saver_info *si, Bool suspend_p)
916 #ifdef SIGSTOP /* older VMS doesn't have it... */
918 for (i = 0; i < si->nscreens; i++)
920 saver_screen_info *ssi = &si->screens[i];
922 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
928 /* Called when we're exiting abnormally, to kill off the subproc. */
930 emergency_kill_subproc (saver_info *si)
934 signal (SIGCHLD, SIG_IGN);
937 for (i = 0; i < si->nscreens; i++)
939 saver_screen_info *ssi = &si->screens[i];
942 kill_job (si, ssi->pid, SIGTERM);
949 screenhack_running_p (saver_info *si)
953 for (i = 0; i < si->nscreens; i++)
955 saver_screen_info *ssi = &si->screens[i];
963 /* Environment variables. */
966 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
967 is defined, the xscreensaver daemon will search that directory for hacks.
970 hack_environment (saver_info *si)
972 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
973 static const char *def_path = DEFAULT_PATH_PREFIX;
974 if (def_path && *def_path)
976 const char *opath = getenv("PATH");
977 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
978 strcpy (npath, "PATH=");
979 strcat (npath, def_path);
981 strcat (npath, opath);
986 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
991 hack_subproc_environment (saver_screen_info *ssi)
993 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
994 the spawned processes inherit is correct. First, it must be on the same
995 host and display as the value of -display passed in on our command line
996 (which is not necessarily the same as what our $DISPLAY variable is.)
997 Second, the screen number in the $DISPLAY passed to the subprocess should
998 be the screen on which this particular hack is running -- not the display
999 specification which the driver itself is using, since the driver ignores
1000 its screen number and manages all existing screens.
1002 saver_info *si = ssi->global;
1003 const char *odpy = DisplayString (si->dpy);
1004 char *ndpy = (char *) malloc(strlen(odpy) + 20);
1008 for (screen_number = 0; screen_number < si->nscreens; screen_number++)
1009 if (ssi == &si->screens[screen_number])
1012 strcpy (ndpy, "DISPLAY=");
1013 s = ndpy + strlen(ndpy);
1016 while (*s && *s != ':') s++; /* skip to colon */
1017 while (*s == ':') s++; /* skip over colons */
1018 while (isdigit(*s)) s++; /* skip over dpy number */
1019 while (*s == '.') s++; /* skip over dot */
1020 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
1021 sprintf(s, "%d", screen_number); /* put on screen number */
1023 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1024 any more, right? It's not Posix, but everyone seems to have it. */
1028 #endif /* HAVE_PUTENV */
1032 /* Restarting the xscreensaver process from scratch. */
1034 static char **saved_argv;
1037 save_argv (int argc, char **argv)
1039 saved_argv = (char **) calloc (argc+2, sizeof (char *));
1040 saved_argv [argc] = 0;
1043 int i = strlen (argv [argc]) + 1;
1044 saved_argv [argc] = (char *) malloc (i);
1045 memcpy (saved_argv [argc], argv [argc], i);
1050 /* Re-execs the process with the arguments in saved_argv.
1051 Does not return unless there was an error.
1054 restart_process (saver_info *si)
1056 if (si->prefs.verbose_p)
1059 fprintf (real_stderr, "%s: re-executing", blurb());
1060 for (i = 0; saved_argv[i]; i++)
1061 fprintf (real_stderr, " %s", saved_argv[i]);
1062 fprintf (real_stderr, "\n");
1064 describe_uids (si, real_stderr);
1065 fprintf (real_stderr, "\n");
1067 fflush (real_stdout);
1068 fflush (real_stderr);
1069 execvp (saved_argv [0], saved_argv); /* shouldn't return */
1072 sprintf (buf, "%s: could not restart process", blurb());