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 */
32 # include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
33 # include <sys/wait.h> /* for waitpid() and associated macros */
37 # if __DECC_VER >= 50200000
38 # include <sys/wait.h>
41 # include <processes.h>
42 # include <unixio.h> /* for close */
43 # include <unixlib.h> /* for getpid */
49 #include <signal.h> /* for the signal names */
51 #if !defined(SIGCHLD) && defined(SIGCLD)
52 #define SIGCHLD SIGCLD
56 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->next_mode_p == 1)
770 new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
771 else if (si->next_mode_p == 2)
772 new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
773 % p->screenhacks_count);
775 while ((new_hack = random () % p->screenhacks_count)
776 == ssi->current_hack)
778 ssi->current_hack = new_hack;
779 hack = p->screenhacks[ssi->current_hack];
781 if (!select_visual_of_hack (ssi, hack))
783 if (++retry_count > (p->screenhacks_count*4))
785 /* Uh, oops. Odds are, there are no suitable visuals,
786 and we're looping. Give up. (This is totally lame,
787 what we should do is make a list of suitable hacks at
788 the beginning, then only loop over them.)
792 "%s: no suitable visuals for these programs.\n",
803 /* If there's a visual description on the front of the command, nuke it.
807 while (isspace(*in)) in++; /* skip whitespace */
809 while (!isspace(*in) && *in != ':') in++; /* snarf first token */
810 while (isspace(*in)) in++; /* skip whitespace */
814 while (isspace(*in)) in++;
819 switch ((int) (forked = fork ()))
822 sprintf (buf, "%s: couldn't fork", blurb());
824 restore_real_vroot (si);
825 saver_exit (si, 1, 0);
828 close (ConnectionNumber (si->dpy)); /* close display fd */
829 nice_subproc (p->nice_inferior); /* change process priority */
830 hack_subproc_environment (ssi); /* set $DISPLAY */
831 exec_screenhack (si, hack); /* this does not return */
837 (void) make_job (forked, hack);
845 spawn_screenhack (saver_info *si, Bool first_time_p)
849 if (!monitor_powered_on_p (si))
851 if (si->prefs.verbose_p)
853 "%s: server reports that monitor has powered down; "
854 "not launching a new hack.\n", blurb());
858 for (i = 0; i < si->nscreens; i++)
860 saver_screen_info *ssi = &si->screens[i];
861 spawn_screenhack_1 (ssi, first_time_p);
867 kill_screenhack (saver_info *si)
870 for (i = 0; i < si->nscreens; i++)
872 saver_screen_info *ssi = &si->screens[i];
874 kill_job (si, ssi->pid, SIGTERM);
881 suspend_screenhack (saver_info *si, Bool suspend_p)
883 #ifdef SIGSTOP /* older VMS doesn't have it... */
885 for (i = 0; i < si->nscreens; i++)
887 saver_screen_info *ssi = &si->screens[i];
889 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
895 /* Called when we're exiting abnormally, to kill off the subproc. */
897 emergency_kill_subproc (saver_info *si)
901 signal (SIGCHLD, SIG_IGN);
904 for (i = 0; i < si->nscreens; i++)
906 saver_screen_info *ssi = &si->screens[i];
909 kill_job (si, ssi->pid, SIGTERM);
916 screenhack_running_p (saver_info *si)
920 for (i = 0; i < si->nscreens; i++)
922 saver_screen_info *ssi = &si->screens[i];
930 /* Restarting the xscreensaver process from scratch. */
932 static char **saved_argv;
935 save_argv (int argc, char **argv)
937 saved_argv = (char **) malloc ((argc + 2) * sizeof (char *));
938 saved_argv [argc] = 0;
941 int i = strlen (argv [argc]) + 1;
942 saved_argv [argc] = (char *) malloc (i);
943 memcpy (saved_argv [argc], argv [argc], i);
948 restart_process (saver_info *si)
950 fflush (real_stdout);
951 fflush (real_stderr);
952 execvp (saved_argv [0], saved_argv); /* shouldn't return */
955 sprintf (buf, "%s: could not restart process", blurb());
961 /* Like restart_process(), but ensures that when it restarts,
962 it comes up in demo-mode. */
964 demo_mode_restart_process (saver_info *si)
967 for (i = 0; saved_argv [i]; i++);
968 /* add the -initial-demo-mode switch; save_argv() left room for this. */
969 saved_argv [i] = "-initial-demo-mode";
970 saved_argv [i+1] = 0;
971 restart_process (si); /* shouldn't return */
977 hack_subproc_environment (saver_screen_info *ssi)
979 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
980 the spawned processes inherit is correct. First, it must be on the same
981 host and display as the value of -display passed in on our command line
982 (which is not necessarily the same as what our $DISPLAY variable is.)
983 Second, the screen number in the $DISPLAY passed to the subprocess should
984 be the screen on which this particular hack is running -- not the display
985 specification which the driver itself is using, since the driver ignores
986 its screen number and manages all existing screens.
988 saver_info *si = ssi->global;
989 const char *odpy = DisplayString (si->dpy);
990 char *ndpy = (char *) malloc(strlen(odpy) + 20);
994 for (screen_number = 0; screen_number < si->nscreens; screen_number++)
995 if (ssi == &si->screens[screen_number])
997 if (screen_number >= si->nscreens) abort();
999 strcpy (ndpy, "DISPLAY=");
1000 s = ndpy + strlen(ndpy);
1003 while (*s && *s != ':') s++; /* skip to colon */
1004 while (*s == ':') s++; /* skip over colons */
1005 while (isdigit(*s)) s++; /* skip over dpy number */
1006 while (*s == '.') s++; /* skip over dot */
1007 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
1008 sprintf(s, "%d", screen_number); /* put on screen number */
1010 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1011 any more, right? It's not Posix, but everyone seems to have it. */
1015 #endif /* HAVE_PUTENV */
1020 hack_environment (saver_info *si)
1022 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
1023 static const char *def_path = DEFAULT_PATH_PREFIX;
1024 if (def_path && *def_path)
1026 const char *opath = getenv("PATH");
1027 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
1028 strcpy (npath, "PATH=");
1029 strcat (npath, def_path);
1030 strcat (npath, ":");
1031 strcat (npath, opath);
1036 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */