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
print_usage(const char * app)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
main(int argc,char * argv[])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