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... */
74 static void hack_subproc_environment (saver_screen_info *ssi);
78 nice_subproc (int nice_level)
83 #if defined(HAVE_NICE)
85 int old_nice = nice (0);
86 int n = nice_level - old_nice;
88 if (nice (n) == -1 && errno != 0)
91 sprintf (buf, "%s: nice(%d) failed", blurb(), n);
95 #elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
96 if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
99 sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
100 blurb(), (unsigned long) getpid(), nice_level);
105 "%s: don't know how to change process priority on this system.\n",
115 exec_simple_command (const char *command)
119 char *token = strtok (strdup(command), " \t");
123 token = strtok(0, " \t");
127 execvp (av[0], av); /* shouldn't return. */
131 sprintf (buf, "%s: could not execute \"%s\"", blurb(), av[0]);
134 if (errno == ENOENT &&
135 (token = getenv("PATH")))
139 # define PATH_MAX MAXPATHLEN
141 # define PATH_MAX 2048
145 fprintf (stderr, "\n");
147 # if defined(HAVE_GETCWD)
148 getcwd (path, sizeof(path));
149 # elif defined(HAVE_GETWD)
153 fprintf (stderr, " Current directory is: %s\n", path);
154 fprintf (stderr, " PATH is:\n");
155 token = strtok (strdup(token), ":");
158 fprintf (stderr, " %s\n", token);
159 token = strtok(0, ":");
161 fprintf (stderr, "\n");
166 exit (1); /* Note that this only exits a child fork. */
171 exec_complex_command (const char *shell, const char *command)
175 char *command2 = (char *) malloc (strlen (command) + 6);
176 memcpy (command2, "exec ", 5);
177 memcpy (command2 + 5, command, strlen (command) + 1);
179 /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
180 av [ac++] = (char *) shell;
182 av [ac++] = command2;
185 execvp (av[0], av); /* shouldn't return. */
189 sprintf (buf, "%s: execvp(\"%s\") failed", blurb(), av[0]);
193 exit (1); /* Note that this only exits a child fork. */
200 exec_vms_command (const char *command)
205 exit (1); /* Note that this only exits a child fork. */
212 exec_screenhack (saver_info *si, const char *command)
214 /* I don't believe what a sorry excuse for an operating system UNIX is!
216 - I want to spawn a process.
217 - I want to know it's pid so that I can kill it.
218 - I would like to receive a message when it dies of natural causes.
219 - I want the spawned process to have user-specified arguments.
221 If shell metacharacters are present (wildcards, backquotes, etc), the
222 only way to parse those arguments is to run a shell to do the parsing
225 And the only way to know the pid of the process is to fork() and exec()
226 it in the spawned side of the fork.
228 But if you're running a shell to parse your arguments, this gives you
229 the pid of the *shell*, not the pid of the *process* that you're
230 actually interested in, which is an *inferior* of the shell. This also
231 means that the SIGCHLD you get applies to the shell, not its inferior.
232 (Why isn't that sufficient? I don't remember any more, but it turns
235 So, the only solution, when metacharacters are present, is to force the
236 shell to exec() its inferior. What a fucking hack! We prepend "exec "
237 to the command string, and hope it doesn't contain unquoted semicolons
238 or ampersands (we don't search for them, because we don't want to
239 prohibit their use in quoted strings (messages, for example) and parsing
240 out the various quote characters is too much of a pain.)
242 (Actually, Clint Wong <clint@jts.com> points out that process groups
243 might be used to take care of this problem; this may be worth considering
244 some day, except that, 1: this code works now, so why fix it, and 2: from
245 what I've seen in Emacs, dealing with process groups isn't especially
248 saver_preferences *p = &si->prefs;
251 Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"");
254 fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
255 blurb(), command, (unsigned long) getpid (),
256 (hairy_p ? " (via shell)" : ""));
259 /* If it contains any shell metacharacters, do it the hard way,
260 and fork a shell to parse the arguments for us. */
261 exec_complex_command (p->shell, command);
263 /* Otherwise, we can just exec the program directly. */
264 exec_simple_command (command);
268 fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
269 blurb(), command, getpid());
270 exec_vms_command (command);
273 abort(); /* that shouldn't have returned. */
278 /* Management of child processes, and de-zombification.
282 job_running, /* the process is still alive */
283 job_stopped, /* we have sent it a STOP signal */
284 job_killed, /* we have sent it a TERM signal */
285 job_dead /* we have wait()ed for it, and it's dead -- this state only
286 occurs so that we can avoid calling free() from a signal
287 handler. Shortly after going into this state, the list
288 element will be removed. */
291 struct screenhack_job {
294 enum job_status status;
295 struct screenhack_job *next;
298 static struct screenhack_job *jobs = 0;
300 /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
304 struct screenhack_job *job;
305 fprintf(stderr, "%s: job list:\n", blurb());
306 for (job = jobs; job; job = job->next)
307 fprintf (stderr, " %5ld: (%s) %s\n",
309 (job->status == job_running ? "running" :
310 job->status == job_stopped ? "stopped" :
311 job->status == job_killed ? " killed" :
312 job->status == job_dead ? " dead" : " ???"),
314 fprintf (stderr, "\n");
318 static void clean_job_list (void);
320 static struct screenhack_job *
321 make_job (pid_t pid, const char *cmd)
323 struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
325 static char name [1024];
326 const char *in = cmd;
331 while (isspace(*in)) in++; /* skip whitespace */
332 while (!isspace(*in) && *in != ':')
333 *out++ = *in++; /* snarf first token */
334 while (isspace(*in)) in++; /* skip whitespace */
335 if (*in == ':') /* token was a visual name; skip it. */
339 while (isspace(*in)) in++; /* skip whitespace */
340 while (!isspace(*in)) *out++ = *in++; /* snarf first token */
344 job->name = strdup(name);
346 job->status = job_running;
355 free_job (struct screenhack_job *job)
359 else if (job == jobs)
363 struct screenhack_job *job2, *prev;
364 for (prev = 0, job2 = jobs;
366 prev = job2, job2 = job2->next)
369 prev->next = job->next;
378 /* Cleans out dead jobs from the jobs list -- this must only be called
379 from the main thread, not from a signal handler.
382 clean_job_list (void)
384 struct screenhack_job *job, *prev, *next;
385 for (prev = 0, job = jobs, next = (job ? job->next : 0);
387 prev = job, job = next, next = (job ? job->next : 0))
389 if (job->status == job_dead)
400 static struct screenhack_job *
403 struct screenhack_job *job;
404 for (job = jobs; job; job = job->next)
410 static void await_dying_children (saver_info *si);
412 static void describe_dead_child (saver_info *, pid_t, int wait_status);
416 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
417 Don't alter this directly -- use block_sigchld() / unblock_sigchld().
419 static int block_sigchld_handler = 0;
425 #ifdef HAVE_SIGACTION
427 sigemptyset (&child_set);
428 sigaddset (&child_set, SIGCHLD);
429 sigprocmask (SIG_BLOCK, &child_set, 0);
430 #endif /* HAVE_SIGACTION */
432 block_sigchld_handler++;
436 unblock_sigchld (void)
438 #ifdef HAVE_SIGACTION
440 sigemptyset(&child_set);
441 sigaddset(&child_set, SIGCHLD);
442 sigprocmask(SIG_UNBLOCK, &child_set, 0);
443 #endif /* HAVE_SIGACTION */
445 block_sigchld_handler--;
449 kill_job (saver_info *si, pid_t pid, int signal)
451 saver_preferences *p = &si->prefs;
452 struct screenhack_job *job;
457 if (block_sigchld_handler)
458 /* This function should not be called from the signal handler. */
461 block_sigchld(); /* we control the horizontal... */
463 job = find_job (pid);
466 job->status == job_killed)
469 fprintf (stderr, "%s: no child %ld to signal!\n",
470 blurb(), (long) pid);
475 case SIGTERM: job->status = job_killed; break;
477 /* #### there must be a way to do this on VMS... */
478 case SIGSTOP: job->status = job_stopped; break;
479 case SIGCONT: job->status = job_running; break;
486 fprintf (stderr, "%s: %s pid %lu.\n", blurb(),
487 (signal == SIGTERM ? "killing" :
488 signal == SIGSTOP ? "suspending" :
489 signal == SIGCONT ? "resuming" : "signalling"),
490 (unsigned long) job->pid);
493 fprintf (stderr, "%s: %s pid %lu.\n", blurb(), "killing",
494 (unsigned long) job->pid);
495 #endif /* !SIGSTOP */
497 status = kill (job->pid, signal);
499 if (p->verbose_p && status < 0)
502 fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
503 blurb(), job->pid, job->name);
507 sprintf (buf, "%s: couldn't kill child process %lu (%s)",
508 blurb(), job->pid, job->name);
513 await_dying_children (si);
517 if (block_sigchld_handler < 0)
527 sigchld_handler (int sig)
529 saver_info *si = global_si_kludge; /* I hate C so much... */
531 if (si->prefs.debug_p)
532 fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
533 (block_sigchld_handler ? " (blocked)" : ""));
535 if (block_sigchld_handler < 0)
537 else if (block_sigchld_handler == 0)
540 await_dying_children (si);
551 await_dying_children (saver_info *si)
559 kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
561 if (si->prefs.debug_p)
563 if (kid < 0 && errno)
564 fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
567 fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
571 /* 0 means no more children to reap.
572 -1 means error -- except "interrupted system call" isn't a "real"
573 error, so if we get that, we should just try again. */
575 (kid < 0 && errno != EINTR))
578 describe_dead_child (si, kid, wait_status);
584 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
587 saver_preferences *p = &si->prefs;
588 struct screenhack_job *job = find_job (kid);
589 const char *name = job ? job->name : "<unknown>";
591 if (WIFEXITED (wait_status))
593 int exit_status = WEXITSTATUS (wait_status);
595 /* Treat exit code as a signed 8-bit quantity. */
596 if (exit_status & 0x80) exit_status |= ~0xFF;
598 /* One might assume that exiting with non-0 means something went wrong.
599 But that loser xswarm exits with the code that it was killed with, so
600 it *always* exits abnormally. Treat abnormal exits as "normal" (don't
601 mention them) if we've just killed the subprocess. But mention them
602 if they happen on their own.
606 (p->verbose_p || job->status != job_killed)))
608 "%s: child pid %lu (%s) exited abnormally (code %d).\n",
609 blurb(), (unsigned long) kid, name, exit_status);
610 else if (p->verbose_p)
611 fprintf (stderr, "%s: child pid %lu (%s) exited normally.\n",
612 blurb(), (unsigned long) kid, name);
615 job->status = job_dead;
617 else if (WIFSIGNALED (wait_status))
621 job->status != job_killed ||
622 WTERMSIG (wait_status) != SIGTERM)
623 fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
624 blurb(), (unsigned long) kid, name,
625 signal_name (WTERMSIG(wait_status)));
628 job->status = job_dead;
630 else if (WIFSTOPPED (wait_status))
633 fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
634 blurb(), (unsigned long) kid, name,
635 signal_name (WSTOPSIG (wait_status)));
638 job->status = job_stopped;
642 fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
643 blurb(), (unsigned long) kid, name);
645 job->status = job_dead;
648 /* Clear out the pid so that screenhack_running_p() knows it's dead.
650 if (!job || job->status == job_dead)
651 for (i = 0; i < si->nscreens; i++)
653 saver_screen_info *ssi = &si->screens[i];
660 static void await_dying_children (saver_info *si) { return; }
669 # ifdef HAVE_SIGACTION /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
671 static Bool sigchld_initialized_p = 0;
672 if (!sigchld_initialized_p)
674 struct sigaction action, old;
676 action.sa_handler = sigchld_handler;
677 sigemptyset(&action.sa_mask);
680 if (sigaction(SIGCHLD, &action, &old) < 0)
683 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
686 sigchld_initialized_p = True;
689 # else /* !HAVE_SIGACTION */
691 if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
694 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
697 # endif /* !HAVE_SIGACTION */
706 select_visual_of_hack (saver_screen_info *ssi, const char *hack)
708 saver_info *si = ssi->global;
709 saver_preferences *p = &si->prefs;
711 static char vis [1024];
712 const char *in = hack;
714 while (isspace(*in)) in++; /* skip whitespace */
715 while (!isspace(*in) && *in != ':')
716 *out++ = *in++; /* snarf first token */
717 while (isspace(*in)) in++; /* skip whitespace */
721 selected = select_visual(ssi, vis);
723 selected = select_visual(ssi, 0);
725 if (!selected && (p->verbose_p || si->demo_mode_p))
727 if (*in == ':') in++;
728 while (isspace(*in)) in++;
731 ? "%s: warning, no \"%s\" visual for \"%s\".\n"
732 : "%s: no \"%s\" visual; skipping \"%s\".\n"),
733 blurb(), (vis ? vis : "???"), in);
741 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
743 saver_info *si = ssi->global;
744 saver_preferences *p = &si->prefs;
745 raise_window (si, first_time_p, True, False);
748 if (p->screenhacks_count || si->demo_mode_p)
757 hack = si->demo_hack;
759 /* Ignore visual-selection failure if in demo mode. */
760 (void) select_visual_of_hack (ssi, hack);
767 if (p->screenhacks_count == 1)
769 else if (si->selection_mode == -1)
770 new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
771 else if (si->selection_mode == -2)
772 new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
773 % p->screenhacks_count);
774 else if (si->selection_mode > 0)
775 new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
777 while ((new_hack = random () % p->screenhacks_count)
778 == ssi->current_hack)
780 ssi->current_hack = new_hack;
781 hack = p->screenhacks[ssi->current_hack];
783 if (!select_visual_of_hack (ssi, hack))
785 if (++retry_count > (p->screenhacks_count*4))
787 /* Uh, oops. Odds are, there are no suitable visuals,
788 and we're looping. Give up. (This is totally lame,
789 what we should do is make a list of suitable hacks at
790 the beginning, then only loop over them.)
794 "%s: no suitable visuals for these programs.\n",
803 if (si->selection_mode < 0)
804 si->selection_mode = 0;
807 /* If there's a visual description on the front of the command, nuke it.
811 while (isspace(*in)) in++; /* skip whitespace */
813 while (!isspace(*in) && *in != ':') in++; /* snarf first token */
814 while (isspace(*in)) in++; /* skip whitespace */
818 while (isspace(*in)) in++;
823 switch ((int) (forked = fork ()))
826 sprintf (buf, "%s: couldn't fork", blurb());
828 restore_real_vroot (si);
829 saver_exit (si, 1, 0);
832 close (ConnectionNumber (si->dpy)); /* close display fd */
833 nice_subproc (p->nice_inferior); /* change process priority */
834 hack_subproc_environment (ssi); /* set $DISPLAY */
835 exec_screenhack (si, hack); /* this does not return */
841 (void) make_job (forked, hack);
849 spawn_screenhack (saver_info *si, Bool first_time_p)
853 if (!monitor_powered_on_p (si))
855 if (si->prefs.verbose_p)
857 "%s: server reports that monitor has powered down; "
858 "not launching a new hack.\n", blurb());
862 for (i = 0; i < si->nscreens; i++)
864 saver_screen_info *ssi = &si->screens[i];
865 spawn_screenhack_1 (ssi, first_time_p);
871 kill_screenhack (saver_info *si)
874 for (i = 0; i < si->nscreens; i++)
876 saver_screen_info *ssi = &si->screens[i];
878 kill_job (si, ssi->pid, SIGTERM);
885 suspend_screenhack (saver_info *si, Bool suspend_p)
887 #ifdef SIGSTOP /* older VMS doesn't have it... */
889 for (i = 0; i < si->nscreens; i++)
891 saver_screen_info *ssi = &si->screens[i];
893 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
899 /* Called when we're exiting abnormally, to kill off the subproc. */
901 emergency_kill_subproc (saver_info *si)
905 signal (SIGCHLD, SIG_IGN);
908 for (i = 0; i < si->nscreens; i++)
910 saver_screen_info *ssi = &si->screens[i];
913 kill_job (si, ssi->pid, SIGTERM);
920 screenhack_running_p (saver_info *si)
924 for (i = 0; i < si->nscreens; i++)
926 saver_screen_info *ssi = &si->screens[i];
934 /* Environment variables. */
937 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
938 is defined, the xscreensaver daemon will search that directory for hacks.
941 hack_environment (saver_info *si)
943 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
944 static const char *def_path = DEFAULT_PATH_PREFIX;
945 if (def_path && *def_path)
947 const char *opath = getenv("PATH");
948 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
949 strcpy (npath, "PATH=");
950 strcat (npath, def_path);
952 strcat (npath, opath);
957 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
962 hack_subproc_environment (saver_screen_info *ssi)
964 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
965 the spawned processes inherit is correct. First, it must be on the same
966 host and display as the value of -display passed in on our command line
967 (which is not necessarily the same as what our $DISPLAY variable is.)
968 Second, the screen number in the $DISPLAY passed to the subprocess should
969 be the screen on which this particular hack is running -- not the display
970 specification which the driver itself is using, since the driver ignores
971 its screen number and manages all existing screens.
973 saver_info *si = ssi->global;
974 const char *odpy = DisplayString (si->dpy);
975 char *ndpy = (char *) malloc(strlen(odpy) + 20);
979 for (screen_number = 0; screen_number < si->nscreens; screen_number++)
980 if (ssi == &si->screens[screen_number])
983 strcpy (ndpy, "DISPLAY=");
984 s = ndpy + strlen(ndpy);
987 while (*s && *s != ':') s++; /* skip to colon */
988 while (*s == ':') s++; /* skip over colons */
989 while (isdigit(*s)) s++; /* skip over dpy number */
990 while (*s == '.') s++; /* skip over dot */
991 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
992 sprintf(s, "%d", screen_number); /* put on screen number */
994 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
995 any more, right? It's not Posix, but everyone seems to have it. */
999 #endif /* HAVE_PUTENV */
1003 /* Restarting the xscreensaver process from scratch. */
1005 static char **saved_argv;
1008 save_argv (int argc, char **argv)
1010 /* Leave room for one more argument, the -initial-demo-mode switch. */
1011 saved_argv = (char **) calloc (argc+2, sizeof (char *));
1012 saved_argv [argc] = 0;
1015 int i = strlen (argv [argc]) + 1;
1016 saved_argv [argc] = (char *) malloc (i);
1017 memcpy (saved_argv [argc], argv [argc], i);
1021 /* Modifies saved_argv to either contain or not contain "-initial-demo-mode".
1024 hack_saved_argv (Bool demo_mode_p)
1026 static char *demo_mode_switch = "-initial-demo-mode";
1028 if (demo_mode_p) /* We want the switch to be in the args. */
1030 /* See if the switch is there already. If so, we're done. */
1032 for (i = 0; saved_argv[i]; i++)
1033 if (!strcmp (saved_argv[i], demo_mode_switch))
1036 /* If it wasn't there, add it to the end. save_argv() made room. */
1037 saved_argv [i] = demo_mode_switch;
1038 saved_argv [i+1] = 0;
1040 else /* We want the switch to not be in the args. */
1043 for (i = 0; saved_argv[i]; i++)
1044 while (!strcmp (saved_argv [i], demo_mode_switch))
1047 for (j = i; saved_argv[j]; j++)
1048 saved_argv [j] = saved_argv [j+1];
1054 /* Re-execs the process with the arguments in saved_argv.
1055 Does not return unless there was an error.
1058 restart_process_1 (saver_info *si)
1060 fflush (real_stdout);
1061 fflush (real_stderr);
1062 execvp (saved_argv [0], saved_argv); /* shouldn't return */
1065 sprintf (buf, "%s: could not restart process", blurb());
1073 /* Re-execs the process with the arguments in saved_argv,
1074 minus -initial-demo-mode.
1075 Does not return unless there was an error.
1078 restart_process (saver_info *si)
1080 hack_saved_argv (True);
1081 restart_process_1 (si);
1084 /* Re-execs the process with the arguments in saved_argv,
1085 plus -initial-demo-mode.
1086 Does not return unless there was an error.
1089 demo_mode_restart_process (saver_info *si)
1091 hack_saved_argv (False);
1092 restart_process_1 (si);