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

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

Simplify use of list_foreach.

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