1 /* vi: set sw=4 ts=4: */
3 * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
5 * Licensed under GPLv2, see file LICENSE in this source tree.
9 //applet:IF_CTTYHACK(APPLET(cttyhack, BB_DIR_BIN, BB_SUID_DROP))
11 //kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o
13 //config:config CTTYHACK
14 //config: bool "cttyhack"
17 //config: One common problem reported on the mailing list is the "can't
18 //config: access tty; job control turned off" error message, which typically
19 //config: appears when one tries to use a shell with stdin/stdout on
20 //config: /dev/console.
21 //config: This device is special - it cannot be a controlling tty.
23 //config: The proper solution is to use the correct device instead of
24 //config: /dev/console.
26 //config: cttyhack provides a "quick and dirty" solution to this problem.
27 //config: It analyzes stdin with various ioctls, trying to determine whether
28 //config: it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line).
29 //config: On Linux it also checks sysfs for a pointer to the active console.
30 //config: If cttyhack is able to find the real console device, it closes
31 //config: stdin/out/err and reopens that device.
32 //config: Then it executes the given program. Opening the device will make
33 //config: that device a controlling tty. This may require cttyhack
34 //config: to be a session leader.
36 //config: Example for /etc/inittab (for busybox init):
38 //config: ::respawn:/bin/cttyhack /bin/sh
40 //config: Starting an interactive shell from boot shell script:
42 //config: setsid cttyhack sh
44 //config: Giving controlling tty to shell running with PID 1:
46 //config: # exec cttyhack sh
48 //config: Without cttyhack, you need to know exact tty name,
49 //config: and do something like this:
51 //config: # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1'
54 //usage:#define cttyhack_trivial_usage
56 //usage:#define cttyhack_full_usage "\n\n"
57 //usage: "Give PROG a controlling tty if possible."
58 //usage: "\nExample for /etc/inittab (for busybox init):"
59 //usage: "\n ::respawn:/bin/cttyhack /bin/sh"
60 //usage: "\nGiving controlling tty to shell running with PID 1:"
61 //usage: "\n $ exec cttyhack sh"
62 //usage: "\nStarting interactive shell from boot shell script:"
63 //usage: "\n setsid cttyhack sh"
65 #if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR
66 # warning cttyhack will not be able to detect a controlling tty on this system
69 /* From <linux/vt.h> */
71 unsigned short v_active; /* active vt */
72 unsigned short v_signal; /* signal to send */
73 unsigned short v_state; /* vt bitmask */
75 enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
77 /* From <linux/serial.h> */
78 struct serial_struct {
87 unsigned short close_delay;
89 char reserved_char[1];
91 unsigned short closing_wait; /* time to wait before closing */
92 unsigned short closing_wait2; /* no longer used... */
93 unsigned char *iomem_base;
94 unsigned short iomem_reg_shift;
95 unsigned int port_high;
96 unsigned long iomap_base; /* cookie passed into ioremap */
100 int cttyhack_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
101 int cttyhack_main(int argc UNUSED_PARAM, char **argv)
104 char console[sizeof(int)*3 + 16];
107 struct serial_struct sr;
108 char paranoia[sizeof(struct serial_struct) * 3];
115 strcpy(console, "/dev/tty");
116 fd = open(console, O_RDWR);
118 /* We already have ctty, nothing to do */
121 /* We don't have ctty (or don't have "/dev/tty" node...) */
124 int s = open_read_close("/sys/class/tty/console/active",
125 console + 5, sizeof(console) - 5 - 1);
127 /* found active console via sysfs (Linux 2.6.38+) */
128 console[5 + s] = '\0';
132 if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
133 /* this is linux virtual tty */
134 sprintf(console + 8, "S%d" + 1, u.vt.v_active);
139 if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
140 /* this is a serial console, asuming it is named /dev/ttySn */
141 sprintf(console + 8, "S%d", u.sr.line);
145 /* nope, could not find it */
149 fd = xopen(console, O_RDWR);
150 //bb_error_msg("switching to '%s'", console);
156 /* Some other session may have it as ctty,
157 * steal it from them:
159 ioctl(0, TIOCSCTTY, 1);
163 BB_EXECVP_or_die(argv);