/* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998
- * Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1991-2001 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
# include <sys/wait.h> /* for waitpid() and associated macros */
#endif
-#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+#if (defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)) || \
+ defined(HAVE_SETRLIMIT)
# include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
+ /* and also setrlimit() and RLIMIT_AS */
#endif
#ifdef VMS
#include "xscreensaver.h"
#include "yarandom.h"
-
+#include "visual.h" /* for id_to_visual() */
extern saver_info *global_si_kludge; /* I hate C so much... */
}
+/* 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.
+ */
+#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)
+{
+#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 */
+}
+
+
+
#ifndef VMS
static void
}
while (isspace(*in)) in++; /* skip whitespace */
- if (first && *in == ':') /* token was a visual name; skip it. */
- {
- out = name;
- first = 0;
- goto AGAIN;
- }
*out = 0;
job->name = strdup(name);
static int block_sigchld_handler = 0;
-static void
+void
block_sigchld (void)
{
#ifdef HAVE_SIGACTION
block_sigchld_handler++;
}
-static void
+void
unblock_sigchld (void)
{
#ifdef HAVE_SIGACTION
\f
static Bool
-hack_enabled_p (const char *hack)
-{
- const char *s = hack;
- while (isspace(*s)) s++;
- return (*s != '-');
-}
-
-static Bool
-select_visual_of_hack (saver_screen_info *ssi, const char *hack)
+select_visual_of_hack (saver_screen_info *ssi, screenhack *hack)
{
saver_info *si = ssi->global;
saver_preferences *p = &si->prefs;
Bool selected;
- static char vis [1024];
- const char *in = hack;
- char *out = vis;
- while (isspace(*in)) in++; /* skip whitespace */
- if (*in == '-') in++; /* skip optional "-" */
- while (isspace(*in)) in++; /* skip whitespace */
- while (!isspace(*in) && *in != ':')
- *out++ = *in++; /* snarf first token */
- while (isspace(*in)) in++; /* skip whitespace */
- *out = 0;
-
- if (*in == ':')
- selected = select_visual(ssi, vis);
+ if (hack->visual && *hack->visual)
+ selected = select_visual(ssi, hack->visual);
else
selected = select_visual(ssi, 0);
if (!selected && (p->verbose_p || si->demoing_p))
- {
- if (*in == ':') in++;
- while (isspace(*in)) in++;
- fprintf (stderr,
- (si->demoing_p
- ? "%s: warning, no \"%s\" visual for \"%s\".\n"
- : "%s: no \"%s\" visual; skipping \"%s\".\n"),
- blurb(), (*vis ? vis : "???"), in);
- }
+ 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);
return selected;
}
if (p->screenhacks_count)
{
- char *hack;
+ screenhack *hack;
pid_t forked;
char buf [255];
int new_hack;
select_visual_of_hack (ssi, hack);
if (!force &&
- (!hack_enabled_p (hack) ||
+ (!hack->enabled_p ||
!select_visual_of_hack (ssi, hack)))
{
if (++retry_count > (p->screenhacks_count*4))
if (si->selection_mode < 0)
si->selection_mode = 0;
-
- /* If there's a visual description on the front of the command, nuke it.
- */
- {
- char *in = hack;
- while (isspace(*in)) in++; /* skip whitespace */
- if (*in == '-') in++; /* skip optional "-" */
- while (isspace(*in)) in++; /* skip whitespace */
- hack = in;
- while (!isspace(*in) && *in != ':') in++; /* snarf first token */
- while (isspace(*in)) in++; /* skip whitespace */
- if (*in == ':')
- {
- in++;
- while (isspace(*in)) in++;
- hack = in;
- }
- }
-
switch ((int) (forked = fork ()))
{
case -1:
case 0:
close (ConnectionNumber (si->dpy)); /* close display fd */
nice_subproc (p->nice_inferior); /* change process priority */
+ limit_subproc_memory (p->inferior_memory_limit, p->verbose_p);
hack_subproc_environment (ssi); /* set $DISPLAY */
- exec_screenhack (si, hack); /* this does not return */
+ exec_screenhack (si, hack->command); /* this does not return */
abort();
break;
default:
ssi->pid = forked;
- (void) make_job (forked, hack);
+ (void) make_job (forked, hack->command);
break;
}
}
void
spawn_screenhack (saver_info *si, Bool first_time_p)
{
- int i;
-
- if (!monitor_powered_on_p (si))
+ if (monitor_powered_on_p (si))
{
- if (si->prefs.verbose_p)
- fprintf (stderr,
- "%s: server reports that monitor has powered down; "
- "not launching a new hack.\n", blurb());
- return;
+ 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());
- for (i = 0; i < si->nscreens; i++)
- {
- saver_screen_info *ssi = &si->screens[i];
- spawn_screenhack_1 (ssi, first_time_p);
- }
+ store_saver_status (si); /* store current hack numbers */
}
Bool
screenhack_running_p (saver_info *si)
{
- Bool result = True;
+ Bool any_running_p = False;
int i;
for (i = 0; i < si->nscreens; i++)
{
saver_screen_info *ssi = &si->screens[i];
- if (!ssi->pid)
- result = False;
+ if (ssi->pid) any_running_p = True;
}
- return result;
+ return any_running_p;
}
\f
#endif /* HAVE_PUTENV */
}
+\f
+/* GL crap */
+
+Visual *
+get_best_gl_visual (saver_screen_info *ssi)
+{
+ saver_info *si = ssi->global;
+ pid_t forked;
+ int fds [2];
+ int in, out;
+ char buf[1024];
+
+ char *av[10];
+ int ac = 0;
+
+ av[ac++] = "xscreensaver-gl-helper";
+ av[ac] = 0;
+
+ if (pipe (fds))
+ {
+ perror ("error creating pipe:");
+ return 0;
+ }
+
+ in = fds [0];
+ out = fds [1];
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ {
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ saver_exit (si, 1, 0);
+ }
+ 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 */
+ {
+ perror ("could not dup() a new stdout:");
+ return 0;
+ }
+ hack_subproc_environment (ssi); /* set $DISPLAY */
+
+ execvp (av[0], av); /* shouldn't return. */
+
+ if (errno != ENOENT || si->prefs.verbose_p)
+ {
+ /* Ignore "no such file or directory" errors, unless verbose.
+ Issue all other exec errors, though. */
+ sprintf (buf, "%s: running %s", blurb(), av[0]);
+ perror (buf);
+ }
+ exit (1); /* exits fork */
+ break;
+ }
+ default:
+ {
+ int result = 0;
+ int wait_status = 0;
+
+ FILE *f = fdopen (in, "r");
+ unsigned long v = 0;
+ char c;
+
+ close (out); /* don't need this one */
+
+ *buf = 0;
+ fgets (buf, sizeof(buf)-1, f);
+ fclose (f);
+
+ /* Wait for the child to die. */
+ waitpid (-1, &wait_status, 0);
+
+ if (1 == sscanf (buf, "0x%x %c", &v, &c))
+ result = (int) v;
+
+ if (result == 0)
+ {
+ if (si->prefs.verbose_p)
+ fprintf (stderr, "%s: %s did not report a GL visual!\n",
+ blurb(), av[0]);
+ return 0;
+ }
+ else
+ {
+ Visual *v = id_to_visual (ssi->screen, result);
+ if (si->prefs.verbose_p)
+ fprintf (stderr, "%s: %s says the GL visual is 0x%X%s.\n",
+ blurb(), av[0], result,
+ (v == ssi->default_visual ? " (the default)" : ""));
+ return v;
+ }
+ }
+ }
+
+ abort();
+}
+
+
\f
/* Restarting the xscreensaver process from scratch. */