source: mainline/uspace/srv/hid/remcons/user.c@ 1433ecda

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1433ecda was 1433ecda, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

  • Property mode set to 100644
File size: 9.8 KB
RevLine 
[30d4706]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#include <async.h>
35#include <stdio.h>
[38d150e]36#include <stdlib.h>
[30d4706]37#include <adt/prodcons.h>
38#include <errno.h>
39#include <str_error.h>
40#include <loc.h>
41#include <io/keycode.h>
42#include <align.h>
43#include <as.h>
44#include <fibril_synch.h>
45#include <task.h>
[fab2746]46#include <inet/tcp.h>
[30d4706]47#include <io/console.h>
48#include <inttypes.h>
[8562724]49#include <assert.h>
[30d4706]50#include "user.h"
[2a180307]51#include "telnet.h"
[30d4706]52
53static FIBRIL_MUTEX_INITIALIZE(users_guard);
54static LIST_INITIALIZE(users);
55
[5923cf82]56/** Create new telnet user.
57 *
[fab2746]58 * @param conn Incoming connection.
[5923cf82]59 * @return New telnet user or NULL when out of memory.
60 */
[fab2746]61telnet_user_t *telnet_user_create(tcp_conn_t *conn)
[30d4706]62{
63 static int telnet_user_id_counter = 0;
64
65 telnet_user_t *user = malloc(sizeof(telnet_user_t));
66 if (user == NULL) {
67 return NULL;
68 }
69
70 user->id = ++telnet_user_id_counter;
71
72 int rc = asprintf(&user->service_name, "%s/telnet%d", NAMESPACE, user->id);
73 if (rc < 0) {
74 free(user);
75 return NULL;
76 }
77
[fab2746]78 user->conn = conn;
[30d4706]79 user->service_id = (service_id_t) -1;
80 prodcons_initialize(&user->in_events);
81 link_initialize(&user->link);
82 user->socket_buffer_len = 0;
83 user->socket_buffer_pos = 0;
84
85 fibril_condvar_initialize(&user->refcount_cv);
[261bbdc]86 fibril_mutex_initialize(&user->guard);
[30d4706]87 user->task_finished = false;
88 user->socket_closed = false;
89 user->locsrv_connection_count = 0;
90
[5d94b16c]91 user->cursor_x = 0;
[30d4706]92
[5d94b16c]93 return user;
94}
95
96void telnet_user_add(telnet_user_t *user)
97{
[30d4706]98 fibril_mutex_lock(&users_guard);
99 list_append(&user->link, &users);
100 fibril_mutex_unlock(&users_guard);
101}
102
[5923cf82]103/** Destroy telnet user structure.
104 *
105 * @param user User to be destroyed.
106 */
[30d4706]107void telnet_user_destroy(telnet_user_t *user)
108{
109 assert(user);
110
111 fibril_mutex_lock(&users_guard);
112 list_remove(&user->link);
113 fibril_mutex_unlock(&users_guard);
114
115 free(user);
116}
117
[5923cf82]118/** Find user by service id and increments reference counter.
119 *
120 * @param id Location service id of the telnet user's terminal.
121 */
[30d4706]122telnet_user_t *telnet_user_get_for_client_connection(service_id_t id)
123{
124 telnet_user_t *user = NULL;
125
126 fibril_mutex_lock(&users_guard);
[feeac0d]127 list_foreach(users, link, telnet_user_t, tmp) {
[30d4706]128 if (tmp->service_id == id) {
129 user = tmp;
130 break;
131 }
132 }
133 if (user == NULL) {
134 fibril_mutex_unlock(&users_guard);
135 return NULL;
136 }
137
138 telnet_user_t *tmp = user;
[261bbdc]139 fibril_mutex_lock(&tmp->guard);
[30d4706]140 user->locsrv_connection_count++;
141
142 /*
143 * Refuse to return user whose task already finished or when
144 * the socket is already closed().
145 */
146 if (user->task_finished || user->socket_closed) {
147 user = NULL;
148 user->locsrv_connection_count--;
149 }
150
[261bbdc]151 fibril_mutex_unlock(&tmp->guard);
[30d4706]152
153
154 fibril_mutex_unlock(&users_guard);
155
156 return user;
157}
158
[5923cf82]159/** Notify that client disconnected from the remote terminal.
160 *
161 * @param user To which user the client was connected.
162 */
[8562724]163void telnet_user_notify_client_disconnected(telnet_user_t *user)
164{
[261bbdc]165 fibril_mutex_lock(&user->guard);
[8562724]166 assert(user->locsrv_connection_count > 0);
167 user->locsrv_connection_count--;
168 fibril_condvar_signal(&user->refcount_cv);
[261bbdc]169 fibril_mutex_unlock(&user->guard);
[8562724]170}
[30d4706]171
[99c2e9f3]172/** Tell whether the launched task already exited and socket is already closed.
173 *
174 * @param user Telnet user in question.
175 */
176bool telnet_user_is_zombie(telnet_user_t *user)
177{
178 fibril_mutex_lock(&user->guard);
179 bool zombie = user->socket_closed || user->task_finished;
180 fibril_mutex_unlock(&user->guard);
181
182 return zombie;
183}
184
185/** Receive next byte from a socket (use buffering.
186 * We need to return the value via extra argument because the read byte
187 * might be negative.
188 */
[b7fd2a0]189static errno_t telnet_user_recv_next_byte_no_lock(telnet_user_t *user, char *byte)
[99c2e9f3]190{
191 /* No more buffered data? */
192 if (user->socket_buffer_len <= user->socket_buffer_pos) {
[b7fd2a0]193 errno_t rc;
[fab2746]194 size_t recv_length;
195
196 rc = tcp_conn_recv_wait(user->conn, user->socket_buffer,
197 BUFFER_SIZE, &recv_length);
198 if (rc != EOK)
199 return rc;
200
201 if (recv_length == 0) {
[99c2e9f3]202 user->socket_closed = true;
[5d94b16c]203 user->srvs.aborted = true;
[99c2e9f3]204 return ENOENT;
205 }
[fab2746]206
[99c2e9f3]207 user->socket_buffer_len = recv_length;
208 user->socket_buffer_pos = 0;
209 }
210
211 *byte = user->socket_buffer[user->socket_buffer_pos++];
212
213 return EOK;
214}
215
216/** Creates new keyboard event from given char.
217 *
218 * @param type Event type (press / release).
219 * @param c Pressed character.
220 */
[1433ecda]221static kbd_event_t *new_kbd_event(kbd_event_type_t type, wchar_t c)
222{
[99c2e9f3]223 kbd_event_t *event = malloc(sizeof(kbd_event_t));
224 assert(event);
225
226 link_initialize(&event->link);
227 event->type = type;
228 event->c = c;
229 event->mods = 0;
[c17c4e28]230
231 switch (c) {
232 case '\n':
233 event->key = KC_ENTER;
234 break;
235 case '\t':
236 event->key = KC_TAB;
237 break;
238 case '\b':
239 case 127: /* This is what Linux telnet sends. */
240 event->key = KC_BACKSPACE;
241 event->c = '\b';
242 break;
243 default:
244 event->key = KC_A;
245 break;
246 }
[99c2e9f3]247
248 return event;
249}
250
[178d6a3]251/** Process telnet command (currently only print to screen).
252 *
253 * @param user Telnet user structure.
254 * @param option_code Command option code.
255 * @param cmd Telnet command.
256 */
[2a180307]257static void process_telnet_command(telnet_user_t *user,
258 telnet_cmd_t option_code, telnet_cmd_t cmd)
259{
260 if (option_code != 0) {
261 telnet_user_log(user, "Ignoring telnet command %u %u %u.",
262 TELNET_IAC, option_code, cmd);
263 } else {
264 telnet_user_log(user, "Ignoring telnet command %u %u.",
265 TELNET_IAC, cmd);
266 }
267}
268
[178d6a3]269/** Get next keyboard event.
270 *
271 * @param user Telnet user.
272 * @param event Where to store the keyboard event.
273 * @return Error code.
274 */
[b7fd2a0]275errno_t telnet_user_get_next_keyboard_event(telnet_user_t *user, kbd_event_t *event)
[99c2e9f3]276{
277 fibril_mutex_lock(&user->guard);
278 if (list_empty(&user->in_events.list)) {
279 char next_byte = 0;
[2a180307]280 bool inside_telnet_command = false;
281
282 telnet_cmd_t telnet_option_code = 0;
283
[99c2e9f3]284 /* Skip zeros, bail-out on error. */
285 while (next_byte == 0) {
[b7fd2a0]286 errno_t rc = telnet_user_recv_next_byte_no_lock(user, &next_byte);
[99c2e9f3]287 if (rc != EOK) {
288 fibril_mutex_unlock(&user->guard);
289 return rc;
290 }
[2a180307]291 uint8_t byte = (uint8_t) next_byte;
292
293 /* Skip telnet commands. */
294 if (inside_telnet_command) {
295 inside_telnet_command = false;
296 next_byte = 0;
297 if (TELNET_IS_OPTION_CODE(byte)) {
298 telnet_option_code = byte;
299 inside_telnet_command = true;
300 } else {
301 process_telnet_command(user,
302 telnet_option_code, byte);
303 }
304 }
305 if (byte == TELNET_IAC) {
306 inside_telnet_command = true;
307 next_byte = 0;
308 }
[99c2e9f3]309 }
310
311 /* CR-LF conversions. */
312 if (next_byte == 13) {
313 next_byte = 10;
314 }
315
316 kbd_event_t *down = new_kbd_event(KEY_PRESS, next_byte);
317 kbd_event_t *up = new_kbd_event(KEY_RELEASE, next_byte);
318 assert(down);
319 assert(up);
320 prodcons_produce(&user->in_events, &down->link);
321 prodcons_produce(&user->in_events, &up->link);
322 }
323
324 link_t *link = prodcons_consume(&user->in_events);
325 kbd_event_t *tmp = list_get_instance(link, kbd_event_t, link);
326
327 fibril_mutex_unlock(&user->guard);
328
329 *event = *tmp;
330
331 free(tmp);
332
333 return EOK;
334}
[30d4706]335
[178d6a3]336/** Send data (convert them first) to the socket, no locking.
337 *
338 * @param user Telnet user.
339 * @param data Data buffer (not zero terminated).
340 * @param size Size of @p data buffer in bytes.
341 */
[b7fd2a0]342static errno_t telnet_user_send_data_no_lock(telnet_user_t *user, uint8_t *data, size_t size)
[178d6a3]343{
344 uint8_t *converted = malloc(3 * size + 1);
345 assert(converted);
346 int converted_size = 0;
347
348 /* Convert new-lines. */
349 for (size_t i = 0; i < size; i++) {
350 if (data[i] == 10) {
351 converted[converted_size++] = 13;
352 converted[converted_size++] = 10;
353 user->cursor_x = 0;
354 } else {
355 converted[converted_size++] = data[i];
356 if (data[i] == '\b') {
357 user->cursor_x--;
358 } else {
359 user->cursor_x++;
360 }
361 }
362 }
363
364
[b7fd2a0]365 errno_t rc = tcp_conn_send(user->conn, converted, converted_size);
[178d6a3]366 free(converted);
367
368 return rc;
369}
370
371/** Send data (convert them first) to the socket.
372 *
373 * @param user Telnet user.
374 * @param data Data buffer (not zero terminated).
375 * @param size Size of @p data buffer in bytes.
376 */
[b7fd2a0]377errno_t telnet_user_send_data(telnet_user_t *user, uint8_t *data, size_t size)
[178d6a3]378{
379 fibril_mutex_lock(&user->guard);
380
[b7fd2a0]381 errno_t rc = telnet_user_send_data_no_lock(user, data, size);
[178d6a3]382
383 fibril_mutex_unlock(&user->guard);
384
385 return rc;
386}
387
388/** Update cursor X position.
389 *
390 * This call may result in sending control commands over socket.
391 *
392 * @param user Telnet user.
393 * @param new_x New cursor location.
394 */
395void telnet_user_update_cursor_x(telnet_user_t *user, int new_x)
396{
397 fibril_mutex_lock(&user->guard);
398 if (user->cursor_x - 1 == new_x) {
399 uint8_t data = '\b';
400 /* Ignore errors. */
401 telnet_user_send_data_no_lock(user, &data, 1);
402 }
403 user->cursor_x = new_x;
404 fibril_mutex_unlock(&user->guard);
405
406}
407
[30d4706]408/**
409 * @}
410 */
Note: See TracBrowser for help on using the repository browser.