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