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

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

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

  • Property mode set to 100644
File size: 9.6 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 <errno.h>
37#include <io/con_srv.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <str_error.h>
41#include <loc.h>
42#include <io/keycode.h>
43#include <align.h>
44#include <fibril_synch.h>
45#include <task.h>
46#include <inet/addr.h>
47#include <inet/endpoint.h>
48#include <inet/tcp.h>
49#include <io/console.h>
50#include <inttypes.h>
51#include "telnet.h"
52#include "user.h"
53
54#define APP_GETTERM "/app/getterm"
55#define APP_SHELL "/app/bdsh"
56
57/** Telnet commands to force character mode
58 * (redundant to be on the safe side).
59 * See
60 * http://stackoverflow.com/questions/273261/force-telnet-user-into-character-mode
61 * for discussion.
62 */
63static const telnet_cmd_t telnet_force_character_mode_command[] = {
64 TELNET_IAC, TELNET_WILL, TELNET_ECHO,
65 TELNET_IAC, TELNET_WILL, TELNET_SUPPRESS_GO_AHEAD,
66 TELNET_IAC, TELNET_WONT, TELNET_LINEMODE
67};
68
69static const size_t telnet_force_character_mode_command_count =
70 sizeof(telnet_force_character_mode_command) / sizeof(telnet_cmd_t);
71
72static int remcons_open(con_srvs_t *, con_srv_t *);
73static int remcons_close(con_srv_t *);
74static int remcons_write(con_srv_t *, void *, size_t);
75static void remcons_sync(con_srv_t *);
76static void remcons_clear(con_srv_t *);
77static void remcons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
78static int remcons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
79static int remcons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
80static int remcons_get_color_cap(con_srv_t *, console_caps_t *);
81static int remcons_get_event(con_srv_t *, cons_event_t *);
82
83static con_ops_t con_ops = {
84 .open = remcons_open,
85 .close = remcons_close,
86 .read = NULL,
87 .write = remcons_write,
88 .sync = remcons_sync,
89 .clear = remcons_clear,
90 .set_pos = remcons_set_pos,
91 .get_pos = remcons_get_pos,
92 .get_size = remcons_get_size,
93 .get_color_cap = remcons_get_color_cap,
94 .set_style = NULL,
95 .set_color = NULL,
96 .set_rgb_color = NULL,
97 .set_cursor_visibility = NULL,
98 .get_event = remcons_get_event
99};
100
101static void remcons_new_conn(tcp_listener_t *lst, tcp_conn_t *conn);
102
103static tcp_listen_cb_t listen_cb = {
104 .new_conn = remcons_new_conn
105};
106
107static tcp_cb_t conn_cb = {
108 .connected = NULL
109};
110
111static telnet_user_t *srv_to_user(con_srv_t *srv)
112{
113 return srv->srvs->sarg;
114}
115
116static int remcons_open(con_srvs_t *srvs, con_srv_t *srv)
117{
118 telnet_user_t *user = srv_to_user(srv);
119
120 telnet_user_log(user, "New client connected (%p).", srv);
121
122 /* Force character mode. */
123 (void) tcp_conn_send(user->conn, (void *)telnet_force_character_mode_command,
124 telnet_force_character_mode_command_count);
125
126 return EOK;
127}
128
129static int remcons_close(con_srv_t *srv)
130{
131 telnet_user_t *user = srv_to_user(srv);
132
133 telnet_user_notify_client_disconnected(user);
134 telnet_user_log(user, "Client disconnected (%p).", srv);
135
136 return EOK;
137}
138
139static int remcons_write(con_srv_t *srv, void *data, size_t size)
140{
141 telnet_user_t *user = srv_to_user(srv);
142 int rc;
143
144 rc = telnet_user_send_data(user, data, size);
145 if (rc != EOK)
146 return rc;
147
148 return size;
149}
150
151static void remcons_sync(con_srv_t *srv)
152{
153 (void) srv;
154}
155
156static void remcons_clear(con_srv_t *srv)
157{
158 (void) srv;
159}
160
161static void remcons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
162{
163 telnet_user_t *user = srv_to_user(srv);
164
165 telnet_user_update_cursor_x(user, col);
166}
167
168static int remcons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
169{
170 telnet_user_t *user = srv_to_user(srv);
171
172 *col = user->cursor_x;
173 *row = 0;
174
175 return EOK;
176}
177
178static int remcons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
179{
180 (void) srv;
181
182 *cols = 100;
183 *rows = 1;
184
185 return EOK;
186}
187
188static int remcons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
189{
190 (void) srv;
191 *ccaps = CONSOLE_CAP_NONE;
192
193 return EOK;
194}
195
196static int remcons_get_event(con_srv_t *srv, cons_event_t *event)
197{
198 telnet_user_t *user = srv_to_user(srv);
199 kbd_event_t kevent;
200 int rc;
201
202 rc = telnet_user_get_next_keyboard_event(user, &kevent);
203 if (rc != EOK) {
204 /* XXX What? */
205 memset(event, 0, sizeof(*event));
206 return EOK;
207 }
208
209 event->type = CEV_KEY;
210 event->ev.key = kevent;
211
212 return EOK;
213}
214
215/** Callback when client connects to a telnet terminal. */
216static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
217{
218 /* Find the user. */
219 telnet_user_t *user = telnet_user_get_for_client_connection(IPC_GET_ARG1(*icall));
220 if (user == NULL) {
221 async_answer_0(iid, ENOENT);
222 return;
223 }
224
225 /* Handle messages. */
226 con_conn(iid, icall, &user->srvs);
227}
228
229/** Fibril for spawning the task running after user connects.
230 *
231 * @param arg Corresponding @c telnet_user_t structure.
232 */
233static int spawn_task_fibril(void *arg)
234{
235 telnet_user_t *user = arg;
236
237 task_id_t task;
238 task_wait_t wait;
239 int rc = task_spawnl(&task, &wait, APP_GETTERM, APP_GETTERM, user->service_name,
240 "/loc", "--msg", "--", APP_SHELL, NULL);
241 if (rc != EOK) {
242 telnet_user_error(user, "Spawning `%s %s /loc --msg -- %s' "
243 "failed: %s.", APP_GETTERM, user->service_name, APP_SHELL,
244 str_error(rc));
245 fibril_mutex_lock(&user->guard);
246 user->task_finished = true;
247 user->srvs.aborted = true;
248 fibril_condvar_signal(&user->refcount_cv);
249 fibril_mutex_unlock(&user->guard);
250 return EOK;
251 }
252
253 fibril_mutex_lock(&user->guard);
254 user->task_id = task;
255 fibril_mutex_unlock(&user->guard);
256
257 task_exit_t task_exit;
258 int task_retval;
259 task_wait(&wait, &task_exit, &task_retval);
260 telnet_user_log(user, "%s terminated %s, exit code %d.", APP_GETTERM,
261 task_exit == TASK_EXIT_NORMAL ? "normally" : "unexpectedly",
262 task_retval);
263
264 /* Announce destruction. */
265 fibril_mutex_lock(&user->guard);
266 user->task_finished = true;
267 user->srvs.aborted = true;
268 fibril_condvar_signal(&user->refcount_cv);
269 fibril_mutex_unlock(&user->guard);
270
271 return EOK;
272}
273
274/** Tell whether given user can be destroyed (has no active clients).
275 *
276 * @param user The telnet user in question.
277 */
278static bool user_can_be_destroyed_no_lock(telnet_user_t *user)
279{
280 return user->task_finished && user->socket_closed &&
281 (user->locsrv_connection_count == 0);
282}
283
284/** Handle network connection.
285 *
286 * @param lst Listener
287 * @param conn Connection
288 */
289static void remcons_new_conn(tcp_listener_t *lst, tcp_conn_t *conn)
290{
291 telnet_user_t *user = telnet_user_create(conn);
292 assert(user);
293
294 con_srvs_init(&user->srvs);
295 user->srvs.ops = &con_ops;
296 user->srvs.sarg = user;
297 user->srvs.abort_timeout = 1000;
298
299 telnet_user_add(user);
300
301 int rc = loc_service_register(user->service_name, &user->service_id);
302 if (rc != EOK) {
303 telnet_user_error(user, "Unable to register %s with loc: %s.",
304 user->service_name, str_error(rc));
305 return;
306 }
307
308 telnet_user_log(user, "Service %s registerd with id %" PRIun ".",
309 user->service_name, user->service_id);
310
311 fid_t spawn_fibril = fibril_create(spawn_task_fibril, user);
312 assert(spawn_fibril);
313 fibril_add_ready(spawn_fibril);
314
315 /* Wait for all clients to exit. */
316 fibril_mutex_lock(&user->guard);
317 while (!user_can_be_destroyed_no_lock(user)) {
318 if (user->task_finished) {
319 user->conn = NULL;
320 user->socket_closed = true;
321 user->srvs.aborted = true;
322 continue;
323 } else if (user->socket_closed) {
324 if (user->task_id != 0) {
325 task_kill(user->task_id);
326 }
327 }
328 fibril_condvar_wait_timeout(&user->refcount_cv, &user->guard, 1000);
329 }
330 fibril_mutex_unlock(&user->guard);
331
332 rc = loc_service_unregister(user->service_id);
333 if (rc != EOK) {
334 telnet_user_error(user,
335 "Unable to unregister %s from loc: %s (ignored).",
336 user->service_name, str_error(rc));
337 }
338
339 telnet_user_log(user, "Destroying...");
340 telnet_user_destroy(user);
341}
342
343int main(int argc, char *argv[])
344{
345 int rc;
346 tcp_listener_t *lst;
347 tcp_t *tcp;
348 inet_ep_t ep;
349
350 async_set_fallback_port_handler(client_connection, NULL);
351 rc = loc_server_register(NAME);
352 if (rc != EOK) {
353 fprintf(stderr, "%s: Unable to register server\n", NAME);
354 return rc;
355 }
356
357 rc = tcp_create(&tcp);
358 if (rc != EOK) {
359 fprintf(stderr, "%s: Error initializing TCP.\n", NAME);
360 return rc;
361 }
362
363 inet_ep_init(&ep);
364 ep.port = 2223;
365
366 rc = tcp_listener_create(tcp, &ep, &listen_cb, NULL, &conn_cb, NULL,
367 &lst);
368 if (rc != EOK) {
369 fprintf(stderr, "%s: Error creating listener.\n", NAME);
370 return rc;
371 }
372
373 printf("%s: HelenOS Remote console service\n", NAME);
374 task_retval(0);
375 async_manager();
376
377 /* Not reached */
378 return 0;
379}
380
381/** @}
382 */
Note: See TracBrowser for help on using the repository browser.