source: mainline/uspace/srv/hid/remcons/remcons.c@ 5576358

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

remcons: add wrappers for client_t

  • 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
35#include <async.h>
36#include <stdio.h>
37#include <adt/prodcons.h>
38#include <ipc/input.h>
39#include <ipc/console.h>
40#include <ipc/vfs.h>
41#include <errno.h>
42#include <str_error.h>
43#include <loc.h>
44#include <event.h>
45#include <io/keycode.h>
46#include <align.h>
47#include <malloc.h>
48#include <as.h>
49#include <fibril_synch.h>
50#include <task.h>
51#include <net/in.h>
52#include <net/inet.h>
53#include <net/socket.h>
54#include <io/console.h>
55#include <inttypes.h>
56
57
58#define APP_GETTERM "/app/getterm"
59#define NAME "remterm"
60#define NAMESPACE "term"
61#define BACKLOG_SIZE 5
62
63#define BUFFER_SIZE 1024
64
65typedef struct {
66 int id;
67 int socket;
68 service_id_t service_id;
69 char *service_name;
70 /** Producer-consumer of kbd_event_t. */
71 prodcons_t in_events;
72 link_t link;
73 char socket_buffer[BUFFER_SIZE];
74 size_t socket_buffer_len;
75 size_t socket_buffer_pos;
76} client_t;
77
78static FIBRIL_MUTEX_INITIALIZE(clients_guard);
79static LIST_INITIALIZE(clients);
80
81static client_t *client_create(int socket)
82{
83 static int client_counter = 0;
84
85 client_t *client = malloc(sizeof(client_t));
86 if (client == NULL) {
87 return NULL;
88 }
89
90 client->id = ++client_counter;
91
92 int rc = asprintf(&client->service_name, "%s/telnet%d", NAMESPACE, client->id);
93 if (rc < 0) {
94 free(client);
95 return NULL;
96 }
97
98 client->socket = socket;
99 client->service_id = (service_id_t) -1;
100 prodcons_initialize(&client->in_events);
101 link_initialize(&client->link);
102 client->socket_buffer_len = 0;
103 client->socket_buffer_pos = 0;
104
105
106 fibril_mutex_lock(&clients_guard);
107 list_append(&client->link, &clients);
108 fibril_mutex_unlock(&clients_guard);
109
110 return client;
111}
112
113static void client_destroy(client_t *client)
114{
115 assert(client);
116
117 fibril_mutex_lock(&clients_guard);
118 list_remove(&client->link);
119 fibril_mutex_unlock(&clients_guard);
120
121 free(client);
122}
123
124static client_t *client_find(service_id_t id)
125{
126 client_t *client = NULL;
127
128 fibril_mutex_lock(&clients_guard);
129 list_foreach(clients, link) {
130 client_t *tmp = list_get_instance(link, client_t, link);
131 if (tmp->service_id == id) {
132 client = tmp;
133 break;
134 }
135 }
136 fibril_mutex_unlock(&clients_guard);
137
138 return client;
139}
140
141
142
143static kbd_event_t* new_kbd_event(kbd_event_type_t type, wchar_t c) {
144 kbd_event_t *event = malloc(sizeof(kbd_event_t));
145 assert(event);
146
147 link_initialize(&event->link);
148 event->type = type;
149 event->c = c;
150 event->mods = 0;
151 event->key = (c == '\n' ? KC_ENTER : KC_A);
152
153 return event;
154}
155
156static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
157{
158 /* Find the client. */
159 client_t *client = client_find(IPC_GET_ARG1(*icall));
160 if (client == NULL) {
161 async_answer_0(iid, ENOENT);
162 return;
163 }
164
165 printf("New client for service %s.\n", client->service_name);
166
167 /* Accept the connection */
168 async_answer_0(iid, EOK);
169
170 /*
171 * Force character mode.
172 * IAC WILL ECHO IAC WILL SUPPRESS_GO_AHEAD IAC WONT LINEMODE
173 * http://stackoverflow.com/questions/273261/force-telnet-client-into-character-mode
174 */
175 const char force_char_mode[] = {255, 251, 1, 255, 251, 3, 255, 252, 34};
176 send(client->socket, (void *)force_char_mode, sizeof(force_char_mode), 0);
177
178 while (true) {
179 ipc_call_t call;
180 ipc_callid_t callid = async_get_call(&call);
181
182 if (!IPC_GET_IMETHOD(call)) {
183 /* Clean-up. */
184 return;
185 }
186
187 switch (IPC_GET_IMETHOD(call)) {
188 case CONSOLE_GET_SIZE:
189 async_answer_2(callid, EOK, 100, 1);
190 break;
191 case CONSOLE_GET_POS:
192 async_answer_2(callid, EOK, 0, 0);
193 break;
194 case CONSOLE_GET_EVENT: {
195 if (list_empty(&client->in_events.list)) {
196 retry:
197 if (client->socket_buffer_len <= client->socket_buffer_pos) {
198 int recv_length = recv(client->socket, client->socket_buffer, BUFFER_SIZE, 0);
199 if (recv_length == 0) {
200 async_answer_0(callid, ENOENT);
201 return;
202 }
203 if (recv_length < 0) {
204 async_answer_0(callid, EINVAL);
205 break;
206 }
207 client->socket_buffer_len = recv_length;
208 client->socket_buffer_pos = 0;
209 }
210 char data = client->socket_buffer[client->socket_buffer_pos++];
211 if (data == 13) {
212 data = 10;
213 }
214 if (data == 0)
215 goto retry;
216
217 kbd_event_t *down = new_kbd_event(KEY_PRESS, data);
218 kbd_event_t *up = new_kbd_event(KEY_RELEASE, data);
219 assert(down);
220 assert(up);
221 prodcons_produce(&client->in_events, &down->link);
222 prodcons_produce(&client->in_events, &up->link);
223 }
224
225
226 link_t *link = prodcons_consume(&client->in_events);
227 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
228 async_answer_4(callid, EOK, event->type, event->key, event->mods, event->c);
229 free(event);
230 break;
231 }
232 case CONSOLE_GOTO:
233 async_answer_0(callid, ENOTSUP);
234 break;
235 case VFS_OUT_READ:
236 async_answer_0(callid, ENOTSUP);
237 break;
238 case VFS_OUT_WRITE: {
239 char *buf;
240 char *buf_converted;
241 size_t size;
242 int rc = async_data_write_accept((void **)&buf, false, 0, 0, 0, &size);
243
244 if (rc != EOK) {
245 async_answer_0(callid, rc);
246 break;
247 }
248 buf_converted = malloc(2 * size);
249 assert(buf_converted);
250 int buf_converted_size = 0;
251 /* Convert new-lines. */
252 for (size_t i = 0; i < size; i++) {
253 if (buf[i] == 10) {
254 buf_converted[buf_converted_size++] = 13;
255 buf_converted[buf_converted_size++] = 10;
256 } else {
257 buf_converted[buf_converted_size++] = buf[i];
258 }
259 }
260 rc = send(client->socket, buf_converted, buf_converted_size, 0);
261 free(buf);
262
263 if (rc != EOK) {
264 printf("Problem sending data: %s\n", str_error(rc));
265 async_answer_0(callid, rc);
266 break;
267 }
268
269 async_answer_1(callid, EOK, size);
270
271 break;
272 }
273 case VFS_OUT_SYNC:
274 async_answer_0(callid, EOK);
275 break;
276 case CONSOLE_CLEAR:
277 async_answer_0(callid, EOK);
278 break;
279
280 case CONSOLE_GET_COLOR_CAP:
281 async_answer_1(callid, EOK, CONSOLE_CAP_NONE);
282 break;
283 case CONSOLE_SET_STYLE:
284 async_answer_0(callid, ENOTSUP);
285 break;
286 case CONSOLE_SET_COLOR:
287 async_answer_0(callid, ENOTSUP);
288 break;
289 case CONSOLE_SET_RGB_COLOR:
290 async_answer_0(callid, ENOTSUP);
291 break;
292
293 case CONSOLE_CURSOR_VISIBILITY:
294 async_answer_0(callid, ENOTSUP);
295 break;
296
297 default:
298 async_answer_0(callid, EINVAL);
299 break;
300 }
301 }
302}
303
304
305static int network_client_fibril(void *arg)
306{
307 int rc;
308 client_t *client = arg;
309
310 rc = loc_service_register(client->service_name, &client->service_id);
311 if (rc != EOK) {
312 fprintf(stderr, "%s: Unable to register device %s\n", NAME,
313 client->service_name);
314 return EOK;
315 }
316 printf("Service %s registered as %" PRIun "\n", client->service_name,
317 client->service_id);
318
319 char term[LOC_NAME_MAXLEN];
320 snprintf(term, LOC_NAME_MAXLEN, "%s/%s", "/loc", client->service_name);
321
322 task_id_t task;
323 rc = task_spawnl(&task, APP_GETTERM, APP_GETTERM, term, "/app/bdsh", NULL);
324 if (rc != EOK) {
325 printf("%s: Error spawning %s -w %s %s (%s)\n", NAME,
326 APP_GETTERM, term, "/app/bdsh", str_error(rc));
327 return EOK;
328 }
329
330 task_exit_t task_exit;
331 int task_retval;
332 task_wait(task, &task_exit, &task_retval);
333 printf("%s: getterm terminated: %d, %d\n", NAME, task_exit, task_retval);
334
335 closesocket(client->socket);
336
337 client_destroy(client);
338
339 return EOK;
340}
341
342int main(int argc, char *argv[])
343{
344 int port = 2223;
345
346 int rc = loc_server_register(NAME, client_connection);
347 if (rc < 0) {
348 printf("%s: Unable to register server (%s).\n", NAME,
349 str_error(rc));
350 return 1;
351 }
352
353 struct sockaddr_in addr;
354
355 addr.sin_family = AF_INET;
356 addr.sin_port = htons(port);
357
358 rc = inet_pton(AF_INET, "127.0.0.1", (void *)
359 &addr.sin_addr.s_addr);
360 if (rc != EOK) {
361 fprintf(stderr, "Error parsing network address (%s)\n",
362 str_error(rc));
363 return 2;
364 }
365
366 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
367 if (listen_sd < 0) {
368 fprintf(stderr, "Error creating listening socket (%s)\n",
369 str_error(listen_sd));
370 return 3;
371 }
372
373 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
374 if (rc != EOK) {
375 fprintf(stderr, "Error binding socket (%s)\n",
376 str_error(rc));
377 return 4;
378 }
379
380 rc = listen(listen_sd, BACKLOG_SIZE);
381 if (rc != EOK) {
382 fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
383 return 5;
384 }
385
386 printf("%s: HelenOS Remote console service\n", NAME);
387
388 while (true) {
389 struct sockaddr_in raddr;
390 socklen_t raddr_len = sizeof(raddr);
391 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
392 &raddr_len);
393
394 if (conn_sd < 0) {
395 fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
396 continue;
397 }
398
399 client_t *client = client_create(conn_sd);
400 assert(client);
401
402 fid_t fid = fibril_create(network_client_fibril, client);
403 assert(fid);
404 fibril_add_ready(fid);
405 }
406
407 return 0;
408}
409
410/** @}
411 */
Note: See TracBrowser for help on using the repository browser.