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) + 10);
175 const char *after_vars;
177 /* Skip leading whitespace.
179 while (*command == ' ' || *command == '\t')
182 /* If the string has a series of tokens with "=" in them at them, set
183 `after_vars' to point into the string after those tokens and any
184 trailing whitespace. Otherwise, after_vars == command.
186 after_vars = command;
187 for (s = command; *s; s++)
189 if (*s == '=') got_eq = 1;
194 while (*s == ' ' || *s == '\t')
205 strncat (command2, command, after_vars - command);
206 strcat (command2, "exec ");
207 strcat (command2, after_vars);
209 /* We have now done these transformations:
210 "foo -x -y" ==> "exec foo -x -y"
211 "BLAT=foop foo -x" ==> "BLAT=foop exec foo -x"
212 "BLAT=foop A=b foo -x" ==> "BLAT=foop A=b exec foo -x"
216 /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
217 av [ac++] = (char *) shell;
219 av [ac++] = command2;
222 execvp (av[0], av); /* shouldn't return. */
226 sprintf (buf, "%s: execvp(\"%s\") failed", blurb(), av[0]);
230 exit (1); /* Note that this only exits a child fork. */
237 exec_vms_command (const char *command)
242 exit (1); /* Note that this only exits a child fork. */
249 exec_screenhack (saver_info *si, const char *command)
251 /* I don't believe what a sorry excuse for an operating system UNIX is!
253 - I want to spawn a process.
254 - I want to know it's pid so that I can kill it.
255 - I would like to receive a message when it dies of natural causes.
256 - I want the spawned process to have user-specified arguments.
258 If shell metacharacters are present (wildcards, backquotes, etc), the
259 only way to parse those arguments is to run a shell to do the parsing
262 And the only way to know the pid of the process is to fork() and exec()
263 it in the spawned side of the fork.
265 But if you're running a shell to parse your arguments, this gives you
266 the pid of the *shell*, not the pid of the *process* that you're
267 actually interested in, which is an *inferior* of the shell. This also
268 means that the SIGCHLD you get applies to the shell, not its inferior.
269 (Why isn't that sufficient? I don't remember any more, but it turns
272 So, the only solution, when metacharacters are present, is to force the
273 shell to exec() its inferior. What a fucking hack! We prepend "exec "
274 to the command string, and hope it doesn't contain unquoted semicolons
275 or ampersands (we don't search for them, because we don't want to
276 prohibit their use in quoted strings (messages, for example) and parsing
277 out the various quote characters is too much of a pain.)
279 (Actually, Clint Wong <clint@jts.com> points out that process groups
280 might be used to take care of this problem; this may be worth considering
281 some day, except that, 1: this code works now, so why fix it, and 2: from
282 what I've seen in Emacs, dealing with process groups isn't especially
285 saver_preferences *p = &si->prefs;
288 Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
289 /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
291 if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0)
293 /* If you're thinking of commenting this out, think again.
294 If you do so, you will open a security hole. Mail jwz
295 so that he may enlighten you as to the error of your ways.
297 fprintf (stderr, "%s: we're still running as root! Disaster!\n",
299 saver_exit (si, 1, 0);
303 fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
304 blurb(), command, (unsigned long) getpid (),
305 (hairy_p ? " (via shell)" : ""));
308 /* If it contains any shell metacharacters, do it the hard way,
309 and fork a shell to parse the arguments for us. */
310 exec_complex_command (p->shell, command);
312 /* Otherwise, we can just exec the program directly. */
313 exec_simple_command (command);
317 fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
318 blurb(), command, getpid());
319 exec_vms_command (command);
322 abort(); /* that shouldn't have returned. */
327 /* Management of child processes, and de-zombification.
331 job_running, /* the process is still alive */
332 job_stopped, /* we have sent it a STOP signal */
333 job_killed, /* we have sent it a TERM signal */
334 job_dead /* we have wait()ed for it, and it's dead -- this state only
335 occurs so that we can avoid calling free() from a signal
336 handler. Shortly after going into this state, the list
337 element will be removed. */
340 struct screenhack_job {
343 enum job_status status;
344 struct screenhack_job *next;
347 static struct screenhack_job *jobs = 0;
349 /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
353 struct screenhack_job *job;
354 fprintf(stderr, "%s: job list:\n", blurb());
355 for (job = jobs; job; job = job->next)
356 fprintf (stderr, " %5ld: (%s) %s\n",
358 (job->status == job_running ? "running" :
359 job->status == job_stopped ? "stopped" :
360 job->status == job_killed ? " killed" :
361 job->status == job_dead ? " dead" : " ???"),
363 fprintf (stderr, "\n");
367 static void clean_job_list (void);
369 static struct screenhack_job *
370 make_job (pid_t pid, const char *cmd)
372 struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
374 static char name [1024];
375 const char *in = cmd;
383 while (isspace(*in)) in++; /* skip whitespace */
384 while (!isspace(*in) && *in != ':') {
385 if (*in == '=') got_eq = 1;
386 *out++ = *in++; /* snarf first token */
389 if (got_eq) /* if the first token was FOO=bar */
390 { /* then get the next token instead. */
397 while (isspace(*in)) in++; /* skip whitespace */
398 if (first && *in == ':') /* token was a visual name; skip it. */
406 job->name = strdup(name);
408 job->status = job_running;
417 free_job (struct screenhack_job *job)
421 else if (job == jobs)
425 struct screenhack_job *job2, *prev;
426 for (prev = 0, job2 = jobs;
428 prev = job2, job2 = job2->next)
431 prev->next = job->next;
440 /* Cleans out dead jobs from the jobs list -- this must only be called
441 from the main thread, not from a signal handler.
444 clean_job_list (void)
446 struct screenhack_job *job, *prev, *next;
447 for (prev = 0, job = jobs, next = (job ? job->next : 0);
449 prev = job, job = next, next = (job ? job->next : 0))
451 if (job->status == job_dead)
462 static struct screenhack_job *
465 struct screenhack_job *job;
466 for (job = jobs; job; job = job->next)
472 static void await_dying_children (saver_info *si);
474 static void describe_dead_child (saver_info *, pid_t, int wait_status);
478 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
479 Don't alter this directly -- use block_sigchld() / unblock_sigchld().
481 static int block_sigchld_handler = 0;
487 #ifdef HAVE_SIGACTION
489 sigemptyset (&child_set);
490 sigaddset (&child_set, SIGCHLD);
491 sigprocmask (SIG_BLOCK, &child_set, 0);
492 #endif /* HAVE_SIGACTION */
494 block_sigchld_handler++;
498 unblock_sigchld (void)
500 #ifdef HAVE_SIGACTION
502 sigemptyset(&child_set);
503 sigaddset(&child_set, SIGCHLD);
504 sigprocmask(SIG_UNBLOCK, &child_set, 0);
505 #endif /* HAVE_SIGACTION */
507 block_sigchld_handler--;
511 kill_job (saver_info *si, pid_t pid, int signal)
513 saver_preferences *p = &si->prefs;
514 struct screenhack_job *job;
519 if (block_sigchld_handler)
520 /* This function should not be called from the signal handler. */
523 block_sigchld(); /* we control the horizontal... */
525 job = find_job (pid);
528 job->status == job_killed)
531 fprintf (stderr, "%s: no child %ld to signal!\n",
532 blurb(), (long) pid);
537 case SIGTERM: job->status = job_killed; break;
539 /* #### there must be a way to do this on VMS... */
540 case SIGSTOP: job->status = job_stopped; break;
541 case SIGCONT: job->status = job_running; break;
548 fprintf (stderr, "%s: %s pid %lu.\n", blurb(),
549 (signal == SIGTERM ? "killing" :
550 signal == SIGSTOP ? "suspending" :
551 signal == SIGCONT ? "resuming" : "signalling"),
552 (unsigned long) job->pid);
555 fprintf (stderr, "%s: %s pid %lu.\n", blurb(), "killing",
556 (unsigned long) job->pid);
557 #endif /* !SIGSTOP */
559 status = kill (job->pid, signal);
561 if (p->verbose_p && status < 0)
564 fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
565 blurb(), job->pid, job->name);
569 sprintf (buf, "%s: couldn't kill child process %lu (%s)",
570 blurb(), job->pid, job->name);
575 await_dying_children (si);
579 if (block_sigchld_handler < 0)
589 sigchld_handler (int sig)
591 saver_info *si = global_si_kludge; /* I hate C so much... */
593 if (si->prefs.debug_p)
594 fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
595 (block_sigchld_handler ? " (blocked)" : ""));
597 if (block_sigchld_handler < 0)
599 else if (block_sigchld_handler == 0)
602 await_dying_children (si);
613 await_dying_children (saver_info *si)
621 kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
623 if (si->prefs.debug_p)
625 if (kid < 0 && errno)
626 fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
629 fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
633 /* 0 means no more children to reap.
634 -1 means error -- except "interrupted system call" isn't a "real"
635 error, so if we get that, we should just try again. */
637 (kid < 0 && errno != EINTR))
640 describe_dead_child (si, kid, wait_status);
646 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
649 saver_preferences *p = &si->prefs;
650 struct screenhack_job *job = find_job (kid);
651 const char *name = job ? job->name : "<unknown>";
653 if (WIFEXITED (wait_status))
655 int exit_status = WEXITSTATUS (wait_status);
657 /* Treat exit code as a signed 8-bit quantity. */
658 if (exit_status & 0x80) exit_status |= ~0xFF;
660 /* One might assume that exiting with non-0 means something went wrong.
661 But that loser xswarm exits with the code that it was killed with, so
662 it *always* exits abnormally. Treat abnormal exits as "normal" (don't
663 mention them) if we've just killed the subprocess. But mention them
664 if they happen on their own.
668 (p->verbose_p || job->status != job_killed)))
670 "%s: child pid %lu (%s) exited abnormally (code %d).\n",
671 blurb(), (unsigned long) kid, name, exit_status);
672 else if (p->verbose_p)
673 fprintf (stderr, "%s: child pid %lu (%s) exited normally.\n",
674 blurb(), (unsigned long) kid, name);
677 job->status = job_dead;
679 else if (WIFSIGNALED (wait_status))
683 job->status != job_killed ||
684 WTERMSIG (wait_status) != SIGTERM)
685 fprintf (stderr, "%s: child pid %lu (%s) terminated with %s.\n",
686 blurb(), (unsigned long) kid, name,
687 signal_name (WTERMSIG(wait_status)));
690 job->status = job_dead;
692 else if (WIFSTOPPED (wait_status))
695 fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
696 blurb(), (unsigned long) kid, name,
697 signal_name (WSTOPSIG (wait_status)));
700 job->status = job_stopped;
704 fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
705 blurb(), (unsigned long) kid, name);
707 job->status = job_dead;
710 /* Clear out the pid so that screenhack_running_p() knows it's dead.
712 if (!job || job->status == job_dead)
713 for (i = 0; i < si->nscreens; i++)
715 saver_screen_info *ssi = &si->screens[i];
722 static void await_dying_children (saver_info *si) { return; }
731 # ifdef HAVE_SIGACTION /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
733 static Bool sigchld_initialized_p = 0;
734 if (!sigchld_initialized_p)
736 struct sigaction action, old;
738 action.sa_handler = sigchld_handler;
739 sigemptyset(&action.sa_mask);
742 if (sigaction(SIGCHLD, &action, &old) < 0)
745 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
748 sigchld_initialized_p = True;
751 # else /* !HAVE_SIGACTION */
753 if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
756 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
759 # endif /* !HAVE_SIGACTION */
768 hack_enabled_p (const char *hack)
770 const char *s = hack;
771 while (isspace(*s)) s++;
776 select_visual_of_hack (saver_screen_info *ssi, const char *hack)
778 saver_info *si = ssi->global;
779 saver_preferences *p = &si->prefs;
781 static char vis [1024];
782 const char *in = hack;
784 while (isspace(*in)) in++; /* skip whitespace */
785 if (*in == '-') in++; /* skip optional "-" */
786 while (isspace(*in)) in++; /* skip whitespace */
788 while (!isspace(*in) && *in != ':')
789 *out++ = *in++; /* snarf first token */
790 while (isspace(*in)) in++; /* skip whitespace */
794 selected = select_visual(ssi, vis);
796 selected = select_visual(ssi, 0);
798 if (!selected && (p->verbose_p || si->demoing_p))
800 if (*in == ':') in++;
801 while (isspace(*in)) in++;
804 ? "%s: warning, no \"%s\" visual for \"%s\".\n"
805 : "%s: no \"%s\" visual; skipping \"%s\".\n"),
806 blurb(), (*vis ? vis : "???"), in);
814 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
816 saver_info *si = ssi->global;
817 saver_preferences *p = &si->prefs;
818 raise_window (si, first_time_p, True, False);
821 if (p->screenhacks_count)
832 if (p->screenhacks_count == 1)
833 /* If there is only one hack in the list, there is no choice. */
836 else if (si->selection_mode == -1)
837 /* Select the next hack, wrapping. */
838 new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
840 else if (si->selection_mode == -2)
841 /* Select the previous hack, wrapping. */
842 new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
843 % p->screenhacks_count);
845 else if (si->selection_mode > 0)
846 /* Select a specific hack, by number. No negotiation. */
848 new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
853 /* Select a random hack (but not the one we just ran.) */
854 while ((new_hack = random () % p->screenhacks_count)
855 == ssi->current_hack)
859 ssi->current_hack = new_hack;
860 hack = p->screenhacks[ssi->current_hack];
862 /* If the hack is disabled, or there is no visual for this hack,
863 then try again (move forward, or backward, or re-randomize.)
864 Unless this hack was specified explicitly, in which case,
868 select_visual_of_hack (ssi, hack);
871 (!hack_enabled_p (hack) ||
872 !select_visual_of_hack (ssi, hack)))
874 if (++retry_count > (p->screenhacks_count*4))
876 /* Uh, oops. Odds are, there are no suitable visuals,
877 and we're looping. Give up. (This is totally lame,
878 what we should do is make a list of suitable hacks at
879 the beginning, then only loop over them.)
883 "%s: no suitable visuals for these programs.\n",
891 /* Turn off "next" and "prev" modes now, but "demo" mode is only
892 turned off by explicit action.
894 if (si->selection_mode < 0)
895 si->selection_mode = 0;
898 /* If there's a visual description on the front of the command, nuke it.
902 while (isspace(*in)) in++; /* skip whitespace */
903 if (*in == '-') in++; /* skip optional "-" */
904 while (isspace(*in)) in++; /* skip whitespace */
906 while (!isspace(*in) && *in != ':') in++; /* snarf first token */
907 while (isspace(*in)) in++; /* skip whitespace */
911 while (isspace(*in)) in++;
916 switch ((int) (forked = fork ()))
919 sprintf (buf, "%s: couldn't fork", blurb());
921 restore_real_vroot (si);
922 saver_exit (si, 1, 0);
925 close (ConnectionNumber (si->dpy)); /* close display fd */
926 nice_subproc (p->nice_inferior); /* change process priority */
927 hack_subproc_environment (ssi); /* set $DISPLAY */
928 exec_screenhack (si, hack); /* this does not return */
934 (void) make_job (forked, hack);
942 spawn_screenhack (saver_info *si, Bool first_time_p)
946 if (!monitor_powered_on_p (si))
948 if (si->prefs.verbose_p)
950 "%s: server reports that monitor has powered down; "
951 "not launching a new hack.\n", blurb());
955 for (i = 0; i < si->nscreens; i++)
957 saver_screen_info *ssi = &si->screens[i];
958 spawn_screenhack_1 (ssi, first_time_p);
964 kill_screenhack (saver_info *si)
967 for (i = 0; i < si->nscreens; i++)
969 saver_screen_info *ssi = &si->screens[i];
971 kill_job (si, ssi->pid, SIGTERM);
978 suspend_screenhack (saver_info *si, Bool suspend_p)
980 #ifdef SIGSTOP /* older VMS doesn't have it... */
982 for (i = 0; i < si->nscreens; i++)
984 saver_screen_info *ssi = &si->screens[i];
986 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
992 /* Called when we're exiting abnormally, to kill off the subproc. */
994 emergency_kill_subproc (saver_info *si)
998 signal (SIGCHLD, SIG_IGN);
1001 for (i = 0; i < si->nscreens; i++)
1003 saver_screen_info *ssi = &si->screens[i];
1006 kill_job (si, ssi->pid, SIGTERM);
1013 screenhack_running_p (saver_info *si)
1017 for (i = 0; i < si->nscreens; i++)
1019 saver_screen_info *ssi = &si->screens[i];
1027 /* Environment variables. */
1030 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
1031 is defined, the xscreensaver daemon will search that directory for hacks.
1034 hack_environment (saver_info *si)
1036 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
1037 static const char *def_path = DEFAULT_PATH_PREFIX;
1038 if (def_path && *def_path)
1040 const char *opath = getenv("PATH");
1041 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
1042 strcpy (npath, "PATH=");
1043 strcat (npath, def_path);
1044 strcat (npath, ":");
1045 strcat (npath, opath);
1050 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
1055 hack_subproc_environment (saver_screen_info *ssi)
1057 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
1058 the spawned processes inherit is correct. First, it must be on the same
1059 host and display as the value of -display passed in on our command line
1060 (which is not necessarily the same as what our $DISPLAY variable is.)
1061 Second, the screen number in the $DISPLAY passed to the subprocess should
1062 be the screen on which this particular hack is running -- not the display
1063 specification which the driver itself is using, since the driver ignores
1064 its screen number and manages all existing screens.
1066 saver_info *si = ssi->global;
1067 const char *odpy = DisplayString (si->dpy);
1068 char *ndpy = (char *) malloc(strlen(odpy) + 20);
1072 for (screen_number = 0; screen_number < si->nscreens; screen_number++)
1073 if (ssi == &si->screens[screen_number])
1076 strcpy (ndpy, "DISPLAY=");
1077 s = ndpy + strlen(ndpy);
1080 while (*s && *s != ':') s++; /* skip to colon */
1081 while (*s == ':') s++; /* skip over colons */
1082 while (isdigit(*s)) s++; /* skip over dpy number */
1083 while (*s == '.') s++; /* skip over dot */
1084 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
1085 sprintf(s, "%d", screen_number); /* put on screen number */
1087 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
1088 any more, right? It's not Posix, but everyone seems to have it. */
1092 #endif /* HAVE_PUTENV */
1096 /* Restarting the xscreensaver process from scratch. */
1098 static char **saved_argv;
1101 save_argv (int argc, char **argv)
1103 saved_argv = (char **) calloc (argc+2, sizeof (char *));
1104 saved_argv [argc] = 0;
1107 int i = strlen (argv [argc]) + 1;
1108 saved_argv [argc] = (char *) malloc (i);
1109 memcpy (saved_argv [argc], argv [argc], i);
1114 /* Re-execs the process with the arguments in saved_argv.
1115 Does not return unless there was an error.
1118 restart_process (saver_info *si)
1120 if (si->prefs.verbose_p)
1123 fprintf (real_stderr, "%s: re-executing", blurb());
1124 for (i = 0; saved_argv[i]; i++)
1125 fprintf (real_stderr, " %s", saved_argv[i]);
1126 fprintf (real_stderr, "\n");
1128 describe_uids (si, real_stderr);
1129 fprintf (real_stderr, "\n");
1131 fflush (real_stdout);
1132 fflush (real_stderr);
1133 execvp (saved_argv [0], saved_argv); /* shouldn't return */
1136 sprintf (buf, "%s: could not restart process", blurb());