source: mainline/uspace/srv/hid/remcons/remcons.c@ 3806317

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

remcons: move telnet commands to telnet.h

  • Property mode set to 100644
File size: 11.4 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
338
339static int network_client_fibril(void *arg)
340{
341 int rc;
342 client_t *client = arg;
343
344 rc = loc_service_register(client->service_name, &client->service_id);
345 if (rc != EOK) {
346 fprintf(stderr, "%s: Unable to register device %s\n", NAME,
347 client->service_name);
348 return EOK;
349 }
350 printf("Service %s registered as %" PRIun "\n", client->service_name,
351 client->service_id);
352
353 char term[LOC_NAME_MAXLEN];
354 snprintf(term, LOC_NAME_MAXLEN, "%s/%s", "/loc", client->service_name);
355
356 task_id_t task;
357 rc = task_spawnl(&task, APP_GETTERM, APP_GETTERM, term, "/app/bdsh", NULL);
358 if (rc != EOK) {
359 printf("%s: Error spawning %s -w %s %s (%s)\n", NAME,
360 APP_GETTERM, term, "/app/bdsh", str_error(rc));
361 return EOK;
362 }
363 fibril_mutex_lock(&client->refcount_mutex);
364 client->refcount++;
365 fibril_mutex_unlock(&client->refcount_mutex);
366
367 task_exit_t task_exit;
368 int task_retval;
369 task_wait(task, &task_exit, &task_retval);
370 printf("%s: getterm terminated: %d, %d\n", NAME, task_exit, task_retval);
371
372 closesocket(client->socket);
373 rc = loc_service_unregister(client->service_id);
374 if (rc != EOK) {
375 fprintf(stderr, "Warning: failed to unregister %s: %s\n", client->service_name, str_error(rc));
376 }
377
378 /* Wait for all clients to exit. */
379 fibril_mutex_lock(&client->refcount_mutex);
380 /* Drop our reference. */
381 client->refcount--;
382 while (client->refcount > 0) {
383 fibril_condvar_wait(&client->refcount_cv, &client->refcount_mutex);
384 }
385 fibril_mutex_unlock(&client->refcount_mutex);
386
387 client_destroy(client);
388
389 return EOK;
390}
391
392int main(int argc, char *argv[])
393{
394 int port = 2223;
395
396 int rc = loc_server_register(NAME, client_connection);
397 if (rc < 0) {
398 printf("%s: Unable to register server (%s).\n", NAME,
399 str_error(rc));
400 return 1;
401 }
402
403 struct sockaddr_in addr;
404
405 addr.sin_family = AF_INET;
406 addr.sin_port = htons(port);
407
408 rc = inet_pton(AF_INET, "127.0.0.1", (void *)
409 &addr.sin_addr.s_addr);
410 if (rc != EOK) {
411 fprintf(stderr, "Error parsing network address (%s)\n",
412 str_error(rc));
413 return 2;
414 }
415
416 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
417 if (listen_sd < 0) {
418 fprintf(stderr, "Error creating listening socket (%s)\n",
419 str_error(listen_sd));
420 return 3;
421 }
422
423 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
424 if (rc != EOK) {
425 fprintf(stderr, "Error binding socket (%s)\n",
426 str_error(rc));
427 return 4;
428 }
429
430 rc = listen(listen_sd, BACKLOG_SIZE);
431 if (rc != EOK) {
432 fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
433 return 5;
434 }
435
436 printf("%s: HelenOS Remote console service\n", NAME);
437
438 while (true) {
439 struct sockaddr_in raddr;
440 socklen_t raddr_len = sizeof(raddr);
441 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
442 &raddr_len);
443
444 if (conn_sd < 0) {
445 fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
446 continue;
447 }
448
449 client_t *client = client_create(conn_sd);
450 assert(client);
451
452 fid_t fid = fibril_create(network_client_fibril, client);
453 assert(fid);
454 fibril_add_ready(fid);
455 }
456
457 return 0;
458}
459
460/** @}
461 */
Note: See TracBrowser for help on using the repository browser.