-/* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@lucid.com>
+/* subprocs.c --- choosing, spawning, and killing screenhacks.
+ * 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
* documentation for any purpose is hereby granted without fee, provided that
* implied warranty.
*/
-/* I would really like some error messages to show up on the screensaver window
- itself when subprocs die, or when we can't launch them. If the process
- produces output, but does not actually die, I would like that output to go
- to the appropriate stdout/stderr as they do now. X and Unix conspire to
- make this incredibly difficult.
-
- - Not all systems have SIGIO, so we can't necessarily be signalled when a
- process dies, so we'd have to poll it with wait() or something awful like
- that, which would mean the main thread waking up more often than it does
- now.
-
- - We can't tell the difference between a process dying, and a process not
- being launched correctly (for example, not being on $PATH) partly because
- of the contortions we need to go through with /bin/sh in order to launch
- it.
-
- - We can't do X stuff from signal handlers, so we'd need to set a flag,
- save the error message, and notice that flag in the main thread. The
- problem is that the main thread is probably sleeping, waiting for the
- next X event, so to do this we'd have to register a pipe FD or something,
- and write to it when something loses.
-
- - We could assume that any output produced by a subproc indicates an error,
- and blast that across the screen. This means we'd need to use popen()
- instead of forking and execing /bin/sh to run it for us. Possibly this
- would work, but see comment in exec_screenhack() about getting pids.
- I think we could do the "exec " trick with popen() but would SIGIO get
- delivered correctly? Who knows. (We could register the pipe-FD with
- Xt, and handle output on it with a callback.)
-
- - For the simple case of the programs not being on $PATH, we could just
- search $PATH before launching the shell, but that seems hardly worth the
- effort... And it's broken!! Why should we have to duplicate half the
- work of the shell? (Because it's Unix, that's why! Bend over.)
- */
-
-#if __STDC__
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
#endif
+#include <ctype.h>
#include <stdio.h>
+#include <string.h>
#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 */
-#include <sys/resource.h> /* for setpriority() and PRIO_PROCESS */
-#include <sys/wait.h> /* for waitpid() and associated macros */
-#include <signal.h> /* for the signal names */
-extern char **environ; /* why isn't this in some header file? */
+#ifdef HAVE_SYS_WAIT_H
+# 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 */
+#endif
+
+#ifdef VMS
+# include <processes.h>
+# include <unixio.h> /* for close */
+# include <unixlib.h> /* for getpid */
+# define pid_t int
+# define fork vfork
+#endif /* VMS */
-#ifndef NO_SETUID
-#include <pwd.h> /* for getpwnam() and struct passwd */
-#include <grp.h> /* for getgrgid() and struct group */
-#endif /* NO_SETUID */
+#include <signal.h> /* for the signal names */
#if !defined(SIGCHLD) && defined(SIGCLD)
-#define SIGCHLD SIGCLD
+# define SIGCHLD SIGCLD
#endif
-#if __STDC__
+#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... */
-extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
+#endif
#endif
-# ifndef random
-# if defined(SVR4) || defined(SYSV)
-# define random() rand()
-# else /* !totally-losing-SYSV */
- extern long random(); /* rand() is in stdlib.h... */
-# endif /* !totally-losing-SYSV */
-# endif /* random defined */
+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... */
+#undef XtPointer
+#define XtAppContext void*
+#define XrmDatabase void*
+#define XtIntervalId void*
+#define XtPointer void*
+#define Widget void*
#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
+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
+}
-/* this must be `sh', not whatever $SHELL happens to be. */
-char *shell;
-static pid_t pid = 0;
-char **screenhacks;
-int screenhacks_count;
-int current_hack = -1;
-char *demo_hack;
-int next_mode_p = 0;
-Bool locking_disabled_p = False;
-char *nolock_reason = 0;
-int nice_inferior = 0;
-extern Bool demo_mode_p;
+#ifndef VMS
static void
-exec_screenhack (command)
- char *command;
+exec_simple_command (const char *command)
{
- char *tmp;
- char buf [512];
- char *av [5];
+ char *av[1024];
int ac = 0;
+ char *token = strtok (strdup(command), " \t");
+ while (token)
+ {
+ av[ac++] = token;
+ token = strtok(0, " \t");
+ }
+ 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. */
+}
+
- /* Close this fork's version of the display's fd. It will open its own. */
- close (ConnectionNumber (dpy));
-
+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++)
+ {
+ 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;
+ av [ac++] = "-c";
+ av [ac++] = command2;
+ av [ac] = 0;
+
+ 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. */
+ }
+}
+
+#else /* VMS */
+
+static void
+exec_vms_command (const char *command)
+{
+ system (command);
+ fflush (stderr);
+ fflush (stdout);
+ exit (1); /* Note that this only exits a child fork. */
+}
+
+#endif /* !VMS */
+
+
+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 would like to receive a message when it dies of natural causes.
- I want the spawned process to have user-specified arguments.
- The *only way* to parse arguments the way the shells do is to run a
- shell (or duplicate what they do, which would be a *lot* of code.)
+ 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.
- The *only way* to know the pid of the process is to fork() and exec()
+ 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.
-
- So, the only solution other than implementing an argument parser here
- is to force the shell to exec() its inferior. What a fucking hack!
- We prepend "exec " to the command string.
+ 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
what I've seen in Emacs, dealing with process groups isn't especially
portable.)
*/
- tmp = command;
- command = (char *) malloc (strlen (tmp) + 6);
- memcpy (command, "exec ", 5);
- memcpy (command + 5, tmp, strlen (tmp) + 1);
+ saver_preferences *p = &si->prefs;
- /* Invoke the shell as "/bin/sh -c 'exec prog -arg -arg ...'" */
- av [ac++] = shell;
- av [ac++] = "-c";
- av [ac++] = command;
- av [ac++] = 0;
-
- if (verbose_p)
- printf ("%s: spawning \"%s\" in pid %d.\n", progname, command, getpid ());
+#ifndef VMS
+ Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
+ /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
-#if defined(SYSV) || defined(SVR4) || defined(__hpux)
- {
- int old_nice = nice (0);
- int n = nice_inferior - old_nice;
- errno = 0;
- if (nice (n) == -1 && errno != 0)
- {
- sprintf (buf, "%s: %snice(%d) failed", progname,
- (verbose_p ? "## " : ""), n);
- perror (buf);
- }
- }
-#else /* !SYSV */
-#ifdef PRIO_PROCESS
- if (setpriority (PRIO_PROCESS, getpid(), nice_inferior) != 0)
+ if (getuid() == (uid_t) 0 || geteuid() == (uid_t) 0)
{
- sprintf (buf, "%s: %ssetpriority(PRIO_PROCESS, %d, %d) failed",
- progname, (verbose_p ? "## " : ""), getpid(), nice_inferior);
- perror (buf);
+ /* 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);
}
-#else /* !PRIO_PROCESS */
- if (nice_inferior != 0)
- fprintf (stderr,
- "%s: %sdon't know how to change process priority on this system.\n",
- progname, (verbose_p ? "## " : ""));
-#endif /* !PRIO_PROCESS */
-#endif /* !SYSV */
- /* Now overlay the current process with /bin/sh running the command.
- If this returns, it's an error.
- */
- execve (av [0], av, environ);
+ if (p->verbose_p)
+ fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
+ blurb(), command, (unsigned long) getpid (),
+ (hairy_p ? " (via shell)" : ""));
- sprintf (buf, "%s: %sexecve() failed", progname, (verbose_p ? "## " : ""));
- perror (buf);
- exit (1); /* Note that this only exits a child fork. */
+ 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);
+
+#else /* VMS */
+ if (p->verbose_p)
+ fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
+ blurb(), command, getpid());
+ exec_vms_command (command);
+#endif /* VMS */
+
+ abort(); /* that shouldn't have returned. */
}
-/* to avoid a race between the main thread and the SIGCHLD handler */
-static int killing = 0;
-static Bool suspending = False;
-static char *current_hack_name P((void));
+\f
+/* Management of child processes, and de-zombification.
+ */
+
+enum job_status {
+ job_running, /* the process is still alive */
+ job_stopped, /* we have sent it a STOP signal */
+ job_killed, /* we have sent it a TERM signal */
+ job_dead /* we have wait()ed for it, and it's dead -- this state only
+ occurs so that we can avoid calling free() from a signal
+ handler. Shortly after going into this state, the list
+ element will be removed. */
+};
+
+struct screenhack_job {
+ char *name;
+ pid_t pid;
+ enum job_status status;
+ struct screenhack_job *next;
+};
+
+static struct screenhack_job *jobs = 0;
+
+/* for debugging -- nothing calls this, but it's useful to invoke from gdb. */
+void
+show_job_list (void)
+{
+ struct screenhack_job *job;
+ fprintf(stderr, "%s: job list:\n", blurb());
+ for (job = jobs; job; job = job->next)
+ fprintf (stderr, " %5ld: (%s) %s\n",
+ (long) job->pid,
+ (job->status == job_running ? "running" :
+ job->status == job_stopped ? "stopped" :
+ job->status == job_killed ? " killed" :
+ job->status == job_dead ? " dead" : " ???"),
+ job->name);
+ fprintf (stderr, "\n");
+}
+
+
+static void clean_job_list (void);
+
+static struct screenhack_job *
+make_job (pid_t pid, const char *cmd)
+{
+ struct screenhack_job *job = (struct screenhack_job *) malloc (sizeof(*job));
+
+ 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 != ':') {
+ if (*in == '=') got_eq = 1;
+ *out++ = *in++; /* snarf first token */
+ }
+
+ if (got_eq) /* if the first token was FOO=bar */
+ { /* then get the next token instead. */
+ got_eq = 0;
+ out = name;
+ first = 0;
+ goto AGAIN;
+ }
+
+ while (isspace(*in)) in++; /* skip whitespace */
+ *out = 0;
+
+ job->name = strdup(name);
+ job->pid = pid;
+ job->status = job_running;
+ job->next = jobs;
+ jobs = job;
+
+ return jobs;
+}
+
static void
-await_child_death (killed)
- Bool killed;
+free_job (struct screenhack_job *job)
{
- Bool suspended_p = False;
- int status;
- pid_t kid;
- killing = 1;
- if (! pid)
+ if (!job)
return;
-
- do
+ else if (job == jobs)
+ jobs = jobs->next;
+ else
{
- kid = waitpid (pid, &status, WUNTRACED);
+ struct screenhack_job *job2, *prev;
+ for (prev = 0, job2 = jobs;
+ job2;
+ prev = job2, job2 = job2->next)
+ if (job2 == job)
+ {
+ prev->next = job->next;
+ break;
+ }
}
- while (kid == -1 && errno == EINTR);
+ free(job->name);
+ free(job);
+}
- if (kid == pid)
+
+/* Cleans out dead jobs from the jobs list -- this must only be called
+ from the main thread, not from a signal handler.
+ */
+static void
+clean_job_list (void)
+{
+ struct screenhack_job *job, *prev, *next;
+ for (prev = 0, job = jobs, next = (job ? job->next : 0);
+ job;
+ prev = job, job = next, next = (job ? job->next : 0))
{
- if (WIFEXITED (status))
- {
- int exit_status = WEXITSTATUS (status);
- if (exit_status & 0x80)
- exit_status |= ~0xFF;
- /* One might assume that exiting with non-0 means something went
- wrong. But that loser xswarm exits with the code that it was
- killed with, so it *always* exits abnormally. Treat abnormal
- exits as "normal" (don't mention them) if we've just killed
- the subprocess. But mention them if they happen on their own.
- */
- if (exit_status != 0 && (verbose_p || (! killed)))
- fprintf (stderr,
- "%s: %schild pid %d (%s) exited abnormally (code %d).\n",
- progname, (verbose_p ? "## " : ""),
- pid, current_hack_name (), exit_status);
- else if (verbose_p)
- printf ("%s: child pid %d (%s) exited normally.\n",
- progname, pid, current_hack_name ());
- }
- else if (WIFSIGNALED (status))
- {
- if (!killed || WTERMSIG (status) != SIGTERM)
- fprintf (stderr,
- "%s: %schild pid %d (%s) terminated with signal %d!\n",
- progname, (verbose_p ? "## " : ""),
- pid, current_hack_name (), WTERMSIG (status));
- else if (verbose_p)
- printf ("%s: child pid %d (%s) terminated with SIGTERM.\n",
- progname, pid, current_hack_name ());
- }
- else if (suspending)
+ if (job->status == job_dead)
{
- suspended_p = True;
- suspending = False; /* complain if it happens twice */
+ if (prev)
+ prev->next = next;
+ free_job (job);
+ job = prev;
}
- else if (WIFSTOPPED (status))
+ }
+}
+
+
+static struct screenhack_job *
+find_job (pid_t pid)
+{
+ struct screenhack_job *job;
+ for (job = jobs; job; job = job->next)
+ if (job->pid == pid)
+ return job;
+ return 0;
+}
+
+static void await_dying_children (saver_info *si);
+#ifndef VMS
+static void describe_dead_child (saver_info *, pid_t, int wait_status);
+#endif
+
+
+/* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
+ Don't alter this directly -- use block_sigchld() / unblock_sigchld().
+ */
+static int block_sigchld_handler = 0;
+
+
+static void
+block_sigchld (void)
+{
+#ifdef HAVE_SIGACTION
+ sigset_t child_set;
+ sigemptyset (&child_set);
+ sigaddset (&child_set, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &child_set, 0);
+#endif /* HAVE_SIGACTION */
+
+ block_sigchld_handler++;
+}
+
+static void
+unblock_sigchld (void)
+{
+#ifdef HAVE_SIGACTION
+ sigset_t child_set;
+ sigemptyset(&child_set);
+ sigaddset(&child_set, SIGCHLD);
+ sigprocmask(SIG_UNBLOCK, &child_set, 0);
+#endif /* HAVE_SIGACTION */
+
+ block_sigchld_handler--;
+}
+
+static int
+kill_job (saver_info *si, pid_t pid, int signal)
+{
+ saver_preferences *p = &si->prefs;
+ struct screenhack_job *job;
+ int status = -1;
+
+ clean_job_list();
+
+ if (block_sigchld_handler)
+ /* This function should not be called from the signal handler. */
+ abort();
+
+ block_sigchld(); /* we control the horizontal... */
+
+ job = find_job (pid);
+ if (!job ||
+ !job->pid ||
+ job->status == job_killed)
+ {
+ if (p->verbose_p)
+ fprintf (stderr, "%s: no child %ld to signal!\n",
+ blurb(), (long) pid);
+ goto DONE;
+ }
+
+ switch (signal) {
+ case SIGTERM: job->status = job_killed; break;
+#ifdef SIGSTOP
+ /* #### there must be a way to do this on VMS... */
+ case SIGSTOP: job->status = job_stopped; break;
+ case SIGCONT: job->status = job_running; break;
+#endif /* SIGSTOP */
+ 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 */
+
+ 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);
+ else
{
- suspended_p = True;
- fprintf (stderr, "%s: %schild pid %d (%s) stopped with signal %d!\n",
- progname, (verbose_p ? "## " : ""), pid,
- current_hack_name (), WSTOPSIG (status));
+ char buf [1024];
+ sprintf (buf, "%s: couldn't kill child process %lu (%s)",
+ blurb(), job->pid, job->name);
+ perror (buf);
}
- else
- fprintf (stderr, "%s: %schild pid %d (%s) died in a mysterious way!",
- progname, (verbose_p ? "## " : ""), pid, current_hack_name());
}
- else if (kid <= 0)
- fprintf (stderr, "%s: %swaitpid(%d, ...) says there are no kids? (%d)\n",
- progname, (verbose_p ? "## " : ""), pid, kid);
- else
- fprintf (stderr, "%s: %swaitpid(%d, ...) says proc %d died, not %d?\n",
- progname, (verbose_p ? "## " : ""), pid, kid, pid);
- killing = 0;
- if (suspended_p != True)
- pid = 0;
+
+ await_dying_children (si);
+
+ DONE:
+ unblock_sigchld();
+ if (block_sigchld_handler < 0)
+ abort();
+
+ clean_job_list();
+ return status;
}
-static char *
-current_hack_name ()
+
+#ifdef SIGCHLD
+static RETSIGTYPE
+sigchld_handler (int sig)
{
- static char chn [1024];
- char *hack = (demo_mode_p ? demo_hack : screenhacks [current_hack]);
- int i;
- for (i = 0; hack [i] != 0 && hack [i] != ' ' && hack [i] != '\t'; i++)
- chn [i] = hack [i];
- chn [i] = 0;
- return chn;
+ saver_info *si = global_si_kludge; /* I hate C so much... */
+
+ if (si->prefs.debug_p)
+ fprintf(stderr, "%s: got SIGCHLD%s\n", blurb(),
+ (block_sigchld_handler ? " (blocked)" : ""));
+
+ if (block_sigchld_handler < 0)
+ abort();
+ else if (block_sigchld_handler == 0)
+ {
+ block_sigchld();
+ await_dying_children (si);
+ unblock_sigchld();
+ }
+
+ init_sigchld();
}
+#endif /* SIGCHLD */
-#ifdef SIGCHLD
+
+#ifndef VMS
static void
-sigchld_handler (sig)
- int sig;
+await_dying_children (saver_info *si)
{
- if (killing)
- return;
- if (! pid)
- abort ();
- await_child_death (False);
+ while (1)
+ {
+ int wait_status = 0;
+ pid_t kid;
+
+ errno = 0;
+ kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
+
+ 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);
+ }
+
+ /* 0 means no more children to reap.
+ -1 means error -- except "interrupted system call" isn't a "real"
+ error, so if we get that, we should just try again. */
+ if (kid == 0 ||
+ (kid < 0 && errno != EINTR))
+ break;
+
+ describe_dead_child (si, kid, wait_status);
+ }
}
-#endif
+
+
+static void
+describe_dead_child (saver_info *si, pid_t kid, int wait_status)
+{
+ int i;
+ saver_preferences *p = &si->prefs;
+ struct screenhack_job *job = find_job (kid);
+ const char *name = job ? job->name : "<unknown>";
+
+ if (WIFEXITED (wait_status))
+ {
+ int exit_status = WEXITSTATUS (wait_status);
+
+ /* Treat exit code as a signed 8-bit quantity. */
+ if (exit_status & 0x80) exit_status |= ~0xFF;
+
+ /* One might assume that exiting with non-0 means something went wrong.
+ But that loser xswarm exits with the code that it was killed with, so
+ it *always* exits abnormally. Treat abnormal exits as "normal" (don't
+ mention them) if we've just killed the subprocess. But mention them
+ if they happen on their own.
+ */
+ 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);
+ else if (p->verbose_p)
+ fprintf (stderr, "%s: child pid %lu (%s) exited normally.\n",
+ blurb(), (unsigned long) kid, name);
+
+ if (job)
+ job->status = job_dead;
+ }
+ else if (WIFSIGNALED (wait_status))
+ {
+ if (p->verbose_p ||
+ !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)));
+
+ 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)));
+
+ if (job)
+ job->status = job_stopped;
+ }
+ else
+ {
+ fprintf (stderr, "%s: child pid %lu (%s) died in a mysterious way!",
+ blurb(), (unsigned long) kid, name);
+ if (job)
+ job->status = job_dead;
+ }
+
+ /* Clear out the pid so that screenhack_running_p() knows it's dead.
+ */
+ if (!job || job->status == job_dead)
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ if (kid == ssi->pid)
+ ssi->pid = 0;
+ }
+}
+
+#else /* VMS */
+static void await_dying_children (saver_info *si) { return; }
+#endif /* VMS */
void
-init_sigchld ()
+init_sigchld (void)
{
#ifdef SIGCHLD
- if (((int) signal (SIGCHLD, sigchld_handler)) == -1)
+
+# ifdef HAVE_SIGACTION /* Thanks to Tom Kelly <tom@ancilla.toronto.on.ca> */
+
+ static Bool sigchld_initialized_p = 0;
+ if (!sigchld_initialized_p)
+ {
+ struct sigaction action, old;
+
+ action.sa_handler = sigchld_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+
+ if (sigaction(SIGCHLD, &action, &old) < 0)
+ {
+ char buf [255];
+ sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
+ perror (buf);
+ }
+ sigchld_initialized_p = True;
+ }
+
+# else /* !HAVE_SIGACTION */
+
+ if (((long) signal (SIGCHLD, sigchld_handler)) == -1L)
{
char buf [255];
- sprintf (buf, "%s: %scouldn't catch SIGCHLD", progname,
- (verbose_p ? "## " : ""));
+ sprintf (buf, "%s: couldn't catch SIGCHLD", blurb());
perror (buf);
}
-#endif
+# endif /* !HAVE_SIGACTION */
+#endif /* SIGCHLD */
}
-extern void raise_window P((Bool inhibit_fade, Bool between_hacks_p));
-void
-spawn_screenhack (first_time_p)
- Bool first_time_p;
+\f
+
+static Bool
+select_visual_of_hack (saver_screen_info *ssi, screenhack *hack)
{
- raise_window (first_time_p, True);
- XFlush (dpy);
+ saver_info *si = ssi->global;
+ saver_preferences *p = &si->prefs;
+ Bool selected;
+
+ if (hack->visual && *hack->visual)
+ selected = select_visual(ssi, hack->visual);
+ else
+ selected = select_visual(ssi, 0);
- if (screenhacks_count || demo_mode_p)
+ 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;
+}
+
+
+static void
+spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
+{
+ saver_info *si = ssi->global;
+ saver_preferences *p = &si->prefs;
+ raise_window (si, first_time_p, True, False);
+ XFlush (si->dpy);
+
+ if (p->screenhacks_count)
{
- char *hack;
+ screenhack *hack;
pid_t forked;
char buf [255];
int new_hack;
- if (demo_mode_p)
+ 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;
+
+ else if (si->selection_mode == -1)
+ /* 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);
+
+ else if (si->selection_mode > 0)
+ /* Select a specific hack, by number. No negotiation. */
{
- hack = demo_hack;
+ new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
+ force = True;
}
else
{
- if (screenhacks_count == 1)
- new_hack = 0;
- else if (next_mode_p == 1)
- new_hack = (current_hack + 1) % screenhacks_count,
- next_mode_p = 0;
- else if (next_mode_p == 2)
+ /* 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 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))
{
- new_hack = ((current_hack + screenhacks_count - 1)
- % screenhacks_count);
- next_mode_p = 0;
+ /* 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
- while ((new_hack = random () % screenhacks_count) == current_hack)
- ;
- current_hack = new_hack;
- hack = screenhacks[current_hack];
+ goto AGAIN;
}
- switch (forked = fork ())
+ /* Turn off "next" and "prev" modes now, but "demo" mode is only
+ turned off by explicit action.
+ */
+ if (si->selection_mode < 0)
+ si->selection_mode = 0;
+
+ switch ((int) (forked = fork ()))
{
case -1:
- sprintf (buf, "%s: %scouldn't fork",
- progname, (verbose_p ? "## " : ""));
+ sprintf (buf, "%s: couldn't fork", blurb());
perror (buf);
- restore_real_vroot ();
- exit (1);
+ restore_real_vroot (si);
+ saver_exit (si, 1, 0);
+
case 0:
- exec_screenhack (hack); /* this does not return */
+ 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();
break;
+
default:
- pid = forked;
+ ssi->pid = forked;
+ (void) make_job (forked, hack->command);
break;
}
}
}
+
void
-kill_screenhack ()
+spawn_screenhack (saver_info *si, Bool first_time_p)
{
- killing = 1;
- if (! pid)
- return;
- if (kill (pid, SIGTERM) < 0)
+ if (monitor_powered_on_p (si))
{
- if (errno == ESRCH)
- {
- /* Sometimes we don't get a SIGCHLD at all! WTF?
- It's a race condition. It looks to me like what's happening is
- something like: a subprocess dies of natural causes. There is a
- small window between when the process dies and when the SIGCHLD
- is (would have been) delivered. If we happen to try to kill()
- the process during that time, the kill() fails, because the
- process is already dead. But! no SIGCHLD is delivered (perhaps
- because the failed kill() has reset some state in the kernel?)
- Anyway, if kill() says "No such process", then we have to wait()
- for it anyway, because the process has already become a zombie.
- I love Unix.
- */
- await_child_death (False);
- }
- else
- {
- char buf [255];
- sprintf (buf, "%s: %scouldn't kill child process %d", progname,
- (verbose_p ? "## " : ""), pid);
- perror (buf);
- }
+ int i;
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ spawn_screenhack_1 (ssi, first_time_p);
+ }
}
- else
+ 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 */
+}
+
+
+void
+kill_screenhack (saver_info *si)
+{
+ int i;
+ for (i = 0; i < si->nscreens; i++)
{
- if (verbose_p)
- printf ("%s: killing pid %d.\n", progname, pid);
- await_child_death (True);
+ saver_screen_info *ssi = &si->screens[i];
+ if (ssi->pid)
+ kill_job (si, ssi->pid, SIGTERM);
+ ssi->pid = 0;
}
}
void
-suspend_screenhack (suspend_p)
- Bool suspend_p;
+suspend_screenhack (saver_info *si, Bool suspend_p)
{
-
- suspending = suspend_p;
- if (! pid)
- ;
- else if (kill (pid, (suspend_p ? SIGSTOP : SIGCONT)) < 0)
+#ifdef SIGSTOP /* older VMS doesn't have it... */
+ int i;
+ for (i = 0; i < si->nscreens; i++)
{
- char buf [255];
- sprintf (buf, "%s: %scouldn't %s child process %d", progname,
- (verbose_p ? "## " : ""),
- (suspend_p ? "suspend" : "resume"),
- pid);
- perror (buf);
+ saver_screen_info *ssi = &si->screens[i];
+ if (ssi->pid)
+ kill_job (si, ssi->pid, (suspend_p ? SIGSTOP : SIGCONT));
}
- else if (verbose_p)
- printf ("%s: %s pid %d.\n", progname,
- (suspend_p ? "suspending" : "resuming"), pid);
+#endif /* SIGSTOP */
}
-\f
-/* Restarting the xscreensaver process from scratch. */
-
-static char **saved_argv;
+/* Called when we're exiting abnormally, to kill off the subproc. */
void
-save_argv (argc, argv)
- int argc;
- char **argv;
+emergency_kill_subproc (saver_info *si)
{
- saved_argv = (char **) malloc ((argc + 2) * sizeof (char *));
- saved_argv [argc] = 0;
- while (argc--)
+ int i;
+#ifdef SIGCHLD
+ signal (SIGCHLD, SIG_IGN);
+#endif /* SIGCHLD */
+
+ for (i = 0; i < si->nscreens; i++)
{
- int i = strlen (argv [argc]) + 1;
- saved_argv [argc] = (char *) malloc (i);
- memcpy (saved_argv [argc], argv [argc], i);
+ saver_screen_info *ssi = &si->screens[i];
+ if (ssi->pid)
+ {
+ kill_job (si, ssi->pid, SIGTERM);
+ ssi->pid = 0;
+ }
}
}
-void
-restart_process ()
+Bool
+screenhack_running_p (saver_info *si)
{
- XCloseDisplay (dpy);
- fflush (stdout);
- fflush (stderr);
- execvp (saved_argv [0], saved_argv);
- fprintf (stderr, "%s: %scould not restart process: %s (%d)\n",
- progname, (verbose_p ? "## " : ""),
- (errno == E2BIG ? "arglist too big" :
- errno == EACCES ? "could not execute" :
- errno == EFAULT ? "memory fault" :
- errno == EIO ? "I/O error" :
- errno == ENAMETOOLONG ? "name too long" :
- errno == ELOOP ? "too many symbolic links" :
- errno == ENOENT ? "file no longer exists" :
- errno == ENOTDIR ? "directory no longer exists" :
- errno == ENOEXEC ? "bad executable file" :
- errno == ENOMEM ? "out of memory" :
- "execvp() returned unknown error code"),
- errno);
- exit (1);
+ Bool any_running_p = False;
+ int i;
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ if (ssi->pid) any_running_p = True;
+ }
+ return any_running_p;
}
+\f
+/* Environment variables. */
+
+
+/* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
+ is defined, the xscreensaver daemon will search that directory for hacks.
+ */
void
-demo_mode_restart_process ()
+hack_environment (saver_info *si)
{
- int i;
- for (i = 0; saved_argv [i]; i++);
- /* add the -demo switch; save_argv() left room for this. */
- saved_argv [i] = "-demo";
- saved_argv [i+1] = 0;
- restart_process ();
+#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
+ static const char *def_path = DEFAULT_PATH_PREFIX;
+ if (def_path && *def_path)
+ {
+ 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
-hack_environment ()
+hack_subproc_environment (saver_screen_info *ssi)
{
/* Store $DISPLAY into the environment, so that the $DISPLAY variable that
- the spawned processes inherit is the same as the value of -display passed
- in on our command line (which is not necessarily the same as what our
- $DISPLAY variable is.)
+ the spawned processes inherit is correct. First, it must be on the same
+ host and display as the value of -display passed in on our command line
+ (which is not necessarily the same as what our $DISPLAY variable is.)
+ Second, the screen number in the $DISPLAY passed to the subprocess should
+ 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.
*/
- char *s, buf [2048];
- int i;
- sprintf (buf, "DISPLAY=%s", DisplayString (dpy));
- i = strlen (buf);
- s = (char *) malloc (i+1);
- strncpy (s, buf, i+1);
- if (putenv (s))
+ saver_info *si = ssi->global;
+ const char *odpy = DisplayString (si->dpy);
+ char *ndpy = (char *) malloc(strlen(odpy) + 20);
+ int screen_number;
+ 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 (*s && *s != ':') s++; /* skip to colon */
+ while (*s == ':') s++; /* skip over colons */
+ 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 */
+
+ /* 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 ();
+#endif /* HAVE_PUTENV */
}
\f
-/* Change the uid/gid of the screensaver process, so that it is safe for it
- to run setuid root (which it needs to do on some systems to read the
- encrypted passwords from the passwd file.)
-
- hack_uid() is run before opening the X connection, so that XAuth works.
- hack_uid_warn() is called after the connection is opened and the command
- line arguments are parsed, so that the messages from hack_uid() get
- printed after we know whether we're in `verbose' mode.
- */
+/* GL crap */
-#ifndef NO_SETUID
+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];
-static int hack_uid_errno;
-static char hack_uid_buf [255], *hack_uid_error;
+ char *av[10];
+ int ac = 0;
-void
-hack_uid ()
-{
- /* If we've been run as setuid or setgid to someone else (most likely root)
- turn off the extra permissions so that random user-specified programs
- don't get special privileges. (On some systems it might be necessary
- to install this as setuid root in order to read the passwd file to
- implement lock-mode...)
- */
- setgid (getgid ());
- setuid (getuid ());
-
- hack_uid_errno = 0;
- hack_uid_error = 0;
-
- /* If we're being run as root (as from xdm) then switch the user id
- to something safe. */
- if (getuid () == 0)
+ av[ac++] = "xscreensaver-gl-helper";
+ av[ac] = 0;
+
+ if (pipe (fds))
{
- struct passwd *p;
- /* Locking can't work when running as root, because we have no way of
- knowing what the user id of the logged in user is (so we don't know
- whose password to prompt for.)
- */
- locking_disabled_p = True;
- nolock_reason = "running as root";
- p = getpwnam ("nobody");
- if (! p) p = getpwnam ("daemon");
- if (! p) p = getpwnam ("bin");
- if (! p) p = getpwnam ("sys");
- if (! p)
- {
- hack_uid_error = "couldn't find safe uid; running as root.";
- hack_uid_errno = -1;
- }
- else
- {
- struct group *g = getgrgid (p->pw_gid);
- hack_uid_error = hack_uid_buf;
- sprintf (hack_uid_error, "changing uid/gid to %s/%s (%d/%d).",
- p->pw_name, (g ? g->gr_name : "???"), p->pw_uid, p->pw_gid);
-
- /* Change the gid to be a safe one. If we can't do that, then
- print a warning. We change the gid before the uid so that we
- change the gid while still root. */
- if (setgid (p->pw_gid) != 0)
- {
- hack_uid_errno = errno;
- sprintf (hack_uid_error, "couldn't set gid to %s (%d)",
- (g ? g->gr_name : "???"), p->pw_gid);
- }
+ perror ("error creating pipe:");
+ return 0;
+ }
- /* Now change the uid to be a safe one. */
- if (setuid (p->pw_uid) != 0)
- {
- hack_uid_errno = errno;
- sprintf (hack_uid_error, "couldn't set uid to %s (%d)",
- p->pw_name, p->pw_uid);
- }
- }
+ 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;
+ }
+ }
}
-#ifndef NO_LOCKING
- else /* disable locking if already being run as "someone else" */
- {
- struct passwd *p = getpwuid (getuid ());
- if (!p ||
- !strcmp (p->pw_name, "root") ||
- !strcmp (p->pw_name, "nobody") ||
- !strcmp (p->pw_name, "daemon") ||
- !strcmp (p->pw_name, "bin") ||
- !strcmp (p->pw_name, "sys"))
- {
- locking_disabled_p = True;
- nolock_reason = hack_uid_buf;
- sprintf (nolock_reason, "running as %s", p->pw_name);
- }
- }
-#endif /* NO_LOCKING */
+
+ abort();
}
+
+\f
+/* Restarting the xscreensaver process from scratch. */
+
+static char **saved_argv;
+
void
-hack_uid_warn ()
+save_argv (int argc, char **argv)
{
- if (! hack_uid_error)
- ;
- else if (hack_uid_errno == 0)
+ saved_argv = (char **) calloc (argc+2, sizeof (char *));
+ saved_argv [argc] = 0;
+ while (argc--)
{
- if (verbose_p)
- printf ("%s: %s\n", progname, hack_uid_error);
+ int i = strlen (argv [argc]) + 1;
+ saved_argv [argc] = (char *) malloc (i);
+ memcpy (saved_argv [argc], argv [argc], i);
}
- else
+}
+
+
+/* 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)
{
- char buf [255];
- sprintf (buf, "%s: %s%s", progname, (verbose_p ? "## " : ""),
- hack_uid_error);
- if (hack_uid_errno == -1)
- fprintf (stderr, "%s\n", buf);
- else
- {
- errno = hack_uid_errno;
- perror (buf);
- }
+ 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");
}
-}
+ describe_uids (si, real_stderr);
+ fprintf (real_stderr, "\n");
-#endif /* !NO_SETUID */
+ 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);
+}