Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/srv/hid/remcons/user.c

    r28a5ebd r5f5d375  
    11/*
     2 * Copyright (c) 2024 Jiri Svoboda
    23 * Copyright (c) 2012 Vojtech Horky
    34 * All rights reserved.
     
    3738#include <adt/prodcons.h>
    3839#include <errno.h>
     40#include <macros.h>
     41#include <mem.h>
    3942#include <str_error.h>
    4043#include <loc.h>
     
    4851#include <inttypes.h>
    4952#include <assert.h>
     53#include "remcons.h"
    5054#include "user.h"
    5155#include "telnet.h"
     
    5458static LIST_INITIALIZE(users);
    5559
     60static errno_t telnet_user_send_raw_locked(telnet_user_t *, const void *,
     61    size_t);
     62static errno_t telnet_user_flush_locked(telnet_user_t *);
     63
    5664/** Create new telnet user.
    5765 *
    5866 * @param conn Incoming connection.
     67 * @param cb Callback functions
     68 * @param arg Argument to callback functions
    5969 * @return New telnet user or NULL when out of memory.
    6070 */
    61 telnet_user_t *telnet_user_create(tcp_conn_t *conn)
     71telnet_user_t *telnet_user_create(tcp_conn_t *conn, telnet_cb_t *cb, void *arg)
    6272{
    6373        static int telnet_user_id_counter = 0;
     
    6878        }
    6979
     80        user->cb = cb;
     81        user->arg = arg;
    7082        user->id = ++telnet_user_id_counter;
    7183
    72         int rc = asprintf(&user->service_name, "%s/telnet%d", NAMESPACE, user->id);
     84        int rc = asprintf(&user->service_name, "%s/telnet%u.%d", NAMESPACE,
     85            (unsigned)task_get_id(), user->id);
    7386        if (rc < 0) {
    7487                free(user);
     
    7891        user->conn = conn;
    7992        user->service_id = (service_id_t) -1;
    80         prodcons_initialize(&user->in_events);
    8193        link_initialize(&user->link);
    8294        user->socket_buffer_len = 0;
    8395        user->socket_buffer_pos = 0;
     96        user->send_buf_used = 0;
    8497
    8598        fibril_condvar_initialize(&user->refcount_cv);
    86         fibril_mutex_initialize(&user->guard);
     99        fibril_mutex_initialize(&user->send_lock);
     100        fibril_mutex_initialize(&user->recv_lock);
    87101        user->task_finished = false;
    88102        user->socket_closed = false;
     
    90104
    91105        user->cursor_x = 0;
     106        user->cursor_y = 0;
    92107
    93108        return user;
     
    137152
    138153        telnet_user_t *tmp = user;
    139         fibril_mutex_lock(&tmp->guard);
     154        fibril_mutex_lock(&tmp->recv_lock);
    140155        user->locsrv_connection_count++;
    141156
     
    149164        }
    150165
    151         fibril_mutex_unlock(&tmp->guard);
     166        fibril_mutex_unlock(&tmp->recv_lock);
    152167
    153168        fibril_mutex_unlock(&users_guard);
     
    162177void telnet_user_notify_client_disconnected(telnet_user_t *user)
    163178{
    164         fibril_mutex_lock(&user->guard);
     179        fibril_mutex_lock(&user->recv_lock);
    165180        assert(user->locsrv_connection_count > 0);
    166181        user->locsrv_connection_count--;
    167182        fibril_condvar_signal(&user->refcount_cv);
    168         fibril_mutex_unlock(&user->guard);
     183        fibril_mutex_unlock(&user->recv_lock);
    169184}
    170185
     
    175190bool telnet_user_is_zombie(telnet_user_t *user)
    176191{
    177         fibril_mutex_lock(&user->guard);
     192        fibril_mutex_lock(&user->recv_lock);
    178193        bool zombie = user->socket_closed || user->task_finished;
    179         fibril_mutex_unlock(&user->guard);
     194        fibril_mutex_unlock(&user->recv_lock);
    180195
    181196        return zombie;
    182197}
    183198
    184 /** Receive next byte from a socket (use buffering.
    185  * We need to return the value via extra argument because the read byte
    186  * might be negative.
    187  */
    188 static errno_t telnet_user_recv_next_byte_no_lock(telnet_user_t *user, char *byte)
    189 {
     199static errno_t telnet_user_fill_recv_buf(telnet_user_t *user)
     200{
     201        errno_t rc;
     202        size_t recv_length;
     203
     204        rc = tcp_conn_recv_wait(user->conn, user->socket_buffer,
     205            BUFFER_SIZE, &recv_length);
     206        if (rc != EOK)
     207                return rc;
     208
     209        if (recv_length == 0) {
     210                user->socket_closed = true;
     211                user->srvs.aborted = true;
     212                return ENOENT;
     213        }
     214
     215        user->socket_buffer_len = recv_length;
     216        user->socket_buffer_pos = 0;
     217
     218        return EOK;
     219}
     220
     221/** Receive next byte from a socket (use buffering).
     222 *
     223 * @param user Telnet user
     224 * @param byte Place to store the received byte
     225 * @return EOK on success or an error code
     226 */
     227static errno_t telnet_user_recv_next_byte_locked(telnet_user_t *user,
     228    uint8_t *byte)
     229{
     230        errno_t rc;
     231
    190232        /* No more buffered data? */
    191233        if (user->socket_buffer_len <= user->socket_buffer_pos) {
    192                 errno_t rc;
    193                 size_t recv_length;
    194 
    195                 rc = tcp_conn_recv_wait(user->conn, user->socket_buffer,
    196                     BUFFER_SIZE, &recv_length);
     234                rc = telnet_user_fill_recv_buf(user);
    197235                if (rc != EOK)
    198236                        return rc;
    199 
    200                 if (recv_length == 0) {
    201                         user->socket_closed = true;
    202                         user->srvs.aborted = true;
    203                         return ENOENT;
    204                 }
    205 
    206                 user->socket_buffer_len = recv_length;
    207                 user->socket_buffer_pos = 0;
    208         }
    209 
    210         *byte = user->socket_buffer[user->socket_buffer_pos++];
    211 
     237        }
     238
     239        *byte = (uint8_t)user->socket_buffer[user->socket_buffer_pos++];
    212240        return EOK;
    213241}
    214242
    215 /** Creates new keyboard event from given char.
    216  *
    217  * @param type Event type (press / release).
    218  * @param c Pressed character.
    219  */
    220 static kbd_event_t *new_kbd_event(kbd_event_type_t type, char32_t c)
    221 {
    222         kbd_event_t *event = malloc(sizeof(kbd_event_t));
    223         assert(event);
    224 
    225         link_initialize(&event->link);
    226         event->type = type;
    227         event->c = c;
    228         event->mods = 0;
    229 
    230         switch (c) {
    231         case '\n':
    232                 event->key = KC_ENTER;
    233                 break;
    234         case '\t':
    235                 event->key = KC_TAB;
    236                 break;
    237         case '\b':
    238         case 127: /* This is what Linux telnet sends. */
    239                 event->key = KC_BACKSPACE;
    240                 event->c = '\b';
    241                 break;
    242         default:
    243                 event->key = KC_A;
    244                 break;
    245         }
    246 
    247         return event;
    248 }
    249 
    250 /** Process telnet command (currently only print to screen).
     243/** Determine if a received byte is available without waiting.
     244 *
     245 * @param user Telnet user
     246 * @return @c true iff a byte is currently available
     247 */
     248static bool telnet_user_byte_avail(telnet_user_t *user)
     249{
     250        return user->socket_buffer_len > user->socket_buffer_pos;
     251}
     252
     253static errno_t telnet_user_send_opt(telnet_user_t *user, telnet_cmd_t cmd,
     254    telnet_cmd_t opt)
     255{
     256        uint8_t cmdb[3];
     257
     258        cmdb[0] = TELNET_IAC;
     259        cmdb[1] = cmd;
     260        cmdb[2] = opt;
     261
     262        return telnet_user_send_raw_locked(user, (char *)cmdb, sizeof(cmdb));
     263}
     264
     265/** Process telnet WILL NAWS command.
     266 *
     267 * @param user Telnet user structure.
     268 * @param cmd Telnet command.
     269 */
     270static void process_telnet_will_naws(telnet_user_t *user)
     271{
     272        telnet_user_log(user, "WILL NAWS");
     273        /* Send DO NAWS */
     274        (void) telnet_user_send_opt(user, TELNET_DO, TELNET_NAWS);
     275        (void) telnet_user_flush_locked(user);
     276}
     277
     278/** Process telnet SB NAWS command.
     279 *
     280 * @param user Telnet user structure.
     281 * @param cmd Telnet command.
     282 */
     283static void process_telnet_sb_naws(telnet_user_t *user)
     284{
     285        uint8_t chi, clo;
     286        uint8_t rhi, rlo;
     287        uint16_t cols;
     288        uint16_t rows;
     289        uint8_t iac;
     290        uint8_t se;
     291        errno_t rc;
     292
     293        telnet_user_log(user, "SB NAWS...");
     294
     295        rc = telnet_user_recv_next_byte_locked(user, &chi);
     296        if (rc != EOK)
     297                return;
     298        rc = telnet_user_recv_next_byte_locked(user, &clo);
     299        if (rc != EOK)
     300                return;
     301
     302        rc = telnet_user_recv_next_byte_locked(user, &rhi);
     303        if (rc != EOK)
     304                return;
     305        rc = telnet_user_recv_next_byte_locked(user, &rlo);
     306        if (rc != EOK)
     307                return;
     308
     309        rc = telnet_user_recv_next_byte_locked(user, &iac);
     310        if (rc != EOK)
     311                return;
     312        rc = telnet_user_recv_next_byte_locked(user, &se);
     313        if (rc != EOK)
     314                return;
     315
     316        cols = (chi << 8) | clo;
     317        rows = (rhi << 8) | rlo;
     318
     319        telnet_user_log(user, "cols=%u rows=%u\n", cols, rows);
     320
     321        if (cols < 1 || rows < 1) {
     322                telnet_user_log(user, "Ignoring invalid window size update.");
     323                return;
     324        }
     325
     326        user->cb->ws_update(user->arg, cols, rows);
     327}
     328
     329/** Process telnet WILL command.
     330 *
     331 * @param user Telnet user structure.
     332 * @param opt Option code.
     333 */
     334static void process_telnet_will(telnet_user_t *user, telnet_cmd_t opt)
     335{
     336        telnet_user_log(user, "WILL");
     337        switch (opt) {
     338        case TELNET_NAWS:
     339                process_telnet_will_naws(user);
     340                return;
     341        }
     342
     343        telnet_user_log(user, "Ignoring telnet command %u %u %u.",
     344            TELNET_IAC, TELNET_WILL, opt);
     345}
     346
     347/** Process telnet SB command.
     348 *
     349 * @param user Telnet user structure.
     350 * @param opt Option code.
     351 */
     352static void process_telnet_sb(telnet_user_t *user, telnet_cmd_t opt)
     353{
     354        telnet_user_log(user, "SB");
     355        switch (opt) {
     356        case TELNET_NAWS:
     357                process_telnet_sb_naws(user);
     358                return;
     359        }
     360
     361        telnet_user_log(user, "Ignoring telnet command %u %u %u.",
     362            TELNET_IAC, TELNET_SB, opt);
     363}
     364
     365/** Process telnet command.
    251366 *
    252367 * @param user Telnet user structure.
     
    257372    telnet_cmd_t option_code, telnet_cmd_t cmd)
    258373{
     374        switch (option_code) {
     375        case TELNET_SB:
     376                process_telnet_sb(user, cmd);
     377                return;
     378        case TELNET_WILL:
     379                process_telnet_will(user, cmd);
     380                return;
     381        }
     382
    259383        if (option_code != 0) {
    260384                telnet_user_log(user, "Ignoring telnet command %u %u %u.",
     
    266390}
    267391
    268 /** Get next keyboard event.
     392/** Receive data from telnet connection.
    269393 *
    270394 * @param user Telnet user.
    271  * @param event Where to store the keyboard event.
    272  * @return Error code.
    273  */
    274 errno_t telnet_user_get_next_keyboard_event(telnet_user_t *user, kbd_event_t *event)
    275 {
    276         fibril_mutex_lock(&user->guard);
    277         if (list_empty(&user->in_events.list)) {
    278                 char next_byte = 0;
     395 * @param buf Destination buffer
     396 * @param size Buffer size
     397 * @param nread Place to store number of bytes read (>0 on success)
     398 * @return EOK on success or an error code
     399 */
     400errno_t telnet_user_recv(telnet_user_t *user, void *buf, size_t size,
     401    size_t *nread)
     402{
     403        uint8_t *bp = (uint8_t *)buf;
     404        fibril_mutex_lock(&user->recv_lock);
     405
     406        assert(size > 0);
     407        *nread = 0;
     408
     409        do {
     410                uint8_t next_byte = 0;
    279411                bool inside_telnet_command = false;
    280412
     
    282414
    283415                /* Skip zeros, bail-out on error. */
    284                 while (next_byte == 0) {
    285                         errno_t rc = telnet_user_recv_next_byte_no_lock(user, &next_byte);
     416                do {
     417                        errno_t rc = telnet_user_recv_next_byte_locked(user,
     418                            &next_byte);
    286419                        if (rc != EOK) {
    287                                 fibril_mutex_unlock(&user->guard);
     420                                fibril_mutex_unlock(&user->recv_lock);
    288421                                return rc;
    289422                        }
    290                         uint8_t byte = (uint8_t) next_byte;
     423                        uint8_t byte = next_byte;
    291424
    292425                        /* Skip telnet commands. */
     
    294427                                inside_telnet_command = false;
    295428                                next_byte = 0;
    296                                 if (TELNET_IS_OPTION_CODE(byte)) {
     429                                if (TELNET_IS_OPTION_CODE(byte) ||
     430                                    byte == TELNET_SB) {
    297431                                        telnet_option_code = byte;
    298432                                        inside_telnet_command = true;
     
    306440                                next_byte = 0;
    307441                        }
    308                 }
     442                } while (next_byte == 0 && telnet_user_byte_avail(user));
    309443
    310444                /* CR-LF conversions. */
     
    313447                }
    314448
    315                 kbd_event_t *down = new_kbd_event(KEY_PRESS, next_byte);
    316                 kbd_event_t *up = new_kbd_event(KEY_RELEASE, next_byte);
    317                 assert(down);
    318                 assert(up);
    319                 prodcons_produce(&user->in_events, &down->link);
    320                 prodcons_produce(&user->in_events, &up->link);
    321         }
    322 
    323         link_t *link = prodcons_consume(&user->in_events);
    324         kbd_event_t *tmp = list_get_instance(link, kbd_event_t, link);
    325 
    326         fibril_mutex_unlock(&user->guard);
    327 
    328         *event = *tmp;
    329 
    330         free(tmp);
     449                if (next_byte != 0) {
     450                        *bp++ = next_byte;
     451                        ++*nread;
     452                        --size;
     453                }
     454        } while (size > 0 && (telnet_user_byte_avail(user) || *nread == 0));
     455
     456        fibril_mutex_unlock(&user->recv_lock);
     457        return EOK;
     458}
     459
     460static errno_t telnet_user_send_raw_locked(telnet_user_t *user,
     461    const void *data, size_t size)
     462{
     463        size_t remain;
     464        size_t now;
     465        errno_t rc;
     466
     467        remain = sizeof(user->send_buf) - user->send_buf_used;
     468        while (size > 0) {
     469                if (remain == 0) {
     470                        rc = tcp_conn_send(user->conn, user->send_buf,
     471                            sizeof(user->send_buf));
     472                        if (rc != EOK)
     473                                return rc;
     474
     475                        user->send_buf_used = 0;
     476                        remain = sizeof(user->send_buf);
     477                }
     478
     479                now = min(remain, size);
     480                memcpy(user->send_buf + user->send_buf_used, data, now);
     481                user->send_buf_used += now;
     482                remain -= now;
     483                data += now;
     484                size -= now;
     485        }
    331486
    332487        return EOK;
     
    339494 * @param size Size of @p data buffer in bytes.
    340495 */
    341 static errno_t telnet_user_send_data_no_lock(telnet_user_t *user, uint8_t *data, size_t size)
     496static errno_t telnet_user_send_data_locked(telnet_user_t *user,
     497    const char *data, size_t size)
    342498{
    343499        uint8_t *converted = malloc(3 * size + 1);
     
    351507                        converted[converted_size++] = 10;
    352508                        user->cursor_x = 0;
     509                        if (user->cursor_y < (int)user->rows - 1)
     510                                ++user->cursor_y;
    353511                } else {
    354512                        converted[converted_size++] = data[i];
     
    361519        }
    362520
    363         errno_t rc = tcp_conn_send(user->conn, converted, converted_size);
     521        errno_t rc = telnet_user_send_raw_locked(user, converted,
     522            converted_size);
    364523        free(converted);
    365524
     
    373532 * @param size Size of @p data buffer in bytes.
    374533 */
    375 errno_t telnet_user_send_data(telnet_user_t *user, uint8_t *data, size_t size)
    376 {
    377         fibril_mutex_lock(&user->guard);
    378 
    379         errno_t rc = telnet_user_send_data_no_lock(user, data, size);
    380 
    381         fibril_mutex_unlock(&user->guard);
    382 
     534errno_t telnet_user_send_data(telnet_user_t *user, const char *data,
     535    size_t size)
     536{
     537        fibril_mutex_lock(&user->send_lock);
     538
     539        errno_t rc = telnet_user_send_data_locked(user, data, size);
     540
     541        fibril_mutex_unlock(&user->send_lock);
     542
     543        return rc;
     544}
     545
     546/** Send raw non-printable data to the socket.
     547 *
     548 * @param user Telnet user.
     549 * @param data Data buffer (not zero terminated).
     550 * @param size Size of @p data buffer in bytes.
     551 */
     552errno_t telnet_user_send_raw(telnet_user_t *user, const char *data,
     553    size_t size)
     554{
     555        fibril_mutex_lock(&user->send_lock);
     556
     557        errno_t rc = telnet_user_send_raw_locked(user, data, size);
     558
     559        fibril_mutex_unlock(&user->send_lock);
     560
     561        return rc;
     562}
     563
     564static errno_t telnet_user_flush_locked(telnet_user_t *user)
     565{
     566        errno_t rc;
     567
     568        rc = tcp_conn_send(user->conn, user->send_buf, user->send_buf_used);
     569        if (rc != EOK)
     570                return rc;
     571
     572        user->send_buf_used = 0;
     573        return EOK;
     574}
     575
     576errno_t telnet_user_flush(telnet_user_t *user)
     577{
     578        errno_t rc;
     579
     580        fibril_mutex_lock(&user->send_lock);
     581        rc = telnet_user_flush_locked(user);
     582        fibril_mutex_unlock(&user->send_lock);
    383583        return rc;
    384584}
     
    393593void telnet_user_update_cursor_x(telnet_user_t *user, int new_x)
    394594{
    395         fibril_mutex_lock(&user->guard);
     595        fibril_mutex_lock(&user->send_lock);
    396596        if (user->cursor_x - 1 == new_x) {
    397                 uint8_t data = '\b';
     597                char data = '\b';
    398598                /* Ignore errors. */
    399                 telnet_user_send_data_no_lock(user, &data, 1);
     599                telnet_user_send_data_locked(user, &data, 1);
    400600        }
    401601        user->cursor_x = new_x;
    402         fibril_mutex_unlock(&user->guard);
    403 
     602        fibril_mutex_unlock(&user->send_lock);
     603
     604}
     605
     606/** Resize telnet session.
     607 *
     608 * @param user Telnet user
     609 * @param cols New number of columns
     610 * @param rows New number of rows
     611 */
     612void telnet_user_resize(telnet_user_t *user, unsigned cols, unsigned rows)
     613{
     614        user->cols = cols;
     615        user->rows = rows;
     616        if ((unsigned)user->cursor_x > cols - 1)
     617                user->cursor_x = cols - 1;
     618        if ((unsigned)user->cursor_y > rows - 1)
     619                user->cursor_y = rows - 1;
    404620}
    405621
Note: See TracChangeset for help on using the changeset viewer.