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)
No binary (build failed)
—
(log yok)
| @@ -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; |
| } | |
| @@ -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" | |
| @@ -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++) | |