/* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991-2005 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1991-2014 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
#include <sys/time.h> /* sys/resource.h needs this for timeval */
+#include <sys/param.h> /* for PATH_MAX */
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h> /* for waitpid() and associated macros */
#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.
static struct screenhack_job *jobs = 0;
-/* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
+/* for debugging -- nothing calls this, but it's useful to invoke from gdb.
+ */
+void show_job_list (void);
+
void
show_job_list (void)
{
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++;
unblock_sigchld (void)
{
#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--;
}
sigchld_handler (int sig)
{
saver_info *si = global_si_kludge; /* I hate C so much... */
+ in_signal_handler_p++;
if (si->prefs.debug_p)
- fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
+ {
+ /* 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");
+ }
if (block_sigchld_handler < 0)
abort();
}
init_sigchld();
+ in_signal_handler_p--;
}
#endif /* SIGCHLD */
#ifndef VMS
+
static void
await_dying_children (saver_info *si)
{
if (si->prefs.debug_p)
{
if (kid < 0 && errno)
- fprintf (stderr, "%s: waitpid(-1) ==> %ld (%d)\n", blurb(),
- (long) kid, errno);
- else
- fprintf (stderr, "%s: waitpid(-1) ==> %ld\n", blurb(),
- (long) kid);
+ {
+ /* 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");
+ }
+ 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");
+ }
}
/* 0 means no more children to reap.
if (!job ||
(exit_status != 0 &&
(p->verbose_p || job->status != job_killed)))
- fprintf (stderr,
- "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n",
- blurb(), screen_no, (unsigned long) kid, name, exit_status);
+ {
+ /* 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)
- fprintf (stderr, "%s: %d: child pid %lu (%s) exited normally.\n",
- blurb(), screen_no, (unsigned long) kid, name);
+ {
+ /* 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");
+ }
if (job)
job->status = job_dead;
!job ||
job->status != job_killed ||
WTERMSIG (wait_status) != SIGTERM)
- fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n",
- blurb(), screen_no, (unsigned long) kid, name,
- signal_name (WTERMSIG(wait_status)));
+ {
+ /* 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");
+ }
if (job)
job->status = job_dead;
else if (WIFSTOPPED (wait_status))
{
if (p->verbose_p)
- fprintf (stderr, "%s: child pid %lu (%s) stopped with %s.\n",
- blurb(), (unsigned long) kid, name,
- signal_name (WSTOPSIG (wait_status)));
+ {
+ /* 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");
+ }
if (job)
job->status = job_stopped;
}
else
{
+ /* Don't call fprintf() from signal handlers, as it might malloc.
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;
}
fprintf (stderr, "\n");
*path = 0;
# if defined(HAVE_GETCWD)
- getcwd (path, sizeof(path));
+ if (! getcwd (path, sizeof(path)))
+ *path = 0;
# elif defined(HAVE_GETWD)
getwd (path);
# endif
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;
/* Use the same hack that's running on screen 0.
(Assumes this function was called on screen 0 first.)
*/
- ssi->current_hack = si->screens[0].current_hack;
+ new_hack = si->screens[0].current_hack;
}
else /* (p->mode == RANDOM_HACKS) */
{
if (!force &&
(!hack->enabled_p ||
+ !on_path_p (hack->command) ||
!select_visual_of_hack (ssi, hack)))
{
if (++retry_count > (p->screenhacks_count*4))
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;
+ char *s, *c;
strcpy (ndpy, "DISPLAY=");
s = ndpy + strlen(ndpy);
strcpy (s, odpy);
- while (*s && *s != ':') s++; /* skip to colon */
- while (*s == ':') s++; /* skip over colons */
+ /* 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", 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);
close (out); /* don't need this one */
*buf = 0;
- fgets (buf, sizeof(buf)-1, f);
+ if (! fgets (buf, sizeof(buf)-1, f))
+ *buf = 0;
fclose (f);
+ if (! si->prefs.verbose_p)
+ {
+ close (errout);
+ close (errin);
+ }
+
/* Wait for the child to die. */
waitpid (-1, &wait_status, 0);
+ 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;
}
}