diff options
author | Simon Rettberg | 2021-04-06 14:43:39 +0200 |
---|---|---|
committer | Simon Rettberg | 2021-04-07 13:38:37 +0200 |
commit | 38886de0c3e9ea5729ef23e4c653fa2822f52e8f (patch) | |
tree | 9b799c8c968a92cc77746a95e0e8bdd90b6b13c3 /driver/subprocs.c | |
parent | Maybe not remove, but ... (diff) | |
download | xscreensaver-openslx.tar.gz xscreensaver-openslx.tar.xz xscreensaver-openslx.zip |
Diffstat (limited to 'driver/subprocs.c')
-rw-r--r-- | driver/subprocs.c | 1315 |
1 files changed, 526 insertions, 789 deletions
diff --git a/driver/subprocs.c b/driver/subprocs.c index 6cb96d3..2b3453d 100644 --- a/driver/subprocs.c +++ b/driver/subprocs.c @@ -1,5 +1,5 @@ /* subprocs.c --- choosing, spawning, and killing screenhacks. - * xscreensaver, Copyright (c) 1991-2019 Jamie Zawinski <jwz@jwz.org> + * xscreensaver, Copyright © 1991-2021 Jamie Zawinski <jwz@jwz.org> * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -19,6 +19,8 @@ #include <string.h> #include <X11/Xlib.h> /* not used for much... */ +#include <X11/Xatom.h> +#include <X11/Intrinsic.h> /* For XtAppAddSignal */ #ifndef ESRCH # include <errno.h> @@ -35,169 +37,23 @@ # include <sys/resource.h> /* for setrlimit() and RLIMIT_AS */ #endif -#ifdef VMS -# include <processes.h> -# include <unixio.h> /* for close */ -# include <unixlib.h> /* for getpid */ -# define pid_t int -# define fork vfork -#endif /* VMS */ - #include <signal.h> /* for the signal names */ #include <time.h> -#if !defined(SIGCHLD) && defined(SIGCLD) -# define SIGCHLD SIGCLD -#endif - -#if 0 /* putenv() is declared in stdlib.h on modern linux systems. */ -#ifdef HAVE_PUTENV -extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */ -#endif +#ifdef ENABLE_NLS +# include <locale.h> +# include <libintl.h> +# define _(S) gettext(S) +#else +# define _(S) (S) #endif -extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ - -/* This file doesn't need the Xt headers, so stub these types out... */ -#undef XtPointer -#define XtAppContext void* -#define XrmDatabase void* -#define XtIntervalId void* -#define XtPointer void* -#define Widget void* - #include "xscreensaver.h" #include "exec.h" #include "yarandom.h" -#include "visual.h" /* for id_to_visual() */ - -extern saver_info *global_si_kludge; /* I hate C so much... */ - - -/* Used when printing error/debugging messages from signal handlers. - */ -static const char * -no_malloc_number_to_string (long num) -{ - static char string[128] = ""; - int num_digits; - Bool negative_p = False; - - num_digits = 0; - - if (num == 0) - return "0"; - - if (num < 0) - { - negative_p = True; - num = -num; - } - - while ((num > 0) && (num_digits < sizeof(string) - 1)) - { - int digit; - digit = (int) num % 10; - num_digits++; - string[sizeof(string) - 1 - num_digits] = digit + '0'; - num /= 10; - } - - if (negative_p) - { - num_digits++; - string[sizeof(string) - 1 - num_digits] = '-'; - } - - return string + sizeof(string) - 1 - num_digits; -} - -/* Like write(), but runs strlen() on the arg to get the length. */ -static int -write_string (int fd, const char *str) -{ - return write (fd, str, strlen (str)); -} - -static int -write_long (int fd, long n) -{ - const char *str = no_malloc_number_to_string (n); - return write_string (fd, str); -} - - -/* RLIMIT_AS (called RLIMIT_VMEM on some systems) controls the maximum size - of a process's address space, i.e., the maximal brk(2) and mmap(2) values. - Setting this lets you put a cap on how much memory a process can allocate. - - Except the "and mmap()" part kinda makes this useless, since many GL - implementations end up using mmap() to pull the whole frame buffer into - memory (or something along those lines) making it appear processes are - using hundreds of megabytes when in fact they're using very little, and - we end up capping their mallocs prematurely. YAY! - */ -#if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS) -# define RLIMIT_AS RLIMIT_VMEM -#endif - -static void -limit_subproc_memory (int address_space_limit, Bool verbose_p) -{ - -/* This has caused way more problems than it has solved... - Let's just completely ignore the "memoryLimit" option now. - */ -#undef HAVE_SETRLIMIT - -#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_AS) - struct rlimit r; - - if (address_space_limit < 10 * 1024) /* let's not be crazy */ - return; - - if (getrlimit (RLIMIT_AS, &r) != 0) - { - char buf [512]; - sprintf (buf, "%s: getrlimit(RLIMIT_AS) failed", blurb()); - perror (buf); - return; - } - - r.rlim_cur = address_space_limit; - - if (setrlimit (RLIMIT_AS, &r) != 0) - { - char buf [512]; - sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed", - blurb(), r.rlim_cur, r.rlim_max); - perror (buf); - return; - } - - if (verbose_p) - { - int i = address_space_limit; - char buf[100]; - if (i >= (1<<30) && i == ((i >> 30) << 30)) - sprintf(buf, "%dG", i >> 30); - else if (i >= (1<<20) && i == ((i >> 20) << 20)) - sprintf(buf, "%dM", i >> 20); - else if (i >= (1<<10) && i == ((i >> 10) << 10)) - sprintf(buf, "%dK", i >> 10); - else - sprintf(buf, "%d bytes", i); - - fprintf (stderr, "%s: limited pid %lu address space to %s.\n", - blurb(), (unsigned long) getpid (), buf); - } - -#endif /* HAVE_SETRLIMIT && RLIMIT_AS */ -} +#include "visual.h" /* for id_to_visual() */ +#include "atoms.h" - -/* Management of child processes, and de-zombification. - */ enum job_status { job_running, /* the process is still alive */ @@ -220,10 +76,107 @@ struct screenhack_job { static struct screenhack_job *jobs = 0; -/* for debugging -- nothing calls this, but it's useful to invoke from gdb. +static void clean_job_list (void); +static void await_dying_children (saver_info *si); +static void describe_dead_child (saver_info *, pid_t, int wait_status, + struct rusage); + +static XtSignalId xt_sigterm_id = 0; +static int sigterm_received = 0; + +static XtSignalId xt_sigchld_id = 0; +static int sigchld_received = 0; + + +const char * +signal_name (int signal) +{ + /* sys_signame[], sys_siglist[], strsignal() and sigabbrev_np() + are an unportable mess. */ + switch (signal) { + case SIGHUP: return "SIGHUP"; + case SIGINT: return "SIGINT"; + case SIGQUIT: return "SIGQUIT"; + case SIGILL: return "SIGILL"; + case SIGTRAP: return "SIGTRAP"; +#ifdef SIGABRT + case SIGABRT: return "SIGABRT"; +#endif + case SIGFPE: return "SIGFPE"; + case SIGKILL: return "SIGKILL"; + case SIGBUS: return "SIGBUS"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; + case SIGALRM: return "SIGALRM"; + case SIGTERM: return "SIGTERM"; +#ifdef SIGSTOP + case SIGSTOP: return "SIGSTOP"; +#endif +#ifdef SIGCONT + case SIGCONT: return "SIGCONT"; +#endif +#ifdef SIGUSR1 + case SIGUSR1: return "SIGUSR1"; +#endif +#ifdef SIGUSR2 + case SIGUSR2: return "SIGUSR2"; +#endif +#ifdef SIGEMT + case SIGEMT: return "SIGEMT"; +#endif +#ifdef SIGSYS + case SIGSYS: return "SIGSYS"; +#endif + case SIGCHLD: return "SIGCHLD"; +#ifdef SIGPWR + case SIGPWR: return "SIGPWR"; +#endif +#ifdef SIGWINCH + case SIGWINCH: return "SIGWINCH"; +#endif +#ifdef SIGURG + case SIGURG: return "SIGURG"; +#endif +#ifdef SIGIO + case SIGIO: return "SIGIO"; +#endif +#ifdef SIGVTALRM + case SIGVTALRM: return "SIGVTALRM"; +#endif +#ifdef SIGXCPU + case SIGXCPU: return "SIGXCPU"; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: return "SIGXFSZ"; +#endif +#ifdef SIGDANGER + case SIGDANGER: return "SIGDANGER"; +#endif + default: + { + static char buf[50]; + sprintf(buf, "signal %d\n", signal); + return buf; + } + } +} + + +/* Management of child processes, and de-zombification. */ -void show_job_list (void); +static char * +timestring (time_t when) +{ + static char buf[255] = { 0 }; + struct tm tm; + localtime_r (&when, &tm); + sprintf (buf, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec); + return buf; +} + +#ifdef DEBUG +void show_job_list (void); void show_job_list (void) { @@ -231,27 +184,24 @@ show_job_list (void) fprintf(stderr, "%s: job list:\n", blurb()); for (job = jobs; job; job = job->next) { - char b[] = " ??:??:?? "; - char *t = (job->killed ? timestring (job->killed) : - job->launched ? timestring (job->launched) : b); - t += 11; - t[8] = 0; - fprintf (stderr, " %5ld: %2d: (%s) %s %s\n", - (long) job->pid, - job->screen, - (job->status == job_running ? "running" : - job->status == job_stopped ? "stopped" : - job->status == job_killed ? " killed" : - job->status == job_dead ? " dead" : " ???"), - t, job->name); + fprintf (stderr, " %5ld: %2d: (%s) %s %s\n", + (long) job->pid, + job->screen, + (job->status == job_running ? "running" : + job->status == job_stopped ? "stopped" : + job->status == job_killed ? " killed" : + job->status == job_dead ? " dead" : " ???"), + (job->killed ? timestring (job->killed) : + job->launched ? timestring (job->launched) : + "??:??:??"), + job->name); } fprintf (stderr, "\n"); } +#endif /* DEBUG */ -static void clean_job_list (void); - -static struct screenhack_job * +static void make_job (pid_t pid, int screen, const char *cmd) { struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job)); @@ -288,8 +238,6 @@ make_job (pid_t pid, int screen, const char *cmd) job->killed = 0; job->next = jobs; jobs = job; - - return jobs; } @@ -317,8 +265,7 @@ free_job (struct screenhack_job *job) } -/* Cleans out dead jobs from the jobs list -- this must only be called - from the main thread, not from a signal handler. +/* Cleans out dead jobs from the jobs list. */ static void clean_job_list (void) @@ -367,79 +314,8 @@ find_job (pid_t pid) return 0; } -static void await_dying_children (saver_info *si); -#ifndef VMS -static void describe_dead_child (saver_info *, pid_t, int wait_status); -#endif - - -/* Semaphore to temporarily turn the SIGCHLD handler into a no-op. - Don't alter this directly -- use block_sigchld() / unblock_sigchld(). - */ -static int block_sigchld_handler = 0; - - -#ifdef HAVE_SIGACTION - sigset_t -#else /* !HAVE_SIGACTION */ - int -#endif /* !HAVE_SIGACTION */ -block_sigchld (void) -{ -#ifdef HAVE_SIGACTION - struct sigaction sa; - sigset_t child_set; - - memset (&sa, 0, sizeof (sa)); - sa.sa_handler = SIG_IGN; - sigaction (SIGPIPE, &sa, NULL); - - sigemptyset (&child_set); - sigaddset (&child_set, SIGCHLD); - sigprocmask (SIG_BLOCK, &child_set, 0); - -#else /* !HAVE_SIGACTION */ - signal (SIGPIPE, SIG_IGN); -#endif /* !HAVE_SIGACTION */ - - block_sigchld_handler++; - -#ifdef HAVE_SIGACTION - return child_set; -#else /* !HAVE_SIGACTION */ - return 0; -#endif /* !HAVE_SIGACTION */ -} - -void -unblock_sigchld (void) -{ - if (block_sigchld_handler <= 0) - abort(); - - if (block_sigchld_handler <= 1) /* only unblock if count going to 0 */ - { -#ifdef HAVE_SIGACTION - struct sigaction sa; - sigset_t child_set; - - memset(&sa, 0, sizeof (sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGPIPE, &sa, NULL); - - sigemptyset(&child_set); - sigaddset(&child_set, SIGCHLD); - sigprocmask(SIG_UNBLOCK, &child_set, 0); - -#else /* !HAVE_SIGACTION */ - signal(SIGPIPE, SIG_DFL); -#endif /* !HAVE_SIGACTION */ - } - - block_sigchld_handler--; -} -int +static int kill_job (saver_info *si, pid_t pid, int signal) { saver_preferences *p = &si->prefs; @@ -448,12 +324,6 @@ kill_job (saver_info *si, pid_t pid, int signal) clean_job_list(); - if (in_signal_handler_p) - /* This function should not be called from the signal handler. */ - abort(); - - block_sigchld(); /* we control the horizontal... */ - job = find_job (pid); if (!job || !job->pid || @@ -471,7 +341,6 @@ kill_job (saver_info *si, pid_t pid, int signal) job->killed = time ((time_t *) 0); break; #ifdef SIGSTOP - /* #### there must be a way to do this on VMS... */ case SIGSTOP: job->status = job_stopped; break; case SIGCONT: job->status = job_running; break; #endif /* SIGSTOP */ @@ -492,7 +361,7 @@ kill_job (saver_info *si, pid_t pid, int signal) { if (errno == ESRCH) fprintf (stderr, - "%s: %d: child process %lu (%s) was already dead.\n", + "%s: %d: child process %lu (%s) was already dead\n", blurb(), job->screen, (unsigned long) job->pid, job->name); else { @@ -506,53 +375,148 @@ kill_job (saver_info *si, pid_t pid, int signal) await_dying_children (si); DONE: - unblock_sigchld(); - if (block_sigchld_handler < 0) - abort(); - clean_job_list(); return status; } -#ifdef SIGCHLD -static RETSIGTYPE -sigchld_handler (int sig) +/* We use Xt-style signal handling. A Unix signal fires, and we inform Xt of + that. Then after we return to the top-level command loop on the main + stack, Xt runs our callback function for that signal. Just like Xt timers. + */ +static void +catch_signal (int sig, RETSIGTYPE (*handler) (int)) { - saver_info *si = global_si_kludge; /* I hate C so much... */ - in_signal_handler_p++; - - if (si->prefs.debug_p) +# ifdef HAVE_SIGACTION + struct sigaction a; + a.sa_handler = handler; + sigemptyset (&a.sa_mask); + a.sa_flags = 0; + if (sigaction (sig, &a, 0) < 0) +# else /* !HAVE_SIGACTION */ + if (((long) signal (sig, handler)) == -1L) +# endif /* !HAVE_SIGACTION */ { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(), - (block_sigchld_handler ? " (blocked)" : "")); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": got SIGCHLD"); - - if (block_sigchld_handler) - write_string (STDERR_FILENO, " (blocked)\n"); - else - write_string (STDERR_FILENO, "\n"); + char buf [255]; + sprintf (buf, "%s: couldn't catch signal %s", blurb(), + signal_name (sig)); + perror (buf); + abort(); } +} + + +/* Exiting gracefully. + + When xscreensaver sends a SIGTERM signal to xscreensaver-gfx, rather + than exiting immediately, we want it to do two things: + + - Send a SIGTERM to each running screenhack. They would *probably* + die of a BadWindow X error once their window was deleted, but this + is cleaner and more immediate. + + - Fade the screens in from black. This might take several seconds. + + Should another signal come in while that is ongoing, we should just + die immediately. + */ - if (block_sigchld_handler < 0) - abort(); - else if (block_sigchld_handler == 0) +static RETSIGTYPE +saver_sigterm_handler (int sig) +{ + /* This is the actual signal handler, running on the signal stack. + After firing once, set this signal back to the default behavior. */ + sigterm_received = sig; + catch_signal (sig, SIG_DFL); + + /* The first time a signal fires, inform Xt of that so that it will run + xt_signal_handler(). "XtNoticeSignal is the only Intrinsics function + that can safely be called from a signal handler". */ + if (xt_sigterm_id) + XtNoticeSignal (xt_sigterm_id); +} + + +static void +xt_sigterm_handler (XtPointer data, XtSignalId *id) +{ + /* This runs from the Xt event loop on the main stack, some time after + the signal fired. */ + saver_info *si = (saver_info *) data; + saver_preferences *p = &si->prefs; + static Bool hit_p = False; + int i; + + if (xt_sigterm_id) + XtRemoveSignal (xt_sigterm_id); + xt_sigterm_id = 0; + + if (hit_p) + fprintf (stderr, "%s: second signal: %s: exiting\n", blurb(), + signal_name (sigterm_received)); + else { - block_sigchld(); - await_dying_children (si); - unblock_sigchld(); + hit_p = True; + if (p->verbose_p) + fprintf (stderr, "%s: %s: unblanking\n", blurb(), + signal_name (sigterm_received)); + + /* Kill before unblanking, to stop drawing as soon as possible. */ + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (ssi->cycle_id) + { + XtRemoveTimeOut (ssi->cycle_id); + ssi->cycle_id = 0; + ssi->cycle_at = 0; + } + kill_screenhack (ssi); + } + unblank_screen (si); + + if (p->verbose_p) + fprintf (stderr, "%s: %s: exiting\n", blurb(), + signal_name (sigterm_received)); } - init_sigchld(); - in_signal_handler_p--; + /* Exit with the original signal received. */ + kill (getpid(), sigterm_received); + abort(); +} + + +/* SIGCHLD handling. Basically the same deal as SIGTERM. + */ + +static RETSIGTYPE +sigchld_handler (int sig) +{ + /* This is the actual signal handler, running on the signal stack. + After firing once, set this signal to fire again. */ + sigchld_received = sig; +# ifndef HAVE_SIGACTION + catch_signal (SIGCHLD, sigchld_handler); +# endif + + if (xt_sigchld_id) + XtNoticeSignal (xt_sigchld_id); } -#endif /* SIGCHLD */ -#ifndef VMS +static void +xt_sigchld_handler (XtPointer data, XtSignalId *id) +{ + /* This runs from the Xt event loop on the main stack, some time after + the signal fired. */ + saver_info *si = (saver_info *) data; + + if (si->prefs.debug_p) + fprintf(stderr, "%s: got SIGCHLD\n", blurb()); + + await_dying_children (si); /* Their first album was better */ +} + static void await_dying_children (saver_info *si) @@ -561,36 +525,19 @@ await_dying_children (saver_info *si) { int wait_status = 0; pid_t kid; + struct rusage rus; errno = 0; - kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED); + kid = wait4 (-1, &wait_status, WNOHANG|WUNTRACED, &rus); if (si->prefs.debug_p) { if (kid < 0 && errno) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(), - (long) kid, errno); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": waitpid(-1) ==> "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_long (STDERR_FILENO, (long) errno); - write_string (STDERR_FILENO, ")\n"); - } + fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(), + (long) kid, errno); else - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(), - (long) kid); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": waitpid(-1) ==> "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, "\n"); - } + fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(), + (long) kid); } /* 0 means no more children to reap. @@ -600,272 +547,193 @@ await_dying_children (saver_info *si) (kid < 0 && errno != EINTR)) break; - describe_dead_child (si, kid, wait_status); + describe_dead_child (si, kid, wait_status, rus); } } static void -describe_dead_child (saver_info *si, pid_t kid, int wait_status) +describe_dead_child (saver_info *si, pid_t kid, int wait_status, + struct rusage rus) { int i; saver_preferences *p = &si->prefs; struct screenhack_job *job = find_job (kid); const char *name = job ? job->name : "<unknown>"; int screen_no = job ? job->screen : 0; + char msg[1024]; + *msg = 0; if (WIFEXITED (wait_status)) { int exit_status = WEXITSTATUS (wait_status); - /* Treat exit code as a signed 8-bit quantity. */ if (exit_status & 0x80) exit_status |= ~0xFF; - /* One might assume that exiting with non-0 means something went wrong. - But that loser xswarm exits with the code that it was killed with, so - it *always* exits abnormally. Treat abnormal exits as "normal" (don't - mention them) if we've just killed the subprocess. But mention them - if they happen on their own. - */ - if (!job || - (exit_status != 0 && - (p->verbose_p || job->status != job_killed))) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, - "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n", - blurb(), screen_no, (unsigned long) kid, name, exit_status); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") exited abnormally (code "); - write_long (STDERR_FILENO, (long) exit_status); - write_string (STDERR_FILENO, ").\n"); - } - else if (p->verbose_p) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: %d: child pid %lu (%s) exited normally.\n", - blurb(), screen_no, (unsigned long) kid, name); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") exited normally.\n"); - } - + sprintf (msg, _("crashed with status %d"), exit_status); + if (p->verbose_p) + fprintf (stderr, + "%s: %d: child pid %lu (%s) exited abnormally" + " with status %d\n", + blurb(), screen_no, (unsigned long) kid, name, exit_status); if (job) job->status = job_dead; } else if (WIFSIGNALED (wait_status)) { - if (p->verbose_p || - !job || - job->status != job_killed || - WTERMSIG (wait_status) != SIGTERM) + const char *sig = signal_name (WTERMSIG (wait_status)); + if (job && + job->status == job_killed && + WTERMSIG (wait_status) == SIGTERM) { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n", - blurb(), screen_no, (unsigned long) kid, name, - signal_name (WTERMSIG(wait_status))); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") terminated with signal "); - write_long (STDERR_FILENO, WTERMSIG(wait_status)); - write_string (STDERR_FILENO, ".\n"); + /* Expected notification after we killed it. */ + sprintf (msg, _("exited normally with %.100s"), sig); + if (p->verbose_p) + fprintf (stderr, "%s: %d: child pid %lu (%s)" + " exited normally with %s\n", + blurb(), screen_no, (unsigned long) kid, name, sig); + } + else + { + /* Unexpected signal. */ + sprintf (msg, _("crashed with %.100s"), sig); + if (p->verbose_p) + fprintf (stderr, "%s: %d: child pid %lu (%s)" + " unexpectedly terminated with %s\n", + blurb(), screen_no, (unsigned long) kid, name, sig); } - if (job) job->status = job_dead; } else if (WIFSTOPPED (wait_status)) { if (p->verbose_p) - { - /* Don't call fprintf() from signal handlers, as it might malloc. - fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n", - blurb(), (unsigned long) kid, name, - signal_name (WSTOPSIG (wait_status))); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") stopped with signal "); - write_long (STDERR_FILENO, WSTOPSIG(wait_status)); - write_string (STDERR_FILENO, ".\n"); - } - + fprintf (stderr, "%s: child pid %lu (%s) stopped with %s\n", + blurb(), (unsigned long) kid, name, + signal_name (WSTOPSIG (wait_status))); if (job) job->status = job_stopped; } else { - /* Don't call fprintf() from signal handlers, as it might malloc. + /* Didn't exit, signal or stop; is this even possible? */ + sprintf (msg, _("crashed mysteriously")); fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!", blurb(), (unsigned long) kid, name); - */ - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": "); - write_long (STDERR_FILENO, (long) screen_no); - write_string (STDERR_FILENO, ": child pid "); - write_long (STDERR_FILENO, (long) kid); - write_string (STDERR_FILENO, " ("); - write_string (STDERR_FILENO, name); - write_string (STDERR_FILENO, ") died in a mysterious way!"); if (job) job->status = job_dead; } - /* Clear out the pid so that screenhack_running_p() knows it's dead. +# ifdef LOG_CPU_TIME + if (p->verbose_p && job && job->status == job_dead) + { + long u = rus.ru_utime.tv_usec / 1000 + rus.ru_utime.tv_sec * 1000; + long s = rus.ru_stime.tv_usec / 1000 + rus.ru_stime.tv_sec * 1000; + fprintf (stderr, "%s: %d: CPU used: %.1fu, %.1fs\n", + blurb(), screen_no, u / 1000.0, s / 1000.0); + } +# endif /* LOG_CPU_TIME */ + + /* Clear out the pid so that any_screenhacks_running_p() knows it's dead. */ if (!job || job->status == job_dead) { - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (kid == ssi->pid) - ssi->pid = 0; - } -# ifdef HAVE_LIBSYSTEMD - if (kid == si->systemd_pid) - si->systemd_pid = 0; -# endif + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + if (kid == ssi->pid) + { + ssi->pid = 0; + if (*msg) + screenhack_obituary (ssi, name, msg); + } + } } } -#else /* VMS */ -static void await_dying_children (saver_info *si) { return; } -#endif /* VMS */ - void -init_sigchld (void) +init_sigchld (saver_info *si) { -#ifdef SIGCHLD - -# ifdef HAVE_SIGACTION /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */ - - static Bool sigchld_initialized_p = 0; - if (!sigchld_initialized_p) - { - struct sigaction action, old; - - action.sa_handler = sigchld_handler; - sigemptyset(&action.sa_mask); - action.sa_flags = 0; - - if (sigaction(SIGCHLD, &action, &old) < 0) - { - char buf [255]; - sprintf (buf, "%s: couldn't catch SIGCHLD", blurb()); - perror (buf); - } - sigchld_initialized_p = True; - } + static Bool signals_initialized_p = 0; + if (signals_initialized_p) return; + signals_initialized_p = True; -# else /* !HAVE_SIGACTION */ + catch_signal (SIGTERM, saver_sigterm_handler); /* kill */ + catch_signal (SIGINT, saver_sigterm_handler); /* shell ^C */ + catch_signal (SIGQUIT, saver_sigterm_handler); /* shell ^| */ + catch_signal (SIGCHLD, sigchld_handler); - if (((long) signal (SIGCHLD, sigchld_handler)) == -1L) - { - char buf [255]; - sprintf (buf, "%s: couldn't catch SIGCHLD", blurb()); - perror (buf); - } -# endif /* !HAVE_SIGACTION */ -#endif /* SIGCHLD */ + xt_sigchld_id = XtAppAddSignal (si->app, xt_sigchld_handler, si); + xt_sigterm_id = XtAppAddSignal (si->app, xt_sigterm_handler, si); } +static void +hack_subproc_environment (Screen *screen, Window saver_window) +{ + /* Store $DISPLAY into the environment, so that the $DISPLAY variable that + the spawned processes inherit is correct. First, it must be on the same + host and display as the value of -display passed in on our command line + (which is not necessarily the same as what our $DISPLAY variable is.) + Second, the screen number in the $DISPLAY passed to the subprocess should + be the screen on which this particular hack is running -- not the display + specification which the driver itself is using, since the driver ignores + its screen number and manages all existing screens. - + Likewise, store a window ID in $XSCREENSAVER_WINDOW -- this is necessary + in a Xinerama or RANDR world where a single X11 'Screen' spans multiple + monitors, and we want to run a hack on each piece of glass, not spanning + them. In that case, multiple hacks have the same $DISPLAY, screen and + root window. + */ + Display *dpy = DisplayOfScreen (screen); + const char *odpy = DisplayString (dpy); + char *ndpy = (char *) malloc (strlen(odpy) + 20); + char *nssw = (char *) malloc (40); + char *s, *c; -static Bool -select_visual_of_hack (saver_screen_info *ssi, screenhack *hack) -{ - saver_info *si = ssi->global; - saver_preferences *p = &si->prefs; - Bool selected; + strcpy (ndpy, "DISPLAY="); + s = ndpy + strlen(ndpy); + strcpy (s, odpy); - if (hack->visual && *hack->visual) - selected = select_visual(ssi, hack->visual); - else - selected = select_visual(ssi, 0); + /* We have to find the last colon since it is the boundary between + hostname & screen - IPv6 numeric format addresses may have many + colons before that point, and DECnet addresses always have two colons */ + c = strrchr(s,':'); /* skip to last colon */ + if (c != NULL) s = c+1; + while (isdigit(*s)) s++; /* skip over dpy number */ + while (*s == '.') s++; /* skip over dot */ + if (s[-1] != '.') *s++ = '.'; /* put on a dot */ + sprintf(s, "%d", screen_number (screen)); /* put on screen number */ - if (!selected && (p->verbose_p || si->demoing_p)) - fprintf (stderr, - (si->demoing_p - ? "%s: warning, no \"%s\" visual for \"%s\".\n" - : "%s: no \"%s\" visual; skipping \"%s\".\n"), - blurb(), - (hack->visual && *hack->visual ? hack->visual : "???"), - hack->command); + sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", (unsigned long) saver_window); - return selected; + if (putenv (ndpy)) + abort (); + if (putenv (nssw)) + abort (); + + /* don't free ndpy/nssw -- some implementations of putenv (BSD 4.4, + glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) + do not. So we must leak it (and/or the previous setting). Yay. + */ } +#ifdef ABORT_TESTER /* Shoot down processes after a bit, for debugging */ static void -print_path_error (const char *program) +abort_debug_timer (XtPointer closure, XtIntervalId *id) { - char buf [512]; - char *cmd = strdup (program); - char *token = strchr (cmd, ' '); - - if (token) *token = 0; - sprintf (buf, "%s: could not execute \"%.100s\"", blurb(), cmd); - free (cmd); - perror (buf); - - if (errno == ENOENT && - (token = getenv("PATH"))) + saver_screen_info *ssi = (saver_screen_info *) closure; + if (ssi->pid) { -# ifndef PATH_MAX -# ifdef MAXPATHLEN -# define PATH_MAX MAXPATHLEN -# else -# define PATH_MAX 2048 -# endif -# endif - char path[PATH_MAX]; - fprintf (stderr, "\n"); - *path = 0; -# if defined(HAVE_GETCWD) - if (! getcwd (path, sizeof(path))) - *path = 0; -# elif defined(HAVE_GETWD) - getwd (path); -# endif - if (*path) - fprintf (stderr, " Current directory is: %s\n", path); - fprintf (stderr, " PATH is:\n"); - token = strtok (strdup(token), ":"); - while (token) - { - fprintf (stderr, " %s\n", token); - token = strtok(0, ":"); - } - fprintf (stderr, "\n"); + fprintf (stderr, "%s: %d: %ld: born to ill\n", blurb(), ssi->number, + (unsigned long) ssi->pid); + kill (ssi->pid, SIGILL); } } +#endif /* ABORT_TESTER */ /* Executes the command in another process. @@ -875,15 +743,10 @@ print_path_error (const char *program) Otherwise, -1 is returned and an error may have been printed to stderr. */ -pid_t +static pid_t fork_and_exec (saver_screen_info *ssi, const char *command) { - return fork_and_exec_1 (ssi->global, ssi, command); -} - -pid_t -fork_and_exec_1 (saver_info *si, saver_screen_info *ssi, const char *command) -{ + saver_info *si = ssi->global; saver_preferences *p = &si->prefs; pid_t forked; @@ -899,92 +762,57 @@ fork_and_exec_1 (saver_info *si, saver_screen_info *ssi, const char *command) case 0: close (ConnectionNumber (si->dpy)); /* close display fd */ - limit_subproc_memory (p->inferior_memory_limit, p->verbose_p); if (ssi) hack_subproc_environment (ssi->screen, ssi->screensaver_window); - if (p->verbose_p) - fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n", - blurb(), (ssi ? ssi->number : 0), command, - (unsigned long) getpid ()); - exec_command (p->shell, command, p->nice_inferior); - - /* If that returned, we were unable to exec the subprocess. - Print an error message, if desired. - */ - if (! p->ignore_uninstalled_p) - print_path_error (command); - + /* If that returned, we were unable to exec the subprocess. */ exit (1); /* exits child fork */ break; default: /* parent */ - (void) make_job (forked, (ssi ? ssi->number : 0), command); + make_job (forked, (ssi ? ssi->number : 0), command); + if (p->verbose_p) + fprintf (stderr, "%s: %d: forked \"%s\" in pid %lu" + " on window 0x%lx\n", + blurb(), (ssi ? ssi->number : 0), command, + (unsigned long) forked, + (unsigned long) ssi->screensaver_window); break; } +# ifdef ABORT_TESTER + if (forked) + XtAppAddTimeOut (si->app, 1000 * (5 + (5 * ssi->number)), + abort_debug_timer, (XtPointer) ssi); +# endif + return forked; } -/* Execute command in another process and wait for it to - * finish. Return exit code of process, or -1 on error - * with fork() or exec(). - */ -int -exec_and_wait (saver_info *si, const char *command) +static Bool +select_visual_of_hack (saver_screen_info *ssi, screenhack *hack) { - pid_t forked; + saver_info *si = ssi->global; saver_preferences *p = &si->prefs; + Bool selected; - switch ((int) (forked = fork ())) - { - case -1: - { - char buf [255]; - sprintf (buf, "%s: couldn't fork", blurb()); - perror (buf); - return -1; - } - - case 0: - close (ConnectionNumber (si->dpy)); /* close display fd */ - limit_subproc_memory (p->inferior_memory_limit, p->verbose_p); - - if (p->verbose_p) - fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n", - blurb(), command, - (unsigned long) getpid ()); - - exec_command (p->shell, command, 0); - - /* If that returned, we were unable to exec the subprocess. - Print an error message, if desired. - */ - print_path_error (command); + if (hack->visual && *hack->visual) + selected = select_visual(ssi, hack->visual); + else + selected = select_visual(ssi, 0); - exit (-1); /* exits child fork */ - break; + if (!selected && (p->verbose_p || si->demoing_p)) + fprintf (stderr, + (si->demoing_p + ? "%s: warning, no \"%s\" visual for \"%s\"\n" + : "%s: no \"%s\" visual; skipping \"%s\"\n"), + blurb(), + (hack->visual && *hack->visual ? hack->visual : "???"), + hack->command); - default: /* parent */ - { - pid_t retpid; - int wstatus; - while ((retpid = waitpid (forked, &wstatus, 0)) == -1) { - if (errno == EINTR) - continue; - perror ("Could not waitpid for child."); - return -1; - } - if (WIFEXITED(wstatus)) - return WEXITSTATUS(wstatus); - if (WIFSIGNALED(wstatus)) - return WTERMSIG(wstatus) + 128; - return -1; - } - } - return -1; + return selected; } @@ -995,13 +823,21 @@ spawn_screenhack (saver_screen_info *ssi) saver_preferences *p = &si->prefs; XFlush (si->dpy); - if (!p->dpms_full_throttle_p && !monitor_powered_on_p (si)) + if (!monitor_powered_on_p (si->dpy)) { if (si->prefs.verbose_p) fprintf (stderr, "%s: %d: X says monitor has powered down; " - "not launching a hack.\n", blurb(), ssi->number); - return; + "not launching a hack\n", blurb(), ssi->number); + ssi->current_hack = -1; + + /* Hooray, this doesn't actually clear the window if it was OpenGL. */ + XClearWindow (si->dpy, ssi->screensaver_window); + + /* Even though we aren't launching a hack, do launch the cycle timer, + in case the monitor powers back up at some point without us having + un-blanked. */ + goto DONE; } if (p->screenhacks_count) @@ -1075,9 +911,7 @@ spawn_screenhack (saver_screen_info *ssi) if (new_hack < 0) /* don't run a hack */ { ssi->current_hack = -1; - if (si->selection_mode < 0) - si->selection_mode = 0; - return; + goto DONE; } ssi->current_hack = new_hack; @@ -1105,7 +939,7 @@ spawn_screenhack (saver_screen_info *ssi) */ if (p->verbose_p) fprintf(stderr, - "%s: %d: no programs enabled, or no suitable visuals.\n", + "%s: %d: no programs enabled, or no suitable visuals\n", blurb(), ssi->number); return; } @@ -1113,12 +947,6 @@ spawn_screenhack (saver_screen_info *ssi) goto AGAIN; } - /* Turn off "next" and "prev" modes now, but "demo" mode is only - turned off by explicit action. - */ - if (si->selection_mode < 0) - si->selection_mode = 0; - forked = fork_and_exec (ssi, hack->command); switch ((int) forked) { @@ -1126,17 +954,95 @@ spawn_screenhack (saver_screen_info *ssi) case 0: /* child fork (can't happen) */ sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); - restore_real_vroot (si); - saver_exit (si, 1, "couldn't fork"); + exit (1); break; default: ssi->pid = forked; break; } + + XChangeProperty (si->dpy, ssi->screensaver_window, XA_WM_COMMAND, + XA_STRING, 8, PropModeReplace, + (unsigned char *) hack->command, + strlen (hack->command)); + XChangeProperty (si->dpy, ssi->screensaver_window, XA_NET_WM_PID, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &ssi->pid, 1); } - store_saver_status (si); /* store current hack number */ + DONE: + + if (ssi->current_hack < 0) + XDeleteProperty (si->dpy, ssi->screensaver_window, XA_WM_COMMAND); + + store_saver_status (si); /* store current hack numbers */ + + /* Now that the hack has launched, queue a timer to cycle it. */ + if (!si->demoing_p && p->cycle) + { + time_t now = time ((time_t *) 0); + Time how_long = p->cycle; + + /* If we're in "SELECT n" mode, the cycle timer going off will just + restart this same hack again. There's not much point in doing this + every 5 or 10 minutes, but on the other hand, leaving one hack + running for days is probably not a great idea, since they tend to + leak and/or crash. So, restart the thing once an hour. + */ + if (si->selection_mode > 0 && ssi->pid) + how_long = 1000 * 60 * 60; + + /* If there are multiple screens, stagger the restart time of subsequent + screens: they will all change every N minutes, but not at the same + time. But don't let that offset be more than about 5 minutes. + + I originally did this by just adding an offset to the very first + cycle only, but after a few days, the cycles would synchronize again! + Are Xt timers implemented with Huygens pendulums?? So compare this + screen's target time against the previous screen's, and offset it as + needed. + */ + if (ssi->number > 0 && + p->mode != RANDOM_HACKS_SAME) + { + saver_screen_info *prev = &si->screens[ssi->number-1]; + time_t cycle_at = now + how_long / 1000; + time_t prev_at = prev->cycle_at; + + Time max = 1000 * 60 * 60 * 10; + Time off = (how_long > max ? max : how_long) / si->nscreens; + + if (cycle_at < prev_at + off / 1000) + { + time_t old = cycle_at; + cycle_at = prev_at + off / 1000; + how_long = 1000 * (cycle_at - now); + + if (p->verbose_p && cycle_at - old > 2) + fprintf (stderr, "%s: %d: offsetting cycle time by %ld sec\n", + blurb(), ssi->number, + cycle_at - old); + } + } + + if (p->debug_p) + fprintf (stderr, "%s: %d: starting cycle_timer (%ld)\n", + blurb(), ssi->number, how_long); + + if (ssi->cycle_id) + XtRemoveTimeOut (ssi->cycle_id); + ssi->cycle_id = + XtAppAddTimeOut (si->app, how_long, cycle_timer, (XtPointer) ssi); + ssi->cycle_at = now + how_long / 1000; + + if (p->verbose_p) + { + time_t t = time((time_t *) 0) + how_long/1000; + fprintf (stderr, "%s: %d: next cycle in %lu sec at %s\n", + blurb(), ssi->number, how_long/1000, timestring(t)); + } + } } @@ -1147,42 +1053,14 @@ kill_screenhack (saver_screen_info *ssi) if (ssi->pid) kill_job (si, ssi->pid, SIGTERM); ssi->pid = 0; -} - -void -suspend_screenhack (saver_screen_info *ssi, Bool suspend_p) -{ -#ifdef SIGSTOP /* older VMS doesn't have it... */ - saver_info *si = ssi->global; - if (ssi->pid) - kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT)); -#endif /* SIGSTOP */ + /* Hooray, this doesn't actually clear the window if it was OpenGL. */ + XClearWindow (si->dpy, ssi->screensaver_window); } -/* Called when we're exiting abnormally, to kill off the subproc. */ -void -emergency_kill_subproc (saver_info *si) -{ - int i; -#ifdef SIGCHLD - signal (SIGCHLD, SIG_IGN); -#endif /* SIGCHLD */ - - for (i = 0; i < si->nscreens; i++) - { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->pid) - { - kill_job (si, ssi->pid, SIGTERM); - ssi->pid = 0; - } - } -} - Bool -screenhack_running_p (saver_info *si) +any_screenhacks_running_p (saver_info *si) { Bool any_running_p = False; int i; @@ -1190,104 +1068,21 @@ screenhack_running_p (saver_info *si) { saver_screen_info *ssi = &si->screens[i]; if (ssi->pid) any_running_p = True; + /* Consider it running if an error dialog is posted, so that we + don't prematurely clear the window. */ + if (ssi->error_dialog) any_running_p = True; } return any_running_p; } - -/* Environment variables. */ - -/* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX - is defined, the xscreensaver daemon will search that directory for hacks. +/* Fork "xscreensaver-gl-visual" and wait for it to print the IDs of + the GL visual that should be used on this screen. */ -void -hack_environment (saver_info *si) -{ -#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX) - static const char *def_path = DEFAULT_PATH_PREFIX; - if (def_path && *def_path) - { - const char *opath = getenv("PATH"); - char *npath; - if (! opath) opath = "/bin:/usr/bin"; /* WTF */ - npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20); - strcpy (npath, "PATH="); - strcat (npath, def_path); - strcat (npath, ":"); - strcat (npath, opath); - - if (putenv (npath)) - abort (); - - /* don't free (npath) -- some implementations of putenv (BSD 4.4, - glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) - do not. So we must leak it (and/or the previous setting). Yay. - */ - } -#endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */ -} - - -void -hack_subproc_environment (Screen *screen, Window saver_window) -{ - /* Store $DISPLAY into the environment, so that the $DISPLAY variable that - the spawned processes inherit is correct. First, it must be on the same - host and display as the value of -display passed in on our command line - (which is not necessarily the same as what our $DISPLAY variable is.) - Second, the screen number in the $DISPLAY passed to the subprocess should - be the screen on which this particular hack is running -- not the display - specification which the driver itself is using, since the driver ignores - its screen number and manages all existing screens. - - Likewise, store a window ID in $XSCREENSAVER_WINDOW -- this will allow - us to (eventually) run multiple hacks in Xinerama mode, where each hack - has the same $DISPLAY but a different piece of glass. - */ - Display *dpy = DisplayOfScreen (screen); - const char *odpy = DisplayString (dpy); - char *ndpy = (char *) malloc (strlen(odpy) + 20); - char *nssw = (char *) malloc (40); - char *s, *c; - - strcpy (ndpy, "DISPLAY="); - s = ndpy + strlen(ndpy); - strcpy (s, odpy); - - /* We have to find the last colon since it is the boundary between - hostname & screen - IPv6 numeric format addresses may have many - colons before that point, and DECnet addresses always have two colons */ - c = strrchr(s,':'); /* skip to last colon */ - if (c != NULL) s = c+1; - while (isdigit(*s)) s++; /* skip over dpy number */ - while (*s == '.') s++; /* skip over dot */ - if (s[-1] != '.') *s++ = '.'; /* put on a dot */ - sprintf(s, "%d", screen_number (screen)); /* put on screen number */ - - sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX", (unsigned long) saver_window); - - /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems - any more, right? It's not Posix, but everyone seems to have it. */ -#ifdef HAVE_PUTENV - if (putenv (ndpy)) - abort (); - if (putenv (nssw)) - abort (); - - /* don't free ndpy/nssw -- some implementations of putenv (BSD 4.4, - glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) - do not. So we must leak it (and/or the previous setting). Yay. - */ -#endif /* HAVE_PUTENV */ -} - - -/* GL crap */ - Visual * get_best_gl_visual (saver_info *si, Screen *screen) { + saver_preferences *p = &si->prefs; pid_t forked; int fds [2]; int in, out; @@ -1298,7 +1093,7 @@ get_best_gl_visual (saver_info *si, Screen *screen) char *av[10]; int ac = 0; - av[ac++] = "xscreensaver-gl-helper"; + av[ac++] = "xscreensaver-gl-visual"; av[ac] = 0; if (pipe (fds)) @@ -1322,18 +1117,13 @@ get_best_gl_visual (saver_info *si, Screen *screen) errout = errfds [1]; } - block_sigchld(); /* This blocks it in the parent and child, to avoid - racing. It is never unblocked in the child before - the child exits, but that doesn't matter. - */ - switch ((int) (forked = fork ())) { case -1: { sprintf (buf, "%s: couldn't fork", blurb()); perror (buf); - saver_exit (si, 1, 0); + exit (1); } case 0: { @@ -1375,11 +1165,17 @@ get_best_gl_visual (saver_info *si, Screen *screen) int result = 0; int wait_status = 0; pid_t pid = -1; - - FILE *f = fdopen (in, "r"); + FILE *f; unsigned long v = 0; char c; + make_job (forked, 0, av[0]); /* Bookkeeping for SIGCHLD */ + + if (p->verbose_p) + fprintf (stderr, "%s: %d: forked \"%s\" in pid %lu\n", + blurb(), 0, av[0], (unsigned long) forked); + + f = fdopen (in, "r"); close (out); /* don't need this one */ *buf = 0; @@ -1396,16 +1192,8 @@ get_best_gl_visual (saver_info *si, Screen *screen) /* Wait for the child to die - wait for this pid only, not others. */ pid = waitpid (forked, &wait_status, 0); if (si->prefs.debug_p) - { - write_string (STDERR_FILENO, blurb()); - write_string (STDERR_FILENO, ": waitpid("); - write_long (STDERR_FILENO, (long) forked); - write_string (STDERR_FILENO, ") ==> "); - write_long (STDERR_FILENO, (long) pid); - write_string (STDERR_FILENO, "\n"); - } - - unblock_sigchld(); /* child is dead and waited, unblock now. */ + fprintf (stderr, "%s: waitpid(%ld) => %ld\n", blurb(), + (long) forked, (long) pid); if (1 == sscanf (buf, "0x%lx %c", &v, &c)) result = (int) v; @@ -1430,7 +1218,7 @@ get_best_gl_visual (saver_info *si, Screen *screen) { Visual *v = id_to_visual (screen, result); if (si->prefs.verbose_p) - fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n", + fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s\n", blurb(), screen_number (screen), av[0], result, (v == DefaultVisualOfScreen (screen) @@ -1442,54 +1230,3 @@ get_best_gl_visual (saver_info *si, Screen *screen) abort(); } - - - -/* Restarting the xscreensaver process from scratch. */ - -static char **saved_argv; - -void -save_argv (int argc, char **argv) -{ - saved_argv = (char **) calloc (argc+2, sizeof (char *)); - saved_argv [argc] = 0; - while (argc--) - { - int i = strlen (argv [argc]) + 1; - saved_argv [argc] = (char *) malloc (i); - memcpy (saved_argv [argc], argv [argc], i); - } -} - - -/* Re-execs the process with the arguments in saved_argv. Does not return. - */ -void -restart_process (saver_info *si) -{ - fflush (stdout); - fflush (stderr); - shutdown_stderr (si); - if (si->prefs.verbose_p) - { - int i; - fprintf (stderr, "%s: re-executing", blurb()); - for (i = 0; saved_argv[i]; i++) - fprintf (stderr, " %s", saved_argv[i]); - fprintf (stderr, "\n"); - } - describe_uids (si, stderr); - fprintf (stderr, "\n"); - - fflush (stdout); - fflush (stderr); - execvp (saved_argv [0], saved_argv); /* shouldn't return */ - { - char buf [512]; - sprintf (buf, "%s: could not restart process", blurb()); - perror(buf); - fflush(stderr); - abort(); - } -} |