X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fcttyhack.c;h=f9b59c26321906d9b7039fc977ebf80cbe8ca6b4;hb=ee0d4cd8cb64a4ef7f10a89c7d51145d44cc6d49;hp=bbe5149330a8b50a037970240f38fe0c7cde5a91;hpb=85c247161b9e1e7c71ebcb874ed7b6a23b6a5b50;p=oweals%2Fbusybox.git diff --git a/shell/cttyhack.c b/shell/cttyhack.c index bbe514933..f9b59c263 100644 --- a/shell/cttyhack.c +++ b/shell/cttyhack.c @@ -1,11 +1,74 @@ /* vi: set sw=4 ts=4: */ /* - * Licensed under GPLv2 - * * Copyright (c) 2007 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" +//applet:IF_CTTYHACK(APPLET(cttyhack, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o + +//config:config CTTYHACK +//config: bool "cttyhack" +//config: default y +//config: help +//config: One common problem reported on the mailing list is the "can't +//config: access tty; job control turned off" error message, which typically +//config: appears when one tries to use a shell with stdin/stdout on +//config: /dev/console. +//config: This device is special - it cannot be a controlling tty. +//config: +//config: The proper solution is to use the correct device instead of +//config: /dev/console. +//config: +//config: cttyhack provides a "quick and dirty" solution to this problem. +//config: It analyzes stdin with various ioctls, trying to determine whether +//config: it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line). +//config: On Linux it also checks sysfs for a pointer to the active console. +//config: If cttyhack is able to find the real console device, it closes +//config: stdin/out/err and reopens that device. +//config: Then it executes the given program. Opening the device will make +//config: that device a controlling tty. This may require cttyhack +//config: to be a session leader. +//config: +//config: Example for /etc/inittab (for busybox init): +//config: +//config: ::respawn:/bin/cttyhack /bin/sh +//config: +//config: Starting an interactive shell from boot shell script: +//config: +//config: setsid cttyhack sh +//config: +//config: Giving controlling tty to shell running with PID 1: +//config: +//config: # exec cttyhack sh +//config: +//config: Without cttyhack, you need to know exact tty name, +//config: and do something like this: +//config: +//config: # exec setsid sh -c 'exec sh /dev/tty1 2>&1' +//config: +//config: Starting getty on a controlling tty from a shell script: +//config: +//config: # getty 115200 $(cttyhack) + +//usage:#define cttyhack_trivial_usage +//usage: "[PROG ARGS]" +//usage:#define cttyhack_full_usage "\n\n" +//usage: "Give PROG a controlling tty if possible." +//usage: "\nExample for /etc/inittab (for busybox init):" +//usage: "\n ::respawn:/bin/cttyhack /bin/sh" +//usage: "\nGiving controlling tty to shell running with PID 1:" +//usage: "\n $ exec cttyhack sh" +//usage: "\nStarting interactive shell from boot shell script:" +//usage: "\n setsid cttyhack sh" + +#if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR +# warning cttyhack will not be able to detect a controlling tty on this system +#endif + /* From */ struct vt_stat { unsigned short v_active; /* active vt */ @@ -38,7 +101,7 @@ struct serial_struct { }; int cttyhack_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int cttyhack_main(int argc ATTRIBUTE_UNUSED, char **argv) +int cttyhack_main(int argc UNUSED_PARAM, char **argv) { int fd; char console[sizeof(int)*3 + 16]; @@ -48,30 +111,78 @@ int cttyhack_main(int argc ATTRIBUTE_UNUSED, char **argv) char paranoia[sizeof(struct serial_struct) * 3]; } u; - if (!*++argv) { - bb_show_usage(); - } - strcpy(console, "/dev/tty"); - if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) { - /* this is a serial console */ - sprintf(console + 8, "S%d", u.sr.line); - } else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) { - /* this is linux virtual tty */ - sprintf(console + 8, "S%d" + 1, u.vt.v_active); + fd = open(console, O_RDWR); + if (fd < 0) { + /* We don't have ctty (or don't have "/dev/tty" node...) */ + do { +#ifdef __linux__ + /* Note that this method does not use _stdin_. + * Thus, "cttyhack 0) { + char *last; + /* Found active console via sysfs (Linux 2.6.38+). + * It looks like "[tty0 ]ttyS0\n" so zap the newline: + */ + console[4 + s] = '\0'; + /* If there are multiple consoles, + * take the last one: + */ + last = strrchr(console + 5, ' '); + if (last) + overlapping_strcpy(console + 5, last + 1); + break; + } + + if (ioctl(0, VT_GETSTATE, &u.vt) == 0) { + /* this is linux virtual tty */ + sprintf(console + 8, "S%u" + 1, (int)u.vt.v_active); + break; + } +#endif +#ifdef TIOCGSERIAL + if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) { + /* this is a serial console; assuming it is named /dev/ttySn */ + sprintf(console + 8, "S%u", (int)u.sr.line); + break; + } +#endif + /* nope, could not find it */ + console[0] = '\0'; + } while (0); } - if (console[8]) { - fd = xopen(console, O_RDWR); - //bb_error_msg("switching to '%s'", console); - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - while (fd > 2) close(fd--); - /* Some other session may have it as ctty. Steal it from them */ - ioctl(0, TIOCSCTTY, 1); + argv++; + if (!argv[0]) { + if (!console[0]) + return EXIT_FAILURE; + puts(console); + return EXIT_SUCCESS; } - BB_EXECVP(argv[0], argv); - bb_perror_msg_and_die("cannot exec '%s'", argv[0]); + if (fd < 0) { + fd = open_or_warn(console, O_RDWR); + if (fd < 0) + goto ret; + } + //bb_error_msg("switching to '%s'", console); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + while (fd > 2) + close(fd--); + /* Some other session may have it as ctty, + * try to steal it from them: + */ + ioctl(0, TIOCSCTTY, 1); + ret: + BB_EXECVP_or_die(argv); }