source: mainline/uspace/srv/hid/remcons/remcons.c@ 4e5dabf

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4e5dabf was a1347a7, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Merge mainline changes

  • Property mode set to 100644
File size: 10.0 KB
Line 
1/*
2 * Copyright (c) 2012 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup remcons
30 * @{
31 */
32/** @file
33 */
34
35#include <async.h>
36#include <stdio.h>
37#include <adt/prodcons.h>
38#include <ipc/input.h>
39#include <ipc/console.h>
40#include <ipc/vfs.h>
41#include <errno.h>
42#include <str_error.h>
43#include <loc.h>
44#include <event.h>
45#include <io/keycode.h>
46#include <align.h>
47#include <malloc.h>
48#include <as.h>
49#include <fibril_synch.h>
50#include <task.h>
51#include <net/in.h>
52#include <net/inet.h>
53#include <net/socket.h>
54#include <io/console.h>
55#include <inttypes.h>
56#include "telnet.h"
57#include "user.h"
58
59#define APP_GETTERM "/app/getterm"
60#define APP_SHELL "/app/bdsh"
61
62/** Telnet commands to force character mode
63 * (redundant to be on the safe side).
64 * See
65 * http://stackoverflow.com/questions/273261/force-telnet-user-into-character-mode
66 * for discussion.
67 */
68static const telnet_cmd_t telnet_force_character_mode_command[] = {
69 TELNET_IAC, TELNET_WILL, TELNET_ECHO,
70 TELNET_IAC, TELNET_WILL, TELNET_SUPPRESS_GO_AHEAD,
71 TELNET_IAC, TELNET_WONT, TELNET_LINEMODE
72};
73static const size_t telnet_force_character_mode_command_count =
74 sizeof(telnet_force_character_mode_command) / sizeof(telnet_cmd_t);
75
76
77/** Handling client requests (VFS and console interface).
78 *
79 * @param user Telnet user the requests belong to.
80 */
81static void client_connection_message_loop(telnet_user_t *user)
82{
83 while (true) {
84 ipc_call_t call;
85 ipc_callid_t callid = 0;
86
87 /*
88 * The getterm task might terminate while we are here,
89 * waiting for a call. Also, the socket might be closed
90 * meanwhile.
91 * We want to detect this situation early, so we use a
92 * timeout variant of async_get_call().
93 */
94 while (callid == 0) {
95 callid = async_get_call_timeout(&call, 1000);
96
97 if (telnet_user_is_zombie(user)) {
98 if (callid != 0) {
99 async_answer_0(callid, EINTR);
100 }
101 return;
102 }
103 }
104
105 if (!IPC_GET_IMETHOD(call)) {
106 return;
107 }
108
109 switch (IPC_GET_IMETHOD(call)) {
110 case CONSOLE_GET_SIZE:
111 async_answer_2(callid, EOK, 100, 1);
112 break;
113 case CONSOLE_GET_POS:
114 fibril_mutex_lock(&user->guard);
115 async_answer_2(callid, EOK, user->cursor_x, 0);
116 fibril_mutex_unlock(&user->guard);
117 break;
118 case CONSOLE_GET_EVENT: {
119 kbd_event_t event;
120 int rc = telnet_user_get_next_keyboard_event(user, &event);
121 if (rc != EOK) {
122 /* Silently ignore. */
123 async_answer_0(callid, EOK);
124 break;
125 }
126 async_answer_4(callid, EOK, event.type, event.key, event.mods, event.c);
127 break;
128 }
129 case CONSOLE_GOTO: {
130 int new_x = IPC_GET_ARG1(call);
131 telnet_user_update_cursor_x(user, new_x);
132 async_answer_0(callid, ENOTSUP);
133 break;
134 }
135 case VFS_OUT_READ:
136 async_answer_0(callid, ENOTSUP);
137 break;
138 case VFS_OUT_WRITE: {
139 uint8_t *buf;
140 size_t size;
141 int rc = async_data_write_accept((void **)&buf, false, 0, 0, 0, &size);
142
143 if (rc != EOK) {
144 async_answer_0(callid, rc);
145 break;
146 }
147
148 rc = telnet_user_send_data(user, buf, size);
149 free(buf);
150
151 if (rc != EOK) {
152 async_answer_0(callid, rc);
153 break;
154 }
155
156 async_answer_1(callid, EOK, size);
157 break;
158 }
159 case VFS_OUT_SYNC:
160 async_answer_0(callid, EOK);
161 break;
162 case CONSOLE_CLEAR:
163 async_answer_0(callid, EOK);
164 break;
165
166 case CONSOLE_GET_COLOR_CAP:
167 async_answer_1(callid, EOK, CONSOLE_CAP_NONE);
168 break;
169 case CONSOLE_SET_STYLE:
170 async_answer_0(callid, ENOTSUP);
171 break;
172 case CONSOLE_SET_COLOR:
173 async_answer_0(callid, ENOTSUP);
174 break;
175 case CONSOLE_SET_RGB_COLOR:
176 async_answer_0(callid, ENOTSUP);
177 break;
178
179 case CONSOLE_CURSOR_VISIBILITY:
180 async_answer_0(callid, ENOTSUP);
181 break;
182
183 default:
184 async_answer_0(callid, EINVAL);
185 break;
186 }
187 }
188}
189
190/** Callback when client connects to a telnet terminal. */
191static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
192{
193 /* Find the user. */
194 telnet_user_t *user = telnet_user_get_for_client_connection(IPC_GET_ARG1(*icall));
195 if (user == NULL) {
196 async_answer_0(iid, ENOENT);
197 return;
198 }
199 async_answer_0(iid, EOK);
200
201 telnet_user_log(user, "New client connected (%" PRIxn").", iid);
202
203 /* Force character mode. */
204 send(user->socket, (void *)telnet_force_character_mode_command,
205 telnet_force_character_mode_command_count, 0);
206
207 /* Handle messages. */
208 client_connection_message_loop(user);
209
210 telnet_user_notify_client_disconnected(user);
211 telnet_user_log(user, "Client disconnected (%" PRIxn").", iid);
212}
213
214/** Fibril for spawning the task running after user connects.
215 *
216 * @param arg Corresponding @c telnet_user_t structure.
217 */
218static int spawn_task_fibril(void *arg)
219{
220 telnet_user_t *user = arg;
221 int rc;
222
223 char term[LOC_NAME_MAXLEN];
224 snprintf(term, LOC_NAME_MAXLEN, "%s/%s", "/loc", user->service_name);
225
226 task_id_t task;
227 rc = task_spawnl(&task, APP_GETTERM, APP_GETTERM, "-w", term, APP_SHELL, NULL);
228 if (rc != EOK) {
229 telnet_user_error(user, "Spawning `%s -w %s %s' failed: %s.",
230 APP_GETTERM, term, APP_SHELL, str_error(rc));
231 fibril_mutex_lock(&user->guard);
232 user->task_finished = true;
233 fibril_condvar_signal(&user->refcount_cv);
234 fibril_mutex_unlock(&user->guard);
235 return EOK;
236 }
237
238 fibril_mutex_lock(&user->guard);
239 user->task_id = task;
240 fibril_mutex_unlock(&user->guard);
241
242 task_exit_t task_exit;
243 int task_retval;
244 task_wait(task, &task_exit, &task_retval);
245 telnet_user_log(user, "%s terminated %s, exit code %d.", APP_GETTERM,
246 task_exit == TASK_EXIT_NORMAL ? "normally" : "unexpectedly",
247 task_retval);
248
249 /* Announce destruction. */
250 fibril_mutex_lock(&user->guard);
251 user->task_finished = true;
252 fibril_condvar_signal(&user->refcount_cv);
253 fibril_mutex_unlock(&user->guard);
254
255 return EOK;
256}
257
258/** Tell whether given user can be destroyed (has no active clients).
259 *
260 * @param user The telnet user in question.
261 */
262static bool user_can_be_destroyed_no_lock(telnet_user_t *user)
263{
264 return user->task_finished && user->socket_closed &&
265 (user->locsrv_connection_count == 0);
266}
267
268/** Fibril for each accepted socket.
269 *
270 * @param arg Corresponding @c telnet_user_t structure.
271 */
272static int network_user_fibril(void *arg)
273{
274 int rc;
275 telnet_user_t *user = arg;
276
277 rc = loc_service_register(user->service_name, &user->service_id);
278 if (rc != EOK) {
279 telnet_user_error(user, "Unable to register %s with loc: %s.",
280 user->service_name, str_error(rc));
281 return EOK;
282 }
283
284 telnet_user_log(user, "Service %s registerd with id %" PRIun ".",
285 user->service_name, user->service_id);
286
287 fid_t spawn_fibril = fibril_create(spawn_task_fibril, user);
288 assert(spawn_fibril);
289 fibril_add_ready(spawn_fibril);
290
291 /* Wait for all clients to exit. */
292 fibril_mutex_lock(&user->guard);
293 while (!user_can_be_destroyed_no_lock(user)) {
294 if (user->task_finished) {
295 closesocket(user->socket);
296 user->socket_closed = true;
297 continue;
298 } else if (user->socket_closed) {
299 if (user->task_id != 0) {
300 task_kill(user->task_id);
301 }
302 }
303 fibril_condvar_wait_timeout(&user->refcount_cv, &user->guard, 1000);
304 }
305 fibril_mutex_unlock(&user->guard);
306
307 rc = loc_service_unregister(user->service_id);
308 if (rc != EOK) {
309 telnet_user_error(user,
310 "Unable to unregister %s from loc: %s (ignored).",
311 user->service_name, str_error(rc));
312 }
313
314 telnet_user_log(user, "Destroying...");
315 telnet_user_destroy(user);
316
317 return EOK;
318}
319
320int main(int argc, char *argv[])
321{
322 int port = 2223;
323
324 async_set_client_connection(client_connection);
325 int rc = loc_server_register(NAME);
326 if (rc < 0) {
327 fprintf(stderr, NAME ": Unable to register server: %s.\n",
328 str_error(rc));
329 return 1;
330 }
331
332 struct sockaddr_in addr;
333
334 addr.sin_family = AF_INET;
335 addr.sin_port = htons(port);
336
337 rc = inet_pton(AF_INET, "127.0.0.1", (void *)
338 &addr.sin_addr.s_addr);
339 if (rc != EOK) {
340 fprintf(stderr, "Error parsing network address: %s.\n",
341 str_error(rc));
342 return 2;
343 }
344
345 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
346 if (listen_sd < 0) {
347 fprintf(stderr, "Error creating listening socket: %s.\n",
348 str_error(listen_sd));
349 return 3;
350 }
351
352 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
353 if (rc != EOK) {
354 fprintf(stderr, "Error binding socket: %s.\n",
355 str_error(rc));
356 return 4;
357 }
358
359 rc = listen(listen_sd, BACKLOG_SIZE);
360 if (rc != EOK) {
361 fprintf(stderr, "listen() failed: %s.\n", str_error(rc));
362 return 5;
363 }
364
365 printf("%s: HelenOS Remote console service\n", NAME);
366
367 while (true) {
368 struct sockaddr_in raddr;
369 socklen_t raddr_len = sizeof(raddr);
370 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
371 &raddr_len);
372
373 if (conn_sd < 0) {
374 fprintf(stderr, "accept() failed: %s.\n",
375 str_error(rc));
376 continue;
377 }
378
379 telnet_user_t *user = telnet_user_create(conn_sd);
380 assert(user);
381
382 fid_t fid = fibril_create(network_user_fibril, user);
383 assert(fid);
384 fibril_add_ready(fid);
385 }
386
387 return 0;
388}
389
390/** @}
391 */
Note: See TracBrowser for help on using the repository browser.