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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4d11204 was 8820544, checked in by Martin Decky <martin@…>, 11 years ago

support for kernel notification multiplexing in the async framework

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