1 /* subprocs.c --- choosing, spawning, and killing screenhacks.
2 * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
21 #include <X11/Xlib.h> /* not used for much... */
27 #include <sys/time.h> /* sys/resource.h needs this for timeval */
29 #ifdef HAVE_SYS_WAIT_H
30 # include <sys/wait.h> /* for waitpid() and associated macros */
34 # include <sys/resource.h> /* for setrlimit() and RLIMIT_AS */
38 # include <processes.h>
39 # include <unixio.h> /* for close */
40 # include <unixlib.h> /* for getpid */
45 #include <signal.h> /* for the signal names */
47 #if !defined(SIGCHLD) && defined(SIGCLD)
48 # define SIGCHLD SIGCLD
51 #if 0 /* putenv() is declared in stdlib.h on modern linux systems. */
53 extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */
57 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
59 /* This file doesn't need the Xt headers, so stub these types out... */
61 #define XtAppContext void*
62 #define XrmDatabase void*
63 #define XtIntervalId void*
64 #define XtPointer void*
67 #include "xscreensaver.h"
69 #include "visual.h" /* for id_to_visual() */
71 extern saver_info *global_si_kludge; /* I hate C so much... */
74 /* RLIMIT_AS (called RLIMIT_VMEM on some systems) controls the maximum size
75 of a process's address space, i.e., the maximal brk(2) and mmap(2) values.
76 Setting this lets you put a cap on how much memory a process can allocate.
78 Except the "and mmap()" part kinda makes this useless, since many GL
79 implementations end up using mmap() to pull the whole frame buffer into
80 memory (or something along those lines) making it appear processes are
81 using hundreds of megabytes when in fact they're using very little, and
82 we end up capping their mallocs prematurely. YAY!
84 #if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS)
85 # define RLIMIT_AS RLIMIT_VMEM
89 limit_subproc_memory (int address_space_limit, Bool verbose_p)
92 /* This has caused way more problems than it has solved...
93 Let's just completely ignore the "memoryLimit" option now.
97 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_AS)
100 if (address_space_limit < 10 * 1024) /* let's not be crazy */
103 if (getrlimit (RLIMIT_AS, &r) != 0)
106 sprintf (buf, "%s: getrlimit(RLIMIT_AS) failed", blurb());
111 r.rlim_cur = address_space_limit;
113 if (setrlimit (RLIMIT_AS, &r) != 0)
116 sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed",
117 blurb(), r.rlim_cur, r.rlim_max);
124 int i = address_space_limit;
126 if (i >= (1<<30) && i == ((i >> 30) << 30))
127 sprintf(buf, "%dG", i >> 30);
128 else if (i >= (1<<20) && i == ((i >> 20) << 20))
129 sprintf(buf, "%dM", i >> 20);
130 else if (i >= (1<<10) && i == ((i >> 10) << 10))
131 sprintf(buf, "%dK", i >> 10);
133 sprintf(buf, "%d bytes", i);
135 fprintf (stderr, "%s: limited pid %lu address space to %s.\n",
136 blurb(), (unsigned long) getpid (), buf);
139 #endif /* HAVE_SETRLIMIT && RLIMIT_AS */
143 /* Management of child processes, and de-zombification.
147 job_running, /* the process is still alive */
148 job_stopped, /* we have sent it a STOP signal */
149 job_killed, /* we have sent it a TERM signal */
150 job_dead /* we have wait()ed for it, and it's dead -- this state only
151 occurs so that we can avoid calling free() from a signal
152 handler. Shortly after going into this state, the list
153 element will be removed. */
156 struct screenhack_job {
160 enum job_status status;
161 struct screenhack_job *next;
164 static struct screenhack_job *jobs = 0;
166 /* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
170 struct screenhack_job *job;
171 fprintf(stderr, "%s: job list:\n", blurb());
172 for (job = jobs; job; job = job->next)
173 fprintf (stderr, " %5ld: %2d: (%s) %s\n",
176 (job->status == job_running ? "running" :
177 job->status == job_stopped ? "stopped" :
178 job->status == job_killed ? " killed" :
179 job->status == job_dead ? " dead" : " ???"),
181 fprintf (stderr, "\n");
185 static void clean_job_list (void);
187 static struct screenhack_job *
188 make_job (pid_t pid, int screen, const char *cmd)
190 struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
192 static char name [1024];
193 const char *in = cmd;
201 while (isspace(*in)) in++; /* skip whitespace */
202 while (!isspace(*in) && *in != ':') {
203 if (*in == '=') got_eq = 1;
204 *out++ = *in++; /* snarf first token */
207 if (got_eq) /* if the first token was FOO=bar */
208 { /* then get the next token instead. */
215 while (isspace(*in)) in++; /* skip whitespace */
218 job->name = strdup(name);
220 job->screen = screen;
221 job->status = job_running;
230 free_job (struct screenhack_job *job)
234 else if (job == jobs)
238 struct screenhack_job *job2, *prev;
239 for (prev = 0, job2 = jobs;
241 prev = job2, job2 = job2->next)
244 prev->next = job->next;
253 /* Cleans out dead jobs from the jobs list -- this must only be called
254 from the main thread, not from a signal handler.
257 clean_job_list (void)
259 struct screenhack_job *job, *prev, *next;
260 for (prev = 0, job = jobs, next = (job ? job->next : 0);
262 prev = job, job = next, next = (job ? job->next : 0))
264 if (job->status == job_dead)
275 static struct screenhack_job *
278 struct screenhack_job *job;
279 for (job = jobs; job; job = job->next)
285 static void await_dying_children (saver_info *si);
287 static void describe_dead_child (saver_info *, pid_t, int wait_status);
291 /* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
292 Don't alter this directly -- use block_sigchld() / unblock_sigchld().
294 static int block_sigchld_handler = 0;
297 #ifdef HAVE_SIGACTION
299 #else /* !HAVE_SIGACTION */
301 #endif /* !HAVE_SIGACTION */
304 #ifdef HAVE_SIGACTION
306 sigemptyset (&child_set);
307 sigaddset (&child_set, SIGCHLD);
308 sigaddset (&child_set, SIGPIPE);
309 sigprocmask (SIG_BLOCK, &child_set, 0);
310 #endif /* HAVE_SIGACTION */
312 block_sigchld_handler++;
314 #ifdef HAVE_SIGACTION
316 #else /* !HAVE_SIGACTION */
318 #endif /* !HAVE_SIGACTION */
322 unblock_sigchld (void)
324 #ifdef HAVE_SIGACTION
326 sigemptyset(&child_set);
327 sigaddset(&child_set, SIGCHLD);
328 sigaddset(&child_set, SIGPIPE);
329 sigprocmask(SIG_UNBLOCK, &child_set, 0);
330 #endif /* HAVE_SIGACTION */
332 block_sigchld_handler--;
336 kill_job (saver_info *si, pid_t pid, int signal)
338 saver_preferences *p = &si->prefs;
339 struct screenhack_job *job;
344 if (block_sigchld_handler)
345 /* This function should not be called from the signal handler. */
348 block_sigchld(); /* we control the horizontal... */
350 job = find_job (pid);
353 job->status == job_killed)
356 fprintf (stderr, "%s: no child %ld to signal!\n",
357 blurb(), (long) pid);
362 case SIGTERM: job->status = job_killed; break;
364 /* #### there must be a way to do this on VMS... */
365 case SIGSTOP: job->status = job_stopped; break;
366 case SIGCONT: job->status = job_running; break;
372 fprintf (stderr, "%s: %d: %s pid %lu (%s)\n",
373 blurb(), job->screen,
374 (job->status == job_killed ? "killing" :
375 job->status == job_stopped ? "suspending" : "resuming"),
376 (unsigned long) job->pid,
379 status = kill (job->pid, signal);
381 if (p->verbose_p && status < 0)
385 "%s: %d: child process %lu (%s) was already dead.\n",
386 blurb(), job->screen, (unsigned long) job->pid, job->name);
390 sprintf (buf, "%s: %d: couldn't kill child process %lu (%s)",
391 blurb(), job->screen, (unsigned long) job->pid, job->name);
396 await_dying_children (si);
400 if (block_sigchld_handler < 0)
410 sigchld_handler (int sig)
412 saver_info *si = global_si_kludge; /* I hate C so much... */
414 if (si->prefs.debug_p)
415 fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
416 (block_sigchld_handler ? " (blocked)" : ""));
418 if (block_sigchld_handler < 0)
420 else if (block_sigchld_handler == 0)
423 await_dying_children (si);
434 await_dying_children (saver_info *si)
442 kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
444 if (si->prefs.debug_p)
446 if (kid < 0 && errno)
447 fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
450 fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
454 /* 0 means no more children to reap.
455 -1 means error -- except "interrupted system call" isn't a "real"
456 error, so if we get that, we should just try again. */
458 (kid < 0 && errno != EINTR))
461 describe_dead_child (si, kid, wait_status);
467 describe_dead_child (saver_info *si, pid_t kid, int wait_status)
470 saver_preferences *p = &si->prefs;
471 struct screenhack_job *job = find_job (kid);
472 const char *name = job ? job->name : "<unknown>";
473 int screen_no = job ? job->screen : 0;
475 if (WIFEXITED (wait_status))
477 int exit_status = WEXITSTATUS (wait_status);
479 /* Treat exit code as a signed 8-bit quantity. */
480 if (exit_status & 0x80) exit_status |= ~0xFF;
482 /* One might assume that exiting with non-0 means something went wrong.
483 But that loser xswarm exits with the code that it was killed with, so
484 it *always* exits abnormally. Treat abnormal exits as "normal" (don't
485 mention them) if we've just killed the subprocess. But mention them
486 if they happen on their own.
490 (p->verbose_p || job->status != job_killed)))
492 "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n",
493 blurb(), screen_no, (unsigned long) kid, name, exit_status);
494 else if (p->verbose_p)
495 fprintf (stderr, "%s: %d: child pid %lu (%s) exited normally.\n",
496 blurb(), screen_no, (unsigned long) kid, name);
499 job->status = job_dead;
501 else if (WIFSIGNALED (wait_status))
505 job->status != job_killed ||
506 WTERMSIG (wait_status) != SIGTERM)
507 fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n",
508 blurb(), screen_no, (unsigned long) kid, name,
509 signal_name (WTERMSIG(wait_status)));
512 job->status = job_dead;
514 else if (WIFSTOPPED (wait_status))
517 fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
518 blurb(), (unsigned long) kid, name,
519 signal_name (WSTOPSIG (wait_status)));
522 job->status = job_stopped;
526 fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
527 blurb(), (unsigned long) kid, name);
529 job->status = job_dead;
532 /* Clear out the pid so that screenhack_running_p() knows it's dead.
534 if (!job || job->status == job_dead)
535 for (i = 0; i < si->nscreens; i++)
537 saver_screen_info *ssi = &si->screens[i];
544 static void await_dying_children (saver_info *si) { return; }
553 # ifdef HAVE_SIGACTION /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
555 static Bool sigchld_initialized_p = 0;
556 if (!sigchld_initialized_p)
558 struct sigaction action, old;
560 action.sa_handler = sigchld_handler;
561 sigemptyset(&action.sa_mask);
564 if (sigaction(SIGCHLD, &action, &old) < 0)
567 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
570 sigchld_initialized_p = True;
573 # else /* !HAVE_SIGACTION */
575 if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
578 sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
581 # endif /* !HAVE_SIGACTION */
590 select_visual_of_hack (saver_screen_info *ssi, screenhack *hack)
592 saver_info *si = ssi->global;
593 saver_preferences *p = &si->prefs;
596 if (hack->visual && *hack->visual)
597 selected = select_visual(ssi, hack->visual);
599 selected = select_visual(ssi, 0);
601 if (!selected && (p->verbose_p || si->demoing_p))
604 ? "%s: warning, no \"%s\" visual for \"%s\".\n"
605 : "%s: no \"%s\" visual; skipping \"%s\".\n"),
607 (hack->visual && *hack->visual ? hack->visual : "???"),
615 print_path_error (const char *program)
618 char *cmd = strdup (program);
619 char *token = strchr (cmd, ' ');
621 if (token) *token = 0;
622 sprintf (buf, "%s: could not execute \"%.100s\"", blurb(), cmd);
626 if (errno == ENOENT &&
627 (token = getenv("PATH")))
631 # define PATH_MAX MAXPATHLEN
633 # define PATH_MAX 2048
637 fprintf (stderr, "\n");
639 # if defined(HAVE_GETCWD)
640 getcwd (path, sizeof(path));
641 # elif defined(HAVE_GETWD)
645 fprintf (stderr, " Current directory is: %s\n", path);
646 fprintf (stderr, " PATH is:\n");
647 token = strtok (strdup(token), ":");
650 fprintf (stderr, " %s\n", token);
651 token = strtok(0, ":");
653 fprintf (stderr, "\n");
659 spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
661 saver_info *si = ssi->global;
662 saver_preferences *p = &si->prefs;
663 raise_window (si, first_time_p, True, False);
666 if (p->screenhacks_count)
677 if (p->screenhacks_count < 1)
679 /* No hacks at all */
682 else if (p->screenhacks_count == 1)
684 /* Exactly one hack in the list */
687 else if (si->selection_mode == -1)
689 /* Select the next hack, wrapping. */
690 new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
692 else if (si->selection_mode == -2)
694 /* Select the previous hack, wrapping. */
695 if (ssi->current_hack < 0)
696 new_hack = p->screenhacks_count - 1;
698 new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
699 % p->screenhacks_count);
701 else if (si->selection_mode > 0)
703 /* Select a specific hack, by number (via the ACTIVATE command.) */
704 new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
707 else if (p->mode == ONE_HACK &&
708 p->selected_hack >= 0)
710 /* Select a specific hack, by number (via "One Saver" mode.) */
711 new_hack = p->selected_hack;
714 else if (p->mode == BLANK_ONLY || p->mode == DONT_BLANK)
718 else /* (p->mode == RANDOM_HACKS) */
720 /* Select a random hack (but not the one we just ran.) */
721 while ((new_hack = random () % p->screenhacks_count)
722 == ssi->current_hack)
726 if (new_hack < 0) /* don't run a hack */
728 ssi->current_hack = -1;
729 if (si->selection_mode < 0)
730 si->selection_mode = 0;
734 ssi->current_hack = new_hack;
735 hack = p->screenhacks[ssi->current_hack];
737 /* If the hack is disabled, or there is no visual for this hack,
738 then try again (move forward, or backward, or re-randomize.)
739 Unless this hack was specified explicitly, in which case,
743 select_visual_of_hack (ssi, hack);
747 !select_visual_of_hack (ssi, hack)))
749 if (++retry_count > (p->screenhacks_count*4))
751 /* Uh, oops. Odds are, there are no suitable visuals,
752 and we're looping. Give up. (This is totally lame,
753 what we should do is make a list of suitable hacks at
754 the beginning, then only loop over them.)
758 "%s: %d: no programs enabled, or no suitable visuals.\n",
759 blurb(), ssi->number);
766 /* Turn off "next" and "prev" modes now, but "demo" mode is only
767 turned off by explicit action.
769 if (si->selection_mode < 0)
770 si->selection_mode = 0;
772 switch ((int) (forked = fork ()))
775 sprintf (buf, "%s: couldn't fork", blurb());
777 restore_real_vroot (si);
778 saver_exit (si, 1, 0);
781 close (ConnectionNumber (si->dpy)); /* close display fd */
782 limit_subproc_memory (p->inferior_memory_limit, p->verbose_p);
783 hack_subproc_environment (ssi); /* set $DISPLAY */
786 fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
787 blurb(), ssi->number, hack->command,
788 (unsigned long) getpid ());
790 exec_command (p->shell, hack->command, p->nice_inferior);
792 /* If that returned, we were unable to exec the subprocess.
793 Print an error message, if desired.
795 if (! p->ignore_uninstalled_p)
796 print_path_error (hack->command);
798 exit (1); /* exits child fork */
803 (void) make_job (forked, ssi->number, hack->command);
811 spawn_screenhack (saver_info *si, Bool first_time_p)
813 if (monitor_powered_on_p (si))
816 for (i = 0; i < si->nscreens; i++)
818 saver_screen_info *ssi = &si->screens[i];
819 spawn_screenhack_1 (ssi, first_time_p);
822 else if (si->prefs.verbose_p)
824 "%s: X says monitor has powered down; "
825 "not launching a hack.\n", blurb());
827 store_saver_status (si); /* store current hack numbers */
832 kill_screenhack (saver_info *si)
835 for (i = 0; i < si->nscreens; i++)
837 saver_screen_info *ssi = &si->screens[i];
839 kill_job (si, ssi->pid, SIGTERM);
846 suspend_screenhack (saver_info *si, Bool suspend_p)
848 #ifdef SIGSTOP /* older VMS doesn't have it... */
850 for (i = 0; i < si->nscreens; i++)
852 saver_screen_info *ssi = &si->screens[i];
854 kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
860 /* Called when we're exiting abnormally, to kill off the subproc. */
862 emergency_kill_subproc (saver_info *si)
866 signal (SIGCHLD, SIG_IGN);
869 for (i = 0; i < si->nscreens; i++)
871 saver_screen_info *ssi = &si->screens[i];
874 kill_job (si, ssi->pid, SIGTERM);
881 screenhack_running_p (saver_info *si)
883 Bool any_running_p = False;
885 for (i = 0; i < si->nscreens; i++)
887 saver_screen_info *ssi = &si->screens[i];
888 if (ssi->pid) any_running_p = True;
890 return any_running_p;
894 /* Environment variables. */
897 /* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
898 is defined, the xscreensaver daemon will search that directory for hacks.
901 hack_environment (saver_info *si)
903 #if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
904 static const char *def_path = DEFAULT_PATH_PREFIX;
905 if (def_path && *def_path)
907 const char *opath = getenv("PATH");
908 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
909 strcpy (npath, "PATH=");
910 strcat (npath, def_path);
912 strcat (npath, opath);
917 /* don't free (npath) -- some implementations of putenv (BSD 4.4,
918 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
919 do not. So we must leak it (and/or the previous setting). Yay.
922 #endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
927 hack_subproc_environment (saver_screen_info *ssi)
929 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
930 the spawned processes inherit is correct. First, it must be on the same
931 host and display as the value of -display passed in on our command line
932 (which is not necessarily the same as what our $DISPLAY variable is.)
933 Second, the screen number in the $DISPLAY passed to the subprocess should
934 be the screen on which this particular hack is running -- not the display
935 specification which the driver itself is using, since the driver ignores
936 its screen number and manages all existing screens.
938 Likewise, store a window ID in $XSCREENSAVER_WINDOW -- this will allow
939 us to (eventually) run multiple hacks in Xinerama mode, where each hack
940 has the same $DISPLAY but a different piece of glass.
942 saver_info *si = ssi->global;
943 const char *odpy = DisplayString (si->dpy);
944 char *ndpy = (char *) malloc (strlen(odpy) + 20);
945 char *nssw = (char *) malloc (40);
948 strcpy (ndpy, "DISPLAY=");
949 s = ndpy + strlen(ndpy);
952 while (*s && *s != ':') s++; /* skip to colon */
953 while (*s == ':') s++; /* skip over colons */
954 while (isdigit(*s)) s++; /* skip over dpy number */
955 while (*s == '.') s++; /* skip over dot */
956 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
957 sprintf(s, "%d", ssi->real_screen_number); /* put on screen number */
959 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX",
960 (unsigned long) ssi->screensaver_window);
962 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
963 any more, right? It's not Posix, but everyone seems to have it. */
970 /* don't free ndpy/nssw -- some implementations of putenv (BSD 4.4,
971 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
972 do not. So we must leak it (and/or the previous setting). Yay.
974 #endif /* HAVE_PUTENV */
981 get_best_gl_visual (saver_screen_info *ssi)
983 saver_info *si = ssi->global;
992 av[ac++] = "xscreensaver-gl-helper";
997 perror ("error creating pipe:");
1004 switch ((int) (forked = fork ()))
1008 sprintf (buf, "%s: couldn't fork", blurb());
1010 saver_exit (si, 1, 0);
1016 close (in); /* don't need this one */
1017 close (ConnectionNumber (si->dpy)); /* close display fd */
1019 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1021 perror ("could not dup() a new stdout:");
1024 hack_subproc_environment (ssi); /* set $DISPLAY */
1026 execvp (av[0], av); /* shouldn't return. */
1028 if (errno != ENOENT || si->prefs.verbose_p)
1030 /* Ignore "no such file or directory" errors, unless verbose.
1031 Issue all other exec errors, though. */
1032 sprintf (buf, "%s: running %s", blurb(), av[0]);
1035 exit (1); /* exits fork */
1041 int wait_status = 0;
1043 FILE *f = fdopen (in, "r");
1044 unsigned long v = 0;
1047 close (out); /* don't need this one */
1050 fgets (buf, sizeof(buf)-1, f);
1053 /* Wait for the child to die. */
1054 waitpid (-1, &wait_status, 0);
1056 if (1 == sscanf (buf, "0x%lx %c", &v, &c))
1061 if (si->prefs.verbose_p)
1063 int L = strlen(buf);
1064 fprintf (stderr, "%s: %s did not report a GL visual!\n",
1067 if (L && buf[L-1] == '\n')
1070 fprintf (stderr, "%s: %s said: \"%s\"\n",
1071 blurb(), av[0], buf);
1077 Visual *v = id_to_visual (ssi->screen, result);
1078 if (si->prefs.verbose_p)
1079 fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n",
1080 blurb(), ssi->number,
1082 (v == ssi->default_visual ? " (default)" : ""));
1093 /* Restarting the xscreensaver process from scratch. */
1095 static char **saved_argv;
1098 save_argv (int argc, char **argv)
1100 saved_argv = (char **) calloc (argc+2, sizeof (char *));
1101 saved_argv [argc] = 0;
1104 int i = strlen (argv [argc]) + 1;
1105 saved_argv [argc] = (char *) malloc (i);
1106 memcpy (saved_argv [argc], argv [argc], i);
1111 /* Re-execs the process with the arguments in saved_argv. Does not return.
1114 restart_process (saver_info *si)
1118 shutdown_stderr (si);
1119 if (si->prefs.verbose_p)
1122 fprintf (stderr, "%s: re-executing", blurb());
1123 for (i = 0; saved_argv[i]; i++)
1124 fprintf (stderr, " %s", saved_argv[i]);
1125 fprintf (stderr, "\n");
1127 describe_uids (si, stderr);
1128 fprintf (stderr, "\n");
1132 execvp (saved_argv [0], saved_argv); /* shouldn't return */
1135 sprintf (buf, "%s: could not restart process", blurb());