source: mainline/uspace/srv/hid/remcons/user.c@ 38d150e

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

Prefer to get memory allocation functions through the standard stdlib header.

  • 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 */
189static int telnet_user_recv_next_byte_no_lock(telnet_user_t *user, char *byte)
190{
191 /* No more buffered data? */
192 if (user->socket_buffer_len <= user->socket_buffer_pos) {
[fab2746]193 int rc;
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 */
221static kbd_event_t* new_kbd_event(kbd_event_type_t type, wchar_t c) {
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;
[c17c4e28]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 }
[99c2e9f3]246
247 return event;
248}
249
[178d6a3]250/** Process telnet command (currently only print to screen).
251 *
252 * @param user Telnet user structure.
253 * @param option_code Command option code.
254 * @param cmd Telnet command.
255 */
[2a180307]256static void process_telnet_command(telnet_user_t *user,
257 telnet_cmd_t option_code, telnet_cmd_t cmd)
258{
259 if (option_code != 0) {
260 telnet_user_log(user, "Ignoring telnet command %u %u %u.",
261 TELNET_IAC, option_code, cmd);
262 } else {
263 telnet_user_log(user, "Ignoring telnet command %u %u.",
264 TELNET_IAC, cmd);
265 }
266}
267
[178d6a3]268/** Get next keyboard event.
269 *
270 * @param user Telnet user.
271 * @param event Where to store the keyboard event.
272 * @return Error code.
273 */
[99c2e9f3]274int 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;
[2a180307]279 bool inside_telnet_command = false;
280
281 telnet_cmd_t telnet_option_code = 0;
282
[99c2e9f3]283 /* Skip zeros, bail-out on error. */
284 while (next_byte == 0) {
285 int rc = telnet_user_recv_next_byte_no_lock(user, &next_byte);
286 if (rc != EOK) {
287 fibril_mutex_unlock(&user->guard);
288 return rc;
289 }
[2a180307]290 uint8_t byte = (uint8_t) next_byte;
291
292 /* Skip telnet commands. */
293 if (inside_telnet_command) {
294 inside_telnet_command = false;
295 next_byte = 0;
296 if (TELNET_IS_OPTION_CODE(byte)) {
297 telnet_option_code = byte;
298 inside_telnet_command = true;
299 } else {
300 process_telnet_command(user,
301 telnet_option_code, byte);
302 }
303 }
304 if (byte == TELNET_IAC) {
305 inside_telnet_command = true;
306 next_byte = 0;
307 }
[99c2e9f3]308 }
309
310 /* CR-LF conversions. */
311 if (next_byte == 13) {
312 next_byte = 10;
313 }
314
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);
331
332 return EOK;
333}
[30d4706]334
[178d6a3]335/** Send data (convert them first) to the socket, no locking.
336 *
337 * @param user Telnet user.
338 * @param data Data buffer (not zero terminated).
339 * @param size Size of @p data buffer in bytes.
340 */
341static int telnet_user_send_data_no_lock(telnet_user_t *user, uint8_t *data, size_t size)
342{
343 uint8_t *converted = malloc(3 * size + 1);
344 assert(converted);
345 int converted_size = 0;
346
347 /* Convert new-lines. */
348 for (size_t i = 0; i < size; i++) {
349 if (data[i] == 10) {
350 converted[converted_size++] = 13;
351 converted[converted_size++] = 10;
352 user->cursor_x = 0;
353 } else {
354 converted[converted_size++] = data[i];
355 if (data[i] == '\b') {
356 user->cursor_x--;
357 } else {
358 user->cursor_x++;
359 }
360 }
361 }
362
363
[fab2746]364 int rc = tcp_conn_send(user->conn, converted, converted_size);
[178d6a3]365 free(converted);
366
367 return rc;
368}
369
370/** Send data (convert them first) to the socket.
371 *
372 * @param user Telnet user.
373 * @param data Data buffer (not zero terminated).
374 * @param size Size of @p data buffer in bytes.
375 */
376int telnet_user_send_data(telnet_user_t *user, uint8_t *data, size_t size)
377{
378 fibril_mutex_lock(&user->guard);
379
380 int rc = telnet_user_send_data_no_lock(user, data, size);
381
382 fibril_mutex_unlock(&user->guard);
383
384 return rc;
385}
386
387/** Update cursor X position.
388 *
389 * This call may result in sending control commands over socket.
390 *
391 * @param user Telnet user.
392 * @param new_x New cursor location.
393 */
394void telnet_user_update_cursor_x(telnet_user_t *user, int new_x)
395{
396 fibril_mutex_lock(&user->guard);
397 if (user->cursor_x - 1 == new_x) {
398 uint8_t data = '\b';
399 /* Ignore errors. */
400 telnet_user_send_data_no_lock(user, &data, 1);
401 }
402 user->cursor_x = new_x;
403 fibril_mutex_unlock(&user->guard);
404
405}
406
[30d4706]407/**
408 * @}
409 */
Note: See TracBrowser for help on using the repository browser.