source: mainline/uspace/srv/hid/remcons/remcons.c@ 235bd1f

Last change on this file since 235bd1f was ca48672, checked in by Jiri Svoboda <jiri@…>, 8 days ago

loc_service_register() needs to take a port ID argument.

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