diff --git a/ChangeLog b/ChangeLog index e56d532..59fe24b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2004-05-21 Karl Runge + * x11vnc: -gone, -passwdfile, -o logfile; add view-only to -accept + 2004-05-08 Karl Runge * x11vnc: add -accept some-command/xmessage/popup diff --git a/contrib/ChangeLog b/contrib/ChangeLog index 04d6575..9e468f6 100644 --- a/contrib/ChangeLog +++ b/contrib/ChangeLog @@ -1,3 +1,10 @@ +2004-05-21 Karl Runge + * -accept: add view-only decision and other improvements. + * add -gone command option for when a client leaves. + Thanks to Jesus Alvarez for these ideas. + * -passwdfile to keep passwd off of cmd line. + * -o logfile send stderr to a logfile. + 2004-05-14 Karl Runge * improvements to -accept popup: yes/no buttons and timeout. * less fprintf under -q so '-q -inetd' has no stderr output. diff --git a/contrib/x11vnc.c b/contrib/x11vnc.c index d1543ce..144b280 100644 --- a/contrib/x11vnc.c +++ b/contrib/x11vnc.c @@ -131,7 +131,7 @@ #endif /* date +'"lastmod: %Y-%m-%d";' */ -char lastmod[] = "lastmod: 2004-05-14"; +char lastmod[] = "lastmod: 2004-05-21"; /* X and rfb framebuffer */ @@ -207,6 +207,7 @@ hint_t *hint_list; int shared = 0; /* share vnc display. */ char *allow_list = NULL; /* for -allow and -localhost */ char *accept_cmd = NULL; /* for -accept */ +char *gone_cmd = NULL; /* for -gone */ int view_only = 0; /* clients can only watch. */ int inetd = 0; /* spawned from inetd(1) */ int connect_once = 1; /* disconnect after first connection session. */ @@ -353,6 +354,7 @@ void clean_up_exit (int ret) { XTestDiscard(dpy); X_UNLOCK; + fflush(stderr); exit(ret); } @@ -437,12 +439,19 @@ void set_signals(void) { X_UNLOCK; } +int run_user_command(char *, rfbClientPtr); + int accepted_client = 0; void client_gone(rfbClientPtr client) { client_count--; rfbLog("client_count: %d\n", client_count); + if (gone_cmd) { + rfbLog("client_gone: using cmd for: %s\n", client->host); + run_user_command(gone_cmd, client); + } + if (inetd) { rfbLog("viewer exited.\n"); clean_up_exit(0); @@ -506,7 +515,7 @@ int check_access(char *addr) { * We go through this pain to avoid dependency on libXt. */ -int ugly_accept_window(char *addr, int timeout) { +int ugly_accept_window(char *addr, int X, int Y, int timeout, char *mode) { #define t2x2_width 16 #define t2x2_height 16 @@ -534,22 +543,62 @@ static char t2x2_bits[] = { double waited = 0.0; /* strings and geometries y/n */ - KeyCode key_y, key_n; - char str1[100]; - char str2[] = "To accept: press \"y\" or click the \"Yes\" button"; - char str3[] = "To reject: press \"n\" or click the \"No\" button"; - char str4[] = "Yes"; - char str5[] = "No"; - int x, y, w = 345, h = 125, ret = 0; + KeyCode key_y, key_n, key_v; + char strh[100]; + char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button"; + char str2_b[] = "To reject: press \"n\" or click the \"No\" button"; + char str3_b[] = "View only: press \"v\" or click the \"View\" button"; + char str1_m[] = "To accept: click the \"Yes\" button"; + char str2_m[] = "To reject: click the \"No\" button"; + char str3_m[] = "View only: click the \"View\" button"; + char str1_k[] = "To accept: press \"y\""; + char str2_k[] = "To reject: press \"n\""; + char str3_k[] = "View only: press \"v\""; + char *str1, *str2, *str3; + char str_y[] = "Yes"; + char str_n[] = "No"; + char str_v[] = "View"; + int x, y, w = 345, h = 150, ret = 0; int X_sh = 20, Y_sh = 30, dY = 20; - int Ye_x = 20, Ye_y = 0, Ye_w = 40, Ye_h = 20; - int No_x = 70, No_y = 0, No_w = 40, No_h = 20; - + int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20; + int No_x = 75, No_y = 0, No_w = 45, No_h = 20; + int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20; + + if (!strcmp(mode, "mouse_only")) { + str1 = str1_m; + str2 = str2_m; + str3 = str3_m; + } else if (!strcmp(mode, "key_only")) { + str1 = str1_k; + str2 = str2_k; + str3 = str3_k; + h -= dY; + } else { + str1 = str1_b; + str2 = str2_b; + str3 = str3_b; + } + if (view_only) { + h -= dY; + } - x = (dpy_x - w)/2; - y = (dpy_y - h)/2; - if (x < 0) x = 0; - if (y < 0) y = 0; + if (X < -dpy_x) { + x = (dpy_x - w)/2; /* large negative: center */ + if (x < 0) x = 0; + } else if (X < 0) { + x = dpy_x + X - w; /* from lower right */ + } else { + x = X; /* from upper left */ + } + + if (Y < -dpy_y) { + y = (dpy_y - h)/2; + if (y < 0) y = 0; + } else if (Y < 0) { + y = dpy_y + Y - h; + } else { + y = Y; + } X_LOCK; @@ -593,12 +642,13 @@ static char t2x2_bits[] = { XMapWindow(dpy, awin); XFlush(dpy); - sprintf(str1, "x11vnc: accept connection from %s?", addr); + sprintf(strh, "x11vnc: accept connection from %s?", addr); key_y = XKeysymToKeycode(dpy, XStringToKeysym("y")); key_n = XKeysymToKeycode(dpy, XStringToKeysym("n")); + key_v = XKeysymToKeycode(dpy, XStringToKeysym("v")); while (1) { - int out = -1, l1, l2, l3, l4, l5, x, y, tw; + int out = -1, x, y, tw, k; if (XCheckWindowEvent(dpy, awin, evmask, &ev)) { ; /* proceed to handling */ @@ -623,29 +673,55 @@ static char t2x2_bits[] = { while (XCheckTypedEvent(dpy, Expose, &ev)) { ; } - l1 = strlen(str1); l2 = strlen(str2); - l3 = strlen(str3); l4 = strlen(str4); l5 = strlen(str5); + k=0; + + /* instructions */ + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + strh, strlen(strh)); + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str1, strlen(str1)); + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str2, strlen(str2)); + if (! view_only) { + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str3, strlen(str3)); + } - XDrawString(dpy, awin, gc, X_sh, Y_sh+0*dY, str1, l1); - XDrawString(dpy, awin, gc, X_sh, Y_sh+1*dY, str2, l2); - XDrawString(dpy, awin, gc, X_sh, Y_sh+2*dY, str3, l3); + if (!strcmp(mode, "key_only")) { + break; + } - Ye_y = Y_sh+3*dY; - No_y = Y_sh+3*dY; + /* buttons */ + Ye_y = Y_sh+k*dY; + No_y = Y_sh+k*dY; + Vi_y = Y_sh+k*dY; XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h); XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h); + if (! view_only) { + XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y, + Vi_w, Vi_h); + } - tw = XTextWidth(font_info, str4, l4); + tw = XTextWidth(font_info, str_y, strlen(str_y)); tw = (Ye_w - tw)/2; if (tw < 0) tw = 1; XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5, - str4, l4); + str_y, strlen(str_y)); - tw = XTextWidth(font_info, str5, l5); + tw = XTextWidth(font_info, str_n, strlen(str_n)); tw = (No_w - tw)/2; if (tw < 0) tw = 1; XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5, - str5, l5); + str_n, strlen(str_n)); + + if (! view_only) { + tw = XTextWidth(font_info, str_v, + strlen(str_v)); + tw = (Vi_w - tw)/2; + if (tw < 0) tw = 1; + XDrawString(dpy, awin, gc, Vi_x+tw, Vi_y+Vi_h-5, + str_v, strlen(str_v)); + } break; @@ -659,20 +735,29 @@ static char t2x2_bits[] = { case ButtonPress: x = ev.xbutton.x; y = ev.xbutton.y; - if (x > No_x && x < No_x+No_w && y > No_y + if (!strcmp(mode, "key_only")) { + ; + } else if (x > No_x && x < No_x+No_w && y > No_y && y < No_y+No_h) { out = 0; } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y && y < Ye_y+Ye_h) { out = 1; + } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w + && y > Vi_y && y < Vi_y+Ye_h) { + out = 2; } break; case KeyPress: - if (ev.xkey.keycode == key_y) { + if (!strcmp(mode, "mouse_only")) { + ; + } else if (ev.xkey.keycode == key_y) { out = 1; } else if (ev.xkey.keycode == key_n) { out = 0; + } else if (! view_only && ev.xkey.keycode == key_v) { + out = 2; } break; default: @@ -692,13 +777,173 @@ static char t2x2_bits[] = { return ret; } +/* + * utility to run a user supplied command setting some RFB_ env vars. + * used by, e.g., accept_client() and client_gone() + */ +int run_user_command(char *cmd, rfbClientPtr client) { + char *dpystr = DisplayString(dpy); + static char *display_env = NULL; + static char env_rfb_client_id[100]; + static char env_rfb_client_ip[100]; + static char env_rfb_client_port[100]; + static char env_rfb_x11vnc_pid[100]; + char *addr = client->host; + int rc, fromlen, fromport; + struct sockaddr_in from; + + if (addr == NULL || addr[0] == '\0') { + addr = "unknown-host"; + } + + /* set RFB_CLIENT_ID to semi unique id for command to use */ + sprintf(env_rfb_client_id, "RFB_CLIENT_ID=%d", (int) client); + putenv(env_rfb_client_id); + + /* set RFB_CLIENT_IP to IP addr for command to use */ + sprintf(env_rfb_client_ip, "RFB_CLIENT_IP=%s", addr); + putenv(env_rfb_client_ip); + + /* set RFB_X11VNC_PID to our pid for command to use */ + sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID=%d", (int) getpid()); + putenv(env_rfb_x11vnc_pid); + + /* set RFB_CLIENT_PORT to peer port for command to use */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + fromport = -1; + if (!getpeername(client->sock, (struct sockaddr *)&from, &fromlen)) { + fromport = ntohs(from.sin_port); + } + sprintf(env_rfb_client_port, "RFB_CLIENT_PORT=%d", fromport); + putenv(env_rfb_client_port); + + /* + * Better set DISPLAY to the one we are polling, if they + * want something trickier, they can handle on their own + * via environment, etc. XXX really should save/restore old. + */ + if (display_env == NULL) { + display_env = (char *) malloc(strlen(dpystr)+10); + } + sprintf(display_env, "DISPLAY=%s", dpystr); + putenv(display_env); + + rfbLog("running command:\n"); + rfbLog(" %s\n", cmd); + + rc = system(cmd); + + if (rc >= 256) { + rc = rc/256; + } + rfbLog("command returned: %d\n", rc); + + sprintf(env_rfb_client_id, "RFB_CLIENT_ID="); + putenv(env_rfb_client_id); + + sprintf(env_rfb_client_ip, "RFB_CLIENT_IP="); + putenv(env_rfb_client_ip); + + sprintf(env_rfb_client_port, "RFB_CLIENT_PORT="); + putenv(env_rfb_client_port); + + sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID="); + putenv(env_rfb_x11vnc_pid); + + return rc; +} + +/* + * process a "yes:0,no:*,view:3" type action list comparing to command + * return code rc. * means the default action with no other match. + */ +int action_match(char *action, int rc) { + char *p, *q, *s = strdup(action); + int cases[4], i, result; + char *labels[4]; + + labels[1] = "yes"; + labels[2] = "no"; + labels[3] = "view"; + + rfbLog("accept_client: process action line: %s\n", + action); + + for (i=1; i <= 3; i++) { + cases[i] = -2; + } + + p = strtok(s, ","); + while (p) { + if ((q = strchr(p, ':')) != NULL) { + int in, k; + *q = '\0'; + q++; + if (strstr(p, "yes") == p) { + k = 1; + } else if (strstr(p, "no") == p) { + k = 2; + } else if (strstr(p, "view") == p) { + k = 3; + } else { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + if (*q == '*') { + cases[k] = -1; + } else if (sscanf(q, "%d", &in) == 1) { + if (in < 0) { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + cases[k] = in; + } else { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + } else { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + p = strtok(NULL, ","); + } + free(s); + + result = -1; + for (i=1; i <= 3; i++) { + if (cases[i] == -1) { + rfbLog("accept_client: default action is case=%d %s\n", + i, labels[i]); + result = i; + break; + } + } + if (result == -1) { + rfbLog("accept_client: no default action\n"); + } + for (i=1; i <= 3; i++) { + if (cases[i] >= 0 && cases[i] == rc) { + rfbLog("accept_client: matched action is case=%d %s\n", + i, labels[i]); + result = i; + break; + } + } + if (result < 0) { + rfbLog("no action match: %s rc=%d set to no\n", action, rc); + result = 2; + } + return result; +} + /* * Simple routine to prompt the user on the X display whether an incoming * client should be allowed to connect or not. If a gui is involved it * will be running in the environment/context of the X11 DISPLAY. * * The command supplied via -accept is run as is (i.e. no string - * substitution) with the RFB_CLIENT environment variable set to the + * substitution) with the RFB_CLIENT_IP environment variable set to the * incoming client's numerical IP address. * * If the external command exits with 0 the client is accepted, otherwise @@ -710,9 +955,11 @@ static char t2x2_bits[] = { * popup: use internal X widgets for prompting. * */ -int accept_client(char *addr) { +int accept_client(rfbClientPtr client) { + char xmessage[200], *cmd = NULL; - static char rfb_client_env[100], *display_env = NULL; + char *addr = client->host; + char *action = NULL; if (accept_cmd == NULL) { return 1; /* no command specified, so we accept */ @@ -722,63 +969,131 @@ int accept_client(char *addr) { addr = "unknown-host"; } - if (!strcmp(accept_cmd, "xmessage")) { - /* make our own command using xmessage(1) */ - sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center " - "'x11vnc: accept connection from %s?'", addr); - cmd = xmessage; - - } else if (strstr(accept_cmd, "popup:") == accept_cmd || - !strcmp(accept_cmd, "popup")) { - int timeout = 120; - char *p; + if (strstr(accept_cmd, "popup") == accept_cmd) { + /* use our builtin popup button */ + + /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */ + + int ret, timeout = 120; + int x = -64000, y = -64000; + char *p, *mode; + + /* extract timeout */ if ((p = strchr(accept_cmd, ':')) != NULL) { int in; if (sscanf(p+1, "%d", &in) == 1) { timeout = in; } } + /* extract geometry */ + if ((p = strpbrk(accept_cmd, "+-")) != NULL) { + int x1, y1; + if (sscanf(p, "+%d+%d", &x1, &y1) == 2) { + x = x1; + y = y1; + } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) { + x = x1; + y = -y1; + } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) { + x = -x1; + y = y1; + } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) { + x = -x1; + y = -y1; + } + } + + /* find mode: mouse, key, or both */ + if (strstr(accept_cmd, "popupmouse") == accept_cmd) { + mode = "mouse_only"; + } else if (strstr(accept_cmd, "popupkey") == accept_cmd) { + mode = "key_only"; + } else { + mode = "both"; + } + rfbLog("accept_client: using builtin popup for: %s\n", addr); - if (ugly_accept_window(addr, timeout)) { + if ((ret = ugly_accept_window(addr, x, y, timeout, mode))) { + if (ret == 2) { + rfbLog("accept_client: viewonly: %s\n", addr); + client->viewOnly = TRUE; + } rfbLog("accept_client: popup accepted: %s\n", addr); return 1; } else { rfbLog("accept_client: popup rejected: %s\n", addr); return 0; } + + } else if (!strcmp(accept_cmd, "xmessage")) { + /* make our own command using xmessage(1) */ + + if (view_only) { + sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center" + " 'x11vnc: accept connection from %s?'", addr); + } else { + sprintf(xmessage, "xmessage -buttons yes:0,no:2," + "view-only:3 -center" " 'x11vnc: accept connection" + " from %s?'", addr); + action = "yes:0,no:*,view:3"; + } + cmd = xmessage; + } else { + /* use the user supplied command: */ + cmd = accept_cmd; + + /* extract any action prefix: yes:N,no:M,view:K */ + if (strstr(accept_cmd, "yes:") == accept_cmd) { + char *p; + if ((p = strpbrk(accept_cmd, " \t")) != NULL) { + int i; + cmd = p; + p = accept_cmd; + for (i=0; i<200; i++) { + if (*p == ' ' || *p == '\t') { + xmessage[i] = '\0'; + break; + } + xmessage[i] = *p; + p++; + } + xmessage[200-1] = '\0'; + action = xmessage; + } + } } if (cmd) { int rc; - char *dpystr = DisplayString(dpy); - /* set RFB_CLIENT to IP addr for command to use */ - sprintf(rfb_client_env, "RFB_CLIENT=%s", addr); - putenv(rfb_client_env); + rfbLog("accept_client: using cmd for: %s\n", addr); + rc = run_user_command(cmd, client); - /* - * Better set DISPLAY to the one we are polling, if they - * want something trickier, they can handle on their own - * via environment, etc. XXX really should save/restore old. - */ - if (display_env == NULL) { - display_env = (char *) malloc(strlen(dpystr)+10); - } - sprintf(display_env, "DISPLAY=%s", dpystr); - putenv(display_env); + if (action) { + int result; - rfbLog("accept_client: running command:\n"); - rfbLog(" %s\n", cmd); - rc = system(cmd); - if (rc >= 256) { - rc = rc/256; - } - rfbLog("accept_client: command returned: %d\n", rc); + if (rc < 0) { + rfbLog("accept_client: cannot use negative " + "rc: %d, action %s\n", rc, action); + result = 2; + } else { + result = action_match(action, rc); + } - sprintf(rfb_client_env, "RFB_CLIENT="); - putenv(rfb_client_env); + if (result == 1) { + rc = 0; + } else if (result == 2) { + rc = 1; + } else if (result == 3) { + rc = 0; + rfbLog("accept_client: viewonly: %s\n", addr); + client->viewOnly = TRUE; + } else { + rc = 1; /* NOTREACHED */ + } + } if (rc == 0) { rfbLog("accept_client: accepted: %s\n", addr); @@ -1014,7 +1329,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { allow_list ? allow_list : "(null)" ); return(RFB_CLIENT_REFUSE); } - if (! accept_client(client->host)) { + if (! accept_client(client)) { rfbLog("denying client: %s local user rejected connection.\n", client->host); rfbLog("denying client: accept_cmd=\"%s\"\n", @@ -1024,6 +1339,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { if (view_only) { client->clientData = (void *) -1; + client->viewOnly = TRUE; } else { client->clientData = (void *) 0; } @@ -1262,7 +1578,8 @@ void tweak_mod(signed char mod, rfbBool down) { } } -static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { +static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { KeyCode k; int tweak = 0; if (debug_keyboard) { @@ -1273,6 +1590,9 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr if (view_only) { return; } + if (client->viewOnly) { + return; + } #define ADJUSTMOD(sym, state) \ if (keysym == sym) { \ @@ -1333,6 +1653,9 @@ static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { if (view_only) { return; } + if (client->viewOnly) { + return; + } last_keyboard_client = client; if (keyremaps) { @@ -1669,7 +1992,9 @@ static void pointer(int mask, int x, int y, rfbClientPtr client) { if (view_only) { return; } - + if (client->viewOnly) { + return; + } if (mask >= 0) { /* @@ -2376,6 +2701,9 @@ void watch_xevents() { */ void xcut_receive(char *text, int len, rfbClientPtr cl) { + if (cl->viewOnly) { + return; + } if (text == NULL || len == 0) { return; } @@ -5419,7 +5747,7 @@ void print_help() { "Once x11vnc establishes connections with the X11 server and starts\n" "listening as a VNC server it will print out a string: PORT=XXXX where\n" "XXXX is typically 5900 (the default VNC port). One would next run something\n" -"like this on the local machine: \"vncviewer host:N\" where N is XXXX - 5900.\n" +"like this on the local machine: \"vncviewer host:N\" where N is XXXX - 5900.\n" "\n" "By default x11vnc will not allow the screen to be shared and it will\n" "exit as soon as a client disconnects. See -shared and -forever below\n" @@ -5439,68 +5767,113 @@ void print_help() { " as the pointer moves from window to window (slow).\n" "-notruecolor Force 8bpp indexed color even if it looks like TrueColor.\n" "\n" -"-visual n Experimental option: probably does not do what you think.\n" -" It simply *forces* the visual used for the framebuffer;\n" -" this may be a bad thing... It is useful for testing and\n" -" for some workarounds. n may be a decimal number, or 0x\n" -" hex. Run xdpyinfo(1) for the values. One may also use\n" -" \"TrueColor\", etc. see for a list. If the\n" -" string ends in \":m\" the visual depth is forced to be m.\n" +"-visual n Experimental option: probably does not do what you\n" +" think. It simply *forces* the visual used for the\n" +" framebuffer; this may be a bad thing... It is useful for\n" +" testing and for some workarounds. n may be a decimal\n" +" number, or 0x hex. Run xdpyinfo(1) for the values.\n" +" One may also use \"TrueColor\", etc. see \n" +" for a list. If the string ends in \":m\" for better\n" +" or for worse the visual depth is forced to be m.\n" "\n" -"-viewonly Clients can only watch (default %s).\n" +"-viewonly All clients can only watch (default %s).\n" "-shared VNC display is shared (default %s).\n" "-forever Keep listening for more connections rather than exiting\n" " as soon as the first client(s) disconnect. Same as -many\n" -"-connect string For use with \"vncviewer -listen\" reverse connections. If\n" -" string has the form \"host\" or \"host:port\" the connection\n" -" is made once at startup. Use commas for a list. If string\n" -" contains \"/\" it is a file to periodically check for new\n" -" hosts. The first line is read and then file is truncated.\n" +"-connect string For use with \"vncviewer -listen\" reverse connections.\n" +" If string has the form \"host\" or \"host:port\"\n" +" the connection is made once at startup. Use commas\n" +" for a list. If string contains \"/\" it is a file to\n" +" periodically check for new hosts. The first line is\n" +" read and then file is truncated.\n" "-vncconnect Monitor the VNC_CONNECT X property set by vncconnect(1).\n" "-auth file Set the X authority file to be \"file\", equivalent to\n" " setting the XAUTHORITY env. var to \"file\" before startup.\n" "-allow addr1[,addr2..] Only allow client connections from IP addresses matching\n" -" the comma separated list of numerical addresses. Can be\n" -" a prefix, e.g. \"192.168.100.\" to match a simple subnet,\n" -" for more control build libvncserver with libwrap support.\n" +" the comma separated list of numerical addresses.\n" +" Can be a prefix, e.g. \"192.168.100.\" to match a\n" +" simple subnet, for more control build libvncserver with\n" +" libwrap support.\n" "-localhost Same as -allow 127.0.0.1\n" -"-accept string Prompt user at the X11 display whether an incoming\n" -" client should be allowed to connect. \"string\" is an\n" -" external command run via system(3). Be sure to quote\n" -" \"string\" if it contains spaces, etc. The RFB_CLIENT env.\n" -" variable will be set to the client's IP number. If the\n" -" external command returns 0 the client is accepted,\n" -" otherwise the client is rejected.\n" +"-passwdfile filename Specify libvncserver -passwd via the first line of file\n" +" \"filename\" instead of via command line. Note: this\n" +" is a simple plaintext passwd, see also -rfbauth below.\n" +"-accept string Run a command (possibly to prompt the user at the\n" +" X11 display) to decide whether an incoming client\n" +" should be allowed to connect or not. \"string\" is\n" +" an external command run via system(3) (see below for\n" +" special cases). Be sure to quote \"string\" if it\n" +" contains spaces, etc. The RFB_CLIENT_IP environment\n" +" variable will be set to the incoming client IP number\n" +" and the port in RFB_CLIENT_PORT (or -1 if unavailable).\n" +" The x11vnc process id will be in RFB_X11VNC_PID and a\n" +" client id number in RFB_CLIENT_ID. If the external\n" +" command returns 0 the client is accepted, otherwise\n" +" the client is rejected. See below for an extension to\n" +" accept a client view-only.\n" +"\n" +" If \"string\" is \"popup\" then a builtin popup window\n" +" is used. The popup will time out after 120 seconds,\n" +" use \"popup:N\" to modify the timeout to N seconds\n" +" (use 0 for no timeout)\n" +"\n" +" If \"string\" is \"xmessage\" then an xmessage(1)\n" +" invocation is used for the command.\n" +"\n" +" Both \"popup\" and \"xmessage\" will present an option\n" +" for accepting the client \"View-Only\" (the client\n" +" can only watch). This option will not be presented if\n" +" -viewonly has been specified, in which case the entire\n" +" display is view only.\n" "\n" -" If string is \"xmessage\" then an xmessage(1) invocation\n" -" is used for the command. If string is \"popup\" then a\n" -" builtin popup window is used. The popup will time out\n" -" after 120 seconds, use \"popup:N\" to modify the timeout\n" -" to N seconds (use 0 for no timeout)\n" +" If the user supplied command is prefixed with something\n" +" like \"yes:0,no:*,view:3 mycommand ...\" then this\n" +" associates the numerical command return code with\n" +" the actions: accept, reject, and accept-view-only,\n" +" respectively. Use \"*\" instead of a number to indicate\n" +" the default action (in case the command returns an\n" +" unexpected value). E.g. \"no:*\" is a good choice.\n" "\n" " Note that x11vnc blocks while the external command or\n" " or popup is running (other clients may see no updates\n" " during this period).\n" +"\n" +" More -accept tricks: use \"popupmouse\" to only allow\n" +" mouse clicks in the builtin popup to be recognized.\n" +" Similarly use \"popupkey\" to only recognize keystroke\n" +" responses. All 3 of the popup keywords can be followed\n" +" by +N+M to supply a position for the popup window.\n" +" The default is to center the popup window.\n" +"\n" +"-gone string As -accept string, except to run a user supplied command\n" +" when a client goes away (disconnects). Unlike -accept,\n" +" the command return code is not interpreted by x11vnc.\n" +"\n" "-inetd Launched by inetd(1): stdio instead of listening socket.\n" " Note: if you are not redirecting stderr to a log file\n" -" you must also specify -q as the first argument.\n" +" you must also specify the -q option.\n" "\n" "-noshm Do not use the MIT-SHM extension for the polling.\n" -" remote displays can be polled this way: be careful\n" -" this can use large amounts of network bandwidth. Also\n" -" of use if machine has a limited number of shm segments.\n" +" Remote displays can be polled this way: be careful this\n" +" can use large amounts of network bandwidth. This is\n" +" also of use if the local machine has a limited number\n" +" of shm segments and -onetile is not sufficient.\n" "-flipbyteorder Sometimes needed if remotely polled host has different\n" " endianness. Ignored unless -noshm is set.\n" "-blackout string Black out rectangles on the screen. string is a comma\n" " separated list of WxH+X+Y type geometries for each rect.\n" -"-xinerama If your screen is composed of multiple monitors glued\n" -" together via XINERAMA, and that screen is non-rectangular\n" -" this option will try to guess the areas to black out.\n" +"-xinerama If your screen is composed of multiple monitors\n" +" glued together via XINERAMA, and that screen is\n" +" non-rectangular this option will try to guess the areas\n" +" to black out (if your system has libXinerama).\n" "\n" -"-q Be quiet by printing less informational output to stderr.\n" -" Same as -quiet\n" -"-bg Go into the background after screen setup.\n" -" Something like this could be useful in a script:\n" +"-o logfile Write stderr messages to file \"logfile\" instead of\n" +" to the terminal. Same as -logfile.\n" +"-q Be quiet by printing less informational output to\n" +" stderr. Same as -quiet.\n" +"-bg Go into the background after screen setup. Messages to\n" +" stderr are lost unless -o logfile is used. Something\n" +" like this could be useful in a script:\n" " port=`ssh $host \"x11vnc -display :0 -bg\" | grep PORT`\n" " port=`echo \"$port\" | sed -e 's/PORT=//'`\n" " port=`expr $port - 5900`\n" @@ -5518,7 +5891,8 @@ void print_help() { "-nofb Ignore framebuffer: only process keyboard and pointer.\n" "-nosel Do not manage exchange of X selection/cutbuffer.\n" "-noprimary Do not poll the PRIMARY selection for changes and send\n" -" back to clients. PRIMARY is set for received changes.\n" +" back to clients. PRIMARY is still set on received\n" +" changes, however.\n" "\n" "-nocursor Do not have the viewer show a local cursor.\n" "-mouse Draw a 2nd cursor at the current X pointer position.\n" @@ -5532,7 +5906,7 @@ void print_help() { "-buttonmap string String to remap mouse buttons. Format: IJK-LMN, this\n" " maps buttons I -> L, etc., e.g. -buttonmap 13-31\n" "\n" -" Button presses can also be mapped to keysyms: replace\n" +" Button presses can also be mapped to keystrokes: replace\n" " a button digit on the right of the dash with ::\n" " or :+: etc. for multiple keys. For example,\n" " if the viewing machine has a mouse-wheel (buttons 4 5)\n" @@ -5540,11 +5914,11 @@ void print_help() { " -buttonmap 12345-123:Prior::Next:\n" " -buttonmap 12345-123:Up+Up+Up::Down+Down+Down:\n" "\n" -" If you include a modifier like \"Shift_L\" the modifier's\n" -" up/down state is toggled, e.g. to send \"The\" use\n" -" :Shift_L+t+Shift_L+h+e: (the 1st one is shift down and\n" -" the 2nd one is shift up). (note: the initial state of\n" -" the modifier is ignored and not reset)\n" +" If you include a modifier like \"Shift_L\" the\n" +" modifier's up/down state is toggled, e.g. to send\n" +" \"The\" use :Shift_L+t+Shift_L+h+e: (the 1st one is\n" +" shift down and the 2nd one is shift up). (note: the\n" +" initial state of the modifier is ignored and not reset)\n" " To include button events use \"Button1\", ... etc.\n" "\n" "-nodragging Do not update the display during mouse dragging events\n" @@ -5561,17 +5935,17 @@ void print_help() { "-debug_pointer Print debugging output for every pointer event.\n" "-debug_keyboard Print debugging output for every keyboard event.\n" "\n" -"-defer time Time in ms to wait for updates before sending to\n" -" client [rfbDeferUpdateTime] (default %d).\n" -"-wait time Time in ms to pause between screen polls. Used\n" -" to cut down on load (default %d).\n" -"-nap Monitor activity and if low take longer naps between\n" +"-defer time Time in ms to wait for updates before sending to client\n" +" [rfbDeferUpdateTime] (default %d).\n" +"-wait time Time in ms to pause between screen polls. Used to cut\n" +" down on load (default %d).\n" +"-nap Monitor activity and if low take longer naps between\n" " polls to really cut down load when idle (default %s).\n" -"-sigpipe string Broken pipe (SIGPIPE) handling. string can be \"ignore\"\n" -" or \"exit\", for the 1st libvncserver will handle the\n" -" abrupt loss of a client and continue, for the 2nd x11vnc\n" -" will cleanup and exit at the 1st broken connection.\n" -" Default is \"ignore\".\n" +"-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" +" \"ignore\" or \"exit\". For \"ignore\" libvncserver\n" +" will handle the abrupt loss of a client and continue,\n" +" for \"exit\" x11vnc will cleanup and exit at the 1st\n" +" broken connection. Default is \"ignore\".\n" #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD "-threads Whether or not to use the threaded libvncserver\n" "-nothreads algorithm [rfbRunEventLoop] (default %s).\n" @@ -5582,8 +5956,8 @@ void print_help() { "-onetile Do not use the new copy_tiles() framebuffer mechanism,\n" " just use 1 shm tile for polling. Same as -old_copytile.\n" " Limits shm segments used to 3.\n" -"-gaps n Heuristic to fill in gaps in rows or cols of n or less\n" -" tiles. Used to improve text paging (default %d).\n" +"-gaps n Heuristic to fill in gaps in rows or cols of n or\n" +" less tiles. Used to improve text paging (default %d).\n" "-grow n Heuristic to grow islands of changed tiles n or wider\n" " by checking the tile near the boundary (default %d).\n" "-fuzz n Tolerance in pixels to mark a tiles edges as changed\n" @@ -5692,6 +6066,8 @@ int main(int argc, char** argv) { char *use_dpy = NULL; char *auth_file = NULL; char *arg, *visual_str = NULL; + char *logfile = NULL; + char *passwdfile = NULL; int pw_loc = -1; int dt = 0; int bg = 0; @@ -5699,7 +6075,7 @@ int main(int argc, char** argv) { int got_deferupdate = 0, got_defer = 0; /* used to pass args we do not know about to rfbGetScreen(): */ - int argc2 = 1; char *argv2[100]; + int argc2 = 1; char *argv2[128]; argv2[0] = strdup(argv[0]); @@ -5728,6 +6104,8 @@ int main(int argc, char** argv) { force_indexed_color = 1; } else if (!strcmp(arg, "-viewonly")) { view_only = 1; + } else if (!strcmp(arg, "-passwdfile")) { + passwdfile = argv[++i]; } else if (!strcmp(arg, "-shared")) { shared = 1; } else if (!strcmp(arg, "-auth")) { @@ -5738,6 +6116,8 @@ int main(int argc, char** argv) { allow_list = "127.0.0.1"; } else if (!strcmp(arg, "-accept")) { accept_cmd = argv[++i]; + } else if (!strcmp(arg, "-gone")) { + gone_cmd = argv[++i]; } else if (!strcmp(arg, "-many") || !strcmp(arg, "-forever")) { connect_once = 0; @@ -5845,6 +6225,8 @@ int main(int argc, char** argv) { } else if (!strcmp(arg, "-h") || !strcmp(arg, "-help") || !strcmp(arg, "-?")) { print_help(); + } else if (!strcmp(arg, "-o") || !strcmp(arg, "-logfile")) { + logfile = argv[++i]; } else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) { quiet = 1; #ifdef LIBVNCSERVER_HAVE_SETSID @@ -5873,16 +6255,39 @@ int main(int argc, char** argv) { if (!strcmp(arg, "-nevershared")) { got_nevershared = 1; } - /* otherwise copy it for use below. */ - if (!quiet && !inetd && i != pw_loc && i != pw_loc+1) { - fprintf(stderr, "passing arg to libvncserver: %s\n", - arg); - } + /* otherwise copy it for libvncserver use below. */ if (argc2 < 100) { argv2[argc2++] = strdup(arg); } } } + if (logfile) { + int n; + if ((n = open(logfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { + fprintf(stderr, "error opening logfile: %s\n", logfile); + perror("open"); + exit(1); + } + if (dup2(n, 2) < 0) { + fprintf(stderr, "dup2 failed\n"); + perror("dup2"); + exit(1); + } + if (n > 2) { + close(n); + } + } + if (! quiet && ! inetd) { + int i; + for (i=1; i < argc2; i++) { + fprintf(stderr, "passing arg to libvncserver: %s\n", + argv2[i]); + if (!strcmp(argv2[i], "-passwd")) { + i++; + } + } + } + /* * If -passwd was used, clear it out of argv. This does not @@ -5899,6 +6304,26 @@ int main(int argc, char** argv) { *p++ = '\0'; } } + } else if (passwdfile) { + char line[512]; + FILE *in; + in = fopen(passwdfile, "r"); + if (in == NULL) { + fprintf(stderr, "cannot open passwdfile: %s\n", + passwdfile); + perror("fopen"); + exit(1); + } + if (fgets(line, 512, in) != NULL) { + line[strlen(line)-1] = '\0'; + argv2[argc2++] = "-passwd"; + argv2[argc2++] = strdup(line); + } else { + fprintf(stderr, "cannot read passwdfile: %s\n", + passwdfile); + perror("fgets"); + exit(1); + } } /* fixup settings that do not make sense */ @@ -5968,10 +6393,16 @@ int main(int argc, char** argv) { fprintf(stderr, "shared: %d\n", shared); fprintf(stderr, "authfile: %s\n", auth_file ? auth_file : "null"); + fprintf(stderr, "passfile: %s\n", passwdfile ? passwdfile + : "null"); + fprintf(stderr, "logfile: %s\n", logfile ? logfile + : "null"); fprintf(stderr, "allow: %s\n", allow_list ? allow_list : "null"); fprintf(stderr, "accept: %s\n", accept_cmd ? accept_cmd : "null"); + fprintf(stderr, "gone: %s\n", gone_cmd ? gone_cmd + : "null"); fprintf(stderr, "conn_once: %d\n", connect_once); fprintf(stderr, "connect: %s\n", client_connect ? client_connect : "null"); @@ -6296,7 +6727,9 @@ int main(int argc, char** argv) { n = open("/dev/null", O_RDONLY); dup2(n, 0); dup2(n, 1); - dup2(n, 2); + if (! logfile) { + dup2(n, 2); + } if (n > 2) { close(n); } diff --git a/httpd.c b/httpd.c index 2f1ffdd..25f2807 100644 --- a/httpd.c +++ b/httpd.c @@ -167,7 +167,7 @@ httpCheckFds(rfbScreenInfoPtr rfbScreen) rfbLog("Rejected HTTP connection from client %s\n", inet_ntoa(addr.sin_addr)); #else - flags = fcntl(rfbScreen->httpSock, F_SETFL); + flags = fcntl(rfbScreen->httpSock, F_GETFL); if (flags < 0 || fcntl(rfbScreen->httpSock, F_SETFL, flags | O_NONBLOCK) == -1) { rfbLogPerror("httpCheckFds: fcntl");