diff -Naur rdesktop/doc/rdesktop.1 rdesktop-modified/doc/rdesktop.1 --- rdesktop/doc/rdesktop.1 2006-11-02 07:52:01.000000000 +1100 +++ rdesktop-modified/doc/rdesktop.1 2006-12-18 00:46:09.000000000 +1100 @@ -86,6 +86,22 @@ Example: rdesktop -A -s 'seamlessrdpshell notepad'. .TP +.BR "-M " +Enable SeamlessRDP master mode. This mode allows additional applications to be +launched from a single SeamlessRDP session as opposed to requiring one session +per application. A control socket path must be specified for use by slave mode +invocations. + +Example: rdesktop -A -s 'seamlessrdpshell notepad' -M /tmp/rdesktop-socket +.TP +.BR "-l " +Use SeamlessRDP slave mode to execute an additional command within the master +SeamlessRDP session. When using this option, a control socket path must be +specified instead of a server, and all other options will be ignored. Note +that a shell does not have to be specified, only the command to be executed. + +Example: rdesktop -l 'notepad' /tmp/rdesktop-socket +.TP .BR "-B" Use the BackingStore of the Xserver instead of the integrated one in rdesktop. diff -Naur rdesktop/proto.h rdesktop-modified/proto.h --- rdesktop/proto.h 2006-12-08 02:23:45.000000000 +1100 +++ rdesktop-modified/proto.h 2006-12-18 01:16:44.000000000 +1100 @@ -303,6 +303,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); /* scard.c */ void scardSetInfo(uint32 device, uint32 id, uint32 bytes_out); int scard_enum_devices(uint32 * id, char *optarg); diff -Naur rdesktop/rdesktop.c rdesktop-modified/rdesktop.c --- rdesktop/rdesktop.c 2006-11-07 10:05:39.000000000 +1100 +++ rdesktop-modified/rdesktop.c 2006-12-18 01:16:45.000000000 +1100 @@ -29,6 +29,7 @@ #include /* toupper */ #include #include "rdesktop.h" +#include "seamless.h" #ifdef HAVE_LOCALE_H #include @@ -91,6 +92,12 @@ BOOL g_owncolmap = False; BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */ BOOL g_seamless_rdp = False; + +/* Master socket identifier */ +char *master_socket = NULL; +/* Command line to execute in slave mode */ +char *slave_cmdline = NULL; + 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 -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"); @@ -443,6 +457,8 @@ g_num_devices = 0; + slave_cmdline = NULL; + #ifdef RDP2VNC #define VNCOPT "V:Q:" #else @@ -450,7 +466,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:l:h?")) != -1) { switch (c) { @@ -783,6 +799,16 @@ 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': + slave_cmdline = xmalloc(sizeof(char) * (strlen(optarg) + 1)); + STRNCPY(slave_cmdline, optarg, sizeof(char) * (strlen(optarg) + 1)); + break; + case 'h': case '?': default: @@ -800,6 +826,15 @@ STRNCPY(server, argv[optind], sizeof(server)); parse_server_and_port(server); + /* If slave mode is being used, the 'server' variable contains the name + * of the control socket to use. */ + if (slave_cmdline != NULL) + { + seamless_socket_send(server, slave_cmdline); + xfree(slave_cmdline); + return 0; + } + if (g_seamless_rdp) { if (g_win_button_size) @@ -837,6 +872,10 @@ g_grab_keyboard = False; } + /* If we're in master mode, create a socket and listen on it. */ + if (master_socket != NULL) + seamless_create_socket(master_socket); + if (!username_option) { pw = getpwuid(getuid()); @@ -980,6 +1019,12 @@ 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); @@ -1624,3 +1669,10 @@ return False; return True; } + +// Send message to rdesktop running in SeamlessrRDP master mode +void +send_seamless_slave_message(char *cmdline) +{ + fprintf(stderr, cmdline); +} diff -Naur rdesktop/rdp.c rdesktop-modified/rdp.c --- rdesktop/rdp.c 2006-11-02 05:38:40.000000000 +1100 +++ rdesktop-modified/rdp.c 2006-12-18 01:16:45.000000000 +1100 @@ -22,6 +22,7 @@ #include #include #include "rdesktop.h" +#include "seamless.h" #ifdef HAVE_ICONV #ifdef HAVE_ICONV_H @@ -51,6 +52,9 @@ extern BOOL g_bitmap_cache_persist_enable; extern BOOL g_numlock_sync; +/* SeamlessRDP master mode socket */ +extern char *master_socket; + uint8 *g_next_packet; uint32 g_rdp_shareid; @@ -74,6 +78,7 @@ static BOOL g_iconv_works = True; #endif + /* Receive an RDP packet */ static STREAM rdp_recv(uint8 * type) @@ -1361,7 +1366,10 @@ rdp_main_loop(BOOL * deactivated, uint32 * ext_disc_reason) { while (rdp_loop(deactivated, ext_disc_reason)) - ; + { + if (master_socket != NULL) + seamless_check_socket(); + } } /* used in uiports and rdp_main_loop, processes the rdp packets waiting */ diff -Naur rdesktop/seamless.c rdesktop-modified/seamless.c --- rdesktop/seamless.c 2006-04-28 17:55:36.000000000 +1000 +++ rdesktop-modified/seamless.c 2006-12-18 01:16:45.000000000 +1100 @@ -21,6 +21,11 @@ #include "rdesktop.h" #include #include +#include +#include +#include +#include +#include "seamless.h" /* #define WITH_DEBUG_SEAMLESS */ @@ -30,6 +35,9 @@ #define DEBUG_SEAMLESS(args) #endif +// Control socket file descriptor +int sock; + extern BOOL g_seamless_rdp; static VCHANNEL *seamless_channel; static unsigned int seamless_serial; @@ -442,7 +450,6 @@ } - unsigned int seamless_send_focus(unsigned long id, unsigned long flags) { @@ -451,3 +458,150 @@ return seamless_send("FOCUS", "0x%08lx,0x%lx", id, flags); } + +/* 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); + } + + /* 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 -Naur rdesktop/seamless.h rdesktop-modified/seamless.h --- rdesktop/seamless.h 2006-03-27 18:17:33.000000000 +1000 +++ rdesktop-modified/seamless.h 2006-12-18 01:16:44.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);