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
Line 
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>
36#include <stdlib.h>
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>
46#include <inet/tcp.h>
47#include <io/console.h>
48#include <inttypes.h>
49#include <assert.h>
50#include "user.h"
51#include "telnet.h"
52
53static FIBRIL_MUTEX_INITIALIZE(users_guard);
54static LIST_INITIALIZE(users);
55
56/** Create new telnet user.
57 *
58 * @param conn Incoming connection.
59 * @return New telnet user or NULL when out of memory.
60 */
61telnet_user_t *telnet_user_create(tcp_conn_t *conn)
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
78 user->conn = conn;
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);
86 fibril_mutex_initialize(&user->guard);
87 user->task_finished = false;
88 user->socket_closed = false;
89 user->locsrv_connection_count = 0;
90
91 user->cursor_x = 0;
92
93 return user;
94}
95
96void telnet_user_add(telnet_user_t *user)
97{
98 fibril_mutex_lock(&users_guard);
99 list_append(&user->link, &users);
100 fibril_mutex_unlock(&users_guard);
101}
102
103/** Destroy telnet user structure.
104 *
105 * @param user User to be destroyed.
106 */
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
118/** Find user by service id and increments reference counter.
119 *
120 * @param id Location service id of the telnet user's terminal.
121 */
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);
127 list_foreach(users, link, telnet_user_t, tmp) {
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;
139 fibril_mutex_lock(&tmp->guard);
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
151 fibril_mutex_unlock(&tmp->guard);
152
153
154 fibril_mutex_unlock(&users_guard);
155
156 return user;
157}
158
159/** Notify that client disconnected from the remote terminal.
160 *
161 * @param user To which user the client was connected.
162 */
163void telnet_user_notify_client_disconnected(telnet_user_t *user)
164{
165 fibril_mutex_lock(&user->guard);
166 assert(user->locsrv_connection_count > 0);
167 user->locsrv_connection_count--;
168 fibril_condvar_signal(&user->refcount_cv);
169 fibril_mutex_unlock(&user->guard);
170}
171
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) {
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) {
202 user->socket_closed = true;
203 user->srvs.aborted = true;
204 return ENOENT;
205 }
206
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;
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).
251 *
252 * @param user Telnet user structure.
253 * @param option_code Command option code.
254 * @param cmd Telnet command.
255 */
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
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 */
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;
279 bool inside_telnet_command = false;
280
281 telnet_cmd_t telnet_option_code = 0;
282
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 }
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 }
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}
334
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
364 int rc = tcp_conn_send(user->conn, converted, converted_size);
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
407/**
408 * @}
409 */
Note: See TracBrowser for help on using the repository browser.