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 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 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 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 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 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* 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 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 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 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 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