source: mainline/uspace/srv/hid/remcons/remcons.c@ 09ab0a9a

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

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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