source: mainline/uspace/srv/hid/remcons/remcons.c@ 5e0acaa

Last change on this file since 5e0acaa was 5e0acaa, checked in by Jiri Svoboda <jiri@…>, 9 months ago

Implement mouse tracking in libvt / remcons

  • Property mode set to 100644
File size: 21.5 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * Copyright (c) 2012 Vojtech Horky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup remcons
31 * @{
32 */
33/** @file
34 */
35
36#include <adt/list.h>
37#include <as.h>
38#include <async.h>
39#include <errno.h>
40#include <io/con_srv.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <str_error.h>
44#include <loc.h>
45#include <io/keycode.h>
46#include <align.h>
47#include <fibril_synch.h>
48#include <task.h>
49#include <inet/addr.h>
50#include <inet/endpoint.h>
51#include <inet/tcp.h>
52#include <io/console.h>
53#include <inttypes.h>
54#include <str.h>
55#include <vt/vt100.h>
56#include "telnet.h"
57#include "user.h"
58#include "remcons.h"
59
60#define APP_GETTERM "/app/getterm"
61#define APP_SHELL "/app/bdsh"
62
63#define DEF_PORT 2223
64
65/** Telnet commands to force character mode
66 * (redundant to be on the safe side).
67 * See
68 * http://stackoverflow.com/questions/273261/force-telnet-user-into-character-mode
69 * for discussion.
70 */
71static const telnet_cmd_t telnet_force_character_mode_command[] = {
72 TELNET_IAC, TELNET_WILL, TELNET_ECHO,
73 TELNET_IAC, TELNET_WILL, TELNET_SUPPRESS_GO_AHEAD,
74 TELNET_IAC, TELNET_WONT, TELNET_LINEMODE
75};
76
77static const size_t telnet_force_character_mode_command_count =
78 sizeof(telnet_force_character_mode_command) / sizeof(telnet_cmd_t);
79
80static errno_t remcons_open(con_srvs_t *, con_srv_t *);
81static errno_t remcons_close(con_srv_t *);
82static errno_t remcons_read(con_srv_t *, void *, size_t, size_t *);
83static errno_t remcons_write(con_srv_t *, void *, size_t, size_t *);
84static void remcons_sync(con_srv_t *);
85static void remcons_clear(con_srv_t *);
86static void remcons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
87static errno_t remcons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
88static errno_t remcons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
89static errno_t remcons_get_color_cap(con_srv_t *, console_caps_t *);
90static void remcons_set_style(con_srv_t *, console_style_t);
91static void remcons_set_color(con_srv_t *, console_color_t,
92 console_color_t, console_color_attr_t);
93static void remcons_set_color(con_srv_t *, console_color_t,
94 console_color_t, console_color_attr_t);
95static void remcons_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
96static void remcons_cursor_visibility(con_srv_t *, bool);
97static errno_t remcons_get_event(con_srv_t *, cons_event_t *);
98static errno_t remcons_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
99static void remcons_unmap(con_srv_t *);
100static void remcons_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
101 sysarg_t);
102
103static con_ops_t con_ops = {
104 .open = remcons_open,
105 .close = remcons_close,
106 .read = remcons_read,
107 .write = remcons_write,
108 .sync = remcons_sync,
109 .clear = remcons_clear,
110 .set_pos = remcons_set_pos,
111 .get_pos = remcons_get_pos,
112 .get_size = remcons_get_size,
113 .get_color_cap = remcons_get_color_cap,
114 .set_style = remcons_set_style,
115 .set_color = remcons_set_color,
116 .set_rgb_color = remcons_set_rgb_color,
117 .set_cursor_visibility = remcons_cursor_visibility,
118 .get_event = remcons_get_event,
119 .map = remcons_map,
120 .unmap = remcons_unmap,
121 .update = remcons_update
122};
123
124static void remcons_vt_putchar(void *, char32_t);
125static void remcons_vt_cputs(void *, const char *);
126static void remcons_vt_flush(void *);
127static void remcons_vt_key(void *, keymod_t, keycode_t, char);
128static void remcons_vt_pos_event(void *, pos_event_t *);
129
130static vt100_cb_t remcons_vt_cb = {
131 .putuchar = remcons_vt_putchar,
132 .control_puts = remcons_vt_cputs,
133 .flush = remcons_vt_flush,
134 .key = remcons_vt_key,
135 .pos_event = remcons_vt_pos_event
136};
137
138static void remcons_new_conn(tcp_listener_t *lst, tcp_conn_t *conn);
139
140static tcp_listen_cb_t listen_cb = {
141 .new_conn = remcons_new_conn
142};
143
144static tcp_cb_t conn_cb = {
145 .connected = NULL
146};
147
148static void remcons_telnet_ws_update(void *, unsigned, unsigned);
149
150static telnet_cb_t remcons_telnet_cb = {
151 .ws_update = remcons_telnet_ws_update
152};
153
154static loc_srv_t *remcons_srv;
155static bool no_ctl;
156static bool no_rgb;
157
158static telnet_user_t *srv_to_user(con_srv_t *srv)
159{
160 remcons_t *remcons = (remcons_t *)srv->srvs->sarg;
161 return remcons->user;
162}
163
164static remcons_t *srv_to_remcons(con_srv_t *srv)
165{
166 remcons_t *remcons = (remcons_t *)srv->srvs->sarg;
167 return remcons;
168}
169
170static errno_t remcons_open(con_srvs_t *srvs, con_srv_t *srv)
171{
172 telnet_user_t *user = srv_to_user(srv);
173
174 telnet_user_log(user, "New client connected (%p).", srv);
175
176 /* Force character mode. */
177 (void) tcp_conn_send(user->conn, (void *)telnet_force_character_mode_command,
178 telnet_force_character_mode_command_count);
179
180 return EOK;
181}
182
183static errno_t remcons_close(con_srv_t *srv)
184{
185 telnet_user_t *user = srv_to_user(srv);
186
187 telnet_user_notify_client_disconnected(user);
188 telnet_user_log(user, "Client disconnected (%p).", srv);
189
190 return EOK;
191}
192
193static errno_t remcons_read(con_srv_t *srv, void *data, size_t size,
194 size_t *nread)
195{
196 telnet_user_t *user = srv_to_user(srv);
197 errno_t rc;
198
199 rc = telnet_user_recv(user, data, size, nread);
200 if (rc != EOK)
201 return rc;
202
203 return EOK;
204}
205
206static errno_t remcons_write(con_srv_t *srv, void *data, size_t size, size_t *nwritten)
207{
208 remcons_t *remcons = srv_to_remcons(srv);
209 errno_t rc;
210
211 rc = telnet_user_send_data(remcons->user, data, size);
212 if (rc != EOK)
213 return rc;
214
215 rc = telnet_user_flush(remcons->user);
216 if (rc != EOK)
217 return rc;
218
219 *nwritten = size;
220 return EOK;
221}
222
223static void remcons_sync(con_srv_t *srv)
224{
225 (void) srv;
226}
227
228static void remcons_clear(con_srv_t *srv)
229{
230 remcons_t *remcons = srv_to_remcons(srv);
231
232 if (remcons->enable_ctl) {
233 vt100_cls(remcons->vt);
234 vt100_set_pos(remcons->vt, 0, 0);
235 remcons->user->cursor_x = 0;
236 remcons->user->cursor_y = 0;
237 }
238}
239
240static void remcons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
241{
242 remcons_t *remcons = srv_to_remcons(srv);
243 telnet_user_t *user = srv_to_user(srv);
244
245 if (remcons->enable_ctl) {
246 vt100_set_pos(remcons->vt, col, row);
247 remcons->user->cursor_x = col;
248 remcons->user->cursor_y = row;
249 (void)telnet_user_flush(remcons->user);
250 } else {
251 telnet_user_update_cursor_x(user, col);
252 }
253}
254
255static errno_t remcons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
256{
257 telnet_user_t *user = srv_to_user(srv);
258
259 *col = user->cursor_x;
260 *row = user->cursor_y;
261
262 return EOK;
263}
264
265static errno_t remcons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
266{
267 remcons_t *remcons = srv_to_remcons(srv);
268
269 if (remcons->enable_ctl) {
270 *cols = remcons->vt->cols;
271 *rows = remcons->vt->rows;
272 } else {
273 *cols = 100;
274 *rows = 1;
275 }
276
277 return EOK;
278}
279
280static errno_t remcons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
281{
282 remcons_t *remcons = srv_to_remcons(srv);
283
284 *ccaps = 0;
285
286 if (remcons->enable_ctl) {
287 *ccaps |= CONSOLE_CAP_CURSORCTL | CONSOLE_CAP_STYLE |
288 CONSOLE_CAP_INDEXED;
289 }
290
291 if (remcons->enable_rgb)
292 *ccaps |= CONSOLE_CAP_RGB;
293
294 return EOK;
295}
296
297static void remcons_set_style(con_srv_t *srv, console_style_t style)
298{
299 remcons_t *remcons = srv_to_remcons(srv);
300 char_attrs_t attrs;
301
302 if (remcons->enable_ctl) {
303 attrs.type = CHAR_ATTR_STYLE;
304 attrs.val.style = style;
305 vt100_set_attr(remcons->vt, attrs);
306 }
307}
308
309static void remcons_set_color(con_srv_t *srv, console_color_t bgcolor,
310 console_color_t fgcolor, console_color_attr_t flags)
311{
312 remcons_t *remcons = srv_to_remcons(srv);
313 char_attrs_t attrs;
314
315 if (remcons->enable_ctl) {
316 attrs.type = CHAR_ATTR_INDEX;
317 attrs.val.index.bgcolor = bgcolor;
318 attrs.val.index.fgcolor = fgcolor;
319 attrs.val.index.attr = flags;
320 vt100_set_attr(remcons->vt, attrs);
321 }
322}
323
324static void remcons_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
325 pixel_t fgcolor)
326{
327 remcons_t *remcons = srv_to_remcons(srv);
328 char_attrs_t attrs;
329
330 if (remcons->enable_ctl) {
331 attrs.type = CHAR_ATTR_RGB;
332 attrs.val.rgb.bgcolor = bgcolor;
333 attrs.val.rgb.fgcolor = fgcolor;
334 vt100_set_attr(remcons->vt, attrs);
335 }
336}
337
338static void remcons_cursor_visibility(con_srv_t *srv, bool visible)
339{
340 remcons_t *remcons = srv_to_remcons(srv);
341
342 if (remcons->enable_ctl) {
343 if (!remcons->curs_visible && visible) {
344 vt100_set_pos(remcons->vt, remcons->user->cursor_x,
345 remcons->user->cursor_y);
346 }
347 vt100_cursor_visibility(remcons->vt, visible);
348 }
349
350 remcons->curs_visible = visible;
351}
352
353/** Creates new keyboard event from given char.
354 *
355 * @param type Event type (press / release).
356 * @param c Pressed character.
357 */
358static remcons_event_t *new_kbd_event(kbd_event_type_t type, keymod_t mods,
359 keycode_t key, char c)
360{
361 remcons_event_t *event = malloc(sizeof(remcons_event_t));
362 if (event == NULL) {
363 fprintf(stderr, "Out of memory.\n");
364 return NULL;
365 }
366
367 link_initialize(&event->link);
368 event->cev.type = CEV_KEY;
369 event->cev.ev.key.type = type;
370 event->cev.ev.key.mods = mods;
371 event->cev.ev.key.key = key;
372 event->cev.ev.key.c = c;
373
374 return event;
375}
376
377/** Creates new position event.
378 *
379 * @param ev Position event.
380 * @param c Pressed character.
381 */
382static remcons_event_t *new_pos_event(pos_event_t *ev)
383{
384 remcons_event_t *event = malloc(sizeof(remcons_event_t));
385 if (event == NULL) {
386 fprintf(stderr, "Out of memory.\n");
387 return NULL;
388 }
389
390 link_initialize(&event->link);
391 event->cev.type = CEV_POS;
392 event->cev.ev.pos = *ev;
393
394 return event;
395}
396
397/** Creates new console resize event.
398 */
399static remcons_event_t *new_resize_event(void)
400{
401 remcons_event_t *event = malloc(sizeof(remcons_event_t));
402 if (event == NULL) {
403 fprintf(stderr, "Out of memory.\n");
404 return NULL;
405 }
406
407 link_initialize(&event->link);
408 event->cev.type = CEV_RESIZE;
409
410 return event;
411}
412
413static errno_t remcons_get_event(con_srv_t *srv, cons_event_t *event)
414{
415 remcons_t *remcons = srv_to_remcons(srv);
416 telnet_user_t *user = srv_to_user(srv);
417 size_t nread;
418
419 while (list_empty(&remcons->in_events)) {
420 char next_byte = 0;
421
422 errno_t rc = telnet_user_recv(user, &next_byte, 1,
423 &nread);
424 if (rc != EOK)
425 return rc;
426
427 vt100_rcvd_char(remcons->vt, next_byte);
428 }
429
430 link_t *link = list_first(&remcons->in_events);
431 list_remove(link);
432
433 remcons_event_t *tmp = list_get_instance(link, remcons_event_t, link);
434
435 *event = tmp->cev;
436 free(tmp);
437
438 return EOK;
439}
440
441static errno_t remcons_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
442 charfield_t **rbuf)
443{
444 remcons_t *remcons = srv_to_remcons(srv);
445 void *buf;
446
447 if (!remcons->enable_ctl)
448 return ENOTSUP;
449
450 if (remcons->ubuf != NULL)
451 return EBUSY;
452
453 buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
454 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
455 if (buf == AS_MAP_FAILED)
456 return ENOMEM;
457
458 remcons->ucols = cols;
459 remcons->urows = rows;
460 remcons->ubuf = buf;
461
462 *rbuf = buf;
463 return EOK;
464
465}
466
467static void remcons_unmap(con_srv_t *srv)
468{
469 remcons_t *remcons = srv_to_remcons(srv);
470 void *buf;
471
472 buf = remcons->ubuf;
473 remcons->ubuf = NULL;
474
475 if (buf != NULL)
476 as_area_destroy(buf);
477}
478
479static void remcons_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
480 sysarg_t c1, sysarg_t r1)
481{
482 remcons_t *remcons = srv_to_remcons(srv);
483 charfield_t *ch;
484 sysarg_t col, row;
485 sysarg_t old_x, old_y;
486
487 if (remcons->ubuf == NULL)
488 return;
489
490 /* Make sure we have meaningful coordinates, within bounds */
491
492 if (c1 > remcons->ucols)
493 c1 = remcons->ucols;
494 if (c1 > remcons->user->cols)
495 c1 = remcons->user->cols;
496 if (c0 >= c1)
497 return;
498
499 if (r1 > remcons->urows)
500 r1 = remcons->urows;
501 if (r1 > remcons->user->rows)
502 r1 = remcons->user->rows;
503 if (r0 >= r1)
504 return;
505
506 /* Update screen from user buffer */
507
508 old_x = remcons->user->cursor_x;
509 old_y = remcons->user->cursor_y;
510
511 if (remcons->curs_visible)
512 vt100_cursor_visibility(remcons->vt, false);
513
514 for (row = r0; row < r1; row++) {
515 for (col = c0; col < c1; col++) {
516 vt100_set_pos(remcons->vt, col, row);
517 ch = &remcons->ubuf[row * remcons->ucols + col];
518 vt100_set_attr(remcons->vt, ch->attrs);
519 vt100_putuchar(remcons->vt, ch->ch);
520 }
521 }
522
523 if (remcons->curs_visible) {
524 old_x = remcons->user->cursor_x = old_x;
525 remcons->user->cursor_y = old_y;
526 vt100_set_pos(remcons->vt, old_x, old_y);
527 vt100_cursor_visibility(remcons->vt, true);
528 }
529
530 /* Flush data */
531 (void)telnet_user_flush(remcons->user);
532}
533
534/** Callback when client connects to a telnet terminal. */
535static void client_connection(ipc_call_t *icall, void *arg)
536{
537 /* Find the user. */
538 telnet_user_t *user = telnet_user_get_for_client_connection(ipc_get_arg2(icall));
539 if (user == NULL) {
540 async_answer_0(icall, ENOENT);
541 return;
542 }
543
544 /* Handle messages. */
545 con_conn(icall, &user->srvs);
546}
547
548/** Fibril for spawning the task running after user connects.
549 *
550 * @param arg Corresponding @c telnet_user_t structure.
551 */
552static errno_t spawn_task_fibril(void *arg)
553{
554 telnet_user_t *user = arg;
555
556 task_id_t task;
557 task_wait_t wait;
558 errno_t rc = task_spawnl(&task, &wait, APP_GETTERM, APP_GETTERM, user->service_name,
559 "/loc", "--msg", "--", APP_SHELL, NULL);
560 if (rc != EOK) {
561 telnet_user_error(user, "Spawning `%s %s /loc --msg -- %s' "
562 "failed: %s.", APP_GETTERM, user->service_name, APP_SHELL,
563 str_error(rc));
564 fibril_mutex_lock(&user->recv_lock);
565 user->task_finished = true;
566 user->srvs.aborted = true;
567 fibril_condvar_signal(&user->refcount_cv);
568 fibril_mutex_unlock(&user->recv_lock);
569 return EOK;
570 }
571
572 fibril_mutex_lock(&user->recv_lock);
573 user->task_id = task;
574 fibril_mutex_unlock(&user->recv_lock);
575
576 task_exit_t task_exit;
577 int task_retval;
578 task_wait(&wait, &task_exit, &task_retval);
579 telnet_user_log(user, "%s terminated %s, exit code %d.", APP_GETTERM,
580 task_exit == TASK_EXIT_NORMAL ? "normally" : "unexpectedly",
581 task_retval);
582
583 /* Announce destruction. */
584 fibril_mutex_lock(&user->recv_lock);
585 user->task_finished = true;
586 user->srvs.aborted = true;
587 fibril_condvar_signal(&user->refcount_cv);
588 fibril_mutex_unlock(&user->recv_lock);
589
590 return EOK;
591}
592
593/** Tell whether given user can be destroyed (has no active clients).
594 *
595 * @param user The telnet user in question.
596 */
597static bool user_can_be_destroyed_no_lock(telnet_user_t *user)
598{
599 return user->task_finished && user->socket_closed &&
600 (user->locsrv_connection_count == 0);
601}
602
603static void remcons_vt_putchar(void *arg, char32_t c)
604{
605 remcons_t *remcons = (remcons_t *)arg;
606 char buf[STR_BOUNDS(1)];
607 size_t off;
608 errno_t rc;
609
610 (void)arg;
611
612 off = 0;
613 rc = chr_encode(c, buf, &off, sizeof(buf));
614 if (rc != EOK)
615 return;
616
617 (void)telnet_user_send_data(remcons->user, buf, off);
618}
619
620static void remcons_vt_cputs(void *arg, const char *str)
621{
622 remcons_t *remcons = (remcons_t *)arg;
623
624 (void)telnet_user_send_raw(remcons->user, str, str_size(str));
625}
626
627static void remcons_vt_flush(void *arg)
628{
629 remcons_t *remcons = (remcons_t *)arg;
630 (void)telnet_user_flush(remcons->user);
631}
632
633static void remcons_vt_key(void *arg, keymod_t mods, keycode_t key, char c)
634{
635 remcons_t *remcons = (remcons_t *)arg;
636
637 remcons_event_t *down = new_kbd_event(KEY_PRESS, mods, key, c);
638 if (down == NULL)
639 return;
640
641 remcons_event_t *up = new_kbd_event(KEY_RELEASE, mods, key, c);
642 if (up == NULL) {
643 free(down);
644 return;
645 }
646
647 list_append(&down->link, &remcons->in_events);
648 list_append(&up->link, &remcons->in_events);
649}
650
651static void remcons_vt_pos_event(void *arg, pos_event_t *ev)
652{
653 remcons_t *remcons = (remcons_t *)arg;
654
655 remcons_event_t *cev = new_pos_event(ev);
656 if (cev == NULL)
657 return;
658
659 list_append(&cev->link, &remcons->in_events);
660}
661
662/** Window size update callback.
663 *
664 * @param arg Argument (remcons_t *)
665 * @param cols New number of columns
666 * @param rows New number of rows
667 */
668static void remcons_telnet_ws_update(void *arg, unsigned cols, unsigned rows)
669{
670 remcons_t *remcons = (remcons_t *)arg;
671
672 vt100_resize(remcons->vt, cols, rows);
673 telnet_user_resize(remcons->user, cols, rows);
674
675 remcons_event_t *resize = new_resize_event();
676 if (resize == NULL)
677 return;
678
679 list_append(&resize->link, &remcons->in_events);
680}
681
682/** Handle network connection.
683 *
684 * @param lst Listener
685 * @param conn Connection
686 */
687static void remcons_new_conn(tcp_listener_t *lst, tcp_conn_t *conn)
688{
689 char_attrs_t attrs;
690 remcons_t *remcons = NULL;
691 telnet_user_t *user = NULL;
692
693 remcons = calloc(1, sizeof(remcons_t));
694 if (remcons == NULL) {
695 fprintf(stderr, "Out of memory.\n");
696 goto error;
697 }
698
699 user = telnet_user_create(conn, &remcons_telnet_cb,
700 (void *)remcons);
701 if (user == NULL) {
702 fprintf(stderr, "Out of memory.\n");
703 goto error;
704 }
705
706 remcons->enable_ctl = !no_ctl;
707 remcons->enable_rgb = !no_ctl && !no_rgb;
708 remcons->user = user;
709 list_initialize(&remcons->in_events);
710
711 if (remcons->enable_ctl) {
712 user->cols = 80;
713 user->rows = 25;
714 } else {
715 user->cols = 100;
716 user->rows = 1;
717 }
718
719 remcons->curs_visible = true;
720
721 remcons->vt = vt100_create((void *)remcons, 80, 25, &remcons_vt_cb);
722 if (remcons->vt == NULL) {
723 fprintf(stderr, "Error creating VT100 driver instance.\n");
724 goto error;
725 }
726
727 remcons->vt->enable_rgb = remcons->enable_rgb;
728
729 if (remcons->enable_ctl) {
730 attrs.type = CHAR_ATTR_STYLE;
731 attrs.val.style = STYLE_NORMAL;
732 vt100_set_sgr(remcons->vt, attrs);
733 vt100_cls(remcons->vt);
734 vt100_set_pos(remcons->vt, 0, 0);
735 vt100_set_button_reporting(remcons->vt, true);
736 }
737
738 con_srvs_init(&user->srvs);
739 user->srvs.ops = &con_ops;
740 user->srvs.sarg = remcons;
741 user->srvs.abort_timeout = 1000;
742
743 telnet_user_add(user);
744
745 errno_t rc = loc_service_register(remcons_srv, user->service_name,
746 &user->service_id);
747 if (rc != EOK) {
748 telnet_user_error(user, "Unable to register %s with loc: %s.",
749 user->service_name, str_error(rc));
750 goto error;
751 }
752
753 telnet_user_log(user, "Service %s registerd with id %" PRIun ".",
754 user->service_name, user->service_id);
755
756 fid_t spawn_fibril = fibril_create(spawn_task_fibril, user);
757 if (spawn_fibril == 0) {
758 fprintf(stderr, "Failed creating fibril.\n");
759 goto error;
760 }
761 fibril_add_ready(spawn_fibril);
762
763 /* Wait for all clients to exit. */
764 fibril_mutex_lock(&user->recv_lock);
765 while (!user_can_be_destroyed_no_lock(user)) {
766 if (user->task_finished) {
767 user->conn = NULL;
768 user->socket_closed = true;
769 user->srvs.aborted = true;
770 continue;
771 } else if (user->socket_closed) {
772 if (user->task_id != 0) {
773 task_kill(user->task_id);
774 }
775 }
776 fibril_condvar_wait_timeout(&user->refcount_cv, &user->recv_lock, 1000);
777 }
778 fibril_mutex_unlock(&user->recv_lock);
779
780 rc = loc_service_unregister(remcons_srv, user->service_id);
781 if (rc != EOK) {
782 telnet_user_error(user,
783 "Unable to unregister %s from loc: %s (ignored).",
784 user->service_name, str_error(rc));
785 }
786
787 telnet_user_log(user, "Destroying...");
788 telnet_user_destroy(user);
789 return;
790error:
791 if (user != NULL && user->service_id != 0)
792 loc_service_unregister(remcons_srv, user->service_id);
793 if (user != NULL)
794 free(user);
795 if (remcons != NULL && remcons->vt != NULL)
796 vt100_destroy(remcons->vt);
797 if (remcons != NULL)
798 free(remcons);
799}
800
801static void print_syntax(void)
802{
803 fprintf(stderr, "syntax: remcons [<options>]\n");
804 fprintf(stderr, "\t--no-ctl Disable all terminal control sequences\n");
805 fprintf(stderr, "\t--no-rgb Disable RGB colors\n");
806 fprintf(stderr, "\t--port <port> Listening port (default: %u)\n",
807 DEF_PORT);
808}
809
810int main(int argc, char *argv[])
811{
812 errno_t rc;
813 tcp_listener_t *lst;
814 tcp_t *tcp;
815 inet_ep_t ep;
816 uint16_t port;
817 int i;
818
819 port = DEF_PORT;
820
821 i = 1;
822 while (i < argc) {
823 if (argv[i][0] == '-') {
824 if (str_cmp(argv[i], "--no-ctl") == 0) {
825 no_ctl = true;
826 } else if (str_cmp(argv[i], "--no-rgb") == 0) {
827 no_rgb = true;
828 } else if (str_cmp(argv[i], "--port") == 0) {
829 ++i;
830 if (i >= argc) {
831 fprintf(stderr, "Option argument "
832 "missing.\n");
833 print_syntax();
834 return EINVAL;
835 }
836 rc = str_uint16_t(argv[i], NULL, 10, true, &port);
837 if (rc != EOK) {
838 fprintf(stderr, "Invalid port number "
839 "'%s'.\n", argv[i]);
840 print_syntax();
841 return EINVAL;
842 }
843 } else {
844 fprintf(stderr, "Unknown option '%s'.\n",
845 argv[i]);
846 print_syntax();
847 return EINVAL;
848 }
849 } else {
850 fprintf(stderr, "Unexpected argument.\n");
851 print_syntax();
852 return EINVAL;
853 }
854
855 ++i;
856 }
857
858 async_set_fallback_port_handler(client_connection, NULL);
859 rc = loc_server_register(NAME, &remcons_srv);
860 if (rc != EOK) {
861 fprintf(stderr, "%s: Unable to register server\n", NAME);
862 return rc;
863 }
864
865 rc = tcp_create(&tcp);
866 if (rc != EOK) {
867 fprintf(stderr, "%s: Error initializing TCP.\n", NAME);
868 return rc;
869 }
870
871 inet_ep_init(&ep);
872 ep.port = port;
873
874 rc = tcp_listener_create(tcp, &ep, &listen_cb, NULL, &conn_cb, NULL,
875 &lst);
876 if (rc != EOK) {
877 fprintf(stderr, "%s: Error creating listener.\n", NAME);
878 return rc;
879 }
880
881 printf("%s: HelenOS Remote console service\n", NAME);
882 task_retval(0);
883 async_manager();
884
885 /* Not reached */
886 return 0;
887}
888
889/** @}
890 */
Note: See TracBrowser for help on using the repository browser.