1 /* subprocs.c --- choosing, spawning, and killing screenhacks.
2 * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997
3 * Jamie Zawinski <jwz@netscape.com>
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 */
52 #include <pwd.h> /* for getpwnam() and struct passwd */
53 #include <grp.h> /* for getgrgid() and struct group */
54 #endif /* NO_SETUID */
56 #if !defined(SIGCHLD) && defined(SIGCLD)
57 #define SIGCHLD SIGCLD
61 extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */
63 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
65 /* This file doesn't need the Xt headers, so stub these types out... */
67 #define XtAppContext void*
68 #define XrmDatabase void*
69 #define XtIntervalId void*
70 #define XtPointer void*
73 #include "xscreensaver.h"
77 extern saver_info *global_si_kludge; /* I hate C so much... */
79 static void hack_environment (saver_screen_info *ssi);
83 nice_subproc (int nice_level)
88 #if defined(HAVE_NICE)
90 int old_nice = nice (0);
91 int n = nice_level - old_nice;
93 if (nice (n) == -1 && errno != 0)
96 sprintf (buf, "%s: nice(%d) failed", progname, n);
100 #elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
101 if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
104 sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
105 progname, (unsigned long) getpid(), nice_level);
110 "%s: don't know how to change process priority on this system.\n",
120 exec_simple_command (const char *command)
124 char *token = strtok (strdup(command), " \t");
128 token = strtok(0, " \t");
132 execvp (av[0], av); /* shouldn't return. */
136 sprintf (buf, "%s: could not execute \"%s\"", progname, av[0]);
139 if (errno == ENOENT &&
140 (token = getenv("PATH")))
144 # define PATH_MAX MAXPATHLEN
146 # define PATH_MAX 2048
150 fprintf (stderr, "\n");
152 # if defined(HAVE_GETCWD)
153 getcwd (path, sizeof(path));
154 # elif defined(HAVE_GETWD)
158 fprintf (stderr, " Current directory is: %s\n", path);
159 fprintf (stderr, " PATH is:\n");
160 token = strtok (strdup(token), ":");
163 fprintf (stderr, " %s\n", token);
164 token = strtok(0, ":");
166 fprintf (stderr, "\n");
171 exit (1); /* Note that this only exits a child fork. */
176 exec_complex_command (const char *shell, const char *command)
180 char *command2 = (char *) malloc (strlen (command) + 6);
181 memcpy (command2, "exec ", 5);
182 memcpy (command2 + 5, command, strlen (command) + 1);
184 /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
185 av [ac++] = (char *) shell;
187 av [ac++] = command2;
190 execvp (av[0], av); /* shouldn't return. */
194 sprintf (buf, "%s: execvp(\"%s\") failed", progname, av[0]);
198 exit (1); /* Note that this only exits a child fork. */
205 exec_vms_command (const char *command)
210 exit (1); /* Note that this only exits a child fork. */
217 exec_screenhack (saver_info *si, const char *command)
219 /* I don't believe what a sorry excuse for an operating system UNIX is!
221 - I want to spawn a process.
222 - I want to know it's pid so that I can kill it.
223 - I would like to receive a message when it dies of natural causes.
224 - I want the spawned process to have user-specified arguments.
226 If shell metacharacters are present (wildcards, backquotes, etc), the
227 only way to parse those arguments is to run a shell to do the parsing
230 And the only way to know the pid of the process is to fork() and exec()
231 it in the spawned side of the fork.
233 But if you're running a shell to parse your arguments, this gives you
234 the pid of the *shell*, not the pid of the *process* that you're
235 actually interested in, which is an *inferior* of the shell. This also
236 means that the SIGCHLD you get applies to the shell, not its inferior.
237 (Why isn't that sufficient? I don't remember any more, but it turns
240 So, the only solution, when metacharacters are present, is to force the
241 shell to exec() its inferior. What a fucking hack! We prepend "exec "
242 to the command string, and hope it doesn't contain unquoted semicolons
243 or ampersands (we don't search for them, because we don't want to
244 prohibit their use in quoted strings (messages, for example) and parsing
245 out the various quote characters is too much of a pain.)
247 (Actually, Clint Wong <clint@jts.com> points out that process groups
248 might be used to take care of this problem; this may be worth considering
249 some day, except that, 1: this code works now, so why fix it, and 2: from
250 what I've seen in Emacs, dealing with process groups isn't especially
253 saver_preferences *p = &si->prefs;
256 Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"");
259 printf ("%s: spawning \"%s\" in pid %lu%s.\n",
260 progname, command, (unsigned long) getpid (),
261 (hairy_p ? " (via shell)" : ""));
264 /* If it contains any shell metacharacters, do it the hard way,
265 and fork a shell to parse the arguments for us. */
266 exec_complex_command (p->shell, command);
268 /* Otherwise, we can just exec the program directly. */
269 exec_simple_command (command);
273 printf ("%s: spawning \"%s\" in pid %lu.\n", progname, command, getpid());
274 exec_vms_command (command);
277 abort(); /* that shouldn't have returned. */
282 /* Management of child processes, and de-zombification.
286 job_running, /* the process is still alive */
287 job_stopped, /* we have sent it a STOP signal */
288 job_killed, /* we have sent it a TERM signal */
289 job_dead /* we have wait()ed for it, and it's dead -- this state only
290 occurs so that we can avoid calling free() from a signal
291 handler. Shortly after going into this state, the list
292 element will be removed. */
295 struct screenhack_job {
298 enum job_status status;
299 struct screenhack_job *next;
302 static struct screenhack_job *jobs = 0;
308 struct screenhack_job *job;
309 fprintf(stderr, "%s: job list:\n", progname);
310 for (job = jobs; job; job = job->next)
311 fprintf (stderr, " %5ld: (%s) %s\n",
313 (job->status == job_running ? "running" :
314 job->status == job_stopped ? "stopped" :
315 job->status == job_killed ? " killed" :
316 job->status == job_dead ? " dead" : " ???"),
318 fprintf (stderr, "\n");
323 static void clean_job_list (void);
325 static struct screenhack_job *
326 make_job (pid_t pid, const char *cmd)
328 struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
330 static char name [1024];
331 const char *in = cmd;
336 while (isspace(*in)) in++; /* skip whitespace */
337 while (!isspace(*in) && *in != ':')
338 *out++ = *in++; /* snarf first token */
339 while (isspace(*in)) in++; /* skip whitespace */
340 if (*in == ':') /* token was a visual name; skip it. */
344 while (isspace(*in)) in++; /* skip whitespace */
345 while (!isspace(*in)) *out++ = *in++; /* snarf first token */
349 job->name = strdup(name);
351 job->status = job_running;
360 free_job (struct screenhack_job *job)
364 else if (job == jobs)
368 struct screenhack_job *job2, *prev;
369 for (prev = 0, job2 = jobs;
371 prev = job2, job2 = job2->next)
374 prev->next = job->next;
383 /* Cleans out dead jobs from the jobs list -- this must only be called
384 from the main thread, not from a signal handler.
387 clean_job_list (void)
389 struct screenhack_job *job, *prev, *next;
390 for (prev = 0, job = jobs, next = (job ? job->next : 0);
392 prev = job, job = next, next = (job ? job->next : 0))
394 if (job->status == job_dead)
405 static struct screenhack_job *
408 struct screenhack_job *job;
409 for (job = jobs; job; job = job->next)
415 static void await_dying_children (saver_info *si);
417 static void describe_dead_child (saver_info *, pid_t, int wait_status);
421 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op. */
422 static int block_sigchld_handler = 0;
425 kill_job (saver_info *si, pid_t pid, int signal)
427 saver_preferences *p = &si->prefs;
428 struct screenhack_job *job;
433 if (block_sigchld_handler)
434 /* This function should not be called from the signal handler. */
437 block_sigchld_handler++; /* we control the horizontal... */
439 job = find_job (pid);
442 job->status == job_killed)
445 fprintf (stderr, "%s: no child %ld to signal!\n",
446 progname, (long) pid);
451 case SIGTERM: job->status = job_killed; break;
453 /* #### there must be a way to do this on VMS... */
454 case SIGSTOP: job->status = job_stopped; break;
455 case SIGCONT: job->status = job_running; break;
462 fprintf (stderr, "%s: %s pid %lu.\n", progname,
463 (signal == SIGTERM ? "killing" :
464 signal == SIGSTOP ? "suspending" :
465 signal == SIGCONT ? "resuming" : "signalling"),
466 (unsigned long) job->pid);
469 fprintf (stderr, "%s: %s pid %lu.\n", progname, "killing",
470 (unsigned long) job->pid);
471 #endif /* !SIGSTOP */
473 status = kill (job->pid, signal);
475 if (p->verbose_p && status < 0)
478 fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
479 progname, job->pid, job->name);
483 sprintf (buf, "%s: couldn't kill child process %lu (%s)",
484 progname, job->pid, job->name);
489 await_dying_children (si);
492 block_sigchld_handler--;
493 if (block_sigchld_handler < 0)
503 sigchld_handler (int sig)
505 saver_info *si = global_si_kludge; /* I hate C so much... */
508 if (si->prefs.debug_p)
509 fprintf(stderr, "%s: got SIGCHLD%s\n", progname,
510 (block_sigchld_handler ? " (blocked)" : ""));
513 if (block_sigchld_handler < 0)
515 else if (block_sigchld_handler == 0)
517 block_sigchld_handler++;
518 await_dying_children (si);
519 block_sigchld_handler--;
529 await_dying_children (saver_info *si)
537 kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
539 if (si->prefs.debug_p)
540 if (kid < 0 && errno)
541 fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", progname,
544 fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", progname, (long) kid);
547 /* 0 means no more children to reap.
548 -1 means error -- except "interrupted system call" isn't a "real"
549 error, so if we get that, we should just try again. */
551 (kid < 0 && errno != EINTR))
554 describe_dead_child (si, kid, wait_status);
560 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
563 saver_preferences *p = &si->prefs;
564 struct screenhack_job *job = find_job (kid);
565 const char *name = job ? job->name : "<unknown>";
567 if (WIFEXITED (wait_status))
569 int exit_status = WEXITSTATUS (wait_status);
571 /* Treat exit code as a signed 8-bit quantity. */
572 if (exit_status & 0x80) exit_status |= ~0xFF;
574 /* One might assume that exiting with non-0 means something went wrong.
575 But that loser xswarm exits with the code that it was killed with, so
576 it *always* exits abnormally. Treat abnormal exits as "normal" (don't
577 mention them) if we've just killed the subprocess. But mention them
578 if they happen on their own.
582 (p->verbose_p || job->status != job_killed)))
584 "%s: child pid %lu (%s) exited abnormally (code %d).\n",
585 progname, (unsigned long) kid, name, exit_status);
586 else if (p->verbose_p)
587 printf ("%s: child pid %lu (%s) exited normally.\n",
588 progname, (unsigned long) kid, name);
591 job->status = job_dead;
593 else if (WIFSIGNALED (wait_status))
597 job->status != job_killed ||
598 WTERMSIG (wait_status) != SIGTERM)
599 fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
600 progname, (unsigned long) kid, name,
601 signal_name (WTERMSIG(wait_status)));
604 job->status = job_dead;
606 else if (WIFSTOPPED (wait_status))
609 fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
610 progname, (unsigned long) kid, name,
611 signal_name (WSTOPSIG (wait_status)));
614 job->status = job_stopped;
618 fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
619 progname, (unsigned long) kid, name);
621 job->status = job_dead;
624 /* Clear out the pid so that screenhack_running_p() knows it's dead.
626 if (!job || job->status == job_dead)
627 for (i = 0; i < si->nscreens; i++)
629 saver_screen_info *ssi = &si->screens[i];
636 static void await_dying_children (saver_info *si) { return; }
644 if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
647 sprintf (buf, "%s: couldn't catch SIGCHLD", progname);
658 select_visual_of_hack (saver_screen_info *ssi, const char *hack)
660 saver_info *si = ssi->global;
661 saver_preferences *p = &si->prefs;
663 static char vis [1024];
664 const char *in = hack;
666 while (isspace(*in)) in++; /* skip whitespace */
667 while (!isspace(*in) && *in != ':')
668 *out++ = *in++; /* snarf first token */
669 while (isspace(*in)) in++; /* skip whitespace */
673 selected = select_visual(ssi, vis);
675 selected = select_visual(ssi, 0);
677 if (!selected && (p->verbose_p || si->demo_mode_p))
679 if (*in == ':') in++;
680 while (isspace(*in)) in++;
683 ? "%s: warning, no \"%s\" visual for \"%s\".\n"
684 : "%s: no \"%s\" visual; skipping \"%s\".\n"),
685 progname, (vis ? vis : "???"), in);
693 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
695 saver_info *si = ssi->global;
696 saver_preferences *p = &si->prefs;
697 raise_window (si, first_time_p, True, False);
700 if (p->screenhacks_count || si->demo_mode_p)
709 hack = si->demo_hack;
711 /* Ignore visual-selection failure if in demo mode. */
712 (void) select_visual_of_hack (ssi, hack);
719 if (p->screenhacks_count == 1)
721 else if (si->next_mode_p == 1)
722 new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
723 else if (si->next_mode_p == 2)
724 new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
725 % p->screenhacks_count);
727 while ((new_hack = random () % p->screenhacks_count)
728 == ssi->current_hack)
730 ssi->current_hack = new_hack;
731 hack = p->screenhacks[ssi->current_hack];
733 if (!select_visual_of_hack (ssi, hack))
735 if (++retry_count > (p->screenhacks_count*4))
737 /* Uh, oops. Odds are, there are no suitable visuals,
738 and we're looping. Give up. (This is totally lame,
739 what we should do is make a list of suitable hacks at
740 the beginning, then only loop over them.)
744 "%s: no suitable visuals for these programs.\n",
755 /* If there's a visual description on the front of the command, nuke it.
759 while (isspace(*in)) in++; /* skip whitespace */
761 while (!isspace(*in) && *in != ':') in++; /* snarf first token */
762 while (isspace(*in)) in++; /* skip whitespace */
766 while (isspace(*in)) in++;
771 switch ((int) (forked = fork ()))
774 sprintf (buf, "%s: couldn't fork", progname);
776 restore_real_vroot (si);
780 close (ConnectionNumber (si->dpy)); /* close display fd */
781 nice_subproc (p->nice_inferior); /* change process priority */
782 hack_environment (ssi); /* set $DISPLAY */
783 exec_screenhack (si, hack); /* this does not return */
789 (void) make_job (forked, hack);
797 spawn_screenhack (saver_info *si, Bool first_time_p)
800 for (i = 0; i < si->nscreens; i++)
802 saver_screen_info *ssi = &si->screens[i];
803 spawn_screenhack_1 (ssi, first_time_p);
809 kill_screenhack (saver_info *si)
812 for (i = 0; i < si->nscreens; i++)
814 saver_screen_info *ssi = &si->screens[i];
816 kill_job (si, ssi->pid, SIGTERM);
823 suspend_screenhack (saver_info *si, Bool suspend_p)
825 #ifdef SIGSTOP /* older VMS doesn't have it... */
827 for (i = 0; i < si->nscreens; i++)
829 saver_screen_info *ssi = &si->screens[i];
831 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
837 /* Called when we're exiting abnormally, to kill off the subproc. */
839 emergency_kill_subproc (saver_info *si)
843 signal (SIGCHLD, SIG_IGN);
846 for (i = 0; i < si->nscreens; i++)
848 saver_screen_info *ssi = &si->screens[i];
851 kill_job (si, ssi->pid, SIGTERM);
858 screenhack_running_p (saver_info *si)
862 for (i = 0; i < si->nscreens; i++)
864 saver_screen_info *ssi = &si->screens[i];
872 /* Restarting the xscreensaver process from scratch. */
874 static char **saved_argv;
877 save_argv (int argc, char **argv)
879 saved_argv = (char **) malloc ((argc + 2) * sizeof (char *));
880 saved_argv [argc] = 0;
883 int i = strlen (argv [argc]) + 1;
884 saved_argv [argc] = (char *) malloc (i);
885 memcpy (saved_argv [argc], argv [argc], i);
890 restart_process (saver_info *si)
892 fflush (real_stdout);
893 fflush (real_stderr);
894 execvp (saved_argv [0], saved_argv); /* shouldn't return */
897 sprintf (buf, "%s: could not restart process", progname);
903 /* Like restart_process(), but ensures that when it restarts,
904 it comes up in demo-mode. */
906 demo_mode_restart_process (saver_info *si)
909 for (i = 0; saved_argv [i]; i++);
910 /* add the -demo switch; save_argv() left room for this. */
911 saved_argv [i] = "-demo";
912 saved_argv [i+1] = 0;
913 restart_process (si); /* shouldn't return */
919 hack_environment (saver_screen_info *ssi)
921 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
922 the spawned processes inherit is correct. First, it must be on the same
923 host and display as the value of -display passed in on our command line
924 (which is not necessarily the same as what our $DISPLAY variable is.)
925 Second, the screen number in the $DISPLAY passed to the subprocess should
926 be the screen on which this particular hack is running -- not the display
927 specification which the driver itself is using, since the driver ignores
928 its screen number and manages all existing screens.
930 saver_info *si = ssi->global;
931 const char *odpy = DisplayString (si->dpy);
932 char *ndpy = (char *) malloc(strlen(odpy) + 20);
936 for (screen_number = 0; screen_number < si->nscreens; screen_number++)
937 if (ssi == &si->screens[screen_number])
939 if (screen_number >= si->nscreens) abort();
941 strcpy (ndpy, "DISPLAY=");
942 s = ndpy + strlen(ndpy);
945 while (*s && *s != ':') s++; /* skip to colon */
946 while (*s == ':') s++; /* skip over colons */
947 while (isdigit(*s)) s++; /* skip over dpy number */
948 while (*s == '.') s++; /* skip over dot */
949 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
950 sprintf(s, "%d", screen_number); /* put on screen number */
952 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
953 any more, right? It's not Posix, but everyone seems to have it. */
957 #endif /* HAVE_PUTENV */
961 /* Change the uid/gid of the screensaver process, so that it is safe for it
962 to run setuid root (which it needs to do on some systems to read the
963 encrypted passwords from the passwd file.)
965 hack_uid() is run before opening the X connection, so that XAuth works.
966 hack_uid_warn() is called after the connection is opened and the command
967 line arguments are parsed, so that the messages from hack_uid() get
968 printed after we know whether we're in `verbose' mode.
973 static int hack_uid_errno;
974 static char hack_uid_buf [255], *hack_uid_error;
977 hack_uid (saver_info *si)
980 /* If we've been run as setuid or setgid to someone else (most likely root)
981 turn off the extra permissions so that random user-specified programs
982 don't get special privileges. (On some systems it might be necessary
983 to install this as setuid root in order to read the passwd file to
984 implement lock-mode...)
992 /* If we're being run as root (as from xdm) then switch the user id
993 to something safe. */
997 /* Locking can't work when running as root, because we have no way of
998 knowing what the user id of the logged in user is (so we don't know
999 whose password to prompt for.)
1001 si->locking_disabled_p = True;
1002 si->nolock_reason = "running as root";
1003 p = getpwnam ("nobody");
1004 if (! p) p = getpwnam ("daemon");
1005 if (! p) p = getpwnam ("bin");
1006 if (! p) p = getpwnam ("sys");
1009 hack_uid_error = "couldn't find safe uid; running as root.";
1010 hack_uid_errno = -1;
1014 struct group *g = getgrgid (p->pw_gid);
1015 hack_uid_error = hack_uid_buf;
1016 sprintf (hack_uid_error, "changing uid/gid to %s/%s (%ld/%ld).",
1017 p->pw_name, (g ? g->gr_name : "???"),
1018 (long) p->pw_uid, (long) p->pw_gid);
1020 /* Change the gid to be a safe one. If we can't do that, then
1021 print a warning. We change the gid before the uid so that we
1022 change the gid while still root. */
1023 if (setgid (p->pw_gid) != 0)
1025 hack_uid_errno = errno;
1026 sprintf (hack_uid_error, "couldn't set gid to %s (%ld)",
1027 (g ? g->gr_name : "???"), (long) p->pw_gid);
1030 /* Now change the uid to be a safe one. */
1031 if (setuid (p->pw_uid) != 0)
1033 hack_uid_errno = errno;
1034 sprintf (hack_uid_error, "couldn't set uid to %s (%ld)",
1035 p->pw_name, (long) p->pw_uid);
1040 else /* disable locking if already being run as "someone else" */
1042 struct passwd *p = getpwuid (getuid ());
1044 !strcmp (p->pw_name, "root") ||
1045 !strcmp (p->pw_name, "nobody") ||
1046 !strcmp (p->pw_name, "daemon") ||
1047 !strcmp (p->pw_name, "bin") ||
1048 !strcmp (p->pw_name, "sys"))
1050 si->locking_disabled_p = True;
1051 si->nolock_reason = hack_uid_buf;
1052 sprintf (si->nolock_reason, "running as %s", p->pw_name);
1055 #endif /* NO_LOCKING */
1059 hack_uid_warn (saver_info *si)
1061 saver_preferences *p = &si->prefs;
1063 if (! hack_uid_error)
1065 else if (hack_uid_errno == 0)
1068 printf ("%s: %s\n", progname, hack_uid_error);
1073 sprintf (buf, "%s: %s", progname, hack_uid_error);
1074 if (hack_uid_errno == -1)
1075 fprintf (stderr, "%s\n", buf);
1078 errno = hack_uid_errno;
1084 #endif /* !NO_SETUID */