source: mainline/uspace/srv/hid/remcons/remcons.c@ 30d4706

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

remcons: split source into more files

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