xref: /haiku/src/apps/remotedesktop/RemoteDesktop.cpp (revision 385ee03ba83b7a40d315e17b03031b3ca37820c0)
1 /*
2  * Copyright 2009-2014, 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 --listenOnly [-p <listenPort>]\n", app);
29 	printf("\t%s <user@host> [-p <listenPort>] [-s <sshPort>] [-c <command>]\n",
30 		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\n");
36 	printf("\t-c\t\tsend a command to the other computer\n");
37 	printf("\t-s\t\tuse ssh & specify the ssh port to communicate on\n");
38 }
39 
40 
41 int
42 main(int argc, char *argv[])
43 {
44 	if (argc < 2 || strcmp(argv[1], "--help") == 0) {
45 		print_usage(argv[0]);
46 		return 1;
47 	}
48 
49 	bool listenOnly = false;
50 	uint32 listenPort = 10900;
51 	uint32 sshPort = 22;
52 	const char* command = NULL;
53 
54 	for (int32 i = 2; i < argc; i++) {
55 		if (strcmp(argv[i], "-p") == 0) {
56 			if (argc < i + 1 || sscanf(argv[i + 1], "%" B_PRIu32, &listenPort)
57 				!= 1) {
58 				print_usage(argv[0]);
59 				return 2;
60 			}
61 
62 			i++;
63 			continue;
64 		}
65 
66 		if (strcmp(argv[i], "-s") == 0) {
67 			if (argc < i + 1 || sscanf(argv[i + 1], "%" B_PRIu32, &sshPort)
68 				!= 1) {
69 				print_usage(argv[0]);
70 				return 2;
71 			}
72 
73 			i++;
74 			continue;
75 		}
76 
77 		if (strcmp(argv[i], "-c") == 0) {
78 			if (argc < i + 1) {
79 				print_usage(argv[0]);
80 				return 2;
81 			}
82 
83 			i++;
84 			command = argv[i];
85 			continue;
86 		}
87 
88 		if (strcmp(argv[i], "--listenOnly") == 0) {
89 			listenOnly = true;
90 			continue;
91 		}
92 
93 		print_usage(argv[0]);
94 		return 2;
95 	}
96 
97 	pid_t sshPID = -1;
98 	if (!listenOnly) {
99 		BPath terminalPath;
100 		if (command == NULL) {
101 			if (find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath)
102 					!= B_OK) {
103 				printf("failed to determine system-apps directory\n");
104 				return 3;
105 			}
106 			if (terminalPath.Append("Terminal") != B_OK) {
107 				printf("failed to append to system-apps path\n");
108 				return 3;
109 			}
110 			command = terminalPath.Path();
111 		}
112 
113 		char shellCommand[4096];
114 		snprintf(shellCommand, sizeof(shellCommand),
115 			"echo connected; export TARGET_SCREEN=localhost:%" B_PRIu32
116 			"; %s\n", listenPort, command);
117 
118 		int pipes[4];
119 		if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0) {
120 			printf("failed to create redirection pipes\n");
121 			return 3;
122 		}
123 
124 		sshPID = fork();
125 		if (sshPID < 0) {
126 			printf("failed to fork ssh process\n");
127 			return 3;
128 		}
129 
130 		if (sshPID == 0) {
131 			// child code, redirect std* and execute ssh
132 			close(STDOUT_FILENO);
133 			close(STDIN_FILENO);
134 			dup2(pipes[1], STDOUT_FILENO);
135 			dup2(pipes[1], STDERR_FILENO);
136 			dup2(pipes[2], STDIN_FILENO);
137 			for (int32 i = 0; i < 4; i++)
138 				close(pipes[i]);
139 
140 			char localRedirect[50];
141 			sprintf(localRedirect, "localhost:%" B_PRIu32 ":localhost:%"
142 				B_PRIu32, listenPort + 1, listenPort + 1);
143 
144 			char remoteRedirect[50];
145 			sprintf(remoteRedirect, "localhost:%" B_PRIu32 ":localhost:%"
146 				B_PRIu32, listenPort, listenPort);
147 
148 			char portNumber[10];
149 			sprintf(portNumber, "%" B_PRIu32, sshPort);
150 
151 			int result = execl("ssh", "-C", "-L", localRedirect,
152 				"-R", remoteRedirect, "-p", portNumber, argv[1],
153 				shellCommand, NULL);
154 
155 			// we don't get here unless there was an error in executing
156 			printf("failed to execute ssh process in child\n");
157 			return result;
158 		} else {
159 			close(pipes[1]);
160 			close(pipes[2]);
161 
162 			char buffer[10];
163 			read(pipes[0], buffer, sizeof(buffer));
164 				// block until connected/error message from ssh
165 		}
166 	}
167 
168 	BApplication app("application/x-vnd.Haiku-RemoteDesktop");
169 	BScreen screen;
170 	BWindow *window = new(std::nothrow) BWindow(screen.Frame(), "RemoteDesktop",
171 		B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE);
172 
173 	if (window == NULL) {
174 		printf("no memory to allocate window\n");
175 		return 4;
176 	}
177 
178 	RemoteView *view = new(std::nothrow) RemoteView(window->Bounds(),
179 		listenPort);
180 	if (view == NULL) {
181 		printf("no memory to allocate remote view\n");
182 		return 4;
183 	}
184 
185 	status_t init = view->InitCheck();
186 	if (init != B_OK) {
187 		printf("initialization of remote view failed: %s\n", strerror(init));
188 		delete view;
189 		return 5;
190 	}
191 
192 	window->AddChild(view);
193 	view->MakeFocus();
194 	window->Show();
195 	app.Run();
196 
197 	if (sshPID >= 0)
198 		kill(sshPID, SIGINT);
199 
200 	return 0;
201 }
202