| /* |
| * 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); |
| } |