/* * blockdpy.c * * Copyright (c) 2004 Karl J. Runge <runge@karlrunge.com> * All rights reserved. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * *----------------------------------------------------------------------- * * This tool is intended for use with x11vnc. It is a kludge to try to * "block" access via the physical display while x11vnc is running. * * The expected application is that of a user who screen-locks his * workstation before leaving and then later unlocks it remotely via * x11vnc. The user is concerned people with physical access to the * machine will be watching, etc. * * Of course if people have physical access to the machine there are * much larger potential security problems, but the idea here is to put * up a larger barrier than simply turning on the monitor and tapping * the mouse (i.e. to wake up the monitor from DPMS and then observe * the x11vnc activity). * * This program requires DPMS support in the video card and monitor, * and the DPMS extension in the X server and the corresponding * library with the DPMS API (libXext). * * It starts off by forcing the state to be DPMSModeOff (lowest power). * Then it periodically (a few times a second) checks if the system is * still in that state. If it discovers it to be in another state, it * immediately runs, as a separate command, a screen-lock program, "xlock" * by default. The environment variable XLOCK_CMD or -lock option can * override this default. "xscreensaver-command" might be another choice. * * It is up to the user to make sure the screen-lock command works * and PATH is set up correctly, etc. The command can do anything, * it doesn't have to lock the screen. It could make the sound of a * dog barking, for example :-) * * The option '-grab' causes the program to additionally call * XGrabServer() to try to prevent physical mouse or keyboard input to get * to any applications on the screen. NOTE: do NOT use, not working yet! * Freezes everything. * * The options: -display and -auth can be used to set the DISPLAY and * XAUTHORITY environment variables via the command line. * * The options -standby and -suspend change the desired DPMS level * to be DPMSModeStandby and DPMSModeSuspend, respectively. * * The option '-f flagfile' indicates a flag file to watch for to cause * the program to clean up and exit once it exists. No screen locking is * done when the file appears: it is an 'all clear' flag. Presumably the * x11vnc user has relocked the screen before the flagfile is created. * See below for coupling this behavior with the -gone command. * * The option '-bg' causes the program to fork into the background and * return 0 if everything looks ok. If there was an error up to that * point the return value would be 1. * * Option '-v' prints more info out, useful for testing and debugging. * * * These options allow this sort of x11vnc usage: * * x11vnc ... -accept "blockdpy -bg -f $HOME/.bdpy" -gone "touch $HOME/.bdpy" * * (this may also work for gone: -gone "killall blockdpy") * * In the above, once a client connects this program starts up in the * background and monitors the DPMS level. When the client disconnects * (he relocked the screen before doing so) the flag file is created and * so this program exits normally. On the other hand, if the physical * mouse or keyboard was used during the session, this program would * have locked the screen as soon as it noticed the DPMS change. * * One could create shell scripts for -accept and -gone that do much * more sophisticated things. This would be needed if more than one * client connects at a time. * * It is important to remember once this program locks the screen * it *exits*, so nothing will be watching the screen at that point. * Don't immediately unlock the screen from in x11vnc!! Best to think * about what might have happened, disconnect the VNC viewer, and then * restart x11vnc (thereby having this monitoring program started again). * * * To compile on Linux or Solaris: cc -o blockdpy blockdpy.c -L /usr/X11R6/lib -L /usr/openwin/lib -lX11 -lXext * (may also need -I /usr/.../include on older machines). * */ #include <stdio.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <signal.h> #include <X11/Xlib.h> #include <X11/Xproto.h> #include <X11/extensions/dpms.h> Display *dpy = NULL; CARD16 standby, suspend, off; int grab = 0; int verbose = 0; int bg = 0; /* for sleeping some number of millisecs */ struct timeval _mysleep; #define msleep(x) \ _mysleep.tv_sec = ((x)*1000) / 1000000; \ _mysleep.tv_usec = ((x)*1000) % 1000000; \ select(0, NULL, NULL, NULL, &_mysleep); /* called on signal or if DPMS changed, or other problem */ void reset(int sig) { if (grab) { if (verbose) { fprintf(stderr, "calling XUngrabServer()\n"); } XUngrabServer(dpy); } if (verbose) { fprintf(stderr, "resetting original DPMS values.\n"); } fprintf(stderr, "blockdpy: reset sig=%d called\n", sig); DPMSEnable(dpy); DPMSSetTimeouts(dpy, standby, suspend, off); XFlush(dpy); if (sig) { XCloseDisplay(dpy); exit(0); } } int main(int argc, char** argv) { int verbose = 0, bg = 0; int i, ev, er; char *lock_cmd = "xlock"; char *flag_file = NULL; char estr[100], cmd[500]; struct stat sbuf; CARD16 power; CARD16 desired = DPMSModeOff; BOOL state; /* setup the lock command. it may be reset by -lock below. */ if (getenv("XLOCK_CMD")) { lock_cmd = (char *) getenv("XLOCK_CMD"); } /* process cmd line: */ for (i=1; i<argc; i++) { if (!strcmp(argv[i], "-display")) { sprintf(estr, "DISPLAY=%s", argv[++i]); putenv(strdup(estr)); } else if (!strcmp(argv[i], "-auth")) { sprintf(estr, "XAUTHORITY=%s", argv[++i]); putenv(strdup(estr)); } else if (!strcmp(argv[i], "-lock")) { lock_cmd = argv[++i]; } else if (!strcmp(argv[i], "-f")) { flag_file = argv[++i]; unlink(flag_file); } else if (!strcmp(argv[i], "-grab")) { grab = 1; } else if (!strcmp(argv[i], "-bg")) { bg = 1; } else if (!strcmp(argv[i], "-v")) { verbose = 1; } else if (!strcmp(argv[i], "-standby")) { desired = DPMSModeStandby; } else if (!strcmp(argv[i], "-suspend")) { desired = DPMSModeSuspend; } else if (!strcmp(argv[i], "-off")) { desired = DPMSModeOff; } } /* we want it to go into background to avoid blocking, so add '&'. */ strcpy(cmd, lock_cmd); strcat(cmd, " &"); lock_cmd = cmd; /* close any file descriptors we may have inherited (e.g. port 5900) */ for (i=3; i<=100; i++) { close(i); } /* open DISPLAY */ dpy = XOpenDisplay(NULL); if (! dpy) { fprintf(stderr, "XOpenDisplay failed.\n"); exit(1); } /* check for DPMS extension */ if (! DPMSQueryExtension(dpy, &ev, &er)) { fprintf(stderr, "DPMSQueryExtension failed.\n"); exit(1); } if (! DPMSCapable(dpy)) { fprintf(stderr, "DPMSCapable failed.\n"); exit(1); } /* make sure DPMS is enabled */ if (! DPMSEnable(dpy)) { fprintf(stderr, "DPMSEnable failed.\n"); exit(1); } /* retrieve the timeouts for later resetting */ if (! DPMSGetTimeouts(dpy, &standby, &suspend, &off)) { fprintf(stderr, "DPMSGetTimeouts failed.\n"); exit(1); } if (! standby || ! suspend || ! off) { /* if none, set to some reasonable values */ standby = 900; suspend = 1200; off = 1800; } if (verbose) { fprintf(stderr, "DPMS timeouts: %d %d %d\n", standby, suspend, off); } /* now set them to very small values */ if (desired == DPMSModeOff) { if (! DPMSSetTimeouts(dpy, 1, 1, 1)) { fprintf(stderr, "DPMSSetTimeouts failed.\n"); exit(1); } } else if (desired == DPMSModeSuspend) { if (! DPMSSetTimeouts(dpy, 1, 1, 0)) { fprintf(stderr, "DPMSSetTimeouts failed.\n"); exit(1); } } else if (desired == DPMSModeStandby) { if (! DPMSSetTimeouts(dpy, 1, 0, 0)) { fprintf(stderr, "DPMSSetTimeouts failed.\n"); exit(1); } } XFlush(dpy); /* set handlers for clean up in case we terminate via signal */ signal(SIGHUP, reset); signal(SIGINT, reset); signal(SIGQUIT, reset); signal(SIGABRT, reset); signal(SIGTERM, reset); /* force state into DPMS Off (lowest power) mode */ if (! DPMSForceLevel(dpy, desired)) { fprintf(stderr, "DPMSForceLevel failed.\n"); exit(1); } XFlush(dpy); /* read state */ msleep(500); if (! DPMSInfo(dpy, &power, &state)) { fprintf(stderr, "DPMSInfo failed.\n"); exit(1); } fprintf(stderr, "power: %d state: %d\n", power, state); /* grab display if desired. NOT WORKING */ if (grab) { if (verbose) { fprintf(stderr, "calling XGrabServer()\n"); } XGrabServer(dpy); } /* go into background if desired. */ if (bg) { pid_t p; if ((p = fork()) != 0) { if (p < 0) { fprintf(stderr, "problem forking.\n"); exit(1); } else { /* XXX no fd closing */ exit(0); } } } /* main loop: */ while (1) { /* reassert DPMSModeOff (desired) */ if (verbose) fprintf(stderr, "reasserting desired DPMSMode\n"); DPMSForceLevel(dpy, desired); XFlush(dpy); /* wait a bit */ msleep(200); /* check for flag file appearence */ if (flag_file && stat(flag_file, &sbuf) == 0) { if (verbose) { fprintf(stderr, "flag found: %s\n", flag_file); } unlink(flag_file); reset(0); exit(0); } /* check state and power level */ if (! DPMSInfo(dpy, &power, &state)) { fprintf(stderr, "DPMSInfo failed.\n"); reset(0); exit(1); } if (verbose) { fprintf(stderr, "power: %d state: %d\n", power, state); } if (!state || power != desired) { /* Someone (or maybe a cat) is evidently watching... */ fprintf(stderr, "DPMS CHANGE: power: %d state: %d\n", power, state); break; } } reset(0); fprintf(stderr, "locking screen with command: \"%s\"\n", lock_cmd); system(lock_cmd); exit(0); }