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