source: mainline/uspace/srv/hid/remcons/remcons.c@ 6d5e378

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

cherrypick GUI implementation (originally by Petr Koupy), with several major changes

  • for character-oriented devices a new output server and output protocol was created based on the original fb server
  • DDF visualizer drivers are pixel-oriented only
  • console and compositor can coexist in the same build
  • terminal widget is self-sufficient, no strange console nesting is needed
  • Property mode set to 100644
File size: 10.0 KB
RevLine 
[21a9869]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>
[3806317]56#include "telnet.h"
[30d4706]57#include "user.h"
[21a9869]58
59#define APP_GETTERM "/app/getterm"
[03e0a244]60#define APP_SHELL "/app/bdsh"
[5576358]61
[3806317]62/** Telnet commands to force character mode
63 * (redundant to be on the safe side).
64 * See
[787b65b]65 * http://stackoverflow.com/questions/273261/force-telnet-user-into-character-mode
[3806317]66 * for discussion.
67 */
68static const telnet_cmd_t telnet_force_character_mode_command[] = {
69 TELNET_IAC, TELNET_WILL, TELNET_ECHO,
70 TELNET_IAC, TELNET_WILL, TELNET_SUPPRESS_GO_AHEAD,
71 TELNET_IAC, TELNET_WONT, TELNET_LINEMODE
72};
[3123d2a]73
[3806317]74static const size_t telnet_force_character_mode_command_count =
75 sizeof(telnet_force_character_mode_command) / sizeof(telnet_cmd_t);
76
[21a9869]77
[5923cf82]78/** Handling client requests (VFS and console interface).
79 *
80 * @param user Telnet user the requests belong to.
81 */
[787b65b]82static void client_connection_message_loop(telnet_user_t *user)
[21a9869]83{
84 while (true) {
85 ipc_call_t call;
[1870d81]86 ipc_callid_t callid = 0;
[99c2e9f3]87
88 /*
89 * The getterm task might terminate while we are here,
90 * waiting for a call. Also, the socket might be closed
91 * meanwhile.
92 * We want to detect this situation early, so we use a
93 * timeout variant of async_get_call().
94 */
[1870d81]95 while (callid == 0) {
96 callid = async_get_call_timeout(&call, 1000);
97
[99c2e9f3]98 if (telnet_user_is_zombie(user)) {
[1870d81]99 if (callid != 0) {
100 async_answer_0(callid, EINTR);
101 }
102 return;
103 }
104 }
[21a9869]105
106 if (!IPC_GET_IMETHOD(call)) {
107 return;
108 }
109
110 switch (IPC_GET_IMETHOD(call)) {
111 case CONSOLE_GET_SIZE:
112 async_answer_2(callid, EOK, 100, 1);
113 break;
114 case CONSOLE_GET_POS:
[178d6a3]115 fibril_mutex_lock(&user->guard);
116 async_answer_2(callid, EOK, user->cursor_x, 0);
117 fibril_mutex_unlock(&user->guard);
[21a9869]118 break;
119 case CONSOLE_GET_EVENT: {
[99c2e9f3]120 kbd_event_t event;
121 int rc = telnet_user_get_next_keyboard_event(user, &event);
122 if (rc != EOK) {
123 /* Silently ignore. */
124 async_answer_0(callid, EOK);
125 break;
[21a9869]126 }
[99c2e9f3]127 async_answer_4(callid, EOK, event.type, event.key, event.mods, event.c);
[21a9869]128 break;
129 }
[178d6a3]130 case CONSOLE_GOTO: {
131 int new_x = IPC_GET_ARG1(call);
132 telnet_user_update_cursor_x(user, new_x);
[21a9869]133 async_answer_0(callid, ENOTSUP);
134 break;
[178d6a3]135 }
[21a9869]136 case VFS_OUT_READ:
137 async_answer_0(callid, ENOTSUP);
138 break;
139 case VFS_OUT_WRITE: {
[178d6a3]140 uint8_t *buf;
[21a9869]141 size_t size;
142 int rc = async_data_write_accept((void **)&buf, false, 0, 0, 0, &size);
143
144 if (rc != EOK) {
145 async_answer_0(callid, rc);
146 break;
147 }
[99c2e9f3]148
[178d6a3]149 rc = telnet_user_send_data(user, buf, size);
[21a9869]150 free(buf);
151
152 if (rc != EOK) {
153 async_answer_0(callid, rc);
154 break;
155 }
156
157 async_answer_1(callid, EOK, size);
158 break;
159 }
160 case VFS_OUT_SYNC:
161 async_answer_0(callid, EOK);
162 break;
163 case CONSOLE_CLEAR:
164 async_answer_0(callid, EOK);
165 break;
166
167 case CONSOLE_GET_COLOR_CAP:
168 async_answer_1(callid, EOK, CONSOLE_CAP_NONE);
169 break;
170 case CONSOLE_SET_STYLE:
171 async_answer_0(callid, ENOTSUP);
172 break;
173 case CONSOLE_SET_COLOR:
174 async_answer_0(callid, ENOTSUP);
175 break;
176 case CONSOLE_SET_RGB_COLOR:
177 async_answer_0(callid, ENOTSUP);
178 break;
179
180 case CONSOLE_CURSOR_VISIBILITY:
181 async_answer_0(callid, ENOTSUP);
182 break;
183
184 default:
185 async_answer_0(callid, EINVAL);
186 break;
187 }
188 }
189}
190
[5923cf82]191/** Callback when client connects to a telnet terminal. */
[6f7cd5d]192static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
193{
[787b65b]194 /* Find the user. */
195 telnet_user_t *user = telnet_user_get_for_client_connection(IPC_GET_ARG1(*icall));
196 if (user == NULL) {
[6f7cd5d]197 async_answer_0(iid, ENOENT);
198 return;
199 }
[99c2e9f3]200 async_answer_0(iid, EOK);
[6f7cd5d]201
[d545d03]202 telnet_user_log(user, "New client connected (%" PRIxn").", iid);
[6f7cd5d]203
[3806317]204 /* Force character mode. */
[787b65b]205 send(user->socket, (void *)telnet_force_character_mode_command,
[3806317]206 telnet_force_character_mode_command_count, 0);
[6f7cd5d]207
[99c2e9f3]208 /* Handle messages. */
[787b65b]209 client_connection_message_loop(user);
[6f7cd5d]210
[8562724]211 telnet_user_notify_client_disconnected(user);
[d545d03]212 telnet_user_log(user, "Client disconnected (%" PRIxn").", iid);
[6f7cd5d]213}
214
[5923cf82]215/** Fibril for spawning the task running after user connects.
216 *
217 * @param arg Corresponding @c telnet_user_t structure.
218 */
[7c2bb2c]219static int spawn_task_fibril(void *arg)
[21a9869]220{
[787b65b]221 telnet_user_t *user = arg;
[7c2bb2c]222 int rc;
[21a9869]223
224 char term[LOC_NAME_MAXLEN];
[787b65b]225 snprintf(term, LOC_NAME_MAXLEN, "%s/%s", "/loc", user->service_name);
[21a9869]226
227 task_id_t task;
[03e0a244]228 rc = task_spawnl(&task, APP_GETTERM, APP_GETTERM, "-w", term, APP_SHELL, NULL);
[21a9869]229 if (rc != EOK) {
[03e0a244]230 telnet_user_error(user, "Spawning `%s -w %s %s' failed: %s.",
231 APP_GETTERM, term, APP_SHELL, str_error(rc));
[261bbdc]232 fibril_mutex_lock(&user->guard);
[787b65b]233 user->task_finished = true;
234 fibril_condvar_signal(&user->refcount_cv);
[261bbdc]235 fibril_mutex_unlock(&user->guard);
[21a9869]236 return EOK;
237 }
238
[261bbdc]239 fibril_mutex_lock(&user->guard);
[787b65b]240 user->task_id = task;
[261bbdc]241 fibril_mutex_unlock(&user->guard);
[1870d81]242
[21a9869]243 task_exit_t task_exit;
244 int task_retval;
245 task_wait(task, &task_exit, &task_retval);
[d545d03]246 telnet_user_log(user, "%s terminated %s, exit code %d.", APP_GETTERM,
247 task_exit == TASK_EXIT_NORMAL ? "normally" : "unexpectedly",
248 task_retval);
[21a9869]249
[7c2bb2c]250 /* Announce destruction. */
[261bbdc]251 fibril_mutex_lock(&user->guard);
[787b65b]252 user->task_finished = true;
253 fibril_condvar_signal(&user->refcount_cv);
[261bbdc]254 fibril_mutex_unlock(&user->guard);
[7c2bb2c]255
256 return EOK;
257}
258
[5923cf82]259/** Tell whether given user can be destroyed (has no active clients).
260 *
261 * @param user The telnet user in question.
262 */
[787b65b]263static bool user_can_be_destroyed_no_lock(telnet_user_t *user)
[1870d81]264{
[787b65b]265 return user->task_finished && user->socket_closed &&
266 (user->locsrv_connection_count == 0);
[1870d81]267}
[7c2bb2c]268
[5923cf82]269/** Fibril for each accepted socket.
270 *
271 * @param arg Corresponding @c telnet_user_t structure.
272 */
[787b65b]273static int network_user_fibril(void *arg)
[7c2bb2c]274{
[787b65b]275 telnet_user_t *user = arg;
[7c2bb2c]276
[3123d2a]277 int rc = loc_service_register(user->service_name, &user->service_id);
[6f7cd5d]278 if (rc != EOK) {
[d545d03]279 telnet_user_error(user, "Unable to register %s with loc: %s.",
280 user->service_name, str_error(rc));
[7c2bb2c]281 return EOK;
[6f7cd5d]282 }
[d545d03]283
284 telnet_user_log(user, "Service %s registerd with id %" PRIun ".",
285 user->service_name, user->service_id);
[3123d2a]286
[787b65b]287 fid_t spawn_fibril = fibril_create(spawn_task_fibril, user);
[7c2bb2c]288 assert(spawn_fibril);
289 fibril_add_ready(spawn_fibril);
[3123d2a]290
[6f7cd5d]291 /* Wait for all clients to exit. */
[261bbdc]292 fibril_mutex_lock(&user->guard);
[787b65b]293 while (!user_can_be_destroyed_no_lock(user)) {
294 if (user->task_finished) {
295 closesocket(user->socket);
296 user->socket_closed = true;
[1870d81]297 continue;
[787b65b]298 } else if (user->socket_closed) {
299 if (user->task_id != 0) {
300 task_kill(user->task_id);
[1870d81]301 }
302 }
[261bbdc]303 fibril_condvar_wait_timeout(&user->refcount_cv, &user->guard, 1000);
[6f7cd5d]304 }
[261bbdc]305 fibril_mutex_unlock(&user->guard);
[3123d2a]306
[787b65b]307 rc = loc_service_unregister(user->service_id);
[7c2bb2c]308 if (rc != EOK) {
[d545d03]309 telnet_user_error(user,
310 "Unable to unregister %s from loc: %s (ignored).",
311 user->service_name, str_error(rc));
[7c2bb2c]312 }
313
[d545d03]314 telnet_user_log(user, "Destroying...");
[787b65b]315 telnet_user_destroy(user);
[5576358]316
[21a9869]317 return EOK;
318}
319
320int main(int argc, char *argv[])
321{
322 int port = 2223;
323
[a1347a7]324 async_set_client_connection(client_connection);
325 int rc = loc_server_register(NAME);
[3123d2a]326 if (rc != EOK) {
327 fprintf(stderr, "%s: Unable to register server\n", NAME);
328 return rc;
[21a9869]329 }
[3123d2a]330
[21a9869]331 struct sockaddr_in addr;
[3123d2a]332
[21a9869]333 addr.sin_family = AF_INET;
334 addr.sin_port = htons(port);
[3123d2a]335
[21a9869]336 rc = inet_pton(AF_INET, "127.0.0.1", (void *)
337 &addr.sin_addr.s_addr);
338 if (rc != EOK) {
[d545d03]339 fprintf(stderr, "Error parsing network address: %s.\n",
[21a9869]340 str_error(rc));
341 return 2;
342 }
343
344 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
345 if (listen_sd < 0) {
[d545d03]346 fprintf(stderr, "Error creating listening socket: %s.\n",
[21a9869]347 str_error(listen_sd));
348 return 3;
349 }
350
351 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
352 if (rc != EOK) {
[d545d03]353 fprintf(stderr, "Error binding socket: %s.\n",
[21a9869]354 str_error(rc));
355 return 4;
356 }
357
358 rc = listen(listen_sd, BACKLOG_SIZE);
359 if (rc != EOK) {
[d545d03]360 fprintf(stderr, "listen() failed: %s.\n", str_error(rc));
[21a9869]361 return 5;
362 }
363
364 printf("%s: HelenOS Remote console service\n", NAME);
[6d5e378]365 task_retval(0);
[21a9869]366
367 while (true) {
368 struct sockaddr_in raddr;
369 socklen_t raddr_len = sizeof(raddr);
370 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
371 &raddr_len);
372
373 if (conn_sd < 0) {
[d545d03]374 fprintf(stderr, "accept() failed: %s.\n",
375 str_error(rc));
[21a9869]376 continue;
377 }
378
[787b65b]379 telnet_user_t *user = telnet_user_create(conn_sd);
380 assert(user);
[21a9869]381
[787b65b]382 fid_t fid = fibril_create(network_user_fibril, user);
[21a9869]383 assert(fid);
384 fibril_add_ready(fid);
385 }
386
387 return 0;
388}
389
390/** @}
391 */
Note: See TracBrowser for help on using the repository browser.