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
25void
26print_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
45int
46main(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