xref: /haiku/src/apps/remotedesktop/RemoteDesktop.cpp (revision a127b88ecbfab58f64944c98aa47722a18e363b2)
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