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