1 /* openvt.c - Run a program on a new VT
2  *
3  * Copyright 2014 Vivek Kumar Bhagat <vivek.bhagat89@gmail.com>
4  *
5  * No Standard
6 
7 USE_OPENVT(NEWTOY(openvt, "c#<1>63sw", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
8 USE_DEALLOCVT(NEWTOY(deallocvt, ">1", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NEEDROOT))
9 
10 config OPENVT
11   bool "openvt"
12   default n
13   help
14     usage: openvt [-c N] [-sw] [command [command_options]]
15 
16     start a program on a new virtual terminal (VT)
17 
18     -c N  Use VT N
19     -s    Switch to new VT
20     -w    Wait for command to exit
21 
22     if -sw used together, switch back to originating VT when command completes
23 
24 config DEALLOCVT
25   bool "deallocvt"
26   default n
27   help
28     usage: deallocvt [N]
29 
30     Deallocate unused virtual terminal /dev/ttyN, or all unused consoles.
31 */
32 
33 #define FOR_openvt
34 #include "toys.h"
35 #include <linux/vt.h>
36 #include <linux/kd.h>
37 
GLOBALS(unsigned long vt_num;)38 GLOBALS(
39   unsigned long vt_num;
40 )
41 
42 int open_console(void)
43 {
44   char arg, *console_name[] = {"/dev/tty", "/dev/tty0", "/dev/console"};
45   int i, fd;
46 
47   for (i = 0; i < ARRAY_LEN(console_name); i++) {
48     fd = open(console_name[i], O_RDWR);
49     if (fd >= 0) {
50       arg = 0;
51       if (!ioctl(fd, KDGKBTYPE, &arg)) return fd;
52       close(fd);
53     }
54   }
55 
56   /* check std fd 0, 1 and 2 */
57   for (fd = 0; fd < 3; fd++) {
58     arg = 0;
59     if (0 == ioctl(fd, KDGKBTYPE, &arg)) return fd;
60   }
61 
62   return -1;
63 }
64 
xvtnum(int fd)65 int xvtnum(int fd)
66 {
67   int ret;
68 
69   ret = ioctl(fd, VT_OPENQRY, (int *)&TT.vt_num);
70   if (ret != 0 || TT.vt_num <= 0) perror_exit("can't find open VT");
71 
72   return TT.vt_num;
73 }
74 
openvt_main(void)75 void openvt_main(void)
76 {
77   int fd, vt_fd, ret = 0;
78   struct vt_stat vstate;
79   pid_t pid;
80 
81   if (!(toys.optflags & FLAG_c)) {
82     // check if fd 0,1 or 2 is already opened
83     for (fd = 0; fd < 3; fd++)
84       if (!ioctl(fd, VT_GETSTATE, &vstate)) {
85         ret = xvtnum(fd);
86         break;
87       }
88 
89     // find VT number using /dev/console
90     if (!ret) {
91       fd = xopen("/dev/console", O_RDONLY | O_NONBLOCK);
92       xioctl(fd, VT_GETSTATE, &vstate);
93       xvtnum(fd);
94     }
95   }
96 
97   sprintf(toybuf, "/dev/tty%lu", TT.vt_num);
98   fd = open_console();
99   xioctl(fd, VT_GETSTATE, &vstate);
100 
101   close(0);  //new vt becomes stdin
102   vt_fd = xopen(toybuf, O_RDWR);
103   if (toys.optflags & FLAG_s) {
104     ioctl(vt_fd, VT_ACTIVATE, TT.vt_num);
105     ioctl(vt_fd, VT_WAITACTIVE, TT.vt_num);
106   }
107 
108   close(1);
109   close(2);
110   dup2(vt_fd, 1);
111   dup2(vt_fd, 2);
112   while (vt_fd > 2)
113     close(vt_fd--);
114 
115   pid = xfork();
116   if (!pid) {
117     setsid();
118     ioctl(vt_fd, TIOCSCTTY, 0);
119     xexec(toys.optargs);
120   }
121 
122   if (toys.optflags & FLAG_w) {
123     while (-1 == waitpid(pid, NULL, 0) && errno == EINTR)
124       ;
125     if (toys.optflags & FLAG_s) {
126       ioctl(fd, VT_ACTIVATE, vstate.v_active);
127       ioctl(fd, VT_WAITACTIVE, vstate.v_active);
128       //check why deallocate isn't working here
129       xioctl(fd, VT_DISALLOCATE, (void *)(ptrdiff_t)TT.vt_num);
130     }
131   }
132 }
133 
deallocvt_main(void)134 void deallocvt_main(void)
135 {
136   long vt_num = 0; // 0 deallocates all unused consoles
137   int fd;
138 
139   if (*toys.optargs) vt_num = atolx_range(*toys.optargs, 1, 63);
140 
141   if ((fd = open_console()) < 0) error_exit("can't open console");
142   xioctl(fd, VT_DISALLOCATE, (void *)vt_num);
143   if (CFG_TOYBOX_FREE) close(fd);
144 }
145