1 /* 2 * Copyright 2009-2017, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Lotz <mmlr@mlotz.ch> 7 */ 8 9 #include <Application.h> 10 #include <FindDirectory.h> 11 #include <Path.h> 12 #include <Screen.h> 13 #include <Window.h> 14 15 #include "RemoteView.h" 16 17 #include <new> 18 #include <signal.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <sys/wait.h> 22 #include <unistd.h> 23 24 25 void 26 print_usage(const char *app) 27 { 28 printf("usage:\t%s <host> [-p <port>] [-w <width>] [-h <height>]\n", app); 29 printf("usage:\t%s <user@host> -s [<sshPort>] [-p <port>] [-w <width>]" 30 " [-h <height>] [-c <command>]\n", app); 31 printf("\t%s --help\n\n", app); 32 33 printf("Connect to & run applications from a different computer\n\n"); 34 printf("Arguments available for use:\n\n"); 35 printf("\t-p\t\tspecify the port to communicate on (default 10900)\n"); 36 printf("\t-c\t\tsend a command to the other computer (default Terminal)\n"); 37 printf("\t-s\t\tuse SSH, optionally specify the SSH port to use (22)\n"); 38 printf("\t-w\t\tmake the virtual desktop use the specified width\n"); 39 printf("\t-h\t\tmake the virtual desktop use the specified height\n"); 40 printf("\nIf no width and height are specified, the window is opened with" 41 " the size of the the local screen.\n"); 42 } 43 44 45 int 46 main(int argc, char *argv[]) 47 { 48 if (argc < 2 || strcmp(argv[1], "--help") == 0) { 49 print_usage(argv[0]); 50 return 1; 51 } 52 53 uint16 port = 10900; 54 uint16 sshPort = 22; 55 int32 width = -1; 56 int32 height = -1; 57 bool useSSH = false; 58 const char *command = NULL; 59 const char *host = argv[1]; 60 61 for (int32 i = 2; i < argc; i++) { 62 if (strcmp(argv[i], "-p") == 0) { 63 if (argc <= i + 1 || sscanf(argv[i + 1], "%" B_SCNu16, &port) != 1) { 64 print_usage(argv[0]); 65 return 2; 66 } 67 68 i++; 69 continue; 70 } 71 72 if (strcmp(argv[i], "-w") == 0) { 73 if (argc <= i + 1 || sscanf(argv[i + 1], "%" B_SCNd32, &width) != 1) { 74 print_usage(argv[0]); 75 return 2; 76 } 77 78 i++; 79 continue; 80 } 81 82 if (strcmp(argv[i], "-h") == 0) { 83 if (argc <= i + 1 || sscanf(argv[i + 1], "%" B_SCNd32, &height) != 1) { 84 print_usage(argv[0]); 85 return 2; 86 } 87 88 i++; 89 continue; 90 } 91 92 if (strcmp(argv[i], "-s") == 0) { 93 if (argc <= i + 1 || sscanf(argv[i + 1], "%" B_SCNu16, &sshPort) != 1) { 94 print_usage(argv[0]); 95 return 2; 96 } 97 98 i++; 99 useSSH = true; 100 continue; 101 } 102 103 if (strcmp(argv[i], "-c") == 0) { 104 if (argc <= i + 1) { 105 print_usage(argv[0]); 106 return 2; 107 } 108 109 i++; 110 command = argv[i]; 111 continue; 112 } 113 114 print_usage(argv[0]); 115 return 2; 116 } 117 118 if (command != NULL && !useSSH) { 119 print_usage(argv[0]); 120 return 2; 121 } 122 123 pid_t sshPID = -1; 124 if (useSSH) { 125 BPath terminalPath; 126 if (command == NULL) { 127 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath) 128 != B_OK) { 129 printf("failed to determine system-apps directory\n"); 130 return 3; 131 } 132 if (terminalPath.Append("Terminal") != B_OK) { 133 printf("failed to append to system-apps path\n"); 134 return 3; 135 } 136 command = terminalPath.Path(); 137 } 138 139 char shellCommand[4096]; 140 snprintf(shellCommand, sizeof(shellCommand), 141 "echo connected; export TARGET_SCREEN=%" B_PRIu16 "; %s\n", port, 142 command); 143 144 int pipes[4]; 145 if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0) { 146 printf("failed to create redirection pipes\n"); 147 return 3; 148 } 149 150 sshPID = fork(); 151 if (sshPID < 0) { 152 printf("failed to fork ssh process\n"); 153 return 3; 154 } 155 156 if (sshPID == 0) { 157 // child code, redirect std* and execute ssh 158 close(STDOUT_FILENO); 159 close(STDIN_FILENO); 160 dup2(pipes[1], STDOUT_FILENO); 161 dup2(pipes[1], STDERR_FILENO); 162 dup2(pipes[2], STDIN_FILENO); 163 for (int32 i = 0; i < 4; i++) 164 close(pipes[i]); 165 166 char localRedirect[50]; 167 sprintf(localRedirect, "localhost:%" B_PRIu16 ":localhost:%" 168 B_PRIu16, port, port); 169 170 char portNumber[10]; 171 sprintf(portNumber, "%" B_PRIu16, sshPort); 172 173 int result = execl("ssh", "-C", "-L", localRedirect, 174 "-p", portNumber, "-o", "ExitOnForwardFailure=yes", host, 175 shellCommand, NULL); 176 177 // we don't get here unless there was an error in executing 178 printf("failed to execute ssh process in child\n"); 179 return result; 180 } else { 181 close(pipes[1]); 182 close(pipes[2]); 183 184 char buffer[10]; 185 read(pipes[0], buffer, sizeof(buffer)); 186 // block until connected/error message from ssh 187 188 host = "localhost"; 189 } 190 } 191 192 BApplication app("application/x-vnd.Haiku-RemoteDesktop"); 193 BRect windowFrame = BRect(0, 0, width - 1, height - 1); 194 if (!windowFrame.IsValid()) { 195 BScreen screen; 196 windowFrame = screen.Frame(); 197 } 198 199 BWindow *window = new(std::nothrow) BWindow(windowFrame, "RemoteDesktop", 200 B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE); 201 202 if (window == NULL) { 203 printf("no memory to allocate window\n"); 204 return 4; 205 } 206 207 RemoteView *view = new(std::nothrow) RemoteView(window->Bounds(), host, 208 port); 209 if (view == NULL) { 210 printf("no memory to allocate remote view\n"); 211 return 4; 212 } 213 214 status_t init = view->InitCheck(); 215 if (init != B_OK) { 216 printf("initialization of remote view failed: %s\n", strerror(init)); 217 delete view; 218 return 5; 219 } 220 221 window->AddChild(view); 222 view->MakeFocus(); 223 window->Show(); 224 app.Run(); 225 226 if (sshPID >= 0) 227 kill(sshPID, SIGHUP); 228 229 return 0; 230 } 231