http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.00.tar.gz
[xscreensaver] / driver / subprocs.c
index fd4cef834fd13d4c56e88bca266d20a63ad1c7b3..e97fc928944ba66a5f30de387d89c2661ef24aae 100644 (file)
@@ -1,6 +1,5 @@
 /* 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-2001 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)
+#if (defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)) || \
+     defined(HAVE_SETRLIMIT)
 # include <sys/resource.h>     /* for setpriority() and PRIO_PROCESS */
+                               /* and also setrlimit() and RLIMIT_AS */
 #endif
 
 #ifdef VMS
@@ -106,6 +107,64 @@ nice_subproc (int nice_level)
 }
 
 
+/* 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.
+ */
+#if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS)
+# define RLIMIT_AS RLIMIT_VMEM
+#endif
+
+static void
+limit_subproc_memory (int address_space_limit, Bool verbose_p)
+{
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_AS)
+  struct rlimit r;
+
+  if (address_space_limit < 10 * 1024)  /* let's not be crazy */
+    return;
+
+  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 (setrlimit (RLIMIT_AS, &r) != 0)
+    {
+      char buf [512];
+      sprintf (buf, "%s: setrlimit(RLIMIT_AS, {%lu, %lu}) failed",
+               blurb(), r.rlim_cur, r.rlim_max);
+      perror (buf);
+      return;
+    }
+
+  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);
+
+      fprintf (stderr, "%s: limited pid %lu address space to %s.\n",
+               blurb(), (unsigned long) getpid (), buf);
+    }
+
+#endif /* HAVE_SETRLIMIT && RLIMIT_AS */
+}
+
+
+
 #ifndef VMS
 
 static void
@@ -246,7 +305,7 @@ exec_vms_command (const char *command)
 
 
 static void
-exec_screenhack (saver_info *si, const char *command)
+exec_screenhack (saver_screen_info *ssi, const char *command)
 {
   /* I don't believe what a sorry excuse for an operating system UNIX is!
 
@@ -282,6 +341,7 @@ exec_screenhack (saver_info *si, const char *command)
      what I've seen in Emacs, dealing with process groups isn't especially
      portable.)
    */
+  saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
 
 #ifndef VMS
@@ -300,8 +360,9 @@ exec_screenhack (saver_info *si, const char *command)
     }
 
   if (p->verbose_p)
-    fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
-            blurb(), command, (unsigned long) getpid (),
+    fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu%s.\n",
+            blurb(), ssi->number, command,
+             (unsigned long) getpid (),
             (hairy_p ? " (via shell)" : ""));
 
   if (hairy_p)
@@ -314,8 +375,8 @@ exec_screenhack (saver_info *si, const char *command)
 
 #else /* VMS */
   if (p->verbose_p)
-    fprintf (stderr, "%s: spawning \"%s\" in pid %lu.\n",
-            blurb(), command, getpid());
+    fprintf (stderr, "%s: %d: spawning \"%s\" in pid %lu.\n",
+            blurb(), ssi->number, command, getpid());
   exec_vms_command (command);
 #endif /* VMS */
 
@@ -340,6 +401,7 @@ enum job_status {
 struct screenhack_job {
   char *name;
   pid_t pid;
+  int screen;
   enum job_status status;
   struct screenhack_job *next;
 };
@@ -353,8 +415,9 @@ 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",
+    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" :
@@ -367,7 +430,7 @@ show_job_list (void)
 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));
 
@@ -399,6 +462,7 @@ make_job (pid_t pid, const char *cmd)
 
   job->name = strdup(name);
   job->pid = pid;
+  job->screen = screen;
   job->status = job_running;
   job->next = jobs;
   jobs = job;
@@ -475,7 +539,7 @@ static void describe_dead_child (saver_info *, pid_t, int wait_status);
 static int block_sigchld_handler = 0;
 
 
-static void
+void
 block_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
@@ -488,7 +552,7 @@ block_sigchld (void)
   block_sigchld_handler++;
 }
 
-static void
+void
 unblock_sigchld (void)
 {
 #ifdef HAVE_SIGACTION
@@ -537,31 +601,27 @@ kill_job (saver_info *si, pid_t pid, int signal)
   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, 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, job->pid, job->name);
          perror (buf);
        }
     }
@@ -643,6 +703,7 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
   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))
     {
@@ -661,11 +722,11 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
          (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);
+                "%s: %d: child pid %lu (%s) exited abnormally (code %d).\n",
+                blurb(), screen_no, (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);
+       fprintf (stderr, "%s: %d: child pid %lu (%s) exited normally.\n",
+                blurb(), screen_no, (unsigned long) kid, name);
 
       if (job)
        job->status = job_dead;
@@ -676,8 +737,8 @@ describe_dead_child (saver_info *si, pid_t kid, int wait_status)
          !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,
+       fprintf (stderr, "%s: %d: child pid %lu (%s) terminated with %s.\n",
+                blurb(), screen_no, (unsigned long) kid, name,
                 signal_name (WTERMSIG(wait_status)));
 
       if (job)
@@ -802,26 +863,43 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
 
     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 (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  /* (p->mode == RANDOM_HACKS) */
        {
          /* Select a random hack (but not the one we just ran.) */
          while ((new_hack = random () % p->screenhacks_count)
@@ -829,6 +907,14 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
            ;
        }
 
+      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];
 
@@ -878,14 +964,15 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
        case 0:
          close (ConnectionNumber (si->dpy));   /* close display fd */
          nice_subproc (p->nice_inferior);      /* change process priority */
+         limit_subproc_memory (p->inferior_memory_limit, p->verbose_p);
          hack_subproc_environment (ssi);       /* set $DISPLAY */
-         exec_screenhack (si, hack->command);  /* this does not return */
+         exec_screenhack (ssi, hack->command); /* this does not return */
          abort();
          break;
 
        default:
          ssi->pid = forked;
-         (void) make_job (forked, hack->command);
+         (void) make_job (forked, ssi->number, hack->command);
          break;
        }
     }
@@ -1018,13 +1105,8 @@ hack_subproc_environment (saver_screen_info *ssi)
   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);
@@ -1034,7 +1116,7 @@ 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", screen_number);             /* put on screen number */
+  sprintf(s, "%d", ssi->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. */
@@ -1137,9 +1219,10 @@ get_best_gl_visual (saver_screen_info *ssi)
           {
             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;
           }
       }