/* openvt.c - Run a program on a new VT
*
* Copyright 2014 Vivek Kumar Bhagat <vivek.bhagat89@gmail.com>
*
* No Standard
USE_OPENVT(NEWTOY(openvt, "c#<1>63sw", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
USE_DEALLOCVT(NEWTOY(deallocvt, ">1", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NEEDROOT))
config OPENVT
bool "openvt"
default n
depends on TOYBOX_FORK
help
usage: openvt [-c N] [-sw] [command [command_options]]
start a program on a new virtual terminal (VT)
-c N Use VT N
-s Switch to new VT
-w Wait for command to exit
if -sw used together, switch back to originating VT when command completes
config DEALLOCVT
bool "deallocvt"
default n
help
usage: deallocvt [N]
Deallocate unused virtual terminal /dev/ttyN, or all unused consoles.
*/
#define FOR_openvt
#include "toys.h"
#include <linux/vt.h>
#include <linux/kd.h>
GLOBALS(
unsigned long vt_num;
)
int open_console(void)
{
char arg, *console_name[] = {"/dev/tty", "/dev/tty0", "/dev/console"};
int i, fd;
for (i = 0; i < ARRAY_LEN(console_name); i++) {
fd = open(console_name[i], O_RDWR);
if (fd >= 0) {
arg = 0;
if (!ioctl(fd, KDGKBTYPE, &arg)) return fd;
close(fd);
}
}
/* check std fd 0, 1 and 2 */
for (fd = 0; fd < 3; fd++) {
arg = 0;
if (0 == ioctl(fd, KDGKBTYPE, &arg)) return fd;
}
return -1;
}
int xvtnum(int fd)
{
int ret;
ret = ioctl(fd, VT_OPENQRY, (int *)&TT.vt_num);
if (ret != 0 || TT.vt_num <= 0) perror_exit("can't find open VT");
return TT.vt_num;
}
void openvt_main(void)
{
int fd, vt_fd, ret = 0;
struct vt_stat vstate;
pid_t pid;
if (!(toys.optflags & FLAG_c)) {
// check if fd 0,1 or 2 is already opened
for (fd = 0; fd < 3; fd++)
if (!ioctl(fd, VT_GETSTATE, &vstate)) {
ret = xvtnum(fd);
break;
}
// find VT number using /dev/console
if (!ret) {
fd = xopen("/dev/console", O_RDONLY | O_NONBLOCK);
xioctl(fd, VT_GETSTATE, &vstate);
xvtnum(fd);
}
}
sprintf(toybuf, "/dev/tty%lu", TT.vt_num);
fd = open_console();
xioctl(fd, VT_GETSTATE, &vstate);
close(0); //new vt becomes stdin
vt_fd = xopen_stdio(toybuf, O_RDWR);
if (toys.optflags & FLAG_s) {
ioctl(vt_fd, VT_ACTIVATE, TT.vt_num);
ioctl(vt_fd, VT_WAITACTIVE, TT.vt_num);
}
close(1);
close(2);
dup2(vt_fd, 1);
dup2(vt_fd, 2);
while (vt_fd > 2)
close(vt_fd--);
pid = xfork();
if (!pid) {
setsid();
ioctl(vt_fd, TIOCSCTTY, 0);
xexec(toys.optargs);
}
if (toys.optflags & FLAG_w) {
while (-1 == waitpid(pid, NULL, 0) && errno == EINTR)
;
if (toys.optflags & FLAG_s) {
ioctl(fd, VT_ACTIVATE, vstate.v_active);
ioctl(fd, VT_WAITACTIVE, vstate.v_active);
//check why deallocate isn't working here
xioctl(fd, VT_DISALLOCATE, (void *)(ptrdiff_t)TT.vt_num);
}
}
}
void deallocvt_main(void)
{
long vt_num = 0; // 0 deallocates all unused consoles
int fd;
if (*toys.optargs) vt_num = atolx_range(*toys.optargs, 1, 63);
if ((fd = open_console()) < 0) error_exit("can't open console");
xioctl(fd, VT_DISALLOCATE, (void *)vt_num);
if (CFG_TOYBOX_FREE) close(fd);
}