/* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998, 1999, 2000
- * 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
# include <sys/wait.h> /* for waitpid() and associated macros */
#endif
-#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
-# include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
+#ifdef HAVE_SETRLIMIT
+# include <sys/resource.h> /* for setrlimit() and RLIMIT_AS */
#endif
#ifdef VMS
extern saver_info *global_si_kludge; /* I hate C so much... */
-static void
-nice_subproc (int nice_level)
-{
- if (nice_level == 0)
- return;
-
-#if defined(HAVE_NICE)
- {
- int old_nice = nice (0);
- int n = nice_level - old_nice;
- errno = 0;
- if (nice (n) == -1 && errno != 0)
- {
- char buf [512];
- sprintf (buf, "%s: nice(%d) failed", blurb(), n);
- perror (buf);
- }
- }
-#elif defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
- if (setpriority (PRIO_PROCESS, getpid(), nice_level) != 0)
- {
- char buf [512];
- sprintf (buf, "%s: setpriority(PRIO_PROCESS, %lu, %d) failed",
- blurb(), (unsigned long) getpid(), nice_level);
- perror (buf);
- }
-#else
- fprintf (stderr,
- "%s: don't know how to change process priority on this system.\n",
- blurb());
-#endif
-}
+/* 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;
-#ifndef VMS
+ if (num == 0)
+ return "0";
-static void
-exec_simple_command (const char *command)
-{
- char *av[1024];
- int ac = 0;
- char *token = strtok (strdup(command), " \t");
- while (token)
+ if (num < 0)
{
- av[ac++] = token;
- token = strtok(0, " \t");
+ negative_p = True;
+ num = -num;
}
- av[ac] = 0;
- execvp (av[0], av); /* shouldn't return. */
-
- {
- char buf [512];
- sprintf (buf, "%s: could not execute \"%s\"", blurb(), av[0]);
- perror (buf);
-
- if (errno == ENOENT &&
- (token = getenv("PATH")))
- {
-# 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)
- getcwd (path, sizeof(path));
-# 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");
- }
- }
- fflush(stderr);
- fflush(stdout);
- exit (1); /* Note that this only exits a child fork. */
-}
-
-
-static void
-exec_complex_command (const char *shell, const char *command)
-{
- char *av[5];
- int ac = 0;
- char *command2 = (char *) malloc (strlen (command) + 10);
- const char *s;
- int got_eq = 0;
- const char *after_vars;
-
- /* Skip leading whitespace.
- */
- while (*command == ' ' || *command == '\t')
- command++;
-
- /* If the string has a series of tokens with "=" in them at them, set
- `after_vars' to point into the string after those tokens and any
- trailing whitespace. Otherwise, after_vars == command.
- */
- after_vars = command;
- for (s = command; *s; s++)
+ while ((num > 0) && (num_digits < sizeof(string - 1)))
{
- if (*s == '=') got_eq = 1;
- else if (*s == ' ')
- {
- if (got_eq)
- {
- while (*s == ' ' || *s == '\t')
- s++;
- after_vars = s;
- got_eq = 0;
- }
- else
- break;
- }
+ int digit;
+ digit = (int) num % 10;
+ num_digits++;
+ string[sizeof(string) - 1 - num_digits] = digit + '0';
+ num /= 10;
}
- *command2 = 0;
- strncat (command2, command, after_vars - command);
- strcat (command2, "exec ");
- strcat (command2, after_vars);
+ if (negative_p)
+ {
+ num_digits++;
+ string[sizeof(string) - 1 - num_digits] = '-';
+ }
- /* We have now done these transformations:
- "foo -x -y" ==> "exec foo -x -y"
- "BLAT=foop foo -x" ==> "BLAT=foop exec foo -x"
- "BLAT=foop A=b foo -x" ==> "BLAT=foop A=b exec foo -x"
- */
+ 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));
+}
- /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
- av [ac++] = (char *) shell;
- av [ac++] = "-c";
- av [ac++] = command2;
- av [ac] = 0;
+static int
+write_long (int fd, long n)
+{
+ const char *str = no_malloc_number_to_string (n);
+ return write_string (fd, str);
+}
- execvp (av[0], av); /* shouldn't return. */
- {
- char buf [512];
- sprintf (buf, "%s: execvp(\"%s\") failed", blurb(), av[0]);
- perror (buf);
- fflush(stderr);
- fflush(stdout);
- exit (1); /* Note that this only exits a child fork. */
- }
-}
+/* 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.
-#else /* VMS */
+ 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
-exec_vms_command (const char *command)
+limit_subproc_memory (int address_space_limit, Bool verbose_p)
{
- system (command);
- fflush (stderr);
- fflush (stdout);
- exit (1); /* Note that this only exits a child fork. */
-}
-#endif /* !VMS */
+/* 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 void
-exec_screenhack (saver_info *si, const char *command)
-{
- /* I don't believe what a sorry excuse for an operating system UNIX is!
-
- - I want to spawn a process.
- - I want to know it's pid so that I can kill it.
- - I would like to receive a message when it dies of natural causes.
- - I want the spawned process to have user-specified arguments.
-
- If shell metacharacters are present (wildcards, backquotes, etc), the
- only way to parse those arguments is to run a shell to do the parsing
- for you.
-
- And the only way to know the pid of the process is to fork() and exec()
- it in the spawned side of the fork.
-
- But if you're running a shell to parse your arguments, this gives you
- the pid of the *shell*, not the pid of the *process* that you're
- actually interested in, which is an *inferior* of the shell. This also
- means that the SIGCHLD you get applies to the shell, not its inferior.
- (Why isn't that sufficient? I don't remember any more, but it turns
- out that it isn't.)
-
- So, the only solution, when metacharacters are present, is to force the
- shell to exec() its inferior. What a fucking hack! We prepend "exec "
- to the command string, and hope it doesn't contain unquoted semicolons
- or ampersands (we don't search for them, because we don't want to
- prohibit their use in quoted strings (messages, for example) and parsing
- out the various quote characters is too much of a pain.)
-
- (Actually, Clint Wong <clint@jts.com> points out that process groups
- might be used to take care of this problem; this may be worth considering
- some day, except that, 1: this code works now, so why fix it, and 2: from
- what I've seen in Emacs, dealing with process groups isn't especially
- portable.)
- */
- saver_preferences *p = &si->prefs;
+ if (address_space_limit < 10 * 1024) /* let's not be crazy */
+ return;
-#ifndef VMS
- Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
- /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
+ 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 (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0)
+ if (setrlimit (RLIMIT_AS, &r) != 0)
{
- /* If you're thinking of commenting this out, think again.
- If you do so, you will open a security hole. Mail jwz
- so that he may enlighten you as to the error of your ways.
- */
- fprintf (stderr, "%s: we're still running as root! Disaster!\n",
- blurb());
- saver_exit (si, 1, 0);
+ char buf [512];
+ sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed",
+ blurb(), r.rlim_cur, r.rlim_max);
+ perror (buf);
+ return;
}
- if (p->verbose_p)
- fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
- blurb(), command, (unsigned long) getpid (),
- (hairy_p ? " (via shell)" : ""));
-
- if (hairy_p)
- /* If it contains any shell metacharacters, do it the hard way,
- and fork a shell to parse the arguments for us. */
- exec_complex_command (p->shell, command);
- else
- /* Otherwise, we can just exec the program directly. */
- exec_simple_command (command);
+ 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);
-#else /* VMS */
- if (p->verbose_p)
- fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
- blurb(), command, getpid());
- exec_vms_command (command);
-#endif /* VMS */
+ fprintf (stderr, "%s: limited pid %lu address space to %s.\n",
+ blurb(), (unsigned long) getpid (), buf);
+ }
- abort(); /* that shouldn't have returned. */
+#endif /* HAVE_SETRLIMIT && RLIMIT_AS */
}
-
\f
/* Management of child processes, and de-zombification.
*/
struct screenhack_job {
char *name;
pid_t pid;
+ int screen;
enum job_status status;
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: (%s) %s\n",
+ 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" :
static void clean_job_list (void);
static struct screenhack_job *
-make_job (pid_t pid, const char *cmd)
+make_job (pid_t pid, int screen, const char *cmd)
{
struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
job->name = strdup(name);
job->pid = pid;
+ job->screen = screen;
job->status = job_running;
job->next = jobs;
jobs = job;
static int block_sigchld_handler = 0;
-static 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 */
}
-static void
+void
unblock_sigchld (void)
{
#ifdef HAVE_SIGACTION
sigset_t child_set;
sigemptyset(&child_set);
sigaddset(&child_set, SIGCHLD);
+ sigaddset(&child_set, SIGPIPE);
sigprocmask(SIG_UNBLOCK, &child_set, 0);
#endif /* HAVE_SIGACTION */
default: abort();
}
-#ifdef SIGSTOP
- if (p->verbose_p)
- fprintf (stderr, "%s: %s pid %lu.\n", blurb(),
- (signal == SIGTERM ? "killing" :
- signal == SIGSTOP ? "suspending" :
- signal == SIGCONT ? "resuming" : "signalling"),
- (unsigned long) job->pid);
-#else /* !SIGSTOP */
if (p->verbose_p)
- fprintf (stderr, "%s: %s pid %lu.\n", blurb(), "killing",
- (unsigned long) job->pid);
-#endif /* !SIGSTOP */
+ fprintf (stderr, "%s: %d: %s pid %lu (%s)\n",
+ blurb(), job->screen,
+ (job->status == job_killed ? "killing" :
+ job->status == job_stopped ? "suspending" : "resuming"),
+ (unsigned long) job->pid,
+ job->name);
status = kill (job->pid, signal);
if (p->verbose_p && status < 0)
{
if (errno == ESRCH)
- fprintf (stderr, "%s: child process %lu (%s) was already dead.\n",
- blurb(), job->pid, job->name);
+ fprintf (stderr,
+ "%s: %d: child process %lu (%s) was already dead.\n",
+ blurb(), job->screen, (unsigned long) job->pid, job->name);
else
{
char buf [1024];
- sprintf (buf, "%s: couldn't kill child process %lu (%s)",
- blurb(), job->pid, job->name);
+ sprintf (buf, "%s: %d: couldn't kill child process %lu (%s)",
+ blurb(), job->screen, (unsigned long) job->pid, job->name);
perror (buf);
}
}
saver_info *si = global_si_kludge; /* I hate C so much... */
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();
#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.
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;
if (WIFEXITED (wait_status))
{
if (!job ||
(exit_status != 0 &&
(p->verbose_p || job->status != job_killed)))
- fprintf (stderr,
- "%s: child pid %lu (%s) exited abnormally (code %d).\n",
- blurb(), (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: child pid %lu (%s) exited normally.\n",
- blurb(), (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: child pid %lu (%s) terminated with %s.\n",
- blurb(), (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;
}
}
+static void
+print_path_error (const char *program)
+{
+ 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")))
+ {
+# 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");
+ }
+}
+
+
+/* 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;
AGAIN:
- if (p->screenhacks_count == 1)
- /* If there is only one hack in the list, there is no choice. */
- new_hack = 0;
-
+ if (p->screenhacks_count < 1)
+ {
+ /* 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 = (ssi->current_hack + 1) % p->screenhacks_count;
-
+ {
+ /* Select the next hack, wrapping. */
+ new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
+ }
else if (si->selection_mode == -2)
- /* Select the previous hack, wrapping. */
- new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
- % p->screenhacks_count);
-
+ {
+ /* Select the previous hack, wrapping. */
+ if (ssi->current_hack < 0)
+ new_hack = p->screenhacks_count - 1;
+ else
+ new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
+ % p->screenhacks_count);
+ }
else if (si->selection_mode > 0)
- /* Select a specific hack, by number. No negotiation. */
{
+ /* Select a specific hack, by number (via the ACTIVATE command.) */
new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
force = True;
}
- else
+ else if (p->mode == ONE_HACK &&
+ p->selected_hack >= 0)
+ {
+ /* Select a specific hack, by number (via "One Saver" mode.) */
+ new_hack = p->selected_hack;
+ force = True;
+ }
+ else if (p->mode == BLANK_ONLY || p->mode == DONT_BLANK)
+ {
+ 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.)
+ */
+ new_hack = si->screens[0].current_hack;
+ }
+ else /* (p->mode == RANDOM_HACKS) */
{
/* Select a random hack (but not the one we just ran.) */
while ((new_hack = random () % p->screenhacks_count)
;
}
+ if (new_hack < 0) /* don't run a hack */
+ {
+ ssi->current_hack = -1;
+ if (si->selection_mode < 0)
+ si->selection_mode = 0;
+ return;
+ }
+
ssi->current_hack = new_hack;
hack = p->screenhacks[ssi->current_hack];
if (!force &&
(!hack->enabled_p ||
+ !on_path_p (hack->command) ||
!select_visual_of_hack (ssi, hack)))
{
if (++retry_count > (p->screenhacks_count*4))
*/
if (p->verbose_p)
fprintf(stderr,
- "%s: no suitable visuals for these programs.\n",
- blurb());
+ "%s: %d: no programs enabled, or no suitable visuals.\n",
+ blurb(), ssi->number);
return;
}
else
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 */
- nice_subproc (p->nice_inferior); /* change process priority */
- hack_subproc_environment (ssi); /* set $DISPLAY */
- exec_screenhack (si, hack->command); /* this does not return */
- abort();
+ saver_exit (si, 1, "couldn't fork");
break;
default:
ssi->pid = forked;
- (void) make_job (forked, hack->command);
break;
}
}
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 */
}
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);
- int screen_number;
+ char *ndpy = (char *) malloc (strlen(odpy) + 20);
+ char *nssw = (char *) malloc (40);
char *s;
- for (screen_number = 0; screen_number < si->nscreens; screen_number++)
- if (ssi == &si->screens[screen_number])
- break;
-
strcpy (ndpy, "DISPLAY=");
s = ndpy + strlen(ndpy);
strcpy (s, odpy);
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); /* 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 ();
+ 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 */
}
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);
/* Wait for the child to die. */
waitpid (-1, &wait_status, 0);
- if (1 == sscanf (buf, "0x%x %c", &v, &c))
+ if (1 == sscanf (buf, "0x%lx %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]);
+ {
+ int L = strlen(buf);
+ fprintf (stderr, "%s: %s did not report a GL visual!\n",
+ blurb(), av[0]);
+
+ if (L && buf[L-1] == '\n')
+ buf[--L] = 0;
+ if (*buf)
+ fprintf (stderr, "%s: %s said: \"%s\"\n",
+ blurb(), av[0], buf);
+ }
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)" : ""));
+ fprintf (stderr, "%s: %d: %s: GL visual is 0x%X%s.\n",
+ blurb(), ssi->number,
+ av[0], result,
+ (v == ssi->default_visual ? " (default)" : ""));
return v;
}
}
}
-/* Re-execs the process with the arguments in saved_argv.
- Does not return unless there was an error.
+/* 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 (real_stderr, "%s: re-executing", blurb());
+ fprintf (stderr, "%s: re-executing", blurb());
for (i = 0; saved_argv[i]; i++)
- fprintf (real_stderr, " %s", saved_argv[i]);
- fprintf (real_stderr, "\n");
+ fprintf (stderr, " %s", saved_argv[i]);
+ fprintf (stderr, "\n");
}
- describe_uids (si, real_stderr);
- fprintf (real_stderr, "\n");
+ describe_uids (si, stderr);
+ fprintf (stderr, "\n");
- fflush (real_stdout);
- fflush (real_stderr);
+ 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();
}
- XBell(si->dpy, 0);
}