1 /*
2 * Copyright 2004-2010, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
8
9
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <termios.h>
17 #include <unistd.h>
18
19 #include <FindDirectory.h>
20 #include <image.h>
21 #include <InterfaceDefs.h>
22 #include <OS.h>
23
24 #include <keyboard_mouse_driver.h>
25 #include <Keymap.h>
26
27
28 struct console;
29
30 struct keyboard {
31 struct keyboard* next;
32 int device;
33 int target;
34 thread_id thread;
35 };
36
37 struct console {
38 int console_fd;
39 thread_id console_writer;
40
41 struct keyboard* keyboards;
42
43 int tty_master_fd;
44 int tty_slave_fd;
45 int tty_num;
46 };
47
48
49 struct console gConsole;
50
51
52 void
error(const char * message,...)53 error(const char* message, ...)
54 {
55 char buffer[2048];
56
57 va_list args;
58 va_start(args, message);
59
60 vsnprintf(buffer, sizeof(buffer), message, args);
61
62 va_end(args);
63
64 // put it out on stderr as well as to serial/syslog
65 fputs(buffer, stderr);
66 debug_printf("%s", buffer);
67 }
68
69
70 void
update_leds(int fd,uint32 modifiers)71 update_leds(int fd, uint32 modifiers)
72 {
73 char lockIO[3] = {0, 0, 0};
74
75 if ((modifiers & B_NUM_LOCK) != 0)
76 lockIO[0] = 1;
77 if ((modifiers & B_CAPS_LOCK) != 0)
78 lockIO[1] = 1;
79 if ((modifiers & B_SCROLL_LOCK) != 0)
80 lockIO[2] = 1;
81
82 ioctl(fd, KB_SET_LEDS, &lockIO, sizeof(lockIO));
83 }
84
85
86 static int32
keyboard_reader(void * arg)87 keyboard_reader(void* arg)
88 {
89 struct keyboard* keyboard = (struct keyboard*)arg;
90 uint8 activeDeadKey = 0;
91 uint32 modifiers = 0;
92
93 BKeymap keymap;
94 // Load current keymap from disk (we can't talk to the input server)
95 // TODO: find a better way (we shouldn't have to care about the on-disk
96 // location)
97 char path[PATH_MAX];
98 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, -1, false,
99 path, sizeof(path));
100 if (status == B_OK) {
101 strlcat(path, "/Key_map", sizeof(path));
102 status = keymap.SetTo(path);
103 }
104 if (status != B_OK)
105 keymap.SetToDefault();
106
107 for (;;) {
108 raw_key_info rawKeyInfo;
109 if (ioctl(keyboard->device, KB_READ, &rawKeyInfo,
110 sizeof(rawKeyInfo)) != 0)
111 break;
112
113 uint32 keycode = rawKeyInfo.keycode;
114 bool isKeyDown = rawKeyInfo.is_keydown;
115
116 if (keycode == 0)
117 continue;
118
119 uint32 changedModifiers = keymap.Modifier(keycode);
120 bool isLock = (changedModifiers
121 & (B_CAPS_LOCK | B_NUM_LOCK | B_SCROLL_LOCK)) != 0;
122 if (changedModifiers != 0 && (!isLock || isKeyDown)) {
123 uint32 oldModifiers = modifiers;
124
125 if ((isKeyDown && !isLock)
126 || (isKeyDown && !(modifiers & changedModifiers)))
127 modifiers |= changedModifiers;
128 else {
129 modifiers &= ~changedModifiers;
130
131 // ensure that we don't clear a combined B_*_KEY when still
132 // one of the individual B_{LEFT|RIGHT}_*_KEY is pressed
133 if (modifiers & (B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY))
134 modifiers |= B_SHIFT_KEY;
135 if (modifiers & (B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY))
136 modifiers |= B_COMMAND_KEY;
137 if (modifiers & (B_LEFT_CONTROL_KEY | B_RIGHT_CONTROL_KEY))
138 modifiers |= B_CONTROL_KEY;
139 if (modifiers & (B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY))
140 modifiers |= B_OPTION_KEY;
141 }
142
143 if (modifiers != oldModifiers) {
144 if (isLock)
145 update_leds(keyboard->device, modifiers);
146 }
147 }
148
149 uint8 newDeadKey = 0;
150 if (activeDeadKey == 0 || !isKeyDown)
151 newDeadKey = keymap.ActiveDeadKey(keycode, modifiers);
152
153 char* string = NULL;
154 int32 numBytes = 0;
155 if (newDeadKey == 0 && isKeyDown) {
156 keymap.GetChars(keycode, modifiers, activeDeadKey, &string,
157 &numBytes);
158 if (numBytes > 0)
159 write(keyboard->target, string, numBytes);
160
161 delete[] string;
162 }
163
164 if (newDeadKey == 0) {
165 if (isKeyDown && !modifiers && activeDeadKey != 0) {
166 // a dead key was completed
167 activeDeadKey = 0;
168 }
169 } else if (isKeyDown) {
170 // start of a dead key
171 activeDeadKey = newDeadKey;
172 }
173 }
174
175 return 0;
176 }
177
178
179 static int32
console_writer(void * arg)180 console_writer(void* arg)
181 {
182 struct console* con = (struct console*)arg;
183
184 for (;;) {
185 char buffer[1024];
186 ssize_t length = read(con->tty_master_fd, buffer, sizeof(buffer));
187 if (length < 0)
188 break;
189
190 write(con->console_fd, buffer, length);
191 }
192
193 return 0;
194 }
195
196
197 static void
stop_keyboards(struct console * con)198 stop_keyboards(struct console* con)
199 {
200 // close devices
201
202 for (struct keyboard* keyboard = con->keyboards; keyboard != NULL;
203 keyboard = keyboard->next) {
204 close(keyboard->device);
205 }
206
207 // wait for the threads
208
209 for (struct keyboard* keyboard = con->keyboards; keyboard != NULL;) {
210 struct keyboard* next = keyboard->next;
211 wait_for_thread(keyboard->thread, NULL);
212
213 delete keyboard;
214 keyboard = next;
215 }
216
217 con->keyboards = NULL;
218 }
219
220
221 /*! Opens the all keyboard drivers it finds starting from the given
222 location \a start that support the debugger extension.
223 */
224 static struct keyboard*
open_keyboards(int target,const char * start,struct keyboard * previous)225 open_keyboards(int target, const char* start, struct keyboard* previous)
226 {
227 // Wait for the directory to appear, if we're loaded early in boot
228 // it may take a while for it to appear while the drivers load.
229 DIR* dir;
230 int32 tries = 0;
231 while (true) {
232 dir = opendir(start);
233 if (dir != NULL)
234 break;
235 if(++tries == 10)
236 return NULL;
237 sleep(1);
238 }
239
240 struct keyboard* keyboard = previous;
241
242 while (true) {
243 dirent* entry = readdir(dir);
244 if (entry == NULL)
245 break;
246 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
247 continue;
248
249 char path[PATH_MAX];
250 strlcpy(path, start, sizeof(path));
251 strlcat(path, "/", sizeof(path));
252 strlcat(path, entry->d_name, sizeof(path));
253
254 struct stat stat;
255 if (::stat(path, &stat) != 0)
256 continue;
257
258 if (S_ISDIR(stat.st_mode)) {
259 keyboard = open_keyboards(target, path, keyboard);
260 continue;
261 }
262
263 // Try to open it as a device
264 int fd = open(path, O_RDONLY);
265 if (fd >= 0) {
266 // Turn on debugger mode
267 if (ioctl(fd, KB_SET_DEBUG_READER, NULL, 0) == 0) {
268 keyboard = new ::keyboard();
269 keyboard->device = fd;
270 keyboard->target = target;
271 keyboard->thread = spawn_thread(&keyboard_reader, path,
272 B_URGENT_DISPLAY_PRIORITY, keyboard);
273 if (keyboard->thread < 0) {
274 close(fd);
275 closedir(dir);
276 delete keyboard;
277 return NULL;
278 }
279
280 if (previous != NULL)
281 previous->next = keyboard;
282
283 resume_thread(keyboard->thread);
284 } else
285 close(fd);
286 }
287 }
288
289 closedir(dir);
290 return keyboard;
291 }
292
293
294 static int
start_console(struct console * con)295 start_console(struct console* con)
296 {
297 memset(con, 0, sizeof(struct console));
298 con->console_fd = -1;
299 con->tty_master_fd = -1;
300 con->tty_slave_fd = -1;
301 con->console_writer = -1;
302
303 con->console_fd = open("/dev/console", O_WRONLY);
304 if (con->console_fd < 0)
305 return -2;
306
307 DIR* dir = opendir("/dev/pt");
308 if (dir != NULL) {
309 struct dirent* entry;
310 char name[64];
311
312 while ((entry = readdir(dir)) != NULL) {
313 if (entry->d_name[0] == '.')
314 continue;
315
316 snprintf(name, sizeof(name), "/dev/pt/%s", entry->d_name);
317
318 con->tty_master_fd = open(name, O_RDWR);
319 if (con->tty_master_fd >= 0) {
320 snprintf(name, sizeof(name), "/dev/tt/%s", entry->d_name);
321
322 con->tty_slave_fd = open(name, O_RDWR);
323 if (con->tty_slave_fd < 0) {
324 error("Could not open tty %s: %s!\n", name,
325 strerror(errno));
326 close(con->tty_master_fd);
327 } else {
328 // set default mode
329 struct termios termios;
330 struct winsize size;
331
332 if (tcgetattr(con->tty_slave_fd, &termios) == 0) {
333 termios.c_iflag = ICRNL;
334 termios.c_oflag = OPOST | ONLCR;
335 termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHONL;
336
337 tcsetattr(con->tty_slave_fd, TCSANOW, &termios);
338 }
339
340 if (ioctl(con->console_fd, TIOCGWINSZ, &size,
341 sizeof(struct winsize)) == 0) {
342 // we got the window size from the console
343 ioctl(con->tty_slave_fd, TIOCSWINSZ, &size,
344 sizeof(struct winsize));
345 }
346 }
347 break;
348 }
349 }
350
351 setenv("TTY", name, true);
352 }
353
354 if (con->tty_master_fd < 0 || con->tty_slave_fd < 0)
355 return -3;
356
357 con->keyboards
358 = open_keyboards(con->tty_master_fd, "/dev/input/keyboard", NULL);
359 if (con->keyboards == NULL)
360 return -4;
361
362 con->console_writer = spawn_thread(&console_writer, "console writer",
363 B_URGENT_DISPLAY_PRIORITY, con);
364 if (con->console_writer < 0)
365 return -5;
366
367 resume_thread(con->console_writer);
368 setenv("TERM", "xterm", true);
369
370 return 0;
371 }
372
373
374 static void
stop_console(struct console * con)375 stop_console(struct console* con)
376 {
377 // close TTY FDs; this will also unblock the threads
378 close(con->tty_master_fd);
379 close(con->tty_slave_fd);
380
381 // close console and keyboards
382 close(con->console_fd);
383 wait_for_thread(con->console_writer, NULL);
384
385 stop_keyboards(con);
386 }
387
388
389 static pid_t
start_process(int argc,const char ** argv,struct console * con)390 start_process(int argc, const char** argv, struct console* con)
391 {
392 int savedInput = dup(0);
393 int savedOutput = dup(1);
394 int savedError = dup(2);
395
396 dup2(con->tty_slave_fd, 0);
397 dup2(con->tty_slave_fd, 1);
398 dup2(con->tty_slave_fd, 2);
399
400 pid_t pid = load_image(argc, argv, (const char**)environ);
401 resume_thread(pid);
402 setpgid(pid, 0);
403 tcsetpgrp(con->tty_slave_fd, pid);
404
405 dup2(savedInput, 0);
406 dup2(savedOutput, 1);
407 dup2(savedError, 2);
408 close(savedInput);
409 close(savedOutput);
410 close(savedError);
411
412 return pid;
413 }
414
415
416 int
main(int argc,char ** argv)417 main(int argc, char** argv)
418 {
419 // we're a session leader
420 setsid();
421
422 int err = start_console(&gConsole);
423 if (err < 0) {
424 error("consoled: error %d starting console.\n", err);
425 return err;
426 }
427
428 // move our stdin and stdout to the console
429 dup2(gConsole.tty_slave_fd, 0);
430 dup2(gConsole.tty_slave_fd, 1);
431 dup2(gConsole.tty_slave_fd, 2);
432
433 if (argc > 1) {
434 // a command was given: we run it only once
435
436 // get the command argument vector
437 int commandArgc = argc - 1;
438 const char** commandArgv = new const char*[commandArgc + 1];
439 for (int i = 0; i < commandArgc; i++)
440 commandArgv[i] = argv[i + 1];
441
442 commandArgv[commandArgc] = NULL;
443
444 // start the process
445 pid_t process = start_process(commandArgc, commandArgv, &gConsole);
446
447 status_t returnCode;
448 wait_for_thread(process, &returnCode);
449 } else {
450 // no command given: start a shell in an endless loop
451 for (;;) {
452 pid_t shellProcess;
453 status_t returnCode;
454 const char* shellArgv[] = { "/bin/sh", "--login", NULL };
455
456 shellProcess = start_process(2, shellArgv, &gConsole);
457
458 wait_for_thread(shellProcess, &returnCode);
459
460 puts("Restart shell");
461 }
462 }
463
464 stop_console(&gConsole);
465
466 return 0;
467 }
468
469