source: mainline/uspace/srv/hid/remcons/remcons.c@ 865b981

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 865b981 was 1c635d6, checked in by Martin Sucha <sucha14@…>, 12 years ago

Do not hold a task's return value after it has disconnected.

Holding the task's return value meant that if nobody waited
for task's result, it polluted NS's memory. This was apparently
done because of a race between spawning a task and waiting for it.

We solve this problem in another way: ns discards the return value
as soon as the task disconnects from it. This typically happens
when the task finishes its execution. In order to avoid the race,
we send the wait request to ns while spawning the task (i.e. when
we talk to the loader), but before we allow the loaded program
to run.

Fixes #132

  • Property mode set to 100644
File size: 10.1 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 <net/in.h>
47#include <net/inet.h>
48#include <net/socket.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 telnet_user_t *srv_to_user(con_srv_t *srv)
102{
103 return srv->srvs->sarg;
104}
105
106static int remcons_open(con_srvs_t *srvs, con_srv_t *srv)
107{
108 telnet_user_t *user = srv_to_user(srv);
109
110 telnet_user_log(user, "New client connected (%p).", srv);
111
112 /* Force character mode. */
113 send(user->socket, (void *)telnet_force_character_mode_command,
114 telnet_force_character_mode_command_count, 0);
115
116 return EOK;
117}
118
119static int remcons_close(con_srv_t *srv)
120{
121 telnet_user_t *user = srv_to_user(srv);
122
123 telnet_user_notify_client_disconnected(user);
124 telnet_user_log(user, "Client disconnected (%p).", srv);
125
126 return EOK;
127}
128
129static int remcons_write(con_srv_t *srv, void *data, size_t size)
130{
131 telnet_user_t *user = srv_to_user(srv);
132 int rc;
133
134 rc = telnet_user_send_data(user, data, size);
135 if (rc != EOK)
136 return rc;
137
138 return size;
139}
140
141static void remcons_sync(con_srv_t *srv)
142{
143 (void) srv;
144}
145
146static void remcons_clear(con_srv_t *srv)
147{
148 (void) srv;
149}
150
151static void remcons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
152{
153 telnet_user_t *user = srv_to_user(srv);
154
155 telnet_user_update_cursor_x(user, col);
156}
157
158static int remcons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
159{
160 telnet_user_t *user = srv_to_user(srv);
161
162 *col = user->cursor_x;
163 *row = 0;
164
165 return EOK;
166}
167
168static int remcons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
169{
170 (void) srv;
171
172 *cols = 100;
173 *rows = 1;
174
175 return EOK;
176}
177
178static int remcons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
179{
180 (void) srv;
181 *ccaps = CONSOLE_CAP_NONE;
182
183 return EOK;
184}
185
186static int remcons_get_event(con_srv_t *srv, cons_event_t *event)
187{
188 telnet_user_t *user = srv_to_user(srv);
189 kbd_event_t kevent;
190 int rc;
191
192 rc = telnet_user_get_next_keyboard_event(user, &kevent);
193 if (rc != EOK) {
194 /* XXX What? */
195 memset(event, 0, sizeof(*event));
196 return EOK;
197 }
198
199 event->type = CEV_KEY;
200 event->ev.key = kevent;
201
202 return EOK;
203}
204
205/** Callback when client connects to a telnet terminal. */
206static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
207{
208 /* Find the user. */
209 telnet_user_t *user = telnet_user_get_for_client_connection(IPC_GET_ARG1(*icall));
210 if (user == NULL) {
211 async_answer_0(iid, ENOENT);
212 return;
213 }
214
215 /* Handle messages. */
216 con_conn(iid, icall, &user->srvs);
217}
218
219/** Fibril for spawning the task running after user connects.
220 *
221 * @param arg Corresponding @c telnet_user_t structure.
222 */
223static int spawn_task_fibril(void *arg)
224{
225 telnet_user_t *user = arg;
226
227 task_id_t task;
228 task_wait_t wait;
229 int rc = task_spawnl(&task, &wait, APP_GETTERM, APP_GETTERM, user->service_name,
230 "/loc", "--msg", "--", APP_SHELL, NULL);
231 if (rc != EOK) {
232 telnet_user_error(user, "Spawning `%s %s /loc --msg -- %s' "
233 "failed: %s.", APP_GETTERM, user->service_name, APP_SHELL,
234 str_error(rc));
235 fibril_mutex_lock(&user->guard);
236 user->task_finished = true;
237 user->srvs.aborted = true;
238 fibril_condvar_signal(&user->refcount_cv);
239 fibril_mutex_unlock(&user->guard);
240 return EOK;
241 }
242
243 fibril_mutex_lock(&user->guard);
244 user->task_id = task;
245 fibril_mutex_unlock(&user->guard);
246
247 task_exit_t task_exit;
248 int task_retval;
249 task_wait(&wait, &task_exit, &task_retval);
250 telnet_user_log(user, "%s terminated %s, exit code %d.", APP_GETTERM,
251 task_exit == TASK_EXIT_NORMAL ? "normally" : "unexpectedly",
252 task_retval);
253
254 /* Announce destruction. */
255 fibril_mutex_lock(&user->guard);
256 user->task_finished = true;
257 user->srvs.aborted = true;
258 fibril_condvar_signal(&user->refcount_cv);
259 fibril_mutex_unlock(&user->guard);
260
261 return EOK;
262}
263
264/** Tell whether given user can be destroyed (has no active clients).
265 *
266 * @param user The telnet user in question.
267 */
268static bool user_can_be_destroyed_no_lock(telnet_user_t *user)
269{
270 return user->task_finished && user->socket_closed &&
271 (user->locsrv_connection_count == 0);
272}
273
274/** Fibril for each accepted socket.
275 *
276 * @param arg Corresponding @c telnet_user_t structure.
277 */
278static int network_user_fibril(void *arg)
279{
280 telnet_user_t *user = arg;
281
282 int rc = loc_service_register(user->service_name, &user->service_id);
283 if (rc != EOK) {
284 telnet_user_error(user, "Unable to register %s with loc: %s.",
285 user->service_name, str_error(rc));
286 return EOK;
287 }
288
289 telnet_user_log(user, "Service %s registerd with id %" PRIun ".",
290 user->service_name, user->service_id);
291
292 fid_t spawn_fibril = fibril_create(spawn_task_fibril, user);
293 assert(spawn_fibril);
294 fibril_add_ready(spawn_fibril);
295
296 /* Wait for all clients to exit. */
297 fibril_mutex_lock(&user->guard);
298 while (!user_can_be_destroyed_no_lock(user)) {
299 if (user->task_finished) {
300 closesocket(user->socket);
301 user->socket_closed = true;
302 user->srvs.aborted = true;
303 continue;
304 } else if (user->socket_closed) {
305 if (user->task_id != 0) {
306 task_kill(user->task_id);
307 }
308 }
309 fibril_condvar_wait_timeout(&user->refcount_cv, &user->guard, 1000);
310 }
311 fibril_mutex_unlock(&user->guard);
312
313 rc = loc_service_unregister(user->service_id);
314 if (rc != EOK) {
315 telnet_user_error(user,
316 "Unable to unregister %s from loc: %s (ignored).",
317 user->service_name, str_error(rc));
318 }
319
320 telnet_user_log(user, "Destroying...");
321 telnet_user_destroy(user);
322
323 return EOK;
324}
325
326int main(int argc, char *argv[])
327{
328 int port = 2223;
329
330 async_set_client_connection(client_connection);
331 int rc = loc_server_register(NAME);
332 if (rc != EOK) {
333 fprintf(stderr, "%s: Unable to register server\n", NAME);
334 return rc;
335 }
336
337 struct sockaddr_in addr;
338
339 addr.sin_family = AF_INET;
340 addr.sin_port = htons(port);
341
342 rc = inet_pton(AF_INET, "127.0.0.1", (void *)
343 &addr.sin_addr.s_addr);
344 if (rc != EOK) {
345 fprintf(stderr, "Error parsing network address: %s.\n",
346 str_error(rc));
347 return 2;
348 }
349
350 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
351 if (listen_sd < 0) {
352 fprintf(stderr, "Error creating listening socket: %s.\n",
353 str_error(listen_sd));
354 return 3;
355 }
356
357 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
358 if (rc != EOK) {
359 fprintf(stderr, "Error binding socket: %s.\n",
360 str_error(rc));
361 return 4;
362 }
363
364 rc = listen(listen_sd, BACKLOG_SIZE);
365 if (rc != EOK) {
366 fprintf(stderr, "listen() failed: %s.\n", str_error(rc));
367 return 5;
368 }
369
370 printf("%s: HelenOS Remote console service\n", NAME);
371 task_retval(0);
372
373 while (true) {
374 struct sockaddr_in raddr;
375 socklen_t raddr_len = sizeof(raddr);
376 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
377 &raddr_len);
378
379 if (conn_sd < 0) {
380 fprintf(stderr, "accept() failed: %s.\n",
381 str_error(rc));
382 continue;
383 }
384
385 telnet_user_t *user = telnet_user_create(conn_sd);
386 assert(user);
387
388 con_srvs_init(&user->srvs);
389 user->srvs.ops = &con_ops;
390 user->srvs.sarg = user;
391 user->srvs.abort_timeout = 1000;
392
393 telnet_user_add(user);
394
395 fid_t fid = fibril_create(network_user_fibril, user);
396 assert(fid);
397 fibril_add_ready(fid);
398 }
399
400 return 0;
401}
402
403/** @}
404 */
Note: See TracBrowser for help on using the repository browser.