diff options
Diffstat (limited to 'console-tools/openvt.c')
-rw-r--r-- | console-tools/openvt.c | 161 |
1 files changed, 148 insertions, 13 deletions
diff --git a/console-tools/openvt.c b/console-tools/openvt.c index 39b9859..6f2aa19 100644 --- a/console-tools/openvt.c +++ b/console-tools/openvt.c @@ -8,29 +8,164 @@ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ -/* getopt not needed */ - +#include <linux/vt.h> #include "libbb.h" +/* "Standard" openvt's man page (we do not support all of this): + +openvt [-c NUM] [-fsulv] [--] [command [args]] + +Find the first available VT, and run command on it. Stdio is directed +to that VT. If no command is specified then $SHELL is used. + +-c NUM + Use the given VT number, not the first free one. +-f + Force opening a VT: don't try to check if VT is already in use. +-s + Switch to the new VT when starting the command. + The VT of the new command will be made the new current VT. +-u + Figure out the owner of the current VT, and run login as that user. + Suitable to be called by init. Shouldn't be used with -c or -l. +-l + Make the command a login shell: a "-" is prepended to the argv[0] + when command is executed. +-v + Verbose. +-w + Wait for command to complete. If -w and -s are used together, + switch back to the controlling terminal when the command completes. + +bbox: +-u: not implemented +-f: always in effect +-l: not implemented, ignored +-v: ignored +-ws: does NOT switch back +*/ + +/* Helper: does this fd understand VT_xxx? */ +static int not_vt_fd(int fd) +{ + struct vt_stat vtstat; + return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */ +} + +/* Helper: get a fd suitable for VT_xxx */ +static int get_vt_fd(void) +{ + int fd; + + /* Do we, by chance, already have it? */ + for (fd = 0; fd < 3; fd++) + if (!not_vt_fd(fd)) + return fd; + /* _only_ O_NONBLOCK: ask for neither read not write perms */ + fd = open(DEV_CONSOLE, O_NONBLOCK); + if (fd >= 0 && !not_vt_fd(fd)) + return fd; + bb_error_msg_and_die("can't find open VT"); +} + +static int find_free_vtno(void) +{ + int vtno; + int fd = get_vt_fd(); + + errno = 0; + /*xfunc_error_retval = 3; - do we need compat? */ + if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0) + bb_perror_msg_and_die("can't find open VT"); +// Not really needed, grep for DAEMON_ONLY_SANITIZE +// if (fd > 2) +// close(fd); + return vtno; +} + +/* vfork scares gcc, it generates bigger code. + * Keep it away from main program. + * TODO: move to libbb; or adapt existing libbb's spawn(). + */ +static NOINLINE void vfork_child(char **argv) +{ + if (vfork() == 0) { + /* CHILD */ + /* Try to make this VT our controlling tty */ + setsid(); /* lose old ctty */ + ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */); + //bb_error_msg("our sid %d", getsid(0)); + //bb_error_msg("our pgrp %d", getpgrp()); + //bb_error_msg("VT's sid %d", tcgetsid(0)); + //bb_error_msg("VT's pgrp %d", tcgetpgrp(0)); + BB_EXECVP(argv[0], argv); + bb_perror_msg_and_die("exec %s", argv[0]); + } +} + int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int openvt_main(int argc, char **argv) +int openvt_main(int argc ATTRIBUTE_UNUSED, char **argv) { - char vtname[sizeof(VC_FORMAT) + 2]; + char vtname[sizeof(VC_FORMAT) + sizeof(int)*3]; + char *str_c; + int vtno; + int flags; + enum { + OPT_c = (1 << 0), + OPT_w = (1 << 1), + OPT_s = (1 << 2), + OPT_l = (1 << 3), + OPT_f = (1 << 4), + OPT_v = (1 << 5), + }; - if (argc < 3) - bb_show_usage(); + /* "+" - stop on first non-option */ + flags = getopt32(argv, "+c:wslfv", &str_c); + argv += optind; - /* check for illegal vt number: < 1 or > 63 */ - sprintf(vtname, VC_FORMAT, (int)xatou_range(argv[1], 1, 63)); + if (flags & OPT_c) { + /* Check for illegal vt number: < 1 or > 63 */ + vtno = xatou_range(str_c, 1, 63); + } else { + vtno = find_free_vtno(); + } - bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); - /* grab new one */ + /* Grab new VT */ + sprintf(vtname, VC_FORMAT, vtno); + /* (Try to) clean up stray open fds above fd 2 */ + bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL); close(0); + /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */ xopen(vtname, O_RDWR); + + if (flags & OPT_s) { + xioctl(0, VT_ACTIVATE, (void*)(ptrdiff_t)vtno); + xioctl(0, VT_WAITACTIVE, (void*)(ptrdiff_t)vtno); + } + + if (!argv[0]) { + argv--; + argv[0] = getenv("SHELL"); + if (!argv[0]) + argv[0] = (char *) DEFAULT_SHELL; + /*argv[1] = NULL; - already is */ + } + xdup2(0, STDOUT_FILENO); xdup2(0, STDERR_FILENO); - argv += 2; - BB_EXECVP(argv[0], argv); - _exit(1); +#ifdef BLOAT + /* Handle -l (login shell) option */ + const char *prog = argv[0]; + if (flags & OPT_l) + argv[0] = xasprintf("-%s", argv[0]); +#endif + + vfork_child(argv); + if (flags & OPT_w) { + wait(NULL); +// TODO: -ws handling should be here + } + + return EXIT_SUCCESS; } |