/* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991-2006 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1991-2017 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
#endif /* VMS */
#include <signal.h> /* for the signal names */
+#include <time.h>
#if !defined(SIGCHLD) && defined(SIGCLD)
# define SIGCHLD SIGCLD
num = -num;
}
- while ((num > 0) && (num_digits < sizeof(string - 1)))
+ while ((num > 0) && (num_digits < sizeof(string) - 1))
{
int digit;
digit = (int) num % 10;
pid_t pid;
int screen;
enum job_status status;
+ time_t launched, killed;
struct screenhack_job *next;
};
struct screenhack_job *job;
fprintf(stderr, "%s: job list:\n", blurb());
for (job = jobs; job; job = job->next)
- fprintf (stderr, " %5ld: %2d: (%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->name);
+ {
+ 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, "\n");
}
const char *in = cmd;
char *out = name;
int got_eq = 0;
- int first = 1;
clean_job_list();
{ /* then get the next token instead. */
got_eq = 0;
out = name;
- first = 0;
goto AGAIN;
}
job->pid = pid;
job->screen = screen;
job->status = job_running;
+ job->launched = time ((time_t *) 0);
+ job->killed = 0;
job->next = jobs;
jobs = job;
clean_job_list (void)
{
struct screenhack_job *job, *prev, *next;
+ time_t now = time ((time_t *) 0);
+ static time_t last_warn = 0;
+ Bool warnedp = False;
+
for (prev = 0, job = jobs, next = (job ? job->next : 0);
job;
prev = job, job = next, next = (job ? job->next : 0))
free_job (job);
job = prev;
}
+ else if (job->status == job_killed &&
+ now - job->killed > 10 &&
+ now - last_warn > 10)
+ {
+ fprintf (stderr,
+ "%s: WARNING: pid %ld (%s) sent SIGTERM %ld seconds ago"
+ " and did not die!\n",
+ blurb(),
+ (long) job->pid,
+ job->name,
+ (long) (now - job->killed));
+ warnedp = True;
+ }
}
+ if (warnedp) last_warn = now;
}
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);
- sigaddset (&child_set, SIGPIPE);
sigprocmask (SIG_BLOCK, &child_set, 0);
-#endif /* HAVE_SIGACTION */
+
+#else /* !HAVE_SIGACTION */
+ signal (SIGPIPE, SIG_IGN);
+#endif /* !HAVE_SIGACTION */
block_sigchld_handler++;
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);
- sigaddset(&child_set, SIGPIPE);
sigprocmask(SIG_UNBLOCK, &child_set, 0);
-#endif /* HAVE_SIGACTION */
+
+#else /* !HAVE_SIGACTION */
+ signal(SIGPIPE, SIG_DFL);
+#endif /* !HAVE_SIGACTION */
+ }
block_sigchld_handler--;
}
clean_job_list();
- if (block_sigchld_handler)
+ if (in_signal_handler_p)
/* This function should not be called from the signal handler. */
abort();
}
switch (signal) {
- case SIGTERM: job->status = job_killed; break;
+ case SIGTERM:
+ job->status = job_killed;
+ 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;
sigchld_handler (int sig)
{
saver_info *si = global_si_kludge; /* I hate C so much... */
+ in_signal_handler_p++;
if (si->prefs.debug_p)
{
}
init_sigchld();
+ in_signal_handler_p--;
}
#endif /* SIGCHLD */
case 0:
close (ConnectionNumber (si->dpy)); /* close display fd */
limit_subproc_memory (p->inferior_memory_limit, p->verbose_p);
- hack_subproc_environment (ssi); /* set $DISPLAY */
+ hack_subproc_environment (ssi->screen, ssi->screensaver_window);
if (p->verbose_p)
fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
}
-static void
-spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
+void
+spawn_screenhack (saver_screen_info *ssi)
{
saver_info *si = ssi->global;
saver_preferences *p = &si->prefs;
- raise_window (si, first_time_p, True, False);
XFlush (si->dpy);
+ if (!monitor_powered_on_p (si))
+ {
+ if (si->prefs.verbose_p)
+ fprintf (stderr,
+ "%s: %d: X says monitor has powered down; "
+ "not launching a hack.\n", blurb(), ssi->number);
+ return;
+ }
+
if (p->screenhacks_count)
{
screenhack *hack;
break;
}
}
-}
-
-
-void
-spawn_screenhack (saver_info *si, Bool first_time_p)
-{
- if (monitor_powered_on_p (si))
- {
- int i;
- for (i = 0; i < si->nscreens; i++)
- {
- saver_screen_info *ssi = &si->screens[i];
- spawn_screenhack_1 (ssi, first_time_p);
- }
- }
- else if (si->prefs.verbose_p)
- fprintf (stderr,
- "%s: X says monitor has powered down; "
- "not launching a hack.\n", blurb());
- store_saver_status (si); /* store current hack numbers */
+ store_saver_status (si); /* store current hack number */
}
void
-kill_screenhack (saver_info *si)
+kill_screenhack (saver_screen_info *ssi)
{
- int i;
- 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;
- }
+ saver_info *si = ssi->global;
+ if (ssi->pid)
+ kill_job (si, ssi->pid, SIGTERM);
+ ssi->pid = 0;
}
void
-suspend_screenhack (saver_info *si, Bool suspend_p)
+suspend_screenhack (saver_screen_info *ssi, Bool suspend_p)
{
#ifdef SIGSTOP /* older VMS doesn't have it... */
- int i;
- for (i = 0; i < si->nscreens; i++)
- {
- saver_screen_info *ssi = &si->screens[i];
- if (ssi->pid)
- kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
- }
+ saver_info *si = ssi->global;
+ if (ssi->pid)
+ kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
#endif /* SIGSTOP */
}
if (def_path && *def_path)
{
const char *opath = getenv("PATH");
- char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
+ 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, ":");
void
-hack_subproc_environment (saver_screen_info *ssi)
+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
us to (eventually) run multiple hacks in Xinerama mode, where each hack
has the same $DISPLAY but a different piece of glass.
*/
- saver_info *si = ssi->global;
- const char *odpy = DisplayString (si->dpy);
+ Display *dpy = DisplayOfScreen (screen);
+ const char *odpy = DisplayString (dpy);
char *ndpy = (char *) malloc (strlen(odpy) + 20);
char *nssw = (char *) malloc (40);
char *s, *c;
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", ssi->real_screen_number); /* put on screen number */
+ sprintf(s, "%d", screen_number (screen)); /* put on screen number */
- sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX",
- (unsigned long) ssi->screensaver_window);
+ 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. */
/* GL crap */
Visual *
-get_best_gl_visual (saver_screen_info *ssi)
+get_best_gl_visual (saver_info *si, Screen *screen)
{
- saver_info *si = ssi->global;
pid_t forked;
int fds [2];
int in, out;
+ int errfds[2];
+ int errin = -1, errout = -1;
char buf[1024];
char *av[10];
in = fds [0];
out = fds [1];
+ if (!si->prefs.verbose_p)
+ {
+ if (pipe (errfds))
+ {
+ perror ("error creating pipe:");
+ return 0;
+ }
+
+ errin = errfds [0];
+ 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:
}
case 0:
{
- int stdout_fd = 1;
-
close (in); /* don't need this one */
close (ConnectionNumber (si->dpy)); /* close display fd */
- if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
+ if (dup2 (out, STDOUT_FILENO) < 0) /* pipe stdout */
{
perror ("could not dup() a new stdout:");
return 0;
}
- hack_subproc_environment (ssi); /* set $DISPLAY */
+
+ if (! si->prefs.verbose_p)
+ {
+ close(errin);
+ if (dup2 (errout, STDERR_FILENO) < 0)
+ {
+ perror ("could not dup() a new stderr:");
+ return 0;
+ }
+ }
+
+ hack_subproc_environment (screen, 0); /* set $DISPLAY */
execvp (av[0], av); /* shouldn't return. */
- if (errno != ENOENT || si->prefs.verbose_p)
+ if (errno != ENOENT /* || si->prefs.verbose_p */ )
{
- /* Ignore "no such file or directory" errors, unless verbose.
+ /* Ignore "no such file or directory" errors.
Issue all other exec errors, though. */
sprintf (buf, "%s: running %s", blurb(), av[0]);
perror (buf);
{
int result = 0;
int wait_status = 0;
+ pid_t pid = -1;
FILE *f = fdopen (in, "r");
unsigned long v = 0;
*buf = 0;
fclose (f);
- /* Wait for the child to die. */
- waitpid (-1, &wait_status, 0);
+ if (! si->prefs.verbose_p)
+ {
+ close (errout);
+ close (errin);
+ }
+
+ /* 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. */
if (1 == sscanf (buf, "0x%lx %c", &v, &c))
result = (int) v;
}
else
{
- Visual *v = id_to_visual (ssi->screen, result);
+ Visual *v = id_to_visual (screen, result);
if (si->prefs.verbose_p)
fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n",
- blurb(), ssi->number,
+ blurb(), screen_number (screen),
av[0], result,
- (v == ssi->default_visual ? " (default)" : ""));
+ (v == DefaultVisualOfScreen (screen)
+ ? " (default)" : ""));
return v;
}
}