1 /* vi: set sw=4 ts=4: */
3 * openvt.c - open a vt to run a command.
5 * busyboxed by Quy Tonthat <quy@signal3.com>
6 * hacked by Tito <farmatito@tiscali.it>
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 //config:config OPENVT
11 //config: bool "openvt (7 kb)"
13 //config: select PLATFORM_LINUX
15 //config: This program is used to start a command on an unused
16 //config: virtual terminal.
18 //applet:IF_OPENVT(APPLET(openvt, BB_DIR_USR_BIN, BB_SUID_DROP))
20 //kbuild:lib-$(CONFIG_OPENVT) += openvt.o
22 //usage:#define openvt_trivial_usage
23 //usage: "[-c N] [-sw] [PROG ARGS]"
24 //usage:#define openvt_full_usage "\n\n"
25 //usage: "Start PROG on a new virtual terminal\n"
26 //usage: "\n -c N Use specified VT"
27 //usage: "\n -s Switch to the VT"
28 /* //usage: "\n -l Run PROG as login shell (by prepending '-')" */
29 //usage: "\n -w Wait for PROG to exit"
31 //usage:#define openvt_example_usage
32 //usage: "openvt 2 /bin/ash\n"
37 /* "Standard" openvt's man page (we do not support all of this):
39 openvt [-c NUM] [-fsulv] [--] [command [args]]
41 Find the first available VT, and run command on it. Stdio is directed
42 to that VT. If no command is specified then $SHELL is used.
45 Use the given VT number, not the first free one.
47 Force opening a VT: don't try to check if VT is already in use.
49 Switch to the new VT when starting the command.
50 The VT of the new command will be made the new current VT.
52 Figure out the owner of the current VT, and run login as that user.
53 Suitable to be called by init. Shouldn't be used with -c or -l.
55 Make the command a login shell: a "-" is prepended to the argv[0]
56 when command is executed.
60 Wait for command to complete. If -w and -s are used together,
61 switch back to the controlling terminal when the command completes.
66 -l: not implemented, ignored
68 -ws: does NOT switch back
71 /* Helper: does this fd understand VT_xxx? */
72 static int not_vt_fd(int fd)
74 struct vt_stat vtstat;
75 return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */
78 /* Helper: get a fd suitable for VT_xxx */
79 static int get_vt_fd(void)
83 /* Do we, by chance, already have it? */
84 for (fd = 0; fd < 3; fd++)
87 fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK);
88 if (fd >= 0 && !not_vt_fd(fd))
90 bb_error_msg_and_die("can't find open VT");
93 static int find_free_vtno(void)
99 /*xfunc_error_retval = 3; - do we need compat? */
100 if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0)
101 bb_perror_msg_and_die("can't find open VT");
102 // Not really needed, grep for DAEMON_CLOSE_EXTRA_FDS
108 /* vfork scares gcc, it generates bigger code.
109 * Keep it away from main program.
110 * TODO: move to libbb; or adapt existing libbb's spawn().
112 static NOINLINE void vfork_child(char **argv)
116 /* Try to make this VT our controlling tty */
117 setsid(); /* lose old ctty */
118 ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
119 //bb_error_msg("our sid %d", getsid(0));
120 //bb_error_msg("our pgrp %d", getpgrp());
121 //bb_error_msg("VT's sid %d", tcgetsid(0));
122 //bb_error_msg("VT's pgrp %d", tcgetpgrp(0));
123 BB_EXECVP_or_die(argv);
127 int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
128 int openvt_main(int argc UNUSED_PARAM, char **argv)
130 char vtname[sizeof(VC_FORMAT) + sizeof(int)*3];
131 struct vt_stat vtstat;
144 /* "+" - stop on first non-option */
145 flags = getopt32(argv, "+c:wslfv", &str_c);
149 /* Check for illegal vt number: < 1 or > 63 */
150 vtno = xatou_range(str_c, 1, 63);
152 vtno = find_free_vtno();
156 sprintf(vtname, VC_FORMAT, vtno);
157 /* (Try to) clean up stray open fds above fd 2 */
158 bb_daemon_helper(DAEMON_CLOSE_EXTRA_FDS);
160 /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */
161 xopen(vtname, O_RDWR);
162 xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat);
165 console_make_active(STDIN_FILENO, vtno);
170 argv[0] = (char *) get_shell_name();
171 /*argv[1] = NULL; - already is */
174 xdup2(STDIN_FILENO, STDOUT_FILENO);
175 xdup2(STDIN_FILENO, STDERR_FILENO);
179 /* Handle -l (login shell) option */
180 const char *prog = argv[0];
182 argv[0] = xasprintf("-%s", argv[0]);
188 /* We have only one child, wait for it */
189 safe_waitpid(-1, NULL, 0); /* loops on EINTR */
191 console_make_active(STDIN_FILENO, vtstat.v_active);
192 // Compat: even with -c N (try to) disallocate:
193 // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5
194 // openvt: could not deallocate console 9
195 xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno);