cut: code shrink
[busybox-git.git] / console-tools / openvt.c
blobc35617eb8d11fbb1bb1769350ffd7b46109dede1
1 /* vi: set sw=4 ts=4: */
2 /*
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.
9 */
10 //config:config OPENVT
11 //config: bool "openvt (7.4 kb)"
12 //config: default y
13 //config: help
14 //config: This program is used to start a command on an unused
15 //config: virtual terminal.
17 //applet:IF_OPENVT(APPLET(openvt, BB_DIR_USR_BIN, BB_SUID_DROP))
19 //kbuild:lib-$(CONFIG_OPENVT) += openvt.o
21 //usage:#define openvt_trivial_usage
22 //usage: "[-c N] [-sw] [PROG ARGS]"
23 //usage:#define openvt_full_usage "\n\n"
24 //usage: "Start PROG on a new virtual terminal\n"
25 //usage: "\n -c N Use specified VT"
26 //usage: "\n -s Switch to the VT"
27 /* //usage: "\n -l Run PROG as login shell (by prepending '-')" */
28 //usage: "\n -w Wait for PROG to exit"
29 //usage:
30 //usage:#define openvt_example_usage
31 //usage: "openvt 2 /bin/ash\n"
33 #include <linux/vt.h>
34 #include "libbb.h"
36 /* "Standard" openvt's man page (we do not support all of this):
38 openvt [-c NUM] [-fsulv] [--] [command [args]]
40 Find the first available VT, and run command on it. Stdio is directed
41 to that VT. If no command is specified then $SHELL is used.
43 -c NUM
44 Use the given VT number, not the first free one.
46 Force opening a VT: don't try to check if VT is already in use.
48 Switch to the new VT when starting the command.
49 The VT of the new command will be made the new current VT.
51 Figure out the owner of the current VT, and run login as that user.
52 Suitable to be called by init. Shouldn't be used with -c or -l.
54 Make the command a login shell: a "-" is prepended to the argv[0]
55 when command is executed.
57 Verbose.
59 Wait for command to complete. If -w and -s are used together,
60 switch back to the controlling terminal when the command completes.
62 bbox:
63 -u: not implemented
64 -f: always in effect
65 -l: not implemented, ignored
66 -v: ignored
67 -ws: does NOT switch back
70 /* Helper: does this fd understand VT_xxx? */
71 static int not_vt_fd(int fd)
73 struct vt_stat vtstat;
74 return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */
77 /* Helper: get a fd suitable for VT_xxx */
78 static int get_vt_fd(void)
80 int fd;
82 /* Do we, by chance, already have it? */
83 for (fd = 0; fd < 3; fd++)
84 if (!not_vt_fd(fd))
85 return fd;
86 fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK);
87 if (fd >= 0 && !not_vt_fd(fd))
88 return fd;
89 bb_simple_error_msg_and_die("can't find open VT");
92 static int find_free_vtno(void)
94 int vtno;
95 int fd = get_vt_fd();
97 errno = 0;
98 /*xfunc_error_retval = 3; - do we need compat? */
99 if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0)
100 bb_simple_perror_msg_and_die("can't find open VT");
101 // Not really needed, grep for DAEMON_CLOSE_EXTRA_FDS
102 // if (fd > 2)
103 // close(fd);
104 return vtno;
107 /* vfork scares gcc, it generates bigger code.
108 * Keep it away from main program.
109 * TODO: move to libbb; or adapt existing libbb's spawn().
111 static NOINLINE void vfork_child(char **argv)
113 if (vfork() == 0) {
114 /* CHILD */
115 /* Try to make this VT our controlling tty */
116 setsid(); /* lose old ctty */
117 ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
118 //bb_error_msg("our sid %d", getsid(0));
119 //bb_error_msg("our pgrp %d", getpgrp());
120 //bb_error_msg("VT's sid %d", tcgetsid(0));
121 //bb_error_msg("VT's pgrp %d", tcgetpgrp(0));
122 BB_EXECVP_or_die(argv);
126 int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
127 int openvt_main(int argc UNUSED_PARAM, char **argv)
129 char vtname[sizeof(VC_FORMAT) + sizeof(int)*3];
130 struct vt_stat vtstat;
131 char *str_c;
132 int vtno;
133 int flags;
134 enum {
135 OPT_c = (1 << 0),
136 OPT_w = (1 << 1),
137 OPT_s = (1 << 2),
138 OPT_l = (1 << 3),
139 OPT_f = (1 << 4),
140 OPT_v = (1 << 5),
143 /* "+" - stop on first non-option */
144 flags = getopt32(argv, "+c:wslfv", &str_c);
145 argv += optind;
147 if (flags & OPT_c) {
148 /* Check for illegal vt number: < 1 or > 63 */
149 vtno = xatou_range(str_c, 1, 63);
150 } else {
151 vtno = find_free_vtno();
154 /* Grab new VT */
155 sprintf(vtname, VC_FORMAT, vtno);
156 /* (Try to) clean up stray open fds above fd 2 */
157 bb_daemon_helper(DAEMON_CLOSE_EXTRA_FDS);
158 close(STDIN_FILENO);
159 /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */
160 xopen(vtname, O_RDWR);
161 xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat);
163 if (flags & OPT_s) {
164 console_make_active(STDIN_FILENO, vtno);
167 if (!argv[0]) {
168 argv--;
169 argv[0] = (char *) get_shell_name();
170 /*argv[1] = NULL; - already is */
173 xdup2(STDIN_FILENO, STDOUT_FILENO);
174 xdup2(STDIN_FILENO, STDERR_FILENO);
176 #ifdef BLOAT
178 /* Handle -l (login shell) option */
179 const char *prog = argv[0];
180 if (flags & OPT_l)
181 argv[0] = xasprintf("-%s", argv[0]);
183 #endif
185 vfork_child(argv);
186 if (flags & OPT_w) {
187 /* We have only one child, wait for it */
188 safe_waitpid(-1, NULL, 0); /* loops on EINTR */
189 if (flags & OPT_s) {
190 console_make_active(STDIN_FILENO, vtstat.v_active);
191 // Compat: even with -c N (try to) disallocate:
192 // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5
193 // openvt: could not deallocate console 9
194 xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno);
197 return EXIT_SUCCESS;