From 95891fc016c9911406cb826540873f0894bcbe8b Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Thu, 27 Mar 2008 16:26:35 +0000 Subject: [PATCH] openvt: fix gross mismatch between us and "standard" openvt. standard one even has different syntax! std: "openvt -c 12", we: "openvt 12" std: "openvt top", we: complain that "top" is not a number. openvt: implement -c -w -s (-l -f -v are also accepted but ingnored) openvt_main 188 343 +155 vfork_child - 67 +67 not_vt_fd - 23 +23 packed_usage 23932 23952 +20 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 2/0 up/down: 265/0) Total: 265 bytes text data bss dec hex filename 801344 641 7380 809365 c5995 busybox_old 801617 641 7380 809638 c5aa6 busybox_unstripped --- console-tools/openvt.c | 163 ++++++++++++++++++++++++++++++++++---- include/usage.h | 10 ++- networking/udhcp/script.c | 2 +- 3 files changed, 158 insertions(+), 17 deletions(-) diff --git a/console-tools/openvt.c b/console-tools/openvt.c index 39b985995..6f2aa19c4 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 #include "libbb.h" -int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int openvt_main(int argc, char **argv) +/* "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) { - char vtname[sizeof(VC_FORMAT) + 2]; + struct vt_stat vtstat; + return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */ +} - if (argc < 3) - bb_show_usage(); +/* Helper: get a fd suitable for VT_xxx */ +static int get_vt_fd(void) +{ + int fd; - /* check for illegal vt number: < 1 or > 63 */ - sprintf(vtname, VC_FORMAT, (int)xatou_range(argv[1], 1, 63)); + /* 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"); +} - bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); - /* grab new one */ +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 ATTRIBUTE_UNUSED, char **argv) +{ + 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), + }; + + /* "+" - stop on first non-option */ + flags = getopt32(argv, "+c:wslfv", &str_c); + argv += optind; + + if (flags & OPT_c) { + /* Check for illegal vt number: < 1 or > 63 */ + vtno = xatou_range(str_c, 1, 63); + } else { + vtno = find_free_vtno(); + } + + /* 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; } diff --git a/include/usage.h b/include/usage.h index f950a0a49..3be317bff 100644 --- a/include/usage.h +++ b/include/usage.h @@ -2818,9 +2818,15 @@ "to standard output. With no FILE or when FILE is -, read standard input." #define openvt_trivial_usage \ - "VTNUM COMMAND [ARGS...]" + "[-c NUM] [-sw] [COMMAND [ARGS]]" #define openvt_full_usage \ - "Start a command on a new virtual terminal" + "Start COMMAND on a new virtual terminal\n" \ + "\nOptions:" \ + "\n -c Use specified VT" \ + "\n -s Switch to the VT" \ +/* "\n -l Run COMMAND as login shell (by prepending '-')" */ \ + "\n -w Wait for COMMAND to exit" \ + #define openvt_example_usage \ "openvt 2 /bin/ash\n" diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c index 71f033328..4f4bc253a 100644 --- a/networking/udhcp/script.c +++ b/networking/udhcp/script.c @@ -230,7 +230,7 @@ void udhcp_run_script(struct dhcpMessage *packet, const char *name) /* exec script */ execle(client_config.script, client_config.script, name, NULL, envp); - bb_perror_msg_and_die("script %s failed", client_config.script); + bb_perror_msg_and_die("exec %s", client_config.script); } safe_waitpid(pid, NULL, 0); for (curr = envp; *curr; curr++)