/* 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 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(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); }