source: mainline/uspace/srv/hid/remcons/remcons.c@ 7c2bb2c

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

remcons: spawn task in separate fibril

  • Property mode set to 100644
File size: 12.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
58
59#define APP_GETTERM "/app/getterm"
60#define NAME "remterm"
61#define NAMESPACE "term"
62#define BACKLOG_SIZE 5
63
64#define BUFFER_SIZE 1024
65
66typedef struct {
67 int id;
68 int socket;
69 service_id_t service_id;
70 char *service_name;
71 /** Producer-consumer of kbd_event_t. */
72 prodcons_t in_events;
73 link_t link;
74 char socket_buffer[BUFFER_SIZE];
75 size_t socket_buffer_len;
76 size_t socket_buffer_pos;
77
78 /* Destruction CV with guard. */
79 int refcount;
80 fibril_condvar_t refcount_cv;
81 fibril_mutex_t refcount_mutex;
82} client_t;
83
84static FIBRIL_MUTEX_INITIALIZE(clients_guard);
85static LIST_INITIALIZE(clients);
86
87/** Telnet commands to force character mode
88 * (redundant to be on the safe side).
89 * See
90 * http://stackoverflow.com/questions/273261/force-telnet-client-into-character-mode
91 * for discussion.
92 */
93static const telnet_cmd_t telnet_force_character_mode_command[] = {
94 TELNET_IAC, TELNET_WILL, TELNET_ECHO,
95 TELNET_IAC, TELNET_WILL, TELNET_SUPPRESS_GO_AHEAD,
96 TELNET_IAC, TELNET_WONT, TELNET_LINEMODE
97};
98static const size_t telnet_force_character_mode_command_count =
99 sizeof(telnet_force_character_mode_command) / sizeof(telnet_cmd_t);
100
101static client_t *client_create(int socket)
102{
103 static int client_counter = 0;
104
105 client_t *client = malloc(sizeof(client_t));
106 if (client == NULL) {
107 return NULL;
108 }
109
110 client->id = ++client_counter;
111
112 int rc = asprintf(&client->service_name, "%s/telnet%d", NAMESPACE, client->id);
113 if (rc < 0) {
114 free(client);
115 return NULL;
116 }
117
118 client->socket = socket;
119 client->service_id = (service_id_t) -1;
120 prodcons_initialize(&client->in_events);
121 link_initialize(&client->link);
122 client->socket_buffer_len = 0;
123 client->socket_buffer_pos = 0;
124
125 fibril_condvar_initialize(&client->refcount_cv);
126 fibril_mutex_initialize(&client->refcount_mutex);
127 client->refcount = 0;
128
129
130 fibril_mutex_lock(&clients_guard);
131 list_append(&client->link, &clients);
132 fibril_mutex_unlock(&clients_guard);
133
134 return client;
135}
136
137static void client_destroy(client_t *client)
138{
139 assert(client);
140
141 fibril_mutex_lock(&clients_guard);
142 list_remove(&client->link);
143 fibril_mutex_unlock(&clients_guard);
144
145 free(client);
146}
147
148static client_t *client_find(service_id_t id)
149{
150 client_t *client = NULL;
151
152 fibril_mutex_lock(&clients_guard);
153 list_foreach(clients, link) {
154 client_t *tmp = list_get_instance(link, client_t, link);
155 if (tmp->service_id == id) {
156 client = tmp;
157 break;
158 }
159 }
160 fibril_mutex_unlock(&clients_guard);
161
162 return client;
163}
164
165
166
167static kbd_event_t* new_kbd_event(kbd_event_type_t type, wchar_t c) {
168 kbd_event_t *event = malloc(sizeof(kbd_event_t));
169 assert(event);
170
171 link_initialize(&event->link);
172 event->type = type;
173 event->c = c;
174 event->mods = 0;
175 event->key = (c == '\n' ? KC_ENTER : KC_A);
176
177 return event;
178}
179
180static void client_connection_message_loop(client_t *client)
181{
182 while (true) {
183 ipc_call_t call;
184 ipc_callid_t callid = async_get_call(&call);
185
186 if (!IPC_GET_IMETHOD(call)) {
187 /* Clean-up. */
188 return;
189 }
190
191 switch (IPC_GET_IMETHOD(call)) {
192 case CONSOLE_GET_SIZE:
193 async_answer_2(callid, EOK, 100, 1);
194 break;
195 case CONSOLE_GET_POS:
196 async_answer_2(callid, EOK, 0, 0);
197 break;
198 case CONSOLE_GET_EVENT: {
199 if (list_empty(&client->in_events.list)) {
200 retry:
201 if (client->socket_buffer_len <= client->socket_buffer_pos) {
202 int recv_length = recv(client->socket, client->socket_buffer, BUFFER_SIZE, 0);
203 if (recv_length == 0) {
204 async_answer_0(callid, ENOENT);
205 return;
206 }
207 if (recv_length < 0) {
208 async_answer_0(callid, EINVAL);
209 break;
210 }
211 client->socket_buffer_len = recv_length;
212 client->socket_buffer_pos = 0;
213 }
214 char data = client->socket_buffer[client->socket_buffer_pos++];
215 if (data == 13) {
216 data = 10;
217 }
218 if (data == 0)
219 goto retry;
220
221 kbd_event_t *down = new_kbd_event(KEY_PRESS, data);
222 kbd_event_t *up = new_kbd_event(KEY_RELEASE, data);
223 assert(down);
224 assert(up);
225 prodcons_produce(&client->in_events, &down->link);
226 prodcons_produce(&client->in_events, &up->link);
227 }
228
229
230 link_t *link = prodcons_consume(&client->in_events);
231 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
232 async_answer_4(callid, EOK, event->type, event->key, event->mods, event->c);
233 free(event);
234 break;
235 }
236 case CONSOLE_GOTO:
237 async_answer_0(callid, ENOTSUP);
238 break;
239 case VFS_OUT_READ:
240 async_answer_0(callid, ENOTSUP);
241 break;
242 case VFS_OUT_WRITE: {
243 char *buf;
244 char *buf_converted;
245 size_t size;
246 int rc = async_data_write_accept((void **)&buf, false, 0, 0, 0, &size);
247
248 if (rc != EOK) {
249 async_answer_0(callid, rc);
250 break;
251 }
252 buf_converted = malloc(2 * size);
253 assert(buf_converted);
254 int buf_converted_size = 0;
255 /* Convert new-lines. */
256 for (size_t i = 0; i < size; i++) {
257 if (buf[i] == 10) {
258 buf_converted[buf_converted_size++] = 13;
259 buf_converted[buf_converted_size++] = 10;
260 } else {
261 buf_converted[buf_converted_size++] = buf[i];
262 }
263 }
264 rc = send(client->socket, buf_converted, buf_converted_size, 0);
265 free(buf);
266
267 if (rc != EOK) {
268 printf("Problem sending data: %s\n", str_error(rc));
269 async_answer_0(callid, rc);
270 break;
271 }
272
273 async_answer_1(callid, EOK, size);
274
275 break;
276 }
277 case VFS_OUT_SYNC:
278 async_answer_0(callid, EOK);
279 break;
280 case CONSOLE_CLEAR:
281 async_answer_0(callid, EOK);
282 break;
283
284 case CONSOLE_GET_COLOR_CAP:
285 async_answer_1(callid, EOK, CONSOLE_CAP_NONE);
286 break;
287 case CONSOLE_SET_STYLE:
288 async_answer_0(callid, ENOTSUP);
289 break;
290 case CONSOLE_SET_COLOR:
291 async_answer_0(callid, ENOTSUP);
292 break;
293 case CONSOLE_SET_RGB_COLOR:
294 async_answer_0(callid, ENOTSUP);
295 break;
296
297 case CONSOLE_CURSOR_VISIBILITY:
298 async_answer_0(callid, ENOTSUP);
299 break;
300
301 default:
302 async_answer_0(callid, EINVAL);
303 break;
304 }
305 }
306}
307
308static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
309{
310 /* Find the client. */
311 client_t *client = client_find(IPC_GET_ARG1(*icall));
312 if (client == NULL) {
313 async_answer_0(iid, ENOENT);
314 return;
315 }
316
317 printf("New client for service %s.\n", client->service_name);
318
319 /* Accept the connection, increment reference. */
320 async_answer_0(iid, EOK);
321 fibril_mutex_lock(&client->refcount_mutex);
322 client->refcount++;
323 fibril_mutex_unlock(&client->refcount_mutex);
324
325 /* Force character mode. */
326 send(client->socket, (void *)telnet_force_character_mode_command,
327 telnet_force_character_mode_command_count, 0);
328
329 client_connection_message_loop(client);
330
331 /* Announce client disconnection. */
332 fibril_mutex_lock(&client->refcount_mutex);
333 client->refcount--;
334 fibril_condvar_signal(&client->refcount_cv);
335 fibril_mutex_unlock(&client->refcount_mutex);
336}
337
338static int spawn_task_fibril(void *arg)
339{
340 client_t *client = arg;
341 int rc;
342
343 char term[LOC_NAME_MAXLEN];
344 snprintf(term, LOC_NAME_MAXLEN, "%s/%s", "/loc", client->service_name);
345
346 task_id_t task;
347 rc = task_spawnl(&task, APP_GETTERM, APP_GETTERM, term, "/app/bdsh", NULL);
348 if (rc != EOK) {
349 printf("%s: Error spawning %s -w %s %s (%s)\n", NAME,
350 APP_GETTERM, term, "/app/bdsh", str_error(rc));
351 fibril_mutex_lock(&client->refcount_mutex);
352 client->refcount--;
353 fibril_condvar_signal(&client->refcount_cv);
354 fibril_mutex_unlock(&client->refcount_mutex);
355 return EOK;
356 }
357
358 task_exit_t task_exit;
359 int task_retval;
360 task_wait(task, &task_exit, &task_retval);
361 printf("%s: getterm terminated: %d, %d\n", NAME, task_exit, task_retval);
362
363 /* Announce destruction. */
364 fibril_mutex_lock(&client->refcount_mutex);
365 client->refcount--;
366 fibril_condvar_signal(&client->refcount_cv);
367 fibril_mutex_unlock(&client->refcount_mutex);
368
369 return EOK;
370}
371
372
373static int network_client_fibril(void *arg)
374{
375 int rc;
376 client_t *client = arg;
377
378 rc = loc_service_register(client->service_name, &client->service_id);
379 if (rc != EOK) {
380 fprintf(stderr, "%s: Unable to register device %s\n", NAME,
381 client->service_name);
382 return EOK;
383 }
384 printf("Service %s registered as %" PRIun "\n", client->service_name,
385 client->service_id);
386
387 fibril_mutex_lock(&client->refcount_mutex);
388 client->refcount++;
389 fibril_mutex_unlock(&client->refcount_mutex);
390
391 fid_t spawn_fibril = fibril_create(spawn_task_fibril, client);
392 assert(spawn_fibril);
393 fibril_add_ready(spawn_fibril);
394
395 /* Wait for all clients to exit. */
396 fibril_mutex_lock(&client->refcount_mutex);
397 while (client->refcount > 0) {
398 fibril_condvar_wait(&client->refcount_cv, &client->refcount_mutex);
399 }
400 fibril_mutex_unlock(&client->refcount_mutex);
401
402 closesocket(client->socket);
403 rc = loc_service_unregister(client->service_id);
404 if (rc != EOK) {
405 fprintf(stderr, "Warning: failed to unregister %s: %s\n", client->service_name, str_error(rc));
406 }
407
408 printf("Destroying service %s.\n", client->service_name);
409 client_destroy(client);
410
411 return EOK;
412}
413
414int main(int argc, char *argv[])
415{
416 int port = 2223;
417
418 int rc = loc_server_register(NAME, client_connection);
419 if (rc < 0) {
420 printf("%s: Unable to register server (%s).\n", NAME,
421 str_error(rc));
422 return 1;
423 }
424
425 struct sockaddr_in addr;
426
427 addr.sin_family = AF_INET;
428 addr.sin_port = htons(port);
429
430 rc = inet_pton(AF_INET, "127.0.0.1", (void *)
431 &addr.sin_addr.s_addr);
432 if (rc != EOK) {
433 fprintf(stderr, "Error parsing network address (%s)\n",
434 str_error(rc));
435 return 2;
436 }
437
438 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
439 if (listen_sd < 0) {
440 fprintf(stderr, "Error creating listening socket (%s)\n",
441 str_error(listen_sd));
442 return 3;
443 }
444
445 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
446 if (rc != EOK) {
447 fprintf(stderr, "Error binding socket (%s)\n",
448 str_error(rc));
449 return 4;
450 }
451
452 rc = listen(listen_sd, BACKLOG_SIZE);
453 if (rc != EOK) {
454 fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
455 return 5;
456 }
457
458 printf("%s: HelenOS Remote console service\n", NAME);
459
460 while (true) {
461 struct sockaddr_in raddr;
462 socklen_t raddr_len = sizeof(raddr);
463 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
464 &raddr_len);
465
466 if (conn_sd < 0) {
467 fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
468 continue;
469 }
470
471 client_t *client = client_create(conn_sd);
472 assert(client);
473
474 fid_t fid = fibril_create(network_client_fibril, client);
475 assert(fid);
476 fibril_add_ready(fid);
477 }
478
479 return 0;
480}
481
482/** @}
483 */
Note: See TracBrowser for help on using the repository browser.