/* subprocs.c --- choosing, spawning, and killing screenhacks.
- * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998
+ * xscreensaver, Copyright (c) 1991, 1992, 1993, 1995, 1997, 1998, 1999, 2000
* Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
#include <X11/Xlib.h> /* not used for much... */
#ifndef ESRCH
-#include <errno.h>
+# include <errno.h>
#endif
#include <sys/time.h> /* sys/resource.h needs this for timeval */
-#ifndef VMS
-
-# include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
+#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h> /* for waitpid() and associated macros */
+#endif
-#else /* VMS */
-
-# if __DECC_VER >= 50200000
-# include <sys/wait.h>
-# endif
+#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+# include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
+#endif
+#ifdef VMS
# include <processes.h>
# include <unixio.h> /* for close */
# include <unixlib.h> /* for getpid */
-# define pid_t int
-# define fork vfork
-
+# define pid_t int
+# define fork vfork
#endif /* VMS */
#include <signal.h> /* for the signal names */
#if !defined(SIGCHLD) && defined(SIGCLD)
-#define SIGCHLD SIGCLD
+# define SIGCHLD SIGCLD
#endif
+#if 0 /* putenv() is declared in stdlib.h on modern linux systems. */
#ifdef HAVE_PUTENV
extern int putenv (/* const char * */); /* getenv() is in stdlib.h... */
#endif
+#endif
+
extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
/* This file doesn't need the Xt headers, so stub these types out... */
#include "xscreensaver.h"
#include "yarandom.h"
-
+#include "visual.h" /* for id_to_visual() */
extern saver_info *global_si_kludge; /* I hate C so much... */
-static void hack_subproc_environment (saver_screen_info *ssi);
-
-
static void
nice_subproc (int nice_level)
{
{
char *av[5];
int ac = 0;
- char *command2 = (char *) malloc (strlen (command) + 6);
- memcpy (command2, "exec ", 5);
- memcpy (command2 + 5, command, strlen (command) + 1);
+ 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++)
+ {
+ if (*s == '=') got_eq = 1;
+ else if (*s == ' ')
+ {
+ if (got_eq)
+ {
+ while (*s == ' ' || *s == '\t')
+ s++;
+ after_vars = s;
+ got_eq = 0;
+ }
+ else
+ break;
+ }
+ }
+
+ *command2 = 0;
+ strncat (command2, command, after_vars - command);
+ strcat (command2, "exec ");
+ strcat (command2, after_vars);
+
+ /* 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"
+ */
+
/* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
av [ac++] = (char *) shell;
saver_preferences *p = &si->prefs;
#ifndef VMS
- Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"");
+ Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
+ /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
+
+ if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 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);
+ }
if (p->verbose_p)
fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
static char name [1024];
const char *in = cmd;
char *out = name;
+ int got_eq = 0;
+ int first = 1;
clean_job_list();
+ AGAIN:
while (isspace(*in)) in++; /* skip whitespace */
- while (!isspace(*in) && *in != ':')
+ while (!isspace(*in) && *in != ':') {
+ if (*in == '=') got_eq = 1;
*out++ = *in++; /* snarf first token */
- while (isspace(*in)) in++; /* skip whitespace */
- if (*in == ':') /* token was a visual name; skip it. */
- {
- in++;
+ }
+
+ if (got_eq) /* if the first token was FOO=bar */
+ { /* then get the next token instead. */
+ got_eq = 0;
out = name;
- while (isspace(*in)) in++; /* skip whitespace */
- while (!isspace(*in)) *out++ = *in++; /* snarf first token */
+ first = 0;
+ goto AGAIN;
}
+
+ while (isspace(*in)) in++; /* skip whitespace */
*out = 0;
job->name = strdup(name);
\f
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 */
- 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->demo_mode_p))
- {
- if (*in == ':') in++;
- while (isspace(*in)) in++;
- fprintf (stderr,
- (si->demo_mode_p
- ? "%s: warning, no \"%s\" visual for \"%s\".\n"
- : "%s: no \"%s\" visual; skipping \"%s\".\n"),
- blurb(), (vis ? vis : "???"), in);
- }
+ if (!selected && (p->verbose_p || si->demoing_p))
+ 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;
}
raise_window (si, first_time_p, True, False);
XFlush (si->dpy);
- if (p->screenhacks_count || si->demo_mode_p)
+ if (p->screenhacks_count)
{
- char *hack;
+ screenhack *hack;
pid_t forked;
char buf [255];
int new_hack;
+ int retry_count = 0;
+ Bool force = False;
- if (si->demo_mode_p)
- {
- hack = si->demo_hack;
+ AGAIN:
+
+ if (p->screenhacks_count == 1)
+ /* If there is only one hack in the list, there is no choice. */
+ new_hack = 0;
+
+ else if (si->selection_mode == -1)
+ /* Select the next hack, wrapping. */
+ new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
- /* Ignore visual-selection failure if in demo mode. */
- (void) select_visual_of_hack (ssi, hack);
+ else if (si->selection_mode == -2)
+ /* Select the previous hack, wrapping. */
+ 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. */
+ {
+ new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
+ force = True;
}
else
{
- int retry_count = 0;
-
- AGAIN:
- if (p->screenhacks_count == 1)
- new_hack = 0;
- else if (si->next_mode_p == 1)
- new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
- else if (si->next_mode_p == 2)
- new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
- % p->screenhacks_count);
- else
- while ((new_hack = random () % p->screenhacks_count)
- == ssi->current_hack)
- ;
- ssi->current_hack = new_hack;
- hack = p->screenhacks[ssi->current_hack];
+ /* Select a random hack (but not the one we just ran.) */
+ while ((new_hack = random () % p->screenhacks_count)
+ == ssi->current_hack)
+ ;
+ }
+
+ ssi->current_hack = new_hack;
+ hack = p->screenhacks[ssi->current_hack];
- if (!select_visual_of_hack (ssi, hack))
+ /* If the hack is disabled, or there is no visual for this hack,
+ then try again (move forward, or backward, or re-randomize.)
+ Unless this hack was specified explicitly, in which case,
+ use it regardless.
+ */
+ if (force)
+ select_visual_of_hack (ssi, hack);
+
+ if (!force &&
+ (!hack->enabled_p ||
+ !select_visual_of_hack (ssi, hack)))
+ {
+ if (++retry_count > (p->screenhacks_count*4))
{
- if (++retry_count > (p->screenhacks_count*4))
- {
- /* Uh, oops. Odds are, there are no suitable visuals,
- and we're looping. Give up. (This is totally lame,
- what we should do is make a list of suitable hacks at
- the beginning, then only loop over them.)
- */
- if (p->verbose_p)
- fprintf(stderr,
- "%s: no suitable visuals for these programs.\n",
- blurb());
- return;
- }
- else
- goto AGAIN;
+ /* Uh, oops. Odds are, there are no suitable visuals,
+ and we're looping. Give up. (This is totally lame,
+ what we should do is make a list of suitable hacks at
+ the beginning, then only loop over them.)
+ */
+ if (p->verbose_p)
+ fprintf(stderr,
+ "%s: no suitable visuals for these programs.\n",
+ blurb());
+ return;
}
+ else
+ goto AGAIN;
}
- si->next_mode_p = 0;
-
- /* If there's a visual description on the front of the command, nuke it.
+ /* Turn off "next" and "prev" modes now, but "demo" mode is only
+ turned off by explicit action.
*/
- {
- char *in = hack;
- 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;
- }
- }
+ if (si->selection_mode < 0)
+ si->selection_mode = 0;
switch ((int) (forked = fork ()))
{
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); /* 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
-/* Restarting the xscreensaver process from scratch. */
+/* Environment variables. */
-static char **saved_argv;
+/* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
+ is defined, the xscreensaver daemon will search that directory for hacks.
+ */
void
-save_argv (int argc, char **argv)
+hack_environment (saver_info *si)
{
- saved_argv = (char **) malloc ((argc + 2) * sizeof (char *));
- saved_argv [argc] = 0;
- while (argc--)
+#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
+ static const char *def_path = DEFAULT_PATH_PREFIX;
+ if (def_path && *def_path)
{
- int i = strlen (argv [argc]) + 1;
- saved_argv [argc] = (char *) malloc (i);
- memcpy (saved_argv [argc], argv [argc], i);
+ const char *opath = getenv("PATH");
+ char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
+ strcpy (npath, "PATH=");
+ strcat (npath, def_path);
+ strcat (npath, ":");
+ strcat (npath, opath);
+
+ if (putenv (npath))
+ abort ();
}
+#endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
}
-void
-restart_process (saver_info *si)
-{
- fflush (real_stdout);
- fflush (real_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);
- }
-}
-/* Like restart_process(), but ensures that when it restarts,
- it comes up in demo-mode. */
void
-demo_mode_restart_process (saver_info *si)
-{
- int i;
- for (i = 0; saved_argv [i]; i++);
- /* add the -initial-demo-mode switch; save_argv() left room for this. */
- saved_argv [i] = "-initial-demo-mode";
- saved_argv [i+1] = 0;
- restart_process (si); /* shouldn't return */
- saved_argv [i] = 0;
- XBell(si->dpy, 0);
-}
-
-static void
hack_subproc_environment (saver_screen_info *ssi)
{
/* Store $DISPLAY into the environment, so that the $DISPLAY variable that
for (screen_number = 0; screen_number < si->nscreens; screen_number++)
if (ssi == &si->screens[screen_number])
break;
- if (screen_number >= si->nscreens) abort();
strcpy (ndpy, "DISPLAY=");
s = ndpy + strlen(ndpy);
#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. */
+
+static char **saved_argv;
void
-hack_environment (saver_info *si)
+save_argv (int argc, char **argv)
{
-#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
- static const char *def_path = DEFAULT_PATH_PREFIX;
- if (def_path && *def_path)
+ saved_argv = (char **) calloc (argc+2, sizeof (char *));
+ saved_argv [argc] = 0;
+ while (argc--)
{
- const char *opath = getenv("PATH");
- char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
- strcpy (npath, "PATH=");
- strcat (npath, def_path);
- strcat (npath, ":");
- strcat (npath, opath);
+ int i = strlen (argv [argc]) + 1;
+ saved_argv [argc] = (char *) malloc (i);
+ memcpy (saved_argv [argc], argv [argc], i);
+ }
+}
- if (putenv (npath))
- abort ();
+
+/* Re-execs the process with the arguments in saved_argv.
+ Does not return unless there was an error.
+ */
+void
+restart_process (saver_info *si)
+{
+ if (si->prefs.verbose_p)
+ {
+ int i;
+ fprintf (real_stderr, "%s: re-executing", blurb());
+ for (i = 0; saved_argv[i]; i++)
+ fprintf (real_stderr, " %s", saved_argv[i]);
+ fprintf (real_stderr, "\n");
}
-#endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
+ describe_uids (si, real_stderr);
+ fprintf (real_stderr, "\n");
+
+ fflush (real_stdout);
+ fflush (real_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);
+ }
+ XBell(si->dpy, 0);
}