/* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991-2002 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1991-2005 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
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;
static int block_sigchld_handler = 0;
-void
+#ifdef HAVE_SIGACTION
+ sigset_t
+#else /* !HAVE_SIGACTION */
+ int
+#endif /* !HAVE_SIGACTION */
block_sigchld (void)
{
#ifdef HAVE_SIGACTION
sigset_t child_set;
sigemptyset (&child_set);
sigaddset (&child_set, SIGCHLD);
+ sigaddset (&child_set, SIGPIPE);
sigprocmask (SIG_BLOCK, &child_set, 0);
#endif /* HAVE_SIGACTION */
block_sigchld_handler++;
+
+#ifdef HAVE_SIGACTION
+ return child_set;
+#else /* !HAVE_SIGACTION */
+ return 0;
+#endif /* !HAVE_SIGACTION */
}
void
sigset_t child_set;
sigemptyset(&child_set);
sigaddset(&child_set, SIGCHLD);
+ sigaddset(&child_set, SIGPIPE);
sigprocmask(SIG_UNBLOCK, &child_set, 0);
#endif /* HAVE_SIGACTION */
}
+/* Executes the command in another process.
+ Command may be any single command acceptable to /bin/sh.
+ It may include wildcards, but no semicolons.
+ If successful, the pid of the other process is returned.
+ Otherwise, -1 is returned and an error may have been
+ printed to stderr.
+ */
+pid_t
+fork_and_exec (saver_screen_info *ssi, const char *command)
+{
+ saver_info *si = ssi->global;
+ saver_preferences *p = &si->prefs;
+ pid_t forked;
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ {
+ char buf [255];
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ break;
+ }
+
+ 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 */
+
+ if (p->verbose_p)
+ fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
+ blurb(), ssi->number, 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);
+
+ exit (1); /* exits child fork */
+ break;
+
+ default: /* parent */
+ (void) make_job (forked, ssi->number, command);
+ break;
+ }
+
+ return forked;
+}
+
+
static void
spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
{
screenhack *hack;
pid_t forked;
char buf [255];
- int new_hack;
+ int new_hack = -1;
int retry_count = 0;
Bool force = False;
/* No hacks at all */
new_hack = -1;
}
+ else if (p->screenhacks_count == 1)
+ {
+ /* Exactly one hack in the list */
+ new_hack = 0;
+ }
else if (si->selection_mode == -1)
{
/* Select the next hack, wrapping. */
{
new_hack = -1;
}
+ else if (p->mode == RANDOM_HACKS_SAME &&
+ ssi->number != 0)
+ {
+ /* 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;
+ }
else /* (p->mode == RANDOM_HACKS) */
{
/* Select a random hack (but not the one we just ran.) */
if (si->selection_mode < 0)
si->selection_mode = 0;
- switch ((int) (forked = fork ()))
+ forked = fork_and_exec (ssi, hack->command);
+ switch ((int) forked)
{
- case -1:
+ case -1: /* fork failed */
+ case 0: /* child fork (can't happen) */
sprintf (buf, "%s: couldn't fork", blurb());
perror (buf);
restore_real_vroot (si);
- saver_exit (si, 1, 0);
-
- 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 */
-
- if (p->verbose_p)
- fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
- blurb(), ssi->number, hack->command,
- (unsigned long) getpid ());
-
- exec_command (p->shell, hack->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 (hack->command);
-
- exit (1); /* exits child fork */
+ saver_exit (si, 1, "couldn't fork");
break;
default:
ssi->pid = forked;
- (void) make_job (forked, ssi->number, hack->command);
break;
}
}
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.
*/
saver_info *si = ssi->global;
const char *odpy = DisplayString (si->dpy);
- char *ndpy = (char *) malloc(strlen(odpy) + 20);
+ char *ndpy = (char *) malloc (strlen(odpy) + 20);
+ char *nssw = (char *) malloc (40);
char *s;
strcpy (ndpy, "DISPLAY=");
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->number); /* put on screen number */
+ sprintf(s, "%d", ssi->real_screen_number); /* put on screen number */
+
+ sprintf (nssw, "XSCREENSAVER_WINDOW=0x%lX",
+ (unsigned long) ssi->screensaver_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 ();
- /* do not free(ndpy) -- see above. */
+ 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 */
}