diff options
Diffstat (limited to 'x11vnc/misc/blockdpy.c')
-rw-r--r-- | x11vnc/misc/blockdpy.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/x11vnc/misc/blockdpy.c b/x11vnc/misc/blockdpy.c new file mode 100644 index 0000000..f3b428e --- /dev/null +++ b/x11vnc/misc/blockdpy.c @@ -0,0 +1,352 @@ +/* + * 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); +} |