source: mainline/uspace/srv/hid/remcons/remcons.c@ 2bd578a

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

Merge mainline changes

  • Property mode set to 100644
File size: 10.0 KB
RevLine 
[21a9869]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>
[3806317]56#include "telnet.h"
[30d4706]57#include "user.h"
[21a9869]58
59#define APP_GETTERM "/app/getterm"
[03e0a244]60#define APP_SHELL "/app/bdsh"
[5576358]61
[3806317]62/** Telnet commands to force character mode
63 * (redundant to be on the safe side).
64 * See
[787b65b]65 * http://stackoverflow.com/questions/273261/force-telnet-user-into-character-mode
[3806317]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
[21a9869]76
[5923cf82]77/** Handling client requests (VFS and console interface).
78 *
79 * @param user Telnet user the requests belong to.
80 */
[787b65b]81static void client_connection_message_loop(telnet_user_t *user)
[21a9869]82{
83 while (true) {
84 ipc_call_t call;
[1870d81]85 ipc_callid_t callid = 0;
[99c2e9f3]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 */
[1870d81]94 while (callid == 0) {
95 callid = async_get_call_timeout(&call, 1000);
96
[99c2e9f3]97 if (telnet_user_is_zombie(user)) {
[1870d81]98 if (callid != 0) {
99 async_answer_0(callid, EINTR);
100 }
101 return;
102 }
103 }
[21a9869]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:
[178d6a3]114 fibril_mutex_lock(&user->guard);
115 async_answer_2(callid, EOK, user->cursor_x, 0);
116 fibril_mutex_unlock(&user->guard);
[21a9869]117 break;
118 case CONSOLE_GET_EVENT: {
[99c2e9f3]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;
[21a9869]125 }
[99c2e9f3]126 async_answer_4(callid, EOK, event.type, event.key, event.mods, event.c);
[21a9869]127 break;
128 }
[178d6a3]129 case CONSOLE_GOTO: {
130 int new_x = IPC_GET_ARG1(call);
131 telnet_user_update_cursor_x(user, new_x);
[21a9869]132 async_answer_0(callid, ENOTSUP);
133 break;
[178d6a3]134 }
[21a9869]135 case VFS_OUT_READ:
136 async_answer_0(callid, ENOTSUP);
137 break;
138 case VFS_OUT_WRITE: {
[178d6a3]139 uint8_t *buf;
[21a9869]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 }
[99c2e9f3]147
[178d6a3]148 rc = telnet_user_send_data(user, buf, size);
[21a9869]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
[5923cf82]190/** Callback when client connects to a telnet terminal. */
[6f7cd5d]191static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
192{
[787b65b]193 /* Find the user. */
194 telnet_user_t *user = telnet_user_get_for_client_connection(IPC_GET_ARG1(*icall));
195 if (user == NULL) {
[6f7cd5d]196 async_answer_0(iid, ENOENT);
197 return;
198 }
[99c2e9f3]199 async_answer_0(iid, EOK);
[6f7cd5d]200
[d545d03]201 telnet_user_log(user, "New client connected (%" PRIxn").", iid);
[6f7cd5d]202
[3806317]203 /* Force character mode. */
[787b65b]204 send(user->socket, (void *)telnet_force_character_mode_command,
[3806317]205 telnet_force_character_mode_command_count, 0);
[6f7cd5d]206
[99c2e9f3]207 /* Handle messages. */
[787b65b]208 client_connection_message_loop(user);
[6f7cd5d]209
[8562724]210 telnet_user_notify_client_disconnected(user);
[d545d03]211 telnet_user_log(user, "Client disconnected (%" PRIxn").", iid);
[6f7cd5d]212}
213
[5923cf82]214/** Fibril for spawning the task running after user connects.
215 *
216 * @param arg Corresponding @c telnet_user_t structure.
217 */
[7c2bb2c]218static int spawn_task_fibril(void *arg)
[21a9869]219{
[787b65b]220 telnet_user_t *user = arg;
[7c2bb2c]221 int rc;
[21a9869]222
223 char term[LOC_NAME_MAXLEN];
[787b65b]224 snprintf(term, LOC_NAME_MAXLEN, "%s/%s", "/loc", user->service_name);
[21a9869]225
226 task_id_t task;
[03e0a244]227 rc = task_spawnl(&task, APP_GETTERM, APP_GETTERM, "-w", term, APP_SHELL, NULL);
[21a9869]228 if (rc != EOK) {
[03e0a244]229 telnet_user_error(user, "Spawning `%s -w %s %s' failed: %s.",
230 APP_GETTERM, term, APP_SHELL, str_error(rc));
[261bbdc]231 fibril_mutex_lock(&user->guard);
[787b65b]232 user->task_finished = true;
233 fibril_condvar_signal(&user->refcount_cv);
[261bbdc]234 fibril_mutex_unlock(&user->guard);
[21a9869]235 return EOK;
236 }
237
[261bbdc]238 fibril_mutex_lock(&user->guard);
[787b65b]239 user->task_id = task;
[261bbdc]240 fibril_mutex_unlock(&user->guard);
[1870d81]241
[21a9869]242 task_exit_t task_exit;
243 int task_retval;
244 task_wait(task, &task_exit, &task_retval);
[d545d03]245 telnet_user_log(user, "%s terminated %s, exit code %d.", APP_GETTERM,
246 task_exit == TASK_EXIT_NORMAL ? "normally" : "unexpectedly",
247 task_retval);
[21a9869]248
[7c2bb2c]249 /* Announce destruction. */
[261bbdc]250 fibril_mutex_lock(&user->guard);
[787b65b]251 user->task_finished = true;
252 fibril_condvar_signal(&user->refcount_cv);
[261bbdc]253 fibril_mutex_unlock(&user->guard);
[7c2bb2c]254
255 return EOK;
256}
257
[5923cf82]258/** Tell whether given user can be destroyed (has no active clients).
259 *
260 * @param user The telnet user in question.
261 */
[787b65b]262static bool user_can_be_destroyed_no_lock(telnet_user_t *user)
[1870d81]263{
[787b65b]264 return user->task_finished && user->socket_closed &&
265 (user->locsrv_connection_count == 0);
[1870d81]266}
[7c2bb2c]267
[5923cf82]268/** Fibril for each accepted socket.
269 *
270 * @param arg Corresponding @c telnet_user_t structure.
271 */
[787b65b]272static int network_user_fibril(void *arg)
[7c2bb2c]273{
274 int rc;
[787b65b]275 telnet_user_t *user = arg;
[7c2bb2c]276
[787b65b]277 rc = loc_service_register(user->service_name, &user->service_id);
[6f7cd5d]278 if (rc != EOK) {
[d545d03]279 telnet_user_error(user, "Unable to register %s with loc: %s.",
280 user->service_name, str_error(rc));
[7c2bb2c]281 return EOK;
[6f7cd5d]282 }
[d545d03]283
284 telnet_user_log(user, "Service %s registerd with id %" PRIun ".",
285 user->service_name, user->service_id);
[7c2bb2c]286
[787b65b]287 fid_t spawn_fibril = fibril_create(spawn_task_fibril, user);
[7c2bb2c]288 assert(spawn_fibril);
289 fibril_add_ready(spawn_fibril);
[6f7cd5d]290
291 /* Wait for all clients to exit. */
[261bbdc]292 fibril_mutex_lock(&user->guard);
[787b65b]293 while (!user_can_be_destroyed_no_lock(user)) {
294 if (user->task_finished) {
295 closesocket(user->socket);
296 user->socket_closed = true;
[1870d81]297 continue;
[787b65b]298 } else if (user->socket_closed) {
299 if (user->task_id != 0) {
300 task_kill(user->task_id);
[1870d81]301 }
302 }
[261bbdc]303 fibril_condvar_wait_timeout(&user->refcount_cv, &user->guard, 1000);
[6f7cd5d]304 }
[261bbdc]305 fibril_mutex_unlock(&user->guard);
[21a9869]306
[787b65b]307 rc = loc_service_unregister(user->service_id);
[7c2bb2c]308 if (rc != EOK) {
[d545d03]309 telnet_user_error(user,
310 "Unable to unregister %s from loc: %s (ignored).",
311 user->service_name, str_error(rc));
[7c2bb2c]312 }
313
[d545d03]314 telnet_user_log(user, "Destroying...");
[787b65b]315 telnet_user_destroy(user);
[5576358]316
[21a9869]317 return EOK;
318}
319
320int main(int argc, char *argv[])
321{
322 int port = 2223;
323
[a1347a7]324 async_set_client_connection(client_connection);
325 int rc = loc_server_register(NAME);
[21a9869]326 if (rc < 0) {
[d545d03]327 fprintf(stderr, NAME ": Unable to register server: %s.\n",
[21a9869]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) {
[d545d03]340 fprintf(stderr, "Error parsing network address: %s.\n",
[21a9869]341 str_error(rc));
342 return 2;
343 }
344
345 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
346 if (listen_sd < 0) {
[d545d03]347 fprintf(stderr, "Error creating listening socket: %s.\n",
[21a9869]348 str_error(listen_sd));
349 return 3;
350 }
351
352 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
353 if (rc != EOK) {
[d545d03]354 fprintf(stderr, "Error binding socket: %s.\n",
[21a9869]355 str_error(rc));
356 return 4;
357 }
358
359 rc = listen(listen_sd, BACKLOG_SIZE);
360 if (rc != EOK) {
[d545d03]361 fprintf(stderr, "listen() failed: %s.\n", str_error(rc));
[21a9869]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) {
[d545d03]374 fprintf(stderr, "accept() failed: %s.\n",
375 str_error(rc));
[21a9869]376 continue;
377 }
378
[787b65b]379 telnet_user_t *user = telnet_user_create(conn_sd);
380 assert(user);
[21a9869]381
[787b65b]382 fid_t fid = fibril_create(network_user_fibril, user);
[21a9869]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.