"Fossies" - the Fresh Open Source Software Archive

Member "xscreensaver-6.01/driver/xscreensaver.c" (28 May 2021, 80330 Bytes) of package /linux/misc/xscreensaver-6.01.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "xscreensaver.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.00_vs_6.01.

    1 /* xscreensaver, Copyright © 1991-2021 Jamie Zawinski <jwz@jwz.org>
    2  *
    3  * Permission to use, copy, modify, distribute, and sell this software and its
    4  * documentation for any purpose is hereby granted without fee, provided that
    5  * the above copyright notice appear in all copies and that both that
    6  * copyright notice and this permission notice appear in supporting
    7  * documentation.  No representations are made about the suitability of this
    8  * software for any purpose.  It is provided "as is" without express or 
    9  * implied warranty.
   10  *
   11  * XScreenSaver Daemon, version 6.
   12  *
   13  * Having started its life in 1991, XScreenSaver acquired a lot of optional
   14  * features to allow it to detect user activity, lock the screen and
   15  * authenticate on systems as varied as VMS, SunOS, HPUX, SGI, and of course
   16  * that young upstart, Linux.  In such a heterogeneous environment, many
   17  * features that would have simplified things were not universally available.
   18  * Though I made an effort to follow a principle of minimal library usage in
   19  * the XScreenSaver daemon, as described in my 2004 article "On Toolkits"
   20  * <https://www.jwz.org/xscreensaver/toolkits.html>, there was still quite a
   21  * lot of code in the daemon, more than is strictly necessary when we consider
   22  * only modern, still-maintained operating systems.
   23  *
   24  * This 2021 refactor had one goal: to minimize the amount of code in which a
   25  * crash will cause the screen to unlock.  In service of that goal, the process
   26  * which holds the keyboard an mouse grabbed should:
   27  *
   28  *   -  Be as small as technically possible, for ease of auditing;
   29  *
   30  *   -  Link against as few libraries as possible, to bring in as little
   31  *      third-party code as possible;
   32  *
   33  *   -  Delegate other tasks to helper programs running in other processes,
   34  *      in such a way that if those other processes should crash, the grabs
   35  *      remain intact and the screen remains locked.
   36  *
   37  * In the XScreenSaver 6 design, the division of labor is as follows:
   38  *
   39  *  A)  "xscreensaver" -- This is the main screen saver daemon.  If this
   40  *      process crashes, the screen unlocks, so this is the one where
   41  *      minimizing the attack surface is critical.
   42  *
   43  *      -  It reads events using the XInput extension version 2, which is
   44  *         required.  This means that X11R7 is also required, which was
   45  *         released in 2009, and is ubiquitous as of 2021.
   46  *
   47  *      -  It links against only libX11 and libXi.
   48  *
   49  *      -  It maps no windows and renders no graphics or text.  It only
   50  *         acquires keyboard and mouse grabs, handles timer logic, and
   51  *         launches and manages several sub-processes.
   52  *
   53  *  B)  "xscreensaver-gfx" -- When the time comes for the screen to blank,
   54  *      this process is launched to fade out, black out every monitor on
   55  *      the system, launch graphics demos to render on those blanked screens,
   56  *      and cycle them from time to time.
   57  *
   58  *      -  If this program crashes, the screen does not unlock.  The keyboard
   59  *         and mouse remain grabbed by the "xscreensaver" process, which will
   60  *         re-launch this process as necessary.
   61  *
   62  *      -  If it does crash, the logged in user's desktop may be momentarily
   63  *         visible before it re-launches, but it will be impossible to interact
   64  *         with it.
   65  *
   66  *      -  This process must react to hot-swapping of monitors in order to
   67  *         keep the screen blanked and the desktop obscured, and manages the
   68  *         life cycle of the graphics demos, each running in their own
   69  *         sub-process of "xscreensaver-gfx".
   70  *
   71  *  C)  The graphics demos themselves.  Launched by "xscreensaver-gfx" to run
   72  *      on the windows provided, there is one of these processes for each
   73  *      screen.  These can use X11 or OpenGL, and have no impact on security;
   74  *      if it breaks, you can keep both pieces.
   75  *
   76  *  D)  "xscreensaver-auth" -- When the time comes to prompt the user for their
   77  *      password to unlock the screen, the "xscreensaver" daemon launches this
   78  *      process.  If it exits with a "success" error code, the screen unlocks,
   79  *      otherwise it does not.  This means that if it crashes, the screen
   80  *      remains locked.
   81  *
   82  *      -  It turns out that programs using the XInput 2 extension are able to
   83  *         snoop the keyboard even when the keyboard is grabbed by another
   84  *         process!  So that's how this program reads user input, even while
   85  *         the "xscreensaver" process has the keyboard grabbed.
   86  *
   87  *      -  This program runs PAM, or whatever other authorization mechanisms
   88  *         are configured.  On some systems, it may need to be setuid in order
   89  *         to read /etc/shadow, meaning it must disavow privileges after
   90  *         initialization but before connecting to the X server.
   91  *
   92  *      -  It gets to use libXft, so the fonts don't suck.
   93  *
   94  *      -  In theory, this program could be implemented using any GUI toolkit,
   95  *         and thus take advantage of input methods, on-screen keyboards,
   96  *         screen readers, etc.  Again, this is permissible because this
   97  *         program fails SAFE: if it crashes, the screen remains locked.
   98  *
   99  *  E)  "xscreensaver-systemd" -- This runs in the background and monitors
  100  *      requests on the systemd dbus.  This is how closing the laptop lid
  101  *      causes the screen to lock, and how video players request that blanking
  102  *      be inhibited.  This program invokes "xscreensaver-command" as needed
  103  *      to pass those requests along to "xscreensaver" via ClientMessages.
  104  */
  105 
  106 #ifdef HAVE_CONFIG_H
  107 # include "config.h"
  108 #endif
  109 
  110 #include "version.h"
  111 
  112 #include <stdlib.h>
  113 #ifdef HAVE_UNISTD_H
  114 # include <unistd.h>
  115 #endif
  116 
  117 #include <stdio.h>
  118 #include <ctype.h>
  119 #include <time.h>
  120 #include <sys/time.h>
  121 #include <sys/types.h>
  122 #include <sys/stat.h>
  123 #include <signal.h>
  124 #include <errno.h>
  125 
  126 #include <pwd.h>        /* for getpwuid() */
  127 #include <grp.h>        /* for getgrgid() */
  128 
  129 #ifdef HAVE_UNAME
  130 # include <sys/utsname.h>   /* for uname() */
  131 #endif /* HAVE_UNAME */
  132 
  133 #ifdef HAVE_SYS_WAIT_H
  134 # include <sys/wait.h>      /* for waitpid() and associated macros */
  135 #endif
  136 
  137 #include <X11/Xlib.h>
  138 #include <X11/Xutil.h>
  139 #include <X11/Xatom.h>
  140 #include <X11/cursorfont.h>
  141 #include <X11/Xos.h>
  142 #include <X11/extensions/XInput2.h>
  143 
  144 #include "xmu.h"
  145 #include "blurb.h"
  146 #include "atoms.h"
  147 #include "clientmsg.h"
  148 #include "xinput.h"
  149 #include "prefs.h"
  150 
  151 
  152 #undef countof
  153 #define countof(x) (sizeof((x))/sizeof((*x)))
  154 
  155 #undef ABS
  156 #define ABS(x)((x)<0?-(x):(x))
  157 
  158 #undef MAX
  159 #define MAX(x,y)((x)>(y)?(x):(y))
  160 
  161 
  162 /* Globals used in this file.
  163  */
  164 Bool debug_p = False;
  165 static Bool splash_p = True;
  166 static const char *version_number = 0;
  167 
  168 /* Preferences. */
  169 static Bool lock_p = False;
  170 static Bool locking_disabled_p = False;
  171 static unsigned int blank_timeout = 0;
  172 static unsigned int lock_timeout = 0;
  173 static unsigned int pointer_hysteresis = 0;
  174 
  175 /* Subprocesses. */
  176 #define SAVER_GFX_PROGRAM     "xscreensaver-gfx"
  177 #define SAVER_AUTH_PROGRAM    "xscreensaver-auth"
  178 #define SAVER_SYSTEMD_PROGRAM "xscreensaver-systemd"
  179 static pid_t saver_gfx_pid     = 0;
  180 static pid_t saver_auth_pid    = 0;
  181 static pid_t saver_systemd_pid = 0;
  182 static int sighup_received  = 0;
  183 static int sigterm_received = 0;
  184 static int sigchld_received = 0;
  185 static Bool gfx_stopped_p = False;
  186 
  187 Window daemon_window = 0;
  188 Cursor blank_cursor = None;
  189 Cursor auth_cursor  = None;
  190 
  191 
  192 /* for the --restart command.
  193  */
  194 static char **saved_argv;
  195 
  196 static void
  197 save_argv (int ac, char **av)
  198 {
  199   saved_argv = (char **) calloc (ac+2, sizeof (char *));
  200   saved_argv [ac] = 0;
  201   while (ac--)
  202     {
  203       int i = strlen (av[ac]) + 1;
  204       saved_argv[ac] = (char *) malloc (i);
  205       memcpy (saved_argv[ac], av[ac], i);
  206     }
  207 }
  208 
  209 
  210 static void
  211 kill_all_subprocs (void)
  212 {
  213   if (saver_gfx_pid)
  214     {
  215       if (verbose_p)
  216         fprintf (stderr, "%s: pid %lu: killing " SAVER_GFX_PROGRAM "\n",
  217                  blurb(), (unsigned long) saver_gfx_pid);
  218       kill (saver_gfx_pid, SIGTERM);
  219 
  220       if (gfx_stopped_p)    /* SIGCONT to allow SIGTERM to proceed */
  221         {
  222           if (verbose_p)
  223             fprintf (stderr, "%s: pid %lu: sending " SAVER_GFX_PROGRAM
  224                      " SIGCONT\n",
  225                      blurb(), (unsigned long) saver_gfx_pid);
  226           gfx_stopped_p = False;
  227           kill (-saver_gfx_pid, SIGCONT);  /* send to process group */
  228         }
  229     }
  230 
  231   if (saver_auth_pid)
  232     {
  233       if (verbose_p)
  234         fprintf (stderr, "%s: pid %lu: killing " SAVER_AUTH_PROGRAM "\n",
  235                  blurb(), (unsigned long) saver_auth_pid);
  236       kill (saver_auth_pid, SIGTERM);
  237     }
  238 
  239   if (saver_systemd_pid)
  240     {
  241       if (verbose_p)
  242         fprintf (stderr, "%s: pid %lu: killing " SAVER_SYSTEMD_PROGRAM "\n",
  243                  blurb(), (unsigned long) saver_systemd_pid);
  244       kill (saver_systemd_pid, SIGTERM);
  245     }
  246 }
  247 
  248 
  249 static void
  250 saver_exit (int status)
  251 {
  252   kill_all_subprocs();
  253   exit (status);
  254 }
  255 
  256 
  257 static void
  258 catch_signal (int sig, RETSIGTYPE (*handler) (int))
  259 {
  260 # ifdef HAVE_SIGACTION
  261 
  262   struct sigaction a;
  263   a.sa_handler = handler;
  264   sigemptyset (&a.sa_mask);
  265   a.sa_flags = 0;
  266 
  267   /* On Linux 2.4.9 (at least) we need to tell the kernel to not mask delivery
  268      of this signal from inside its handler, or else when we execvp() the
  269      process again, it starts up with SIGHUP blocked, meaning that killing
  270      it with -HUP only works *once*.  You'd think that execvp() would reset
  271      all the signal masks, but it doesn't.
  272    */
  273 #  if defined(SA_NOMASK)
  274   a.sa_flags |= SA_NOMASK;
  275 #  elif defined(SA_NODEFER)
  276   a.sa_flags |= SA_NODEFER;
  277 #  endif
  278 
  279   if (sigaction (sig, &a, 0) < 0)
  280 # else  /* !HAVE_SIGACTION */
  281   if (((long) signal (sig, handler)) == -1L)
  282 # endif /* !HAVE_SIGACTION */
  283     {
  284       char buf [255];
  285       sprintf (buf, "%s: couldn't catch signal %d", blurb(), sig);
  286       perror (buf);
  287       saver_exit (1);
  288     }
  289 }
  290 
  291 
  292 /* Re-execs the process with the arguments in saved_argv.  Does not return.
  293    Do not call this while the screen is locked: user must unlock first.
  294  */
  295 static void
  296 restart_process (void)
  297 {
  298   kill_all_subprocs();
  299   execvp (saved_argv [0], saved_argv);  /* shouldn't return */
  300   {
  301     char buf [512];
  302     sprintf (buf, "%s: could not restart process", blurb());
  303     perror(buf);
  304     fflush(stderr);
  305     abort();
  306   }
  307 }
  308 
  309 static RETSIGTYPE saver_sighup_handler  (int sig) { sighup_received  = sig; }
  310 static RETSIGTYPE saver_sigchld_handler (int sig) { sigchld_received = sig; }
  311 static RETSIGTYPE saver_sigterm_handler (int sig) { sigterm_received = sig; }
  312 
  313 static void
  314 handle_signals (void)
  315 {
  316   catch_signal (SIGHUP,  saver_sighup_handler);
  317   catch_signal (SIGCHLD, saver_sigchld_handler);
  318   catch_signal (SIGTERM, saver_sigterm_handler);    /* kill     */
  319   catch_signal (SIGINT,  saver_sigterm_handler);    /* shell ^C */
  320   catch_signal (SIGQUIT, saver_sigterm_handler);    /* shell ^| */
  321 }
  322 
  323 
  324 static pid_t
  325 fork_and_exec (Display *dpy, int argc, char **argv)
  326 {
  327   char buf [255];
  328   pid_t forked = fork();
  329   switch ((int) forked) {
  330   case -1:
  331     sprintf (buf, "%s: couldn't fork", blurb());
  332     perror (buf);
  333     break;
  334 
  335   case 0:
  336     close (ConnectionNumber (dpy)); /* close display fd */
  337     execvp (argv[0], argv);     /* shouldn't return. */
  338 
  339     sprintf (buf, "%s: pid %lu: couldn't exec %s", blurb(),
  340              (unsigned long) getpid(), argv[0]);
  341     perror (buf);
  342     exit (1);               /* exits child fork */
  343     break;
  344 
  345   default:              /* parent fork */
  346     if (verbose_p)
  347       {
  348         int i;
  349         fprintf (stderr, "%s: pid %lu: launched",
  350                  blurb(), (unsigned long) forked);
  351         for (i = 0; i < argc; i++)
  352           fprintf (stderr, " %s", argv[i]);
  353         fprintf (stderr, "\n");
  354       }
  355 
  356     /* Put each launched process in its own process group so that
  357        SIGSTOP will affect all of its inferior processes as well.
  358      */
  359     if (setpgid (forked, 0))
  360       {
  361         char buf [255];
  362         sprintf (buf, "%s: setpgid %d", blurb(), forked);
  363         perror (buf);
  364       }
  365     break;
  366   }
  367 
  368   return forked;
  369 }
  370 
  371 
  372 static int respawn_thrashing_count = 0;
  373 
  374 /* Called from the main loop some time after the SIGCHLD signal has fired.
  375    Returns true if:
  376      - the process that died was "xscreensaver-auth", and
  377      - it exited with a "success" status meaning "user is authenticated".
  378  */
  379 static Bool
  380 handle_sigchld (Display *dpy, Bool blanked_p)
  381 {
  382   Bool authenticated_p = False;
  383 
  384   sigchld_received = 0;
  385   if (debug_p)
  386     fprintf (stderr, "%s: SIGCHLD received\n", blurb());
  387 
  388   /* Reap every now-dead inferior without blocking. */
  389   while (1)
  390     {
  391       int wait_status = 0;
  392       pid_t kid = waitpid (-1, &wait_status, WNOHANG | WUNTRACED);
  393       char how[100];
  394 
  395       /* 0 means no more children to reap.
  396          -1 means error -- except "interrupted system call"
  397          isn't a "real" error, so if we get that, try again.
  398        */
  399       if (kid == 0 ||
  400           (kid < 0 && errno != EINTR))
  401         break;
  402 
  403       /* We get SIGCHLD after sending SIGSTOP, but no action is required. */
  404       if (WIFSTOPPED (wait_status))
  405         {
  406           if (verbose_p)
  407             fprintf (stderr, "%s: pid %lu: %s stopped\n", blurb(),
  408                      (unsigned long) kid,
  409                      (kid == saver_gfx_pid     ? SAVER_GFX_PROGRAM :
  410                       kid == saver_auth_pid    ? SAVER_AUTH_PROGRAM :
  411                       kid == saver_systemd_pid ? SAVER_SYSTEMD_PROGRAM :
  412                       "unknown"));
  413           continue;
  414         }
  415 
  416       if (WIFSIGNALED (wait_status))
  417         {
  418           if (WTERMSIG (wait_status) == SIGTERM)
  419             strcpy (how, "with SIGTERM");
  420           else
  421             sprintf (how, "with signal %d", WTERMSIG (wait_status));
  422         }
  423       else if (WIFEXITED (wait_status))
  424         {
  425           int exit_status = WEXITSTATUS (wait_status);
  426           /* Treat exit code as a signed 8-bit quantity. */
  427           if (exit_status & 0x80) exit_status |= ~0xFF;
  428           if (exit_status)
  429             sprintf (how, "with status %d", exit_status);
  430           else
  431             strcpy (how, "normally");
  432         }
  433       else
  434         sprintf (how, "for unknown reason %d", wait_status);
  435 
  436       if (kid == saver_gfx_pid)
  437         {
  438           saver_gfx_pid = 0;
  439           gfx_stopped_p = False;
  440           if (blanked_p)
  441             {
  442               if (respawn_thrashing_count > 5)
  443                 {
  444                   /* If we have tried to re-launch this pid N times in a row
  445                      without unblanking, give up instead of forking it in a
  446                      tight loop.  Screen will remain grabbed, but desktop will
  447                      be visible.
  448                    */
  449                   fprintf (stderr, "%s: pid %lu: " SAVER_GFX_PROGRAM
  450                            " won't re-launch!\n",
  451                            blurb(), (unsigned long) kid);
  452                 }
  453               else
  454                 {
  455                   char *av[10];
  456                   int ac = 0;
  457                   av[ac++] = SAVER_GFX_PROGRAM;
  458                   av[ac++] = "--emergency";
  459                   if (verbose_p)     av[ac++] = "--verbose";
  460                   if (verbose_p > 1) av[ac++] = "--verbose";
  461                   if (verbose_p > 2) av[ac++] = "--verbose";
  462                   if (debug_p)       av[ac++] = "--debug";
  463                   av[ac] = 0;
  464                   fprintf (stderr, "%s: pid %lu: " SAVER_GFX_PROGRAM
  465                            " exited unexpectedly %s: re-launching\n",
  466                            blurb(), (unsigned long) kid, how);
  467                   gfx_stopped_p = False;
  468                   saver_gfx_pid = fork_and_exec (dpy, ac, av);
  469                   respawn_thrashing_count++;
  470                 }
  471             }
  472           else
  473             {
  474               /* Should not have been running anyway. */
  475               if (verbose_p)
  476                 fprintf (stderr, "%s: pid %lu: " SAVER_GFX_PROGRAM
  477                          " exited %s\n", blurb(), (unsigned long) kid, how);
  478             }
  479         }
  480       else if (kid == saver_systemd_pid)
  481         {
  482           /* xscreensaver-systemd might fail if systemd isn't running, or if
  483              xscreensaver-systemd was already running, or if some other
  484              program has bound to our targets.  Or if it doesn't exist because
  485              this system doesn't use systemd.  So don't re-launch it if it
  486              failed to start, or dies.
  487            */
  488           saver_systemd_pid = 0;
  489           fprintf (stderr, "%s: pid %lu: " SAVER_SYSTEMD_PROGRAM
  490                    " exited unexpectedly %s\n",
  491                    blurb(), (unsigned long) kid, how);
  492         }
  493       else if (kid == saver_auth_pid)
  494         {
  495           saver_auth_pid = 0;
  496 
  497           /* xscreensaver-auth exits with status 200 to mean "ok to unlock".
  498              Any other exit code, or dying with a signal, means "nope".
  499            */
  500           if (!WIFSIGNALED (wait_status) &&
  501               WIFEXITED (wait_status))
  502             {
  503               unsigned int status = (unsigned int) WEXITSTATUS (wait_status);
  504               if (status == 200)
  505                 {
  506                   authenticated_p = True;
  507                   strcpy (how, "and authenticated");
  508                 }
  509               else if (status == 0 && !blanked_p)
  510                 strcpy (how, "normally");   /* This was the splash dialog */
  511               else if (status == 0)
  512                 strcpy (how, "and authentication canceled");
  513               else if (status == 255)  /* which is -1 */
  514                 strcpy (how, "and authentication failed");
  515             }
  516 
  517           if (verbose_p)
  518             fprintf (stderr, "%s: pid %lu: " SAVER_AUTH_PROGRAM " exited %s\n",
  519                      blurb(), (unsigned long) kid, how);
  520         }
  521       else if (verbose_p)
  522         {
  523           fprintf (stderr, "%s: pid %lu: unknown child"
  524                    " exited unexpectedly %s\n",
  525                    blurb(), (unsigned long) kid, how);
  526         }
  527     }
  528 
  529   return authenticated_p;
  530 }
  531 
  532 
  533 /* Add DEFAULT_PATH_PREFIX to the front of $PATH.
  534    Typically "/usr/libexec/xscreensaver".
  535  */
  536 static void
  537 hack_environment (void)
  538 {
  539   static const char *def_path = DEFAULT_PATH_PREFIX;
  540   const char *opath = getenv("PATH");
  541   char *npath;
  542   if (! opath) opath = "/bin:/usr/bin";  /* WTF */
  543   npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
  544   strcpy (npath, "PATH=");
  545   strcat (npath, def_path);
  546   strcat (npath, ":");
  547   strcat (npath, opath);
  548 
  549   /* Can fail if out of memory, I guess. Ignore errors. */
  550   putenv (npath);
  551 
  552   /* don't free (npath) -- some implementations of putenv (BSD 4.4,
  553      glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
  554      do not.  So we must leak it (and/or the previous setting). Yay.
  555    */
  556 }
  557 
  558 
  559 static void
  560 print_banner(void)
  561 {
  562   const time_t rel = XSCREENSAVER_RELEASED;
  563   struct tm *tm = localtime (&rel);
  564   char buf[100];
  565   int months = (time ((time_t *) 0) - XSCREENSAVER_RELEASED) / (60*60*24*30);
  566   int years = months / 12.0 + 0.7;
  567 
  568   strftime (buf, sizeof(buf), "%b %Y", tm);
  569 
  570   if (months > 18)
  571     sprintf (buf + strlen(buf),  " -- %d years ago", years);
  572   else if (months > 1)
  573     sprintf (buf + strlen(buf), " -- %d months ago", months);
  574 
  575   if (verbose_p || debug_p)
  576     {
  577       fprintf (stderr,
  578                "\tXScreenSaver " XSCREENSAVER_VERSION ", released %s\n"
  579                "\tCopyright \302\251 1991-%d by"
  580                " Jamie Zawinski <jwz@jwz.org>\n\n",
  581                buf, tm->tm_year + 1900);
  582       if (months > 18)
  583         fprintf (stderr,
  584                  /* Hey jerks, the only time someone will see this particular
  585                     message is if they are running xscreensaver with '-log' in
  586                     order to send me a bug report, and they had damned well
  587                     better try the latest release before they do that. */
  588                  "\t   ###################################################\n"
  589                  "\t   ###                                             ###\n"
  590                  "\t   ###  THIS VERSION IS VERY OLD! PLEASE UPGRADE!  ###\n"
  591                  "\t   ###                                             ###\n"
  592                  "\t   ###################################################\n"
  593                  "\n");
  594     }
  595 
  596   if (debug_p)
  597       fprintf (stderr,
  598                "#####################################"
  599                "#####################################\n"
  600                "\n"
  601                "\t\t\t DEBUG MODE IS NOT SECURE\n"
  602                "\n"
  603                "\tThe XScreenSaver window will only cover the left half of\n"
  604                "\tthe screen.  Position your terminal window on the right.\n"
  605                "\tWARNING: stderr and the log file will include every\n"
  606                "\tcharacter that you type, including passwords.\n"
  607                "\n"
  608                "#####################################"
  609                "#####################################\n"
  610                "\n");
  611 
  612   version_number = XSCREENSAVER_VERSION;
  613 }
  614 
  615 
  616 static void
  617 fix_fds (void)
  618 {
  619   /* Bad Things Happen if stdin, stdout, and stderr have been closed
  620      (as by the `sh incantation "xscreensaver >&- 2>&-").  When you do
  621      that, the X connection gets allocated to one of these fds, and
  622      then some random library writes to stderr, and random bits get
  623      stuffed down the X pipe, causing "Xlib: sequence lost" errors.
  624      So, we cause the first three file descriptors to be open to
  625      /dev/null if they aren't open to something else already.  This
  626      must be done before any other files are opened (or the closing
  627      of that other file will again free up one of the "magic" first
  628      three FDs.)
  629 
  630      We do this by opening /dev/null three times, and then closing
  631      those fds, *unless* any of them got allocated as #0, #1, or #2,
  632      in which case we leave them open.  Gag.
  633 
  634      Really, this crap is technically required of *every* X program,
  635      if you want it to be robust in the face of "2>&-".
  636    */
  637   int fd0 = open ("/dev/null", O_RDWR);
  638   int fd1 = open ("/dev/null", O_RDWR);
  639   int fd2 = open ("/dev/null", O_RDWR);
  640   if (fd0 > 2) close (fd0);
  641   if (fd1 > 2) close (fd1);
  642   if (fd2 > 2) close (fd2);
  643 }
  644 
  645 
  646 /* Mostly duplicated in resources.c.
  647  */
  648 static int
  649 parse_time (const char *string)
  650 {
  651   unsigned int h, m, s;
  652   char c;
  653   if (3 == sscanf (string,   " %u : %2u : %2u %c", &h, &m, &s, &c))
  654     ;
  655   else if (2 == sscanf (string, " : %2u : %2u %c", &m, &s, &c) ||
  656        2 == sscanf (string,    " %u : %2u %c", &m, &s, &c))
  657     h = 0;
  658   else if (1 == sscanf (string,       " : %2u %c", &s, &c))
  659     h = m = 0;
  660   else if (1 == sscanf (string,          " %u %c", &m, &c))
  661     h = s = 0;
  662   else
  663     {
  664       fprintf (stderr, "%s: unparsable duration \"%s\"\n", blurb(), string);
  665       return -1;
  666     }
  667   if (s >= 60 && (h != 0 || m != 0))
  668     {
  669       fprintf (stderr, "%s: seconds > 59 in \"%s\"\n", blurb(), string);
  670       return -1;
  671     }
  672   if (m >= 60 && h > 0)
  673     {
  674       fprintf (stderr, "%s: minutes > 59 in \"%s\"\n", blurb(), string);
  675       return -1;
  676     }
  677   return ((h * 60 * 60) + (m * 60) + s);
  678 }
  679 
  680 
  681 /* This program only needs a very few options from the init file, so it
  682    just reads the .ad file and the .xscreensaver file directly rather
  683    than going through Xt and Xrm.
  684  */
  685 static void init_line_handler (int lineno, 
  686                                const char *key, const char *val,
  687                                void *closure)
  688 {
  689   if      (!strcmp (key, "verbose")) verbose_p = !strcasecmp (val, "true");
  690   else if (!strcmp (key, "splash"))  splash_p  = !strcasecmp (val, "true");
  691   else if (!strcmp (key, "lock"))    lock_p    = !strcasecmp (val, "true");
  692   else if (!strcmp (key, "timeout"))
  693     {
  694       int t = parse_time (val);
  695       if (t > 0) blank_timeout = t;
  696     }
  697   if (!strcmp (key, "lockTimeout"))
  698     {
  699       int t = parse_time (val);
  700       if (t >= 0) lock_timeout = t;
  701     }
  702   if (!strcmp (key, "pointerHysteresis"))
  703     {
  704       int i = atoi (val);
  705       if (i >= 0)
  706         pointer_hysteresis = i;
  707     }
  708 }
  709 
  710 static void
  711 read_init_file_simple (const char *filename)
  712 {
  713   if (debug_p)
  714     fprintf (stderr, "%s: reading %s\n", blurb(), filename);
  715   parse_init_file (filename, init_line_handler, 0);
  716 }
  717 
  718 
  719 static time_t init_file_time = 0;
  720 
  721 static void
  722 read_init_files (Bool both_p)
  723 {
  724   static const char *home = 0;
  725   char *fn;
  726   struct stat st;
  727   Bool read_p = False;
  728 
  729   if (!home)
  730     {
  731       home = getenv("HOME");
  732       if (!home) home = "";
  733     }
  734 
  735   if (both_p)
  736     {
  737       read_init_file_simple (AD_DIR "/XScreenSaver");
  738       read_p = True;
  739     }
  740 
  741   fn = (char *) malloc (strlen(home) + 40);
  742   sprintf (fn, "%s/.xscreensaver", home);
  743 
  744   if (!stat (fn, &st))
  745     {
  746       if (both_p || st.st_mtime != init_file_time)
  747         {
  748           Bool ov = verbose_p;
  749           init_file_time = st.st_mtime;
  750           read_init_file_simple (fn);
  751           read_p = True;
  752 
  753           /* Changes to verbose in .xscreenaver after startup are ignored; else
  754              running xscreensaver-settings would turn off cmd line -verbose. */
  755           if (!both_p) verbose_p = ov;
  756         }
  757     }
  758 
  759   if (blank_timeout < 5)
  760     blank_timeout = 60 * 60 * 10;
  761 
  762   free (fn);
  763 
  764   if (read_p && verbose_p)
  765     {
  766       fprintf (stderr, "%s: blank after: %d\n", blurb(), blank_timeout);
  767       if (lock_p)
  768         fprintf (stderr, "%s: lock after:  %d\n", blurb(), lock_timeout);
  769     }
  770 }
  771 
  772 
  773 
  774 
  775 /* We trap and ignore ALL protocol errors that happen after initialization.
  776    By default, Xlib would exit.  Ignoring an X error is less bad than
  777    crashing and unlocking.
  778  */
  779 static Bool ignore_x11_error_p  = False;
  780 static Bool error_handler_hit_p = False;
  781 static Bool print_x11_error_p   = True;
  782 
  783 static int
  784 error_handler (Display *dpy, XErrorEvent *event)
  785 {
  786   error_handler_hit_p = True;
  787 
  788   if (print_x11_error_p)
  789     {
  790       const char *b = blurb();
  791       const char *p = progname;
  792       fprintf (stderr, "\n%s: X ERROR! PLEASE REPORT THIS BUG!\n\n", b);
  793       progname = b;
  794       XmuPrintDefaultErrorMessage (dpy, event, stderr);
  795       progname = p;
  796 
  797 # ifdef __GNUC__
  798   __extension__   /* don't warn about "string length is greater than the
  799                      length ISO C89 compilers are required to support". */
  800 # endif
  801     fprintf (stderr, "\n"
  802    "#######################################################################\n"
  803    "\n"
  804    "    If at all possible, please re-run xscreensaver with the command\n"
  805    "    line arguments \"-sync -log log.txt\", and reproduce this bug.\n"
  806    "    Please include the complete \"log.txt\" file with your bug report.\n"
  807    "\n"
  808    "    https://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
  809    "    the most useful bug reports.\n"
  810    "\n"
  811    "    The more information you can provide, the better.  But please\n"
  812    "    report this bug, regardless!\n"
  813    "\n"
  814    "#######################################################################\n"
  815    "\n"
  816    "\n");
  817 
  818       fflush (stderr);
  819 
  820       if (! ignore_x11_error_p)
  821         abort();
  822     }
  823 
  824   return 0;
  825 }
  826 
  827 
  828 /* Check for other running instances of XScreenSaver, gnome-screensaver, etc.
  829  */
  830 static void
  831 ensure_no_screensaver_running (Display *dpy)
  832 {
  833   int screen, nscreens = ScreenCount (dpy);
  834 
  835   /* Silently ignore BadWindow race conditions. */
  836   Bool op = print_x11_error_p;
  837   print_x11_error_p = False;
  838 
  839   for (screen = 0; screen < nscreens; screen++)
  840     {
  841       int i;
  842       Window root = RootWindow (dpy, screen);
  843       Window root2 = 0, parent = 0, *kids = 0;
  844       unsigned int nkids = 0;
  845 
  846       if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
  847         continue;
  848       if (root != root2)
  849         continue;
  850       if (parent)
  851         continue;
  852       for (i = 0; i < nkids; i++)
  853         {
  854           Atom type;
  855           int format;
  856           unsigned long nitems, bytesafter;
  857           unsigned char *version;
  858 
  859           if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_VERSION, 0, 1,
  860                                   False, XA_STRING, &type, &format, &nitems,
  861                                   &bytesafter, &version)
  862               == Success
  863               && type != None)
  864             {
  865               unsigned char *id;
  866               if (XGetWindowProperty (dpy, kids[i], XA_SCREENSAVER_ID, 0, 512,
  867                                       False, XA_STRING, &type, &format,
  868                                       &nitems, &bytesafter, &id)
  869                   != Success
  870                   || type == None)
  871                 id = (unsigned char *) "???";
  872 
  873               fprintf (stderr,
  874                        "%s: already running on display %s"
  875                        " (window 0x%x)\n from process %s\n",
  876                        blurb(), DisplayString (dpy), (int) kids [i],
  877                        (char *) id);
  878               saver_exit (1);
  879             }
  880           else if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
  881                                        False, XA_STRING, &type, &format,
  882                                        &nitems, &bytesafter, &version)
  883                    == Success
  884                    && type != None
  885                    && (!strcmp ((char *) version, "gnome-screensaver") ||
  886                        !strcmp ((char *) version, "mate-screensaver") ||
  887                        !strcmp ((char *) version, "cinnamon-screensaver")))
  888             {
  889               fprintf (stderr,
  890                        "%s: \"%s\" is already running on display %s"
  891                        " (window 0x%x)\n",
  892                        blurb(), (char *) version,
  893                        DisplayString (dpy), (int) kids [i]);
  894               saver_exit (1);
  895             }
  896         }
  897       if (kids) XFree ((char *) kids);
  898     }
  899 
  900   print_x11_error_p = op;
  901 }
  902 
  903 
  904 /* Store a property on the root window indicating that xscreensaver is
  905    running, and whether it is blanked or locked.  This property is read
  906    by "xscreensaver-command" and by ensure_no_screensaver_running().
  907    This property is also overwritten by "xscreensaver-gfx" to indicate
  908    which screenhacks are running.
  909  */
  910 static void
  911 store_saver_status (Display *dpy,
  912                     Bool blanked_p, Bool locked_p, time_t blank_time)
  913 {
  914   /* The contents of XA_SCREENSAVER_STATUS has LOCK/BLANK/0 in the first slot,
  915      the time at which that state began in the second slot, and the ordinal of
  916      the running hacks on each screen (1-based) in subsequent slots.  Since
  917      we don't know the hacks here (or even how many monitors are attached) we
  918      leave whatever was there before unchanged: it will be updated by
  919      "xscreensaver-gfx".
  920 
  921      XA_SCREENSAVER_STATUS is stored on the (real) root window of screen 0.
  922 
  923      XA_SCREENSAVER_VERSION and XA_SCREENSAVER_ID are stored on the unmapped
  924      window created by the "xscreensaver" process.  ClientMessage events are
  925      sent to that window, and the responses are sent via the
  926      XA_SCREENSAVER_RESPONSE property on it.
  927 
  928      These properties are not used on the windows created by "xscreensaver-gfx"
  929      for use by the display hacks.
  930 
  931      See the different version of this function in windows.c.
  932    */
  933   Window w = RootWindow (dpy, 0);  /* always screen 0 */
  934   Atom type;
  935   unsigned char *dataP = 0;
  936   PROP32 *status = 0;
  937   int format;
  938   unsigned long nitems, bytesafter;
  939 
  940   /* Read the old property, so we can change just parts. */
  941   if (XGetWindowProperty (dpy, w,
  942                           XA_SCREENSAVER_STATUS,
  943                           0, 999, False, XA_INTEGER,
  944                           &type, &format, &nitems, &bytesafter,
  945                           &dataP)
  946       == Success
  947       && type == XA_INTEGER
  948       && nitems >= 3
  949       && dataP)
  950     status = (PROP32 *) dataP;
  951 
  952   if (!status)  /* There was no existing property */
  953     {
  954       nitems = 3;
  955       status = (PROP32 *) malloc (nitems * sizeof(*status));
  956     }
  957 
  958   status[0] = (PROP32) (locked_p  ? XA_LOCK  : blanked_p ? XA_BLANK : 0);
  959   status[1] = (PROP32) blank_time;  /* Y2038 bug: unsigned 32 bit time_t */
  960   XChangeProperty (dpy, w, XA_SCREENSAVER_STATUS, XA_INTEGER, 32,
  961                    PropModeReplace, (unsigned char *) status, nitems);
  962   XSync (dpy, False);
  963 
  964 # if 0
  965   if (debug_p && verbose_p)
  966     {
  967       int i;
  968       fprintf (stderr, "%s: wrote status property: 0x%lx: %s", blurb(),
  969                (unsigned long) w,
  970                (status[0] == XA_LOCK  ? "LOCK" :
  971                 status[0] == XA_BLANK ? "BLANK" :
  972                 status[0] == 0 ? "0" : "???"));
  973       for (i = 1; i < nitems; i++)
  974         fprintf (stderr, ", %lu", status[i]);
  975       fprintf (stderr, "\n");
  976       if (system ("xprop -root _SCREENSAVER_STATUS") <= 0)
  977         fprintf (stderr, "%s: xprop exec failed\n", blurb());
  978     }
  979 # endif /* 0 */
  980 
  981   if (status != (PROP32 *) dataP)
  982     free (status);
  983   if (dataP)
  984     XFree (dataP);
  985 }
  986 
  987 
  988 /* This process does not map any windows on the screen.  However, it creates
  989    one hidden window on screen 0, which is the rendezvous point for
  990    communication with xscreensaver-command: that window is how it can tell
  991    that XScreenSaver is running, what the version number is, and it is where
  992    bidirectional ClientMessage communication takes place.  Since there are
  993    no "blanking" windows around at all when xscreensaver-gfx is not running,
  994    this window is needed.  We could have instead re-tooled xscreensaver-command
  995    to do all of its communication through the root window instead, but this 
  996    seemed easier.
  997  */
  998 static void
  999 create_daemon_window (Display *dpy)
 1000 {
 1001   XClassHint class_hints;
 1002   XSetWindowAttributes attrs;
 1003   unsigned long attrmask = 0;
 1004   const char *name = "???";
 1005   const char *host = "???";
 1006   char buf[20];
 1007   pid_t pid = getpid();
 1008   struct passwd *p = getpwuid (getuid ());
 1009   time_t now = time ((time_t *) 0);
 1010   char *id;
 1011 # ifdef HAVE_UNAME
 1012   struct utsname uts;
 1013 # endif
 1014 
 1015   if (p && p->pw_name && *p->pw_name)
 1016     name = p->pw_name;
 1017   else if (p)
 1018     {
 1019       sprintf (buf, "%lu", (unsigned long) p->pw_uid);
 1020       name = buf;
 1021     }
 1022   else
 1023     name = "???";
 1024 
 1025 # ifdef HAVE_UNAME
 1026   {
 1027     if (! uname (&uts))
 1028       host = uts.nodename;
 1029   }
 1030 # endif
 1031 
 1032   class_hints.res_name  = (char *) progname;  /* not const? */
 1033   class_hints.res_class = "XScreenSaver";
 1034   id = (char *) malloc (strlen(name) + strlen(host) + 50);
 1035   sprintf (id, "%lu (%s@%s)", (unsigned long) pid, name, host);
 1036 
 1037   attrmask = CWOverrideRedirect | CWEventMask;
 1038   attrs.override_redirect = True;
 1039   attrs.event_mask = PropertyChangeMask;
 1040 
 1041   daemon_window = XCreateWindow (dpy, RootWindow (dpy, 0),
 1042                                  0, 0, 1, 1, 0,
 1043                                  DefaultDepth (dpy, 0), InputOutput,
 1044                                  DefaultVisual (dpy, 0), attrmask, &attrs);
 1045   XStoreName (dpy, daemon_window, "XScreenSaver Daemon");
 1046   XSetClassHint (dpy, daemon_window, &class_hints);
 1047   XChangeProperty (dpy, daemon_window, XA_WM_COMMAND, XA_STRING,
 1048            8, PropModeReplace, (unsigned char *) progname,
 1049                    strlen (progname));
 1050   XChangeProperty (dpy, daemon_window, XA_SCREENSAVER_VERSION, XA_STRING,
 1051                    8, PropModeReplace, (unsigned char *) version_number,
 1052            strlen (version_number));
 1053   XChangeProperty (dpy, daemon_window, XA_SCREENSAVER_ID, XA_STRING,
 1054            8, PropModeReplace, (unsigned char *) id, strlen (id));
 1055 
 1056   store_saver_status (dpy, False, False, now);
 1057   free (id);
 1058 }
 1059 
 1060 
 1061 static const char *
 1062 grab_string (int status)
 1063 {
 1064   switch (status) {
 1065   case GrabSuccess:     return "GrabSuccess";
 1066   case AlreadyGrabbed:  return "AlreadyGrabbed";
 1067   case GrabInvalidTime: return "GrabInvalidTime";
 1068   case GrabNotViewable: return "GrabNotViewable";
 1069   case GrabFrozen:      return "GrabFrozen";
 1070   default:
 1071     {
 1072       static char buf[255];
 1073       sprintf(buf, "unknown status: %d", status);
 1074       return buf;
 1075     }
 1076   }
 1077 }
 1078 
 1079 static int
 1080 grab_kbd (Screen *screen)
 1081 {
 1082   Display *dpy = DisplayOfScreen (screen);
 1083   Window w = RootWindowOfScreen (screen);
 1084   int status = XGrabKeyboard (dpy, w, True, GrabModeAsync, GrabModeAsync,
 1085                               CurrentTime);
 1086   if (verbose_p)
 1087     fprintf (stderr, "%s: grabbing keyboard on 0x%lx: %s\n",
 1088              blurb(), (unsigned long) w, grab_string (status));
 1089   return status;
 1090 }
 1091 
 1092 
 1093 static int
 1094 grab_mouse (Screen *screen, Cursor cursor)
 1095 {
 1096   Display *dpy = DisplayOfScreen (screen);
 1097   Window w = RootWindowOfScreen (screen);
 1098   int status = XGrabPointer (dpy, w, True, 
 1099                              (ButtonPressMask   | ButtonReleaseMask |
 1100                               EnterWindowMask   | LeaveWindowMask |
 1101                               PointerMotionMask | PointerMotionHintMask |
 1102                               Button1MotionMask | Button2MotionMask |
 1103                               Button3MotionMask | Button4MotionMask |
 1104                               Button5MotionMask | ButtonMotionMask),
 1105                              GrabModeAsync, GrabModeAsync, w,
 1106                              cursor, CurrentTime);
 1107   if (verbose_p)
 1108     fprintf (stderr, "%s: grabbing mouse on 0x%lx... %s\n",
 1109              blurb(), (unsigned long) w, grab_string (status));
 1110   return status;
 1111 }
 1112 
 1113 
 1114 static void
 1115 ungrab_kbd (Display *dpy)
 1116 {
 1117   if (verbose_p)
 1118     fprintf (stderr, "%s: ungrabbing keyboard\n", blurb());
 1119   XUngrabKeyboard (dpy, CurrentTime);
 1120 }
 1121 
 1122 
 1123 static void
 1124 ungrab_mouse (Display *dpy)
 1125 {
 1126   if (verbose_p)
 1127     fprintf (stderr, "%s: ungrabbing mouse\n", blurb());
 1128   XUngrabPointer (dpy, CurrentTime);
 1129 }
 1130 
 1131 
 1132 /* Some remote desktop clients (e.g., "rdesktop") hold the keyboard GRABBED the
 1133    whole time they have focus!  This is idiotic because the whole point of
 1134    grabbing is to get events when you do *not* have focus, so grabbing only
 1135    when* you have focus is redundant.  Anyway, that prevents us from getting a
 1136    keyboard grab.  It turns out that for some of these apps, de-focusing them
 1137    forces them to release their grab.
 1138 
 1139    So if we fail to grab the keyboard four times in a row, we forcibly set
 1140    focus to "None" and try four more times.  We don't touch focus unless we're
 1141    already having a hard time getting a grab.
 1142  */
 1143 static void
 1144 nuke_focus (Screen *screen)
 1145 {
 1146   Display *dpy = DisplayOfScreen (screen);
 1147   Window focus = 0;
 1148   int rev = 0;
 1149 
 1150   XGetInputFocus (dpy, &focus, &rev);
 1151 
 1152   if (verbose_p)
 1153     {
 1154       char w[255], r[255];
 1155 
 1156       if      (focus == PointerRoot) strcpy (w, "PointerRoot");
 1157       else if (focus == None)        strcpy (w, "None");
 1158       else    sprintf (w, "0x%lx", (unsigned long) focus);
 1159 
 1160       if      (rev == RevertToParent)      strcpy (r, "RevertToParent");
 1161       else if (rev == RevertToPointerRoot) strcpy (r, "RevertToPointerRoot");
 1162       else if (rev == RevertToNone)        strcpy (r, "RevertToNone");
 1163       else    sprintf (r, "0x%x", rev);
 1164 
 1165       fprintf (stderr, "%s: removing focus from %s / %s\n",
 1166                blurb(), w, r);
 1167     }
 1168 
 1169   XSetInputFocus (dpy, None, RevertToNone, CurrentTime);
 1170 }
 1171 
 1172 
 1173 static void
 1174 ungrab_keyboard_and_mouse (Display *dpy)
 1175 {
 1176   ungrab_mouse (dpy);
 1177   ungrab_kbd (dpy);
 1178 }
 1179 
 1180 
 1181 /* Returns true if it succeeds.
 1182  */
 1183 static Bool
 1184 grab_keyboard_and_mouse (Screen *screen)
 1185 {
 1186   Display *dpy = DisplayOfScreen (screen);
 1187   Status mstatus = 0, kstatus = 0;
 1188   int i;
 1189   int retries = 4;
 1190   Bool focus_fuckus = False;
 1191 
 1192  AGAIN:
 1193 
 1194   for (i = 0; i < retries; i++)
 1195     {
 1196       XSync (dpy, False);
 1197       kstatus = grab_kbd (screen);
 1198       if (kstatus == GrabSuccess)
 1199         break;
 1200 
 1201       /* else, wait a second and try to grab again. */
 1202       sleep (1);
 1203     }
 1204 
 1205   if (kstatus != GrabSuccess)
 1206     {
 1207       fprintf (stderr, "%s: couldn't grab keyboard: %s\n",
 1208                blurb(), grab_string (kstatus));
 1209 
 1210       if (! focus_fuckus)
 1211         {
 1212           focus_fuckus = True;
 1213           nuke_focus (screen);
 1214           goto AGAIN;
 1215         }
 1216     }
 1217 
 1218   for (i = 0; i < retries; i++)
 1219     {
 1220       XSync (dpy, False);
 1221       mstatus = grab_mouse (screen, blank_cursor);
 1222       if (mstatus == GrabSuccess)
 1223         break;
 1224 
 1225       /* else, wait a second and try to grab again. */
 1226       sleep (1);
 1227     }
 1228 
 1229   if (mstatus != GrabSuccess)
 1230     fprintf (stderr, "%s: couldn't grab pointer: %s\n",
 1231              blurb(), grab_string (mstatus));
 1232 
 1233 
 1234   /* When should we allow blanking to proceed?  The current theory
 1235      is that a keyboard grab is mandatory; a mouse grab is optional.
 1236 
 1237      - If we don't have a keyboard grab, then we won't be able to
 1238        read a password to unlock, so the kbd grab is mandatory.
 1239        (We can't conditionalize this on locked_p, because someone
 1240        might run "xscreensaver-command -lock" at any time.)
 1241 
 1242      - If we don't have a mouse grab, then we might not see mouse
 1243        clicks as a signal to unblank -- but we will still see kbd
 1244        activity, so that's not a disaster.
 1245 
 1246      If the mouse grab failed with AlreadyGrabbed, then I *think*
 1247      that means that we will still see the mouse events via XInput2.
 1248      But if it failed with GrabFrozen, that means that the grabber
 1249      used GrabModeSync, and we will only receive those mouse events
 1250      as a replay after they release the grab, which doesn't help us.
 1251 
 1252      If the keyboard grab failed with AlreadyGrabbed rather than
 1253      GrabFrozen then we may still get those keypresses -- but so will
 1254      the program holding the grab, so that's unacceptable for our
 1255      purpose of reading passwords.
 1256 
 1257      It has been suggested that we should allow blanking if locking
 1258      is disabled, and we have a mouse grab but no keyboard grab.
 1259      That would allow screen blanking (but not locking) while the gdm
 1260      login screen had the keyboard grabbed, but one would have to use
 1261      the mouse to unblank.  Keyboard characters would go to the gdm
 1262      login field without unblanking.  I have not made this change
 1263      because I'm not completely convinced it is a safe thing to do.
 1264    */
 1265 
 1266   if (kstatus != GrabSuccess)   /* Do not blank without a kbd grab.   */
 1267     {
 1268       /* If we didn't get both grabs, release the one we did get. */
 1269       ungrab_keyboard_and_mouse (dpy);
 1270       return False;
 1271     }
 1272 
 1273   return True;          /* Grab is good, go ahead and blank.  */
 1274 }
 1275 
 1276 
 1277 /* Which screen is the mouse on?
 1278  */
 1279 static Screen *
 1280 mouse_screen (Display *dpy)
 1281 {
 1282   int i, nscreens = ScreenCount (dpy);
 1283   if (nscreens > 1)
 1284     for (i = 0; i < nscreens; i++)
 1285       {
 1286         Window pointer_root, pointer_child;
 1287         int root_x, root_y, win_x, win_y;
 1288         unsigned int mask;
 1289         int status = XQueryPointer (dpy, RootWindow (dpy, i),
 1290                                     &pointer_root, &pointer_child,
 1291                                     &root_x, &root_y, &win_x, &win_y, &mask);
 1292         if (status != None)
 1293           {
 1294             if (verbose_p)
 1295               fprintf (stderr, "%s: mouse is on screen %d of %d\n",
 1296                        blurb(), i, nscreens);
 1297             return ScreenOfDisplay (dpy, i);
 1298           }
 1299       }
 1300 
 1301   return ScreenOfDisplay (dpy, 0);
 1302 }
 1303 
 1304 
 1305 static void
 1306 maybe_disable_locking (Display *dpy)
 1307 {
 1308   const char *why = 0;
 1309   Bool wayland_p = (getenv ("WAYLAND_DISPLAY") ||
 1310                     getenv ("WAYLAND_SOCKET"));
 1311 
 1312 # ifdef NO_LOCKING
 1313   why = "locking disabled at compile time";
 1314 # endif
 1315 
 1316   if (!why)
 1317     {
 1318       uid_t u = getuid();
 1319       if (u == 0 || u == (uid_t) -1 || u == (uid_t) -2)
 1320         why = "cannot lock when running as root";
 1321     }
 1322 
 1323   if (!why && getenv ("RUNNING_UNDER_GDM"))
 1324     /* Launched as GDM's "Background" program */
 1325     why = "cannot lock when running under GDM";
 1326 
 1327   /* X11 grabs don't work under Wayland's embedded X11 server.  The Wayland
 1328      window manager lives at a higher level than the X11 emulation layer. */
 1329   if (!why && wayland_p)
 1330     why = "cannot lock securely under Wayland";
 1331 
 1332   if (!why)
 1333     {
 1334       /* Grabs under the macOS XDarwin server only affect other X11 programs:
 1335          you can Cmd-Tab to "Terminal".  These extensions exist on later
 1336          releases XQuartz. */
 1337       int op = 0, event = 0, error = 0;
 1338       if (XQueryExtension (dpy, "Apple-DRI", &op, &event, &error) ||
 1339           XQueryExtension (dpy, "Apple-WM",  &op, &event, &error))
 1340         why = "cannot lock securely under macOS X11";
 1341     }
 1342 
 1343   if (why)
 1344     {
 1345       if (debug_p)
 1346         {
 1347           fprintf (stderr, "%s: %s\n", blurb(), why);
 1348           fprintf (stderr, "%s: DEBUG MODE: allowing locking anyway!\n",
 1349                    blurb());
 1350         }
 1351       else if (wayland_p)
 1352         {
 1353           const char *s = blurb();
 1354           locking_disabled_p = True;
 1355 
 1356           /* Maybe we should just refuse to launch instead?  We can operate
 1357              properly only if the user uses only X11 programs, and doesn't
 1358              want to lock the screen.
 1359            */
 1360           fprintf (stderr, "\n"
 1361               "%s: WARNING: Wayland is not supported.\n"
 1362               "\n"
 1363               "%s:     Under Wayland, idle-detection fails when non-X11\n"
 1364               "%s:     programs are selected, meaning the screen may\n"
 1365               "%s:     blank prematurely.  Also, locking is impossible.\n"
 1366               "%s:     See the manual for instructions on configuring\n"
 1367               "%s:     your system to use X11 instead of Wayland.\n\n",
 1368                    s, s, s, s, s, s);
 1369         }
 1370       else
 1371         {
 1372           locking_disabled_p = True;
 1373           if (lock_p || verbose_p)
 1374             fprintf (stderr, "%s: locking disabled: %s\n", blurb(), why);
 1375         }
 1376     }
 1377 }
 1378 
 1379 
 1380 static void
 1381 main_loop (Display *dpy)
 1382 {
 1383   int xi_opcode;
 1384   time_t now = time ((time_t *) 0);
 1385   time_t active_at = now;
 1386   time_t blanked_at = 0;
 1387   time_t ignore_activity_before = now;
 1388   time_t last_checked_init_file = now;
 1389   Bool authenticated_p = False;
 1390   Bool ignore_motion_p = False;
 1391 
 1392   enum { UNBLANKED, BLANKED, LOCKED, AUTH } current_state = UNBLANKED;
 1393 
 1394   struct { time_t time; int x, y; } last_mouse = { 0, 0, 0 };
 1395 
 1396   maybe_disable_locking (dpy);
 1397   init_xscreensaver_atoms (dpy);
 1398   ensure_no_screensaver_running (dpy);
 1399 
 1400   if (! init_xinput (dpy, &xi_opcode))
 1401     saver_exit (1);
 1402 
 1403   /* Disable server built-in screen saver. */
 1404   XSetScreenSaver (dpy, 0, 0, 0, 0);
 1405   XForceScreenSaver (dpy, ScreenSaverReset);
 1406 
 1407   /* It would be nice to sync the server's DPMS settings here to what is
 1408      specified in the .xscreensaver file, but xscreensaver-gfx handles that,
 1409      so that won't happen until the first time the screen blanks. */
 1410 
 1411   create_daemon_window (dpy);
 1412 
 1413   handle_signals();
 1414 
 1415   blank_cursor = None;   /* Cursor of window under mouse (which is blank). */
 1416   auth_cursor = XCreateFontCursor (dpy, XC_top_left_arrow);
 1417 
 1418   if (strchr (version_number, 'a') || strchr (version_number, 'b'))
 1419     splash_p = True;  /* alpha and beta releases */
 1420 
 1421   /* Launch "xscreensaver-auth" once at startup with either --splash or --init,
 1422      The latter to handle the OOM-killer while setuid. */
 1423   {
 1424     char *av[10];
 1425     int ac = 0;
 1426     av[ac++] = SAVER_AUTH_PROGRAM;
 1427     av[ac++] = (splash_p ? "--splash" : "--init");
 1428     if (verbose_p)     av[ac++] = "--verbose";
 1429     if (verbose_p > 1) av[ac++] = "--verbose";
 1430     if (verbose_p > 2) av[ac++] = "--verbose";
 1431     if (debug_p)       av[ac++] = "--debug";
 1432     av[ac] = 0;
 1433     saver_auth_pid = fork_and_exec (dpy, ac, av);
 1434   }
 1435 
 1436 # if defined(HAVE_LIBSYSTEMD) || defined(HAVE_LIBELOGIND) 
 1437   /* Launch xscreensaver-systemd at startup. */
 1438   {
 1439     char *av[10];
 1440     int ac = 0;
 1441     av[ac++] = SAVER_SYSTEMD_PROGRAM;
 1442     if (verbose_p || debug_p)
 1443       av[ac++] = "--verbose";
 1444     av[ac] = 0;
 1445     saver_systemd_pid = fork_and_exec (dpy, ac, av);
 1446   }
 1447 # endif /* HAVE_LIBSYSTEMD || HAVE_LIBELOGIND */
 1448 
 1449 
 1450   /* X11 errors during startup initialization were fatal.
 1451      Once we enter the main loop, they are printed but ignored.
 1452    */
 1453   XSync (dpy, False);
 1454   ignore_x11_error_p = True;
 1455 
 1456   /************************************************************************
 1457    Main loop
 1458   ************************************************************************/
 1459 
 1460   while (1)
 1461     {
 1462       Bool force_blank_p = False;
 1463       Bool force_lock_p = False;
 1464       Atom blank_mode = 0;
 1465       char blank_mode_arg[20] = { 0 };
 1466 
 1467       /* Wait until an event comes in, or a timeout. */
 1468       {
 1469         int xfd = ConnectionNumber (dpy);
 1470         fd_set in_fds;
 1471         struct timeval tv;
 1472         time_t until;
 1473         switch (current_state) {
 1474         case UNBLANKED: until = active_at + blank_timeout; break;
 1475         case BLANKED:   until = blanked_at + lock_timeout; break;
 1476         default:        until = 0;
 1477         }
 1478 
 1479         tv.tv_sec = 0;
 1480         tv.tv_usec = 0;
 1481         if (until >= now)
 1482           tv.tv_sec = until - now;
 1483           
 1484         if (verbose_p > 3)
 1485           {
 1486             if (!tv.tv_sec)
 1487               fprintf (stderr, "%s: block until input\n", blurb());
 1488             else
 1489               {
 1490                 struct tm tm;
 1491                 time_t t = now + tv.tv_sec;
 1492                 localtime_r (&t, &tm);
 1493                 fprintf (stderr,
 1494                          "%s: block for %ld sec until %02d:%02d:%02d\n", 
 1495                          blurb(), tv.tv_sec, tm.tm_hour, tm.tm_min, tm.tm_sec);
 1496               }
 1497           }
 1498 
 1499         FD_ZERO (&in_fds);
 1500         FD_SET (xfd, &in_fds);
 1501         select (xfd + 1, &in_fds, NULL, NULL, (tv.tv_sec ? &tv : NULL));
 1502       }
 1503 
 1504       now = time ((time_t *) 0);
 1505 
 1506 
 1507       /********************************************************************
 1508        Signal handlers
 1509        ********************************************************************/
 1510 
 1511 
 1512       /* If a signal handler fired, it set a flag, and that caused select()
 1513          to return.  Now that we are back on the program stack, handle
 1514          those signals. */
 1515 
 1516       /* SIGHUP is the same as "xscreensaver-command -restart". */
 1517       if (sighup_received)
 1518         {
 1519           sighup_received = 0;
 1520           if (current_state == LOCKED)
 1521             {
 1522               fprintf (stderr,
 1523                        "%s: SIGHUP received while locked: ignoring\n",
 1524                        blurb());
 1525             }
 1526           else
 1527             {
 1528               fprintf (stderr, "%s: SIGHUP received: restarting\n", blurb());
 1529               restart_process();   /* Does not return */
 1530               fprintf (stderr, "%s: SIGHUP RESTART FAILED!\n", blurb());
 1531             }
 1532         }
 1533 
 1534       /* Upon SIGTERM, SIGINT or SIGQUIT we must kill our subprocesses
 1535          before exiting.
 1536        */
 1537       if (sigterm_received)
 1538         {
 1539           int sig = sigterm_received;  /* Same handler for all 3 signals */
 1540           const char *sn = (sig == SIGINT  ? "SIGINT" :
 1541                             sig == SIGQUIT ? "SIGQUIT" : "SIGTERM");
 1542           sigterm_received = 0;
 1543           fprintf (stderr, "%s: %s received%s: exiting\n", blurb(), sn,
 1544                    (current_state == LOCKED ? " while locked" : ""));
 1545 
 1546           /* Rather than calling saver_exit(), set our SIGTERM handler back to
 1547              the default and re-signal it so that this process actually dies
 1548              with a signal. */
 1549           signal (sig, SIG_DFL);
 1550           kill_all_subprocs();
 1551           kill (getpid(), sig);
 1552           abort();
 1553         }
 1554 
 1555       /* SIGCHLD is fired any time one of our subprocesses dies.
 1556          When "xscreensaver-auth" dies, it analyzes its exit code.
 1557        */
 1558       if (sigchld_received)
 1559         authenticated_p = handle_sigchld (dpy, current_state != UNBLANKED);
 1560 
 1561       /* Now process any outstanding X11 events on the queue: user activity
 1562          from XInput, and ClientMessages from xscreensaver-command.
 1563        */
 1564       while (XPending (dpy))
 1565         {
 1566           XEvent xev;
 1567           XNextEvent (dpy, &xev);
 1568           now = time ((time_t *) 0);
 1569 
 1570           /****************************************************************
 1571            Client Messages
 1572            Both xscreensaver and xscreensaver-gfx handle these; some are
 1573            handled exclusively by one program or another, and some of them
 1574            (select, next, prev) are handled by xscreensaver only if
 1575            xscreensaver-gfx is not already running.
 1576            ****************************************************************/
 1577 
 1578           switch (xev.xany.type) {
 1579           case ClientMessage:
 1580             {
 1581               Atom msg = xev.xclient.data.l[0];
 1582 
 1583               /* Re-load the .xscreensaver file before processing the results
 1584                  of any ClientMessage, in case they just changed the timeouts.
 1585                */
 1586               last_checked_init_file = 0;
 1587 
 1588               if (! (xev.xclient.message_type == XA_SCREENSAVER &&
 1589                      xev.xclient.format == 32 &&
 1590                      xev.xclient.data.l[0]))
 1591                 {
 1592                   goto BAD_CM;
 1593                 }
 1594               else if (msg == XA_ACTIVATE ||
 1595                        msg == XA_SELECT ||
 1596                        msg == XA_DEMO ||
 1597                        msg == XA_NEXT ||
 1598                        msg == XA_PREV)
 1599                 {
 1600                   /* The others are the same as -activate except that they
 1601                      cause some extra args to be added to the xscreensaver-gfx
 1602                      command line.
 1603                    */
 1604                   if (msg != XA_ACTIVATE)
 1605                     blank_mode = msg;
 1606                   if (msg == XA_SELECT || msg == XA_DEMO)
 1607                     { /* see remote.c */
 1608                       unsigned long  n = xev.xclient.data.l[1];
 1609                       if (n == 5000) n = xev.xclient.data.l[2];
 1610                       sprintf (blank_mode_arg, "%lu", n);
 1611                     }
 1612 
 1613                   if (msg == XA_DEMO) ignore_motion_p = True;
 1614 
 1615                   if (current_state == UNBLANKED)
 1616                     {
 1617                       force_blank_p = True;
 1618                       ignore_activity_before = now + 2;
 1619                       clientmessage_response (dpy, &xev, True, "blanking");
 1620                     }
 1621                   else if (msg == XA_SELECT ||
 1622                            msg == XA_NEXT ||
 1623                            msg == XA_PREV)
 1624                     {
 1625                       /* When active, these are handled by xscreensaver-gfx
 1626                          instead of xscreensaver, so silently ignore them,
 1627                          and allow xscreensaver-gfx to reply instead. */
 1628                     }
 1629                   else
 1630                     clientmessage_response (dpy, &xev, False,
 1631                                             "already active");
 1632                 }
 1633               else if (msg == XA_CYCLE)
 1634                 {
 1635                   if (current_state == UNBLANKED)
 1636                     /* Only allowed when screen already blanked */
 1637                     clientmessage_response (dpy, &xev, False, "not blanked");
 1638                   /* else xscreensaver-gfx will respond to this. */
 1639                 }
 1640               else if (msg == XA_DEACTIVATE)
 1641                 {
 1642                   if (current_state == UNBLANKED)
 1643                     {
 1644                       clientmessage_response (dpy, &xev, True,
 1645                                  "already inactive, resetting activity time");
 1646                       active_at = now;
 1647                       ignore_activity_before = now;
 1648                     }
 1649                   else
 1650                     {
 1651                       /* This behaves just like user input: if state is
 1652                          LOCKED, it will advance to AUTH. */
 1653                       active_at = now;
 1654                       ignore_activity_before = now;
 1655                       clientmessage_response (dpy, &xev, True, "deactivating");
 1656                     }
 1657 
 1658                   /* DEACTIVATE while inactive also needs to reset the
 1659                      server's DPMS time, but doing that here would mean
 1660                      linking with additional libraries, doing additional X
 1661                      protocol, and also some finicky error handling, since
 1662                      the DPMS extension is a pain in the ass.  So instead,
 1663                      I made xscreensaver-command do that instead.  This
 1664                      somewhat breaks the abstraction of ClientMessage
 1665                      handling, but it's more robust. */
 1666                 }
 1667               else if (msg == XA_LOCK)
 1668                 {
 1669                   if (locking_disabled_p)
 1670                     clientmessage_response (dpy, &xev, False,
 1671                                             "locking disabled");
 1672                   else if (current_state == UNBLANKED ||
 1673                            current_state == BLANKED)
 1674                     {
 1675                       force_lock_p = True;
 1676                       ignore_activity_before = now + 2;
 1677                       clientmessage_response (dpy, &xev, True, "locking");
 1678                     }
 1679                   else
 1680                     clientmessage_response (dpy, &xev, False,
 1681                                             "already locked");
 1682                 }
 1683               else if (msg == XA_SUSPEND)
 1684                 {
 1685                   force_blank_p = True;
 1686                   if (lock_p) force_lock_p = True;
 1687                   ignore_activity_before = now + 2;
 1688                   blank_mode = msg;
 1689                   clientmessage_response (dpy, &xev, True, "suspending");
 1690                 }
 1691               else if (msg == XA_EXIT)
 1692                 {
 1693                   if (current_state == UNBLANKED ||
 1694                       current_state == BLANKED)
 1695                     {
 1696                       clientmessage_response (dpy, &xev, True, "exiting");
 1697                       XSync (dpy, False);
 1698                       saver_exit (0);
 1699                     }
 1700                   else
 1701                     clientmessage_response (dpy, &xev, False,
 1702                                             "screen is locked");
 1703                 }
 1704               else if (msg == XA_RESTART)
 1705                 {
 1706                   if (current_state == UNBLANKED ||
 1707                       current_state == BLANKED)
 1708                     {
 1709                       clientmessage_response (dpy, &xev, True, "restarting");
 1710                       XSync (dpy, False);
 1711                       restart_process();   /* Does not return */
 1712                       fprintf (stderr, "%s: RESTART FAILED!\n", blurb());
 1713                     }
 1714                   else
 1715                     clientmessage_response (dpy, &xev, False,
 1716                                             "screen is locked");
 1717                 }
 1718               else
 1719                 {
 1720                 BAD_CM:
 1721                   if (verbose_p)
 1722                     {
 1723                       Atom type;
 1724                       char *tstr, *name;
 1725                       Bool op = print_x11_error_p;
 1726                       print_x11_error_p = False;     /* Ignore BadAtom */
 1727                       type = xev.xclient.message_type;
 1728                       tstr = type ? XGetAtomName (dpy, type) : 0;
 1729                       name = msg  ? XGetAtomName (dpy, msg)  : 0;
 1730                       fprintf (stderr,
 1731                                "%s: unrecognized ClientMessage %s %s\n",
 1732                                blurb(),
 1733                                (tstr ? tstr : "???"),
 1734                                (name ? name : "???"));
 1735                       if (tstr) XFree (tstr);
 1736                       if (name) XFree (name);
 1737                       print_x11_error_p = op;
 1738                     }
 1739                 }
 1740               continue;
 1741             }
 1742 
 1743 
 1744           /****************************************************************
 1745            Normal X11 keyboard and mouse events
 1746            ****************************************************************/
 1747 
 1748           /* XInput2 "raw" events bypass X11 grabs, but grabs take priority.
 1749              If this process has the keyboard and mouse grabbed, it receives
 1750              the following events:
 1751 
 1752                - X11 KeyPress, KeyRelease
 1753                - X11 ButtonPress, ButtonRelease
 1754                - X11 MotionNotify
 1755                - XInput XI_RawButtonPress, XI_RawButtonRelease
 1756                - XInput XI_RawMotion
 1757 
 1758              Note that button and motion events are doubly reported, but
 1759              keyboard events are not.
 1760 
 1761              If this process does *not* have the keyboard and mouse grabbed,
 1762              it receives the following events, regardless of the window in
 1763              which they occur:
 1764 
 1765                - XInput XI_RawKeyPress, XI_RawKeyRelease
 1766                - XInput XI_RawButtonPress, XI_RawButtonRelease
 1767                - XInput XI_RawMotion
 1768 
 1769              But here's an irritating kink: though XInput2 generally allows
 1770              snooping of everything, it respects GrabModeSync.  What this
 1771              means is that if some other process has the keyboard grabbed with
 1772              "Sync" instead of "Async", then this process will not see any of
 1773              the events until that process releases its grab, and then the
 1774              events come in late, all at once.  Verify this by running:
 1775 
 1776                test-xinput &
 1777                   Note that keyboard and mouse events are detected.
 1778                test-grab --mouse --mouse-async --kbd-async
 1779                   Same.
 1780                test-grab --mouse --mouse-sync --kbd-async
 1781                   Keyboard events are detected.
 1782                   No motion or button events until "test-grab" is killed.
 1783                test-grab --mouse --mouse-async --kbd-sync
 1784                   Vice versa.
 1785            */
 1786 
 1787           case KeyPress:
 1788           case KeyRelease:
 1789             if (current_state != AUTH &&  /* logged by xscreensaver-auth */
 1790                 (verbose_p > 1 ||
 1791                  (verbose_p && now - active_at > 1)))
 1792               print_xinput_event (dpy, &xev, "");
 1793             active_at = now;
 1794             continue;
 1795             break;
 1796           case ButtonPress:
 1797           case ButtonRelease:
 1798             active_at = now;
 1799             if (verbose_p)
 1800               print_xinput_event (dpy, &xev, "");
 1801             continue;
 1802             break;
 1803           case MotionNotify:
 1804             /* Since we always get XI_RawMotion, but only get MotionNotify
 1805                when grabbed, we can just ignore MotionNotify and let the
 1806                XI_RawMotion clause handle hysteresis. */
 1807             if (verbose_p > 1)
 1808               print_xinput_event (dpy, &xev, "ignored");
 1809             continue;
 1810             break;
 1811           default:
 1812             break;
 1813           }
 1814 
 1815 
 1816           /****************************************************************
 1817            XInput keyboard and mouse events
 1818            ****************************************************************/
 1819 
 1820           if (xev.xcookie.type != GenericEvent ||
 1821               xev.xcookie.extension != xi_opcode)
 1822             continue;  /* not an XInput event */
 1823 
 1824           if (!xev.xcookie.data)
 1825             XGetEventData (dpy, &xev.xcookie);
 1826           if (!xev.xcookie.data)
 1827             continue;  /* Bogus XInput event */
 1828 
 1829           switch (xev.xcookie.evtype) {
 1830           case XI_RawKeyPress:
 1831           case XI_RawKeyRelease:
 1832           case XI_RawButtonPress:
 1833           case XI_RawButtonRelease:
 1834             if (current_state != AUTH &&  /* logged by xscreensaver-auth */
 1835                 (verbose_p > 1 ||
 1836                  (verbose_p && now - active_at > 1)))
 1837               print_xinput_event (dpy, &xev, "");
 1838             active_at = now;
 1839             break;
 1840 
 1841           case XI_RawMotion:
 1842             {
 1843               /* Mouse wheel scrolling sends Button4 and Button5 events as well
 1844                  as motion, so we handled those above, in XI_RawButtonPress.
 1845                  The raw_values in the motion event for a mouse wheel reflect
 1846                  the position of the wheel sensor.
 1847 
 1848                  On a trackpad where two-finger-swipe is a scroll gesture, I
 1849                  saw behavior identical to a mouse wheel -- it does not send
 1850                  RawTouch events.
 1851                */
 1852 
 1853               /* Don't poll the mouse position more frequently than once
 1854                  a second.  The motion only counts as activity if it has
 1855                  moved farther than N pixels per second.
 1856                */
 1857               int secs = now - last_mouse.time;
 1858               if (secs >= 1)
 1859                 {
 1860                   Window root_ret, child_ret;
 1861                   int root_x, root_y;
 1862                   int win_x, win_y;
 1863                   unsigned int mask;
 1864                   int dist;
 1865                   Bool ignored_p = False;
 1866 
 1867                   XQueryPointer (dpy, DefaultRootWindow (dpy),
 1868                                  &root_ret, &child_ret, &root_x, &root_y,
 1869                                  &win_x, &win_y, &mask);
 1870                   dist = MAX (ABS (last_mouse.x - root_x),
 1871                               ABS (last_mouse.y - root_y));
 1872 
 1873                   ignored_p = (ignore_motion_p || dist < pointer_hysteresis);
 1874 
 1875                   if (! ignored_p)
 1876                     {
 1877                       active_at = now;
 1878                       last_mouse.time = now;
 1879                       last_mouse.x = root_x;
 1880                       last_mouse.y = root_y;
 1881                     }
 1882 
 1883                   if (verbose_p > 1 ||
 1884                       (verbose_p && now - active_at > 5))
 1885                     print_xinput_event (dpy, &xev, 
 1886                                         (ignored_p ? " ignored" : ""));
 1887                 }
 1888             }
 1889             break;
 1890 
 1891           default:
 1892             if (verbose_p)
 1893               print_xinput_event (dpy, &xev, "");
 1894             break;
 1895           }
 1896 
 1897           XFreeEventData (dpy, &xev.xcookie);
 1898         }
 1899 
 1900 
 1901       /********************************************************************
 1902        Advancing the state machine
 1903        ********************************************************************/
 1904 
 1905       /* If it's time, see if the .xscreensaver file has changed, since that
 1906          might change the blank and lock timeouts.
 1907        */
 1908       if (now >= last_checked_init_file + 60)
 1909         {
 1910           last_checked_init_file = now;
 1911           if (verbose_p)
 1912             fprintf(stderr,"%s: checking init file\n", blurb());
 1913           read_init_files (False);
 1914         }
 1915 
 1916       /* Now that events have been processed, see if the state should change,
 1917          based on any events received and the current time.
 1918        */
 1919       switch (current_state) {
 1920       case UNBLANKED:
 1921         if (!locking_disabled_p &&
 1922             (force_lock_p ||
 1923              (lock_p && 
 1924               now >= active_at + blank_timeout + lock_timeout)))
 1925           {
 1926             fprintf (stderr, "%s: locking\n", blurb());
 1927             if (grab_keyboard_and_mouse (mouse_screen (dpy)))
 1928               {
 1929                 current_state = LOCKED;
 1930                 blanked_at = now;
 1931                 authenticated_p = False;
 1932                 store_saver_status (dpy, True, True, now);
 1933               }
 1934             else
 1935               fprintf (stderr, "%s: unable to grab -- locking aborted!\n",
 1936                        blurb());
 1937 
 1938             force_lock_p = False;   /* Single shot */
 1939           }
 1940         else if (force_blank_p ||
 1941                  now >= active_at + blank_timeout)
 1942           {
 1943             fprintf (stderr, "%s: blanking\n", blurb());
 1944             if (grab_keyboard_and_mouse (mouse_screen (dpy)))
 1945               {
 1946                 current_state = BLANKED;
 1947                 blanked_at = now;
 1948                 store_saver_status (dpy, True, False, now);
 1949               }
 1950             else
 1951               fprintf (stderr, "%s: unable to grab -- blanking aborted!\n",
 1952                        blurb());
 1953           }
 1954 
 1955         if (current_state == BLANKED || current_state == LOCKED)
 1956           {
 1957             /* Grab succeeded and state changed: launch graphics. */
 1958             if (! saver_gfx_pid)
 1959               {
 1960                 static Bool first_time_p = True;
 1961                 char *av[20];
 1962                 int ac = 0;
 1963                 av[ac++] = SAVER_GFX_PROGRAM;
 1964                 if (first_time_p)  av[ac++] = "--init";
 1965                 if (verbose_p)     av[ac++] = "--verbose";
 1966                 if (verbose_p > 1) av[ac++] = "--verbose";
 1967                 if (verbose_p > 2) av[ac++] = "--verbose";
 1968                 if (debug_p)       av[ac++] = "--debug";
 1969 
 1970                 if (blank_mode == XA_NEXT)
 1971                   av[ac++] = "--next";
 1972                 else if (blank_mode == XA_PREV)
 1973                   av[ac++] = "--prev";
 1974                 else if (blank_mode == XA_SELECT)
 1975                   av[ac++] = "--select";
 1976                 else if (blank_mode == XA_DEMO)
 1977                   av[ac++] = "--demo";
 1978                 else if (blank_mode == XA_SUSPEND)
 1979                   av[ac++] = "--emergency";
 1980                   
 1981                 if (blank_mode == XA_SELECT || blank_mode == XA_DEMO)
 1982                   av[ac++] = blank_mode_arg;
 1983 
 1984                 av[ac] = 0;
 1985                 gfx_stopped_p = False;
 1986                 saver_gfx_pid = fork_and_exec (dpy, ac, av);
 1987                 respawn_thrashing_count = 0;
 1988                 first_time_p = False;
 1989               }
 1990           }
 1991         break;
 1992 
 1993       case BLANKED:
 1994         if (!locking_disabled_p &&
 1995             (force_lock_p ||
 1996              (lock_p && 
 1997               now >= blanked_at + lock_timeout)))
 1998           {
 1999             if (verbose_p)
 2000               fprintf (stderr, "%s: locking%s\n", blurb(),
 2001                        (force_lock_p ? "" : " after timeout"));
 2002             current_state = LOCKED;
 2003             authenticated_p = False;
 2004             store_saver_status (dpy, True, True, now);
 2005             force_lock_p = False;   /* Single shot */
 2006           }
 2007         else if (active_at >= now &&
 2008                  active_at >= ignore_activity_before)
 2009           {
 2010           UNBLANK:
 2011             if (verbose_p)
 2012               fprintf (stderr, "%s: unblanking\n", blurb());
 2013             current_state = UNBLANKED;
 2014             ignore_motion_p = False;
 2015             store_saver_status (dpy, False, False, now);
 2016 
 2017             if (saver_gfx_pid)
 2018               {
 2019                 if (verbose_p)
 2020                   fprintf (stderr,
 2021                            "%s: pid %lu: killing " SAVER_GFX_PROGRAM "\n",
 2022                            blurb(), (unsigned long) saver_gfx_pid);
 2023                 kill (saver_gfx_pid, SIGTERM);
 2024                 respawn_thrashing_count = 0;
 2025 
 2026                 if (gfx_stopped_p)  /* SIGCONT to allow SIGTERM to proceed */
 2027                   {
 2028                     if (verbose_p)
 2029                       fprintf (stderr, "%s: pid %lu: sending "
 2030                                SAVER_GFX_PROGRAM " SIGCONT\n",
 2031                                blurb(), (unsigned long) saver_gfx_pid);
 2032                     gfx_stopped_p = False;
 2033                     kill (-saver_gfx_pid, SIGCONT);  /* send to process group */
 2034                   }
 2035               }
 2036 
 2037             ungrab_keyboard_and_mouse (dpy);
 2038           }
 2039         break;
 2040 
 2041       case LOCKED:
 2042         if (active_at >= now &&
 2043             active_at >= ignore_activity_before)
 2044           {
 2045             char *av[10];
 2046             int ac = 0;
 2047 
 2048             if (saver_gfx_pid)
 2049               {
 2050                 /* To suspend or not suspend?  If we don't suspend, then a
 2051                    misbehaving or heavily-loaded screenhack might make it more
 2052                    difficult to type in the password.  Though that seems pretty
 2053                    unlikely.
 2054 
 2055                    But if we do suspend, then attaching a new monitor while
 2056                    the unlock dialog is up will cause a new desktop to be
 2057                    visible, since "xscreensaver-gfx" is the process that
 2058                    handles RANDR.  You can't interact with that new desktop,
 2059                    but you can see it, possibly including (depending on the
 2060                    window manager) the names of file icons on the desktop, or
 2061                    other things. */
 2062 # if 0
 2063                 if (verbose_p)
 2064                   fprintf (stderr, "%s: pid %lu: sending " SAVER_GFX_PROGRAM
 2065                            " SIGSTOP\n", blurb(),
 2066                            (unsigned long) saver_gfx_pid);
 2067                 gfx_stopped_p = True;
 2068                 kill (-saver_gfx_pid, SIGSTOP);  /* send to process group */
 2069 # endif
 2070               }
 2071 
 2072             if (verbose_p)
 2073               fprintf (stderr, "%s: authorizing\n", blurb());
 2074             current_state = AUTH;
 2075 
 2076             /* We already hold the mouse grab, but try to re-grab it with
 2077                a different mouse pointer, so that the pointer shows up while
 2078                the auth dialog is raised.  We can ignore failures here. */
 2079             grab_mouse (mouse_screen (dpy), auth_cursor);
 2080 
 2081             av[ac++] = SAVER_AUTH_PROGRAM;
 2082             if (verbose_p)     av[ac++] = "--verbose";
 2083             if (verbose_p > 1) av[ac++] = "--verbose";
 2084             if (verbose_p > 2) av[ac++] = "--verbose";
 2085             if (debug_p)       av[ac++] = "--debug";
 2086             av[ac] = 0;
 2087             saver_auth_pid = fork_and_exec (dpy, ac, av);
 2088           }
 2089         break;
 2090 
 2091       case AUTH:
 2092         if (saver_auth_pid)
 2093           {
 2094             /* xscreensaver-auth still running -- wait for it to exit. */
 2095           }
 2096         else if (authenticated_p)
 2097           {
 2098             /* xscreensaver-auth exited with "success" status */
 2099             if (verbose_p)
 2100               fprintf (stderr, "%s: unlocking\n", blurb());
 2101             authenticated_p = False;
 2102             goto UNBLANK;
 2103           }
 2104         else
 2105           {
 2106             /* xscreensaver-auth exited with non-success, or with signal. */
 2107             if (verbose_p)
 2108               fprintf (stderr, "%s: authorization failed\n", blurb());
 2109             current_state = LOCKED;
 2110             authenticated_p = False;
 2111 
 2112             /* We already hold the mouse grab, but try to re-grab it with
 2113                a different mouse pointer, to hide the pointer again now that
 2114                the auth dialog is gone.  We can ignore failures here. */
 2115             grab_mouse (mouse_screen (dpy), blank_cursor);
 2116 
 2117             /* When the unlock dialog is dismissed, ignore any input for a
 2118                second to give the user time to take their hands off of the
 2119                keyboard and mouse, so that it doesn't pop up again
 2120                immediately. */
 2121             ignore_activity_before = now + 1;
 2122 
 2123             if (gfx_stopped_p)  /* SIGCONT to resume savers */
 2124               {
 2125                 if (verbose_p)
 2126                   fprintf (stderr, "%s: pid %lu: sending " SAVER_GFX_PROGRAM
 2127                            " SIGCONT\n",
 2128                            blurb(), (unsigned long) saver_gfx_pid);
 2129                 gfx_stopped_p = False;
 2130                 kill (-saver_gfx_pid, SIGCONT);  /* send to process group */
 2131               }
 2132           }
 2133         break;
 2134 
 2135       default:
 2136         /* abort(); */
 2137         break;
 2138       }
 2139     }
 2140 }
 2141 
 2142 
 2143 /* There is no good reason for this executable to be setuid, but in case
 2144    it is, turn that off.
 2145  */
 2146 static const char *
 2147 disavow_privileges (void)
 2148 {
 2149   static char msg[255];
 2150   uid_t euid = geteuid();
 2151   gid_t egid = getegid();
 2152   uid_t uid = getuid();
 2153   gid_t gid = getgid();
 2154 
 2155   if (uid == euid && gid == egid)
 2156     return 0;
 2157 
 2158   /* Without setgroups(), the process will retain any supplementary groups
 2159      associated with the uid, e.g. the default groups of the "root" user.
 2160      But setgroups() can only be called by root, and returns EPERM for other
 2161      users even if the call would be a no-op.  So ignore its error status.
 2162    */
 2163   setgroups (1, &gid);
 2164 
 2165   if (gid != egid && setgid (gid) != 0)
 2166     {
 2167       fprintf (stderr, "%s: setgid %d -> %d failed\n", blurb(), egid, gid);
 2168       exit (1);
 2169     }
 2170 
 2171   if (uid != euid && setgid (gid) != 0)
 2172     {
 2173       fprintf (stderr, "%s: setuid %d -> %d failed\n", blurb(), euid, uid);
 2174       exit (1);
 2175     }
 2176 
 2177   /* Return the message since this is before verbose_p is set or log opened. */
 2178   sprintf (msg, "setuid %d:%d -> %d:%d", euid, egid, uid, gid);
 2179   return msg;
 2180 }
 2181 
 2182 
 2183 int
 2184 main (int argc, char **argv)
 2185 {
 2186   Display *dpy = 0;
 2187   char *dpy_str = getenv ("DISPLAY");
 2188   char *logfile = 0;
 2189   Bool sync_p = False;
 2190   Bool cmdline_verbose_val = False, cmdline_verbose_p = False;
 2191   Bool cmdline_splash_val  = False, cmdline_splash_p  = False;
 2192   const char *s;
 2193   const char *pmsg;
 2194   int i;
 2195 
 2196   progname = argv[0];
 2197   s = strrchr (progname, '/');
 2198   if (s) progname = s+1;
 2199 
 2200   pmsg = disavow_privileges();
 2201 
 2202   fclose (stdin);
 2203   fix_fds();
 2204 
 2205   for (i = 1; i < argc; i++)
 2206     {
 2207       const char *oa = argv[i];
 2208       /* XScreenSaver predates the "--arg" convention. */
 2209       if (argv[i][0] == '-' && argv[i][1] == '-')
 2210         argv[i]++;
 2211 
 2212       if (!strcmp (argv[i], "-debug"))
 2213         debug_p = True;
 2214       else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
 2215         {
 2216           verbose_p++;
 2217           cmdline_verbose_p = True, cmdline_verbose_val = verbose_p;
 2218         }
 2219       else if (!strcmp (argv[i], "-vv"))
 2220         {
 2221           verbose_p += 2;
 2222           cmdline_verbose_p = True, cmdline_verbose_val = verbose_p;
 2223         }
 2224       else if (!strcmp (argv[i], "-vvv"))
 2225         {
 2226           verbose_p += 3;
 2227           cmdline_verbose_p = True, cmdline_verbose_val = verbose_p;
 2228         }
 2229       else if (!strcmp (argv[i], "-vvvv"))
 2230         {
 2231           verbose_p += 4;
 2232           cmdline_verbose_p = True, cmdline_verbose_val = verbose_p;
 2233         }
 2234       else if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-quiet"))
 2235         {
 2236           verbose_p = 0;
 2237           cmdline_verbose_p = True, cmdline_verbose_val = verbose_p;
 2238         }
 2239       else if (!strcmp (argv[i], "-splash"))
 2240         {
 2241           splash_p = True;
 2242           cmdline_splash_p = True, cmdline_splash_val = splash_p;
 2243         }
 2244       else if (!strcmp (argv[i], "-no-splash") ||
 2245                !strcmp (argv[i], "-nosplash"))
 2246         {
 2247           splash_p = False;
 2248           cmdline_splash_p = True, cmdline_splash_val = splash_p;
 2249         }
 2250       else if (!strcmp (argv[i], "-log"))
 2251         {
 2252           logfile = argv[++i];
 2253           if (!logfile) goto HELP;
 2254         }
 2255       else if (!strcmp (argv[i], "-d") ||
 2256                !strcmp (argv[i], "-dpy") ||
 2257                !strcmp (argv[i], "-disp") ||
 2258                !strcmp (argv[i], "-display"))
 2259         {
 2260           dpy_str = argv[++i];
 2261           if (!dpy_str) goto HELP;
 2262         }
 2263       else if (!strcmp (argv[i], "-sync") ||
 2264                !strcmp (argv[i], "-synch") ||
 2265                !strcmp (argv[i], "-synchronize") ||
 2266                !strcmp (argv[i], "-synchronise"))
 2267         sync_p = True;
 2268       else if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "-help"))
 2269         {
 2270         HELP:
 2271           print_banner();
 2272           fprintf (stderr, 
 2273                    "\t\thttps://www.jwz.org/xscreensaver/\n"
 2274                    "\n"
 2275                    "\tOptions:\n\n"
 2276                    "\t\t--dpy host:display.screen\n"
 2277                    "\t\t--verbose\n"
 2278                    "\t\t--no-splash\n"
 2279                    "\t\t--log logfile\n"
 2280                    "\n"
 2281                    "\tRun 'xscreensaver-settings' to configure.\n"
 2282                    "\n");
 2283           saver_exit (1);
 2284         }
 2285       else
 2286         {
 2287           fprintf (stderr, "\n%s: unknown option: %s\n\n", blurb(), oa);
 2288           goto HELP;
 2289         }
 2290     }
 2291 
 2292   if (logfile)
 2293     {
 2294       int stdout_fd = 1;
 2295       int stderr_fd = 2;
 2296       int fd = open (logfile, O_WRONLY | O_APPEND | O_CREAT, 0666);
 2297       if (fd < 0)
 2298         {
 2299           char buf[255];
 2300         FAIL:
 2301           sprintf (buf, "%.100s: %.100s", blurb(), logfile);
 2302           perror (buf);
 2303           fflush (stderr);
 2304           fflush (stdout);
 2305           saver_exit (1);
 2306         }
 2307 
 2308       fprintf (stderr, "%s: logging to file %s\n", blurb(), logfile);
 2309 
 2310       if (dup2 (fd, stdout_fd) < 0) goto FAIL;
 2311       if (dup2 (fd, stderr_fd) < 0) goto FAIL;
 2312 
 2313       fprintf (stderr, "\n\n"
 2314                "#####################################"
 2315                "#####################################\n"
 2316                "%s: logging to \"%s\"\n"
 2317                "#####################################"
 2318                "#####################################\n"
 2319                "\n",
 2320                blurb(), logfile);
 2321 
 2322       if (!verbose_p)
 2323         verbose_p = True;
 2324       cmdline_verbose_val = verbose_p;
 2325     }
 2326 
 2327   save_argv (argc, argv);
 2328   hack_environment();
 2329   print_banner();
 2330   read_init_files (True);
 2331 
 2332   /* Command line overrides init file */
 2333   if (cmdline_verbose_p) verbose_p = cmdline_verbose_val;
 2334   if (cmdline_splash_p)  splash_p  = cmdline_splash_val;
 2335 
 2336   if (verbose_p)
 2337     fprintf (stderr, "%s: running in process %lu\n", blurb(),
 2338              (unsigned long) getpid());
 2339 
 2340   if (verbose_p && pmsg)
 2341     fprintf (stderr, "%s: %s\n", blurb(), pmsg);
 2342 
 2343   if (! dpy_str)
 2344     {
 2345       dpy_str = ":0.0";
 2346       fprintf (stderr,
 2347                "%s: warning: $DISPLAY is not set: defaulting to \"%s\"\n",
 2348                blurb(), dpy_str);
 2349     }
 2350 
 2351   /* Copy the -dpy arg to $DISPLAY for subprocesses. */
 2352   {
 2353     char *s = (char *) malloc (strlen(dpy_str) + 20);
 2354     sprintf (s, "DISPLAY=%s", dpy_str);
 2355     putenv (s);
 2356     /* free (s); */  /* some versions of putenv do not copy */
 2357   }
 2358 
 2359   dpy = XOpenDisplay (dpy_str);
 2360   if (!dpy) saver_exit (1);
 2361 
 2362   if (sync_p)
 2363     {
 2364       XSynchronize (dpy, True);
 2365       XSync (dpy, False);
 2366     }
 2367 
 2368   XSetErrorHandler (error_handler);
 2369 
 2370   main_loop (dpy);
 2371   saver_exit (0);
 2372   return (1);
 2373 }