source: mainline/uspace/srv/hid/remcons/remcons.c@ 6f7cd5d

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

remcons: safer wrt. fibrils

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