1/* vi: set sw=4 ts=4: */ 2/* 3 * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com> 4 * 5 * Licensed under GPLv2, see file LICENSE in this source tree. 6 */ 7#include "libbb.h" 8 9//applet:IF_CTTYHACK(APPLET(cttyhack, _BB_DIR_BIN, _BB_SUID_DROP)) 10 11//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o 12 13//config:config CTTYHACK 14//config: bool "cttyhack" 15//config: default y 16//config: help 17//config: One common problem reported on the mailing list is "can't access tty; 18//config: job control turned off" error message which typically appears when 19//config: one tries to use shell with stdin/stdout opened to /dev/console. 20//config: This device is special - it cannot be a controlling tty. 21//config: 22//config: Proper solution is to use correct device instead of /dev/console. 23//config: 24//config: cttyhack provides "quick and dirty" solution to this problem. 25//config: It analyzes stdin with various ioctls, trying to determine whether 26//config: it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line). 27//config: If it detects one, it closes stdin/out/err and reopens that device. 28//config: Then it executes given program. Opening the device will make 29//config: that device a controlling tty. This may require cttyhack 30//config: to be a session leader. 31//config: 32//config: Example for /etc/inittab (for busybox init): 33//config: 34//config: ::respawn:/bin/cttyhack /bin/sh 35//config: 36//config: Starting an interactive shell from boot shell script: 37//config: 38//config: setsid cttyhack sh 39//config: 40//config: Giving controlling tty to shell running with PID 1: 41//config: 42//config: # exec cttyhack sh 43//config: 44//config: Without cttyhack, you need to know exact tty name, 45//config: and do something like this: 46//config: 47//config: # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1' 48//config: 49 50//usage:#define cttyhack_trivial_usage 51//usage: "PROG ARGS" 52//usage:#define cttyhack_full_usage "\n\n" 53//usage: "Give PROG a controlling tty if possible." 54//usage: "\nExample for /etc/inittab (for busybox init):" 55//usage: "\n ::respawn:/bin/cttyhack /bin/sh" 56//usage: "\nGiving controlling tty to shell running with PID 1:" 57//usage: "\n $ exec cttyhack sh" 58//usage: "\nStarting interactive shell from boot shell script:" 59//usage: "\n setsid cttyhack sh" 60 61#if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR 62# warning cttyhack will not be able to detect a controlling tty on this system 63#endif 64 65/* From <linux/vt.h> */ 66struct vt_stat { 67 unsigned short v_active; /* active vt */ 68 unsigned short v_signal; /* signal to send */ 69 unsigned short v_state; /* vt bitmask */ 70}; 71enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ 72 73/* From <linux/serial.h> */ 74struct serial_struct { 75 int type; 76 int line; 77 unsigned int port; 78 int irq; 79 int flags; 80 int xmit_fifo_size; 81 int custom_divisor; 82 int baud_base; 83 unsigned short close_delay; 84 char io_type; 85 char reserved_char[1]; 86 int hub6; 87 unsigned short closing_wait; /* time to wait before closing */ 88 unsigned short closing_wait2; /* no longer used... */ 89 unsigned char *iomem_base; 90 unsigned short iomem_reg_shift; 91 unsigned int port_high; 92 unsigned long iomap_base; /* cookie passed into ioremap */ 93 int reserved[1]; 94}; 95 96int cttyhack_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 97int cttyhack_main(int argc UNUSED_PARAM, char **argv) 98{ 99 int fd; 100 char console[sizeof(int)*3 + 16]; 101 union { 102 struct vt_stat vt; 103 struct serial_struct sr; 104 char paranoia[sizeof(struct serial_struct) * 3]; 105 } u; 106 107 if (!*++argv) { 108 bb_show_usage(); 109 } 110 111 strcpy(console, "/dev/tty"); 112 fd = open(console, O_RDWR); 113 if (fd >= 0) { 114 /* We already have ctty, nothing to do */ 115 close(fd); 116 } else { 117 /* We don't have ctty (or don't have "/dev/tty" node...) */ 118 if (0) {} 119#ifdef TIOCGSERIAL 120 else if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) { 121 /* this is a serial console */ 122 sprintf(console + 8, "S%d", u.sr.line); 123 } 124#endif 125#ifdef __linux__ 126 else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) { 127 /* this is linux virtual tty */ 128 sprintf(console + 8, "S%d" + 1, u.vt.v_active); 129 } 130#endif 131 if (console[8]) { 132 fd = xopen(console, O_RDWR); 133 //bb_error_msg("switching to '%s'", console); 134 dup2(fd, 0); 135 dup2(fd, 1); 136 dup2(fd, 2); 137 while (fd > 2) 138 close(fd--); 139 /* Some other session may have it as ctty, 140 * steal it from them: 141 */ 142 ioctl(0, TIOCSCTTY, 1); 143 } 144 } 145 146 BB_EXECVP_or_die(argv); 147} 148