xref: /haiku/src/apps/remotedesktop/RemoteDesktop.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
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)
64 				!= 1) {
65 				print_usage(argv[0]);
66 				return 2;
67 			}
68 
69 			i++;
70 			continue;
71 		}
72 
73 		if (strcmp(argv[i], "-w") == 0) {
74 			if (argc < i + 1 || sscanf(argv[i + 1], "%" B_SCNd32, &width) != 1)
75 			{
76 				print_usage(argv[0]);
77 				return 2;
78 			}
79 
80 			i++;
81 			continue;
82 		}
83 
84 		if (strcmp(argv[i], "-h") == 0) {
85 			if (argc < i + 1 || sscanf(argv[i + 1], "%" B_SCNd32, &height) != 1)
86 			{
87 				print_usage(argv[0]);
88 				return 2;
89 			}
90 
91 			i++;
92 			continue;
93 		}
94 
95 		if (strcmp(argv[i], "-s") == 0) {
96 			if (argc >= i + 1
97 				&& sscanf(argv[i + 1], "%" B_SCNu16, &sshPort) == 1) {
98 				i++;
99 			}
100 
101 			useSSH = true;
102 			continue;
103 		}
104 
105 		if (strcmp(argv[i], "-c") == 0) {
106 			if (argc < i + 1) {
107 				print_usage(argv[0]);
108 				return 2;
109 			}
110 
111 			i++;
112 			command = argv[i];
113 			continue;
114 		}
115 
116 		print_usage(argv[0]);
117 		return 2;
118 	}
119 
120 	if (command != NULL && !useSSH) {
121 		print_usage(argv[0]);
122 		return 2;
123 	}
124 
125 	pid_t sshPID = -1;
126 	if (useSSH) {
127 		BPath terminalPath;
128 		if (command == NULL) {
129 			if (find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath)
130 					!= B_OK) {
131 				printf("failed to determine system-apps directory\n");
132 				return 3;
133 			}
134 			if (terminalPath.Append("Terminal") != B_OK) {
135 				printf("failed to append to system-apps path\n");
136 				return 3;
137 			}
138 			command = terminalPath.Path();
139 		}
140 
141 		char shellCommand[4096];
142 		snprintf(shellCommand, sizeof(shellCommand),
143 			"echo connected; export TARGET_SCREEN=%" B_PRIu16 "; %s\n", port,
144 			command);
145 
146 		int pipes[4];
147 		if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0) {
148 			printf("failed to create redirection pipes\n");
149 			return 3;
150 		}
151 
152 		sshPID = fork();
153 		if (sshPID < 0) {
154 			printf("failed to fork ssh process\n");
155 			return 3;
156 		}
157 
158 		if (sshPID == 0) {
159 			// child code, redirect std* and execute ssh
160 			close(STDOUT_FILENO);
161 			close(STDIN_FILENO);
162 			dup2(pipes[1], STDOUT_FILENO);
163 			dup2(pipes[1], STDERR_FILENO);
164 			dup2(pipes[2], STDIN_FILENO);
165 			for (int32 i = 0; i < 4; i++)
166 				close(pipes[i]);
167 
168 			char localRedirect[50];
169 			sprintf(localRedirect, "localhost:%" B_PRIu16 ":localhost:%"
170 				B_PRIu16, port, port);
171 
172 			char portNumber[10];
173 			sprintf(portNumber, "%" B_PRIu16, sshPort);
174 
175 			int result = execl("ssh", "-C", "-L", localRedirect,
176 				"-p", portNumber, "-o", "ExitOnForwardFailure=yes", host,
177 				shellCommand, NULL);
178 
179 			// we don't get here unless there was an error in executing
180 			printf("failed to execute ssh process in child\n");
181 			return result;
182 		} else {
183 			close(pipes[1]);
184 			close(pipes[2]);
185 
186 			char buffer[10];
187 			read(pipes[0], buffer, sizeof(buffer));
188 				// block until connected/error message from ssh
189 
190 			host = "localhost";
191 		}
192 	}
193 
194 	BApplication app("application/x-vnd.Haiku-RemoteDesktop");
195 	BRect windowFrame = BRect(0, 0, width - 1, height - 1);
196 	if (!windowFrame.IsValid()) {
197 		BScreen screen;
198 		windowFrame = screen.Frame();
199 	}
200 
201 	BWindow *window = new(std::nothrow) BWindow(windowFrame, "RemoteDesktop",
202 		B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE);
203 
204 	if (window == NULL) {
205 		printf("no memory to allocate window\n");
206 		return 4;
207 	}
208 
209 	RemoteView *view = new(std::nothrow) RemoteView(window->Bounds(), host,
210 		port);
211 	if (view == NULL) {
212 		printf("no memory to allocate remote view\n");
213 		return 4;
214 	}
215 
216 	status_t init = view->InitCheck();
217 	if (init != B_OK) {
218 		printf("initialization of remote view failed: %s\n", strerror(init));
219 		delete view;
220 		return 5;
221 	}
222 
223 	window->AddChild(view);
224 	view->MakeFocus();
225 	window->Show();
226 	app.Run();
227 
228 	if (sshPID >= 0)
229 		kill(sshPID, SIGHUP);
230 
231 	return 0;
232 }
233