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
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 errno_t 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 errno_t 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{
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;
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 }
247
248 return event;
249}
250
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 */
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
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 */
275errno_t telnet_user_get_next_keyboard_event(telnet_user_t *user, kbd_event_t *event)
276{
277 fibril_mutex_lock(&user->guard);
278 if (list_empty(&user->in_events.list)) {
279 char next_byte = 0;
280 bool inside_telnet_command = false;
281
282 telnet_cmd_t telnet_option_code = 0;
283
284 /* Skip zeros, bail-out on error. */
285 while (next_byte == 0) {
286 errno_t rc = telnet_user_recv_next_byte_no_lock(user, &next_byte);
287 if (rc != EOK) {
288 fibril_mutex_unlock(&user->guard);
289 return rc;
290 }
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 }
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}
335
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 */
342static errno_t telnet_user_send_data_no_lock(telnet_user_t *user, uint8_t *data, size_t size)
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
365 errno_t rc = tcp_conn_send(user->conn, converted, converted_size);
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 */
377errno_t telnet_user_send_data(telnet_user_t *user, uint8_t *data, size_t size)
378{
379 fibril_mutex_lock(&user->guard);
380
381 errno_t rc = telnet_user_send_data_no_lock(user, data, size);
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
408/**
409 * @}
410 */
Note: See TracBrowser for help on using the repository browser.