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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 99c2e9f3 was 99c2e9f3, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

remcons: more refactoring

  • Property mode set to 100644
File size: 6.9 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 <ipc/input.h>
38#include <ipc/console.h>
39#include <ipc/vfs.h>
40#include <errno.h>
41#include <str_error.h>
42#include <loc.h>
43#include <event.h>
44#include <io/keycode.h>
45#include <align.h>
46#include <malloc.h>
47#include <as.h>
48#include <fibril_synch.h>
49#include <task.h>
50#include <net/in.h>
51#include <net/inet.h>
52#include <net/socket.h>
53#include <io/console.h>
54#include <inttypes.h>
55#include <assert.h>
56#include "user.h"
57
58static FIBRIL_MUTEX_INITIALIZE(users_guard);
59static LIST_INITIALIZE(users);
60
61/** Create new telnet user.
62 *
63 * @param socket Socket the user communicates through.
64 * @return New telnet user or NULL when out of memory.
65 */
66telnet_user_t *telnet_user_create(int socket)
67{
68 static int telnet_user_id_counter = 0;
69
70 telnet_user_t *user = malloc(sizeof(telnet_user_t));
71 if (user == NULL) {
72 return NULL;
73 }
74
75 user->id = ++telnet_user_id_counter;
76
77 int rc = asprintf(&user->service_name, "%s/telnet%d", NAMESPACE, user->id);
78 if (rc < 0) {
79 free(user);
80 return NULL;
81 }
82
83 user->socket = socket;
84 user->service_id = (service_id_t) -1;
85 prodcons_initialize(&user->in_events);
86 link_initialize(&user->link);
87 user->socket_buffer_len = 0;
88 user->socket_buffer_pos = 0;
89
90 fibril_condvar_initialize(&user->refcount_cv);
91 fibril_mutex_initialize(&user->guard);
92 user->task_finished = false;
93 user->socket_closed = false;
94 user->locsrv_connection_count = 0;
95
96
97 fibril_mutex_lock(&users_guard);
98 list_append(&user->link, &users);
99 fibril_mutex_unlock(&users_guard);
100
101 return user;
102}
103
104/** Destroy telnet user structure.
105 *
106 * @param user User to be destroyed.
107 */
108void telnet_user_destroy(telnet_user_t *user)
109{
110 assert(user);
111
112 fibril_mutex_lock(&users_guard);
113 list_remove(&user->link);
114 fibril_mutex_unlock(&users_guard);
115
116 free(user);
117}
118
119/** Find user by service id and increments reference counter.
120 *
121 * @param id Location service id of the telnet user's terminal.
122 */
123telnet_user_t *telnet_user_get_for_client_connection(service_id_t id)
124{
125 telnet_user_t *user = NULL;
126
127 fibril_mutex_lock(&users_guard);
128 list_foreach(users, link) {
129 telnet_user_t *tmp = list_get_instance(link, telnet_user_t, link);
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 return ENOENT;
199 }
200 if (recv_length < 0) {
201 return recv_length;
202 }
203 user->socket_buffer_len = recv_length;
204 user->socket_buffer_pos = 0;
205 }
206
207 *byte = user->socket_buffer[user->socket_buffer_pos++];
208
209 return EOK;
210}
211
212/** Creates new keyboard event from given char.
213 *
214 * @param type Event type (press / release).
215 * @param c Pressed character.
216 */
217static kbd_event_t* new_kbd_event(kbd_event_type_t type, wchar_t c) {
218 kbd_event_t *event = malloc(sizeof(kbd_event_t));
219 assert(event);
220
221 link_initialize(&event->link);
222 event->type = type;
223 event->c = c;
224 event->mods = 0;
225 event->key = (c == '\n' ? KC_ENTER : KC_A);
226
227 return event;
228}
229
230int telnet_user_get_next_keyboard_event(telnet_user_t *user, kbd_event_t *event)
231{
232 fibril_mutex_lock(&user->guard);
233 if (list_empty(&user->in_events.list)) {
234 char next_byte = 0;
235 /* Skip zeros, bail-out on error. */
236 while (next_byte == 0) {
237 int rc = telnet_user_recv_next_byte_no_lock(user, &next_byte);
238 DEBUG("Got %d.\n", next_byte);
239 if (rc != EOK) {
240 fibril_mutex_unlock(&user->guard);
241 return rc;
242 }
243 }
244
245 /* CR-LF conversions. */
246 if (next_byte == 13) {
247 next_byte = 10;
248 }
249
250 kbd_event_t *down = new_kbd_event(KEY_PRESS, next_byte);
251 kbd_event_t *up = new_kbd_event(KEY_RELEASE, next_byte);
252 assert(down);
253 assert(up);
254 prodcons_produce(&user->in_events, &down->link);
255 prodcons_produce(&user->in_events, &up->link);
256 }
257
258 link_t *link = prodcons_consume(&user->in_events);
259 kbd_event_t *tmp = list_get_instance(link, kbd_event_t, link);
260
261 fibril_mutex_unlock(&user->guard);
262
263 *event = *tmp;
264
265 free(tmp);
266
267 return EOK;
268}
269
270/**
271 * @}
272 */
Note: See TracBrowser for help on using the repository browser.