diff -Nur cvs/rdesktop/doc/rdesktop.1 connection_sharing/rdesktop/doc/rdesktop.1 --- cvs/rdesktop/doc/rdesktop.1 2007-01-20 00:47:35.000000000 +1100 +++ connection_sharing/rdesktop/doc/rdesktop.1 2008-03-11 09:55:32.000000000 +1100 @@ -82,9 +82,29 @@ server side component, which is available from \fIhttp://www.cendio.com/seamlessrdp/\fR. When using this option, you should specify a startup shell which -launches the desired application through SeamlessRDP. +launches the desired application through SeamlessRDP. +When using SeamlessRDP, a control socket is created that allows additional +commands to be executed inside the RDP session. Additonal commands can be +executed by using subsequent invocations of rdesktop with the -l option, +and the control socket path can be specified with the -M option. -Example: rdesktop -A -s 'seamlessrdpshell notepad'. +Example: rdesktop -A -s 'seamlessrdpshell notepad' +.TP +.BR "-M " +Specify the path for the SeamlessRDP control socket. Defaults to +$HOME/.rdesktop/seamless.socket + +Example: rdesktop -A -s 'seamlessrdpshell notepad' -M /tmp/rdesktop-socket +.TP +.BR "-l" +Use SeamlessRDP slave mode to execute an additional command within an existing +SeamlessRDP session. When using this flag, the command line to execute on the +RDP server replaces the server argument to the rdesktop command. Can be used +in conjunction with the -M option to use a specific control socket, otherwise +the default of $HOME/.rdesktop/seamless.socket is used. All other options are +ignored. + +Example: rdesktop -l 'calc' .TP .BR "-B" Use the BackingStore of the Xserver instead of the integrated one in diff -Nur cvs/rdesktop/proto.h connection_sharing/rdesktop/proto.h --- cvs/rdesktop/proto.h 2008-03-07 22:26:28.000000000 +1100 +++ connection_sharing/rdesktop/proto.h 2008-03-11 09:56:37.000000000 +1100 @@ -309,6 +309,7 @@ void seamless_select_timeout(struct timeval *tv); unsigned int seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags); unsigned int seamless_send_focus(unsigned long id, unsigned long flags); +unsigned int seamless_send_spawn(char *cmdline); unsigned int seamless_send_destroy(unsigned long id); /* scard.c */ void scard_lock(int lock); diff -Nur cvs/rdesktop/rdesktop.c connection_sharing/rdesktop/rdesktop.c --- cvs/rdesktop/rdesktop.c 2008-01-05 16:43:02.000000000 +1100 +++ connection_sharing/rdesktop/rdesktop.c 2008-03-11 09:55:32.000000000 +1100 @@ -29,6 +29,7 @@ #include /* toupper */ #include #include "rdesktop.h" +#include "seamless.h" #ifdef HAVE_LOCALE_H #include @@ -91,6 +92,12 @@ RD_BOOL g_owncolmap = False; RD_BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */ RD_BOOL g_seamless_rdp = False; + +/* Master socket identifier */ +char *master_socket = NULL; +/* Seamless slave mode flag */ +RD_BOOL seamless_slave = False; + uint32 g_embed_wnd; uint32 g_rdp5_performanceflags = RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS; @@ -122,6 +129,10 @@ rdp2vnc_connect(char *server, uint32 flags, char *domain, char *password, char *shell, char *directory); #endif + +// Send message to rdesktop running in SeamlessrRDP master mode +void send_seamless_slave_message(char *cmdline); + /* Display usage information */ static void usage(char *program) @@ -131,6 +142,7 @@ fprintf(stderr, "See http://www.rdesktop.org/ for more information.\n\n"); fprintf(stderr, "Usage: %s [options] server[:port]\n", program); + fprintf(stderr, " %s [-M ] -l \n", program); #ifdef RDP2VNC fprintf(stderr, " -V: vnc port\n"); fprintf(stderr, " -Q: defer time (ms)\n"); @@ -149,6 +161,8 @@ fprintf(stderr, " -L: local codepage\n"); #endif fprintf(stderr, " -A: enable SeamlessRDP mode\n"); + fprintf(stderr, " -M: SeamlessRDP master mode socket path\n"); + fprintf(stderr, " -l: SeamlessRDP slave mode\n"); fprintf(stderr, " -B: use BackingStore of X-server (if available)\n"); fprintf(stderr, " -e: disable encryption (French TS)\n"); fprintf(stderr, " -E: disable encryption from client to server\n"); @@ -442,7 +456,7 @@ g_embed_wnd = 0; g_num_devices = 0; - + #ifdef RDP2VNC #define VNCOPT "V:Q:" #else @@ -450,7 +464,7 @@ #endif while ((c = getopt(argc, argv, - VNCOPT "Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1) + VNCOPT "Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045M:lh?")) != -1) { switch (c) { @@ -783,6 +797,15 @@ case '5': g_use_rdp5 = True; break; + + case 'M': + master_socket = xmalloc(sizeof(char) * (strlen(optarg) + 1)); + STRNCPY(master_socket, optarg, sizeof(char) * (strlen(optarg) + 1)); + break; + + case 'l': + seamless_slave = True; + break; case 'h': case '?': @@ -792,12 +815,44 @@ } } + /* If no master socket path was supplied, use ~/.rdesktop/rdpsocket */ + if (master_socket == NULL) + { + char *home; + + home = getenv("HOME"); + if (home == NULL) + { + warning("HOME environment variable undefined; could not create $HOME/.rdesktop/rdpsocket\n"); + return 1; + } + + master_socket = xmalloc(strlen(home) + sizeof("/.rdesktop/seamless.socket")); + + sprintf(master_socket, "%s/.rdesktop", home); + if ((mkdir(master_socket, 0700) == -1) && errno != EEXIST) + { + perror(master_socket); + return 1; + } + + sprintf(master_socket, "%s/.rdesktop/seamless.socket", home); + } + if (argc - optind != 1) { usage(argv[0]); return 1; } + /* If slave mode is being used, send the command line to the master + * process and then exit. */ + if (seamless_slave) + { + seamless_socket_send(master_socket, argv[optind]); + return 0; + } + STRNCPY(server, argv[optind], sizeof(server)); parse_server_and_port(server); @@ -836,6 +891,9 @@ } g_width = -100; g_grab_keyboard = False; + + /* Create a control socket as we're not in slave mode. */ + seamless_create_socket(master_socket); } if (!username_option) @@ -981,6 +1039,13 @@ cache_save_state(); ui_deinit(); + + /* If we opened a socket, clean it up. */ + if (master_socket != NULL) + { + seamless_close_socket(master_socket); + xfree(master_socket); + } if (ext_disc_reason >= 2) print_disconnect_reason(ext_disc_reason); diff -Nur cvs/rdesktop/seamless.c connection_sharing/rdesktop/seamless.c --- cvs/rdesktop/seamless.c 2008-03-07 22:26:29.000000000 +1100 +++ connection_sharing/rdesktop/seamless.c 2008-03-11 09:55:32.000000000 +1100 @@ -22,6 +22,12 @@ #include "rdesktop.h" #include #include +#include +#include +#include +#include +#include +#include "seamless.h" #ifdef WITH_DEBUG_SEAMLESS #define DEBUG_SEAMLESS(args) printf args; @@ -29,6 +35,9 @@ #define DEBUG_SEAMLESS(args) #endif +// Control socket file descriptor +int sock; + extern RD_BOOL g_seamless_rdp; static VCHANNEL *seamless_channel; static unsigned int seamless_serial; @@ -516,3 +525,151 @@ { return seamless_send("DESTROY", "0x%08lx", id); } + +/* Send client-to-server message to spawn a new process on the server. */ +unsigned int +seamless_send_spawn(char *cmdline) +{ + if (!g_seamless_rdp) + return (unsigned int) -1; + + return seamless_send("SPAWN", cmdline); +} + +/* Check seamless master mode socket and send spawn command if input found. + * Returns 0 if a slave connected and sent command, 1 otherwise. */ +int +seamless_check_socket() +{ + fd_set rfds; + struct timeval tv; + int slaves, index, ns; + struct sockaddr_un fsaun; + char cmdline[256]; + socklen_t fromlen; + FILE *fp; + char c; + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + + /* Don't wait - set timeout to zero. */ + tv.tv_sec = 0; + tv.tv_usec = 0; + + /* See if any slaves are trying to connect. */ + slaves = select(sock + 1, &rfds, NULL, NULL, &tv); + + if (slaves == -1) + { + perror("Error checking socket: select()"); + return 1; + } + /* Return if no waiting slaves */ + else if (slaves == 0) + { + return 1; + } + + /* Accept connection */ + fromlen = sizeof(fsaun); + if ((ns = accept(sock, (struct sockaddr *) &fsaun, &fromlen)) < 0) + { + perror("server: accept"); + exit(1); + } + + /* Read command from client socket */ + fp = fdopen(ns, "r"); + index = 0; + while ((c = fgetc(fp)) != EOF && index < 256) + { + cmdline[index] = c; + + index++; + } + cmdline[index] = '\0'; + + /* Send spawn command to server-side SeamlessRDP component */ + seamless_send_spawn(cmdline); + + return 0; +} + +/* Create control socket */ +void +seamless_create_socket(char *socket_name) +{ + struct sockaddr_un saun; + + /* Create socket */ + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + perror("Error creating socket: socket"); + exit(1); + } + + /* Bind to the socket. Any older socket with the same name will be + * unlinked first. */ + memset(&saun, 0, sizeof(struct sockaddr_un)); + saun.sun_family = AF_UNIX; + strncpy(saun.sun_path, socket_name, sizeof(saun.sun_path)); + unlink(socket_name); + if (bind(sock, (struct sockaddr *) &saun, sizeof(struct sockaddr_un)) < 0) + { + perror("Error binding to socket: bind"); + exit(1); + } + /* Set some more restrictive permissions on the socket. */ + chmod(socket_name, S_IRUSR | S_IWUSR | S_IXUSR); + + /* Listen on the socket */ + if (listen(sock, 5) < 0) + { + perror("Error listening on socket: listen"); + exit(1); + } +} + +/* Close control socket */ +void +seamless_close_socket(char *socket_name) +{ + close(sock); + unlink(socket_name); + + return; +} + +/* Send a command line to a master process via a socket. */ +int +seamless_socket_send(char *socket_name, char *cmdline) +{ + register int s, len; + struct sockaddr_un saun; + + /* Create socket */ + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + perror("Error creating socket: socket"); + exit(1); + } + + /* Connect to server */ + saun.sun_family = AF_UNIX; + strcpy(saun.sun_path, socket_name); + len = sizeof(saun.sun_family) + strlen(saun.sun_path); + if (connect(s, (struct sockaddr *) &saun, len) < 0) + { + perror("Error connecting to socket: connect"); + exit(1); + } + + /* Send command */ + send(s, cmdline, strlen(cmdline), 0); + + /* Close socket */ + close(s); + + return 0; +} diff -Nur cvs/rdesktop/seamless.h connection_sharing/rdesktop/seamless.h --- cvs/rdesktop/seamless.h 2007-01-04 16:39:39.000000000 +1100 +++ connection_sharing/rdesktop/seamless.h 2008-03-11 09:55:32.000000000 +1100 @@ -17,3 +17,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Check seamless master mode socket and send spawn command if input found. */ +int seamless_check_socket(); +/* Create control socket */ +void seamless_create_socket(char *socket_name); +/* Close control socket */ +void seamless_close_socket(char *socket_name); +/* Send a command line to seamless master socket. */ +int seamless_socket_send(char *socket_name, char *cmdline); diff -Nur cvs/rdesktop/xwin.c connection_sharing/rdesktop/xwin.c --- cvs/rdesktop/xwin.c 2008-03-07 22:26:29.000000000 +1100 +++ connection_sharing/rdesktop/xwin.c 2008-03-11 09:55:32.000000000 +1100 @@ -29,6 +29,7 @@ #include #include "rdesktop.h" #include "xproto.h" +#include "seamless.h" extern int g_width; extern int g_height; @@ -88,6 +89,9 @@ static RD_BOOL g_seamless_hidden = False; /* Desktop is hidden on server */ extern RD_BOOL g_seamless_rdp; +/* SeamlessRDP master mode socket */ +extern char *master_socket; + extern uint32 g_embed_wnd; RD_BOOL g_enable_compose = False; RD_BOOL g_Unobscured; /* used for screenblt */ @@ -2331,6 +2335,13 @@ seamless_select_timeout(&tv); n++; + + // check seamless control socket to see if any slave rdesktop + // processes are trying to send a command + if (master_socket != NULL) + { + seamless_check_socket(); + } switch (select(n, &rfds, &wfds, NULL, &tv)) {