Changes for kernel and Busybox
[tomato.git] / release / src / router / busybox / console-tools / openvt.c
blobe523566926c01b2fd323bddf7beb83a8f4d72118
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 */
11 //usage:#define openvt_trivial_usage
12 //usage: "[-c N] [-sw] [PROG ARGS]"
13 //usage:#define openvt_full_usage "\n\n"
14 //usage: "Start PROG on a new virtual terminal\n"
15 //usage: "\n -c N Use specified VT"
16 //usage: "\n -s Switch to the VT"
17 /* //usage: "\n -l Run PROG as login shell (by prepending '-')" */
18 //usage: "\n -w Wait for PROG to exit"
19 //usage:
20 //usage:#define openvt_example_usage
21 //usage: "openvt 2 /bin/ash\n"
23 #include <linux/vt.h>
24 #include "libbb.h"
26 /* "Standard" openvt's man page (we do not support all of this):
28 openvt [-c NUM] [-fsulv] [--] [command [args]]
30 Find the first available VT, and run command on it. Stdio is directed
31 to that VT. If no command is specified then $SHELL is used.
33 -c NUM
34 Use the given VT number, not the first free one.
36 Force opening a VT: don't try to check if VT is already in use.
38 Switch to the new VT when starting the command.
39 The VT of the new command will be made the new current VT.
41 Figure out the owner of the current VT, and run login as that user.
42 Suitable to be called by init. Shouldn't be used with -c or -l.
44 Make the command a login shell: a "-" is prepended to the argv[0]
45 when command is executed.
47 Verbose.
49 Wait for command to complete. If -w and -s are used together,
50 switch back to the controlling terminal when the command completes.
52 bbox:
53 -u: not implemented
54 -f: always in effect
55 -l: not implemented, ignored
56 -v: ignored
57 -ws: does NOT switch back
60 /* Helper: does this fd understand VT_xxx? */
61 static int not_vt_fd(int fd)
63 struct vt_stat vtstat;
64 return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */
67 /* Helper: get a fd suitable for VT_xxx */
68 static int get_vt_fd(void)
70 int fd;
72 /* Do we, by chance, already have it? */
73 for (fd = 0; fd < 3; fd++)
74 if (!not_vt_fd(fd))
75 return fd;
76 fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK);
77 if (fd >= 0 && !not_vt_fd(fd))
78 return fd;
79 bb_error_msg_and_die("can't find open VT");
82 static int find_free_vtno(void)
84 int vtno;
85 int fd = get_vt_fd();
87 errno = 0;
88 /*xfunc_error_retval = 3; - do we need compat? */
89 if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0)
90 bb_perror_msg_and_die("can't find open VT");
91 // Not really needed, grep for DAEMON_ONLY_SANITIZE
92 // if (fd > 2)
93 // close(fd);
94 return vtno;
97 /* vfork scares gcc, it generates bigger code.
98 * Keep it away from main program.
99 * TODO: move to libbb; or adapt existing libbb's spawn().
101 static NOINLINE void vfork_child(char **argv)
103 if (vfork() == 0) {
104 /* CHILD */
105 /* Try to make this VT our controlling tty */
106 setsid(); /* lose old ctty */
107 ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
108 //bb_error_msg("our sid %d", getsid(0));
109 //bb_error_msg("our pgrp %d", getpgrp());
110 //bb_error_msg("VT's sid %d", tcgetsid(0));
111 //bb_error_msg("VT's pgrp %d", tcgetpgrp(0));
112 BB_EXECVP_or_die(argv);
116 int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
117 int openvt_main(int argc UNUSED_PARAM, char **argv)
119 char vtname[sizeof(VC_FORMAT) + sizeof(int)*3];
120 struct vt_stat vtstat;
121 char *str_c;
122 int vtno;
123 int flags;
124 enum {
125 OPT_c = (1 << 0),
126 OPT_w = (1 << 1),
127 OPT_s = (1 << 2),
128 OPT_l = (1 << 3),
129 OPT_f = (1 << 4),
130 OPT_v = (1 << 5),
133 /* "+" - stop on first non-option */
134 flags = getopt32(argv, "+c:wslfv", &str_c);
135 argv += optind;
137 if (flags & OPT_c) {
138 /* Check for illegal vt number: < 1 or > 63 */
139 vtno = xatou_range(str_c, 1, 63);
140 } else {
141 vtno = find_free_vtno();
144 /* Grab new VT */
145 sprintf(vtname, VC_FORMAT, vtno);
146 /* (Try to) clean up stray open fds above fd 2 */
147 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL);
148 close(STDIN_FILENO);
149 /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */
150 xopen(vtname, O_RDWR);
151 xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat);
153 if (flags & OPT_s) {
154 console_make_active(STDIN_FILENO, vtno);
157 if (!argv[0]) {
158 argv--;
159 argv[0] = (char *) get_shell_name();
160 /*argv[1] = NULL; - already is */
163 xdup2(STDIN_FILENO, STDOUT_FILENO);
164 xdup2(STDIN_FILENO, STDERR_FILENO);
166 #ifdef BLOAT
168 /* Handle -l (login shell) option */
169 const char *prog = argv[0];
170 if (flags & OPT_l)
171 argv[0] = xasprintf("-%s", argv[0]);
173 #endif
175 vfork_child(argv);
176 if (flags & OPT_w) {
177 /* We have only one child, wait for it */
178 safe_waitpid(-1, NULL, 0); /* loops on EINTR */
179 if (flags & OPT_s) {
180 console_make_active(STDIN_FILENO, vtstat.v_active);
181 // Compat: even with -c N (try to) disallocate:
182 // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5
183 // openvt: could not deallocate console 9
184 xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno);
187 return EXIT_SUCCESS;