X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fsubprocs.c;h=01329d0effb0426a26b27ba3042e5e8e978510c6;hb=0d6b320def9180cf907ceaed56b23a972a11b757;hp=527f379d4b5278ff1b3df095a7d2539e6a93bcd8;hpb=6cee540bdbb571485cd5e519f89f389faebd0495;p=xscreensaver diff --git a/driver/subprocs.c b/driver/subprocs.c index 527f379d..01329d0e 100644 --- a/driver/subprocs.c +++ b/driver/subprocs.c @@ -1,5 +1,5 @@ /* subprocs.c --- choosing, spawning, and killing screenhacks. - * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski + * xscreensaver, Copyright (c) 1991-2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -71,6 +71,59 @@ extern int kill (pid_t, int); /* signal() is in sys/signal.h... */ extern saver_info *global_si_kludge; /* I hate C so much... */ +/* 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; + + if (num == 0) + return "0"; + + if (num < 0) + { + negative_p = True; + num = -num; + } + + while ((num > 0) && (num_digits < sizeof(string - 1))) + { + int digit; + digit = (int) num % 10; + num_digits++; + string[sizeof(string) - 1 - num_digits] = digit + '0'; + num /= 10; + } + + if (negative_p) + { + num_digits++; + string[sizeof(string) - 1 - num_digits] = '-'; + } + + 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)); +} + +static int +write_long (int fd, long n) +{ + const char *str = no_malloc_number_to_string (n); + return write_string (fd, str); +} + + /* 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. @@ -294,17 +347,28 @@ static void describe_dead_child (saver_info *, pid_t, int wait_status); 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 @@ -314,6 +378,7 @@ unblock_sigchld (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 */ @@ -400,8 +465,19 @@ sigchld_handler (int sig) 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(); @@ -418,6 +494,7 @@ sigchld_handler (int sig) #ifndef VMS + static void await_dying_children (saver_info *si) { @@ -432,11 +509,29 @@ 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. @@ -476,12 +571,38 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status) if (!job || (exit_status != 0 && (p->verbose_p || job->status != job_killed))) - fprintf (stderr, - "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n", - blurb(), screen_no, (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: %d: child pid %lu (%s) exited normally.\n", - blurb(), screen_no, (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; @@ -492,9 +613,23 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status) !job || job->status != job_killed || WTERMSIG (wait_status) != SIGTERM) - fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n", - blurb(), screen_no, (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; @@ -502,17 +637,41 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status) 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; } @@ -625,7 +784,8 @@ print_path_error (const char *program) fprintf (stderr, "\n"); *path = 0; # if defined(HAVE_GETCWD) - getcwd (path, sizeof(path)); + if (! getcwd (path, sizeof(path))) + *path = 0; # elif defined(HAVE_GETWD) getwd (path); # endif @@ -643,6 +803,60 @@ print_path_error (const char *program) } +/* 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) { @@ -656,7 +870,7 @@ 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; @@ -703,6 +917,14 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p) { 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.) */ @@ -757,38 +979,19 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p) 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; } } @@ -922,10 +1125,15 @@ hack_subproc_environment (saver_screen_info *ssi) 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="); @@ -937,14 +1145,23 @@ hack_subproc_environment (saver_screen_info *ssi) 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 */ } @@ -1021,7 +1238,8 @@ get_best_gl_visual (saver_screen_info *ssi) 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. */