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 tarball for details.
14 /* "Standard" openvt's man page (we do not support all of this):
16 openvt [-c NUM] [-fsulv] [--] [command [args]]
18 Find the first available VT, and run command on it. Stdio is directed
19 to that VT. If no command is specified then $SHELL is used.
22 Use the given VT number, not the first free one.
24 Force opening a VT: don't try to check if VT is already in use.
26 Switch to the new VT when starting the command.
27 The VT of the new command will be made the new current VT.
29 Figure out the owner of the current VT, and run login as that user.
30 Suitable to be called by init. Shouldn't be used with -c or -l.
32 Make the command a login shell: a "-" is prepended to the argv[0]
33 when command is executed.
37 Wait for command to complete. If -w and -s are used together,
38 switch back to the controlling terminal when the command completes.
43 -l: not implemented, ignored
45 -ws: does NOT switch back
48 /* Helper: does this fd understand VT_xxx? */
49 static int not_vt_fd(int fd)
51 struct vt_stat vtstat;
52 return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */
55 /* Helper: get a fd suitable for VT_xxx */
56 static int get_vt_fd(void)
60 /* Do we, by chance, already have it? */
61 for (fd = 0; fd < 3; fd++)
64 /* _only_ O_NONBLOCK: ask for neither read not write perms */
65 fd = open(DEV_CONSOLE, O_NONBLOCK);
66 if (fd >= 0 && !not_vt_fd(fd))
68 bb_error_msg_and_die("can't find open VT");
71 static int find_free_vtno(void)
77 /*xfunc_error_retval = 3; - do we need compat? */
78 if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0)
79 bb_perror_msg_and_die("can't find open VT");
80 // Not really needed, grep for DAEMON_ONLY_SANITIZE
86 /* vfork scares gcc, it generates bigger code.
87 * Keep it away from main program.
88 * TODO: move to libbb; or adapt existing libbb's spawn().
90 static NOINLINE void vfork_child(char **argv)
94 /* Try to make this VT our controlling tty */
95 setsid(); /* lose old ctty */
96 ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
97 //bb_error_msg("our sid %d", getsid(0));
98 //bb_error_msg("our pgrp %d", getpgrp());
99 //bb_error_msg("VT's sid %d", tcgetsid(0));
100 //bb_error_msg("VT's pgrp %d", tcgetpgrp(0));
101 BB_EXECVP(argv[0], argv);
102 bb_perror_msg_and_die("exec %s", argv[0]);
106 int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
107 int openvt_main(int argc ATTRIBUTE_UNUSED, char **argv)
109 char vtname[sizeof(VC_FORMAT) + sizeof(int)*3];
110 struct vt_stat vtstat;
123 /* "+" - stop on first non-option */
124 flags = getopt32(argv, "+c:wslfv", &str_c);
128 /* Check for illegal vt number: < 1 or > 63 */
129 vtno = xatou_range(str_c, 1, 63);
131 vtno = find_free_vtno();
135 sprintf(vtname, VC_FORMAT, vtno);
136 /* (Try to) clean up stray open fds above fd 2 */
137 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL);
139 /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */
140 xopen(vtname, O_RDWR);
141 xioctl(0, VT_GETSTATE, &vtstat);
144 xioctl(0, VT_ACTIVATE, (void*)(ptrdiff_t)vtno);
145 xioctl(0, VT_WAITACTIVE, (void*)(ptrdiff_t)vtno);
150 argv[0] = getenv("SHELL");
152 argv[0] = (char *) DEFAULT_SHELL;
153 /*argv[1] = NULL; - already is */
156 xdup2(0, STDOUT_FILENO);
157 xdup2(0, STDERR_FILENO);
160 /* Handle -l (login shell) option */
161 const char *prog = argv[0];
163 argv[0] = xasprintf("-%s", argv[0]);
168 /* We have only one child, wait for it */
169 safe_waitpid(-1, NULL, 0); /* loops on EINTR */
171 xioctl(0, VT_ACTIVATE, (void*)(ptrdiff_t)(vtstat.v_active));
172 xioctl(0, VT_WAITACTIVE, (void*)(ptrdiff_t)(vtstat.v_active));
173 // Compat: even with -c N (try to) disallocate:
174 // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5
175 // openvt: could not deallocate console 9
176 xioctl(0, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno);