8 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
9 |
*/ |
*/ |
10 |
|
|
11 |
/* getopt not needed */ |
#include <linux/vt.h> |
12 |
|
#include "libbb.h" |
13 |
|
|
14 |
#include "busybox.h" |
/* "Standard" openvt's man page (we do not support all of this): |
15 |
|
|
16 |
int openvt_main(int argc, char **argv) |
openvt [-c NUM] [-fsulv] [--] [command [args]] |
17 |
|
|
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. |
20 |
|
|
21 |
|
-c NUM |
22 |
|
Use the given VT number, not the first free one. |
23 |
|
-f |
24 |
|
Force opening a VT: don't try to check if VT is already in use. |
25 |
|
-s |
26 |
|
Switch to the new VT when starting the command. |
27 |
|
The VT of the new command will be made the new current VT. |
28 |
|
-u |
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. |
31 |
|
-l |
32 |
|
Make the command a login shell: a "-" is prepended to the argv[0] |
33 |
|
when command is executed. |
34 |
|
-v |
35 |
|
Verbose. |
36 |
|
-w |
37 |
|
Wait for command to complete. If -w and -s are used together, |
38 |
|
switch back to the controlling terminal when the command completes. |
39 |
|
|
40 |
|
bbox: |
41 |
|
-u: not implemented |
42 |
|
-f: always in effect |
43 |
|
-l: not implemented, ignored |
44 |
|
-v: ignored |
45 |
|
-ws: does NOT switch back |
46 |
|
*/ |
47 |
|
|
48 |
|
/* Helper: does this fd understand VT_xxx? */ |
49 |
|
static int not_vt_fd(int fd) |
50 |
|
{ |
51 |
|
struct vt_stat vtstat; |
52 |
|
return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */ |
53 |
|
} |
54 |
|
|
55 |
|
/* Helper: get a fd suitable for VT_xxx */ |
56 |
|
static int get_vt_fd(void) |
57 |
{ |
{ |
58 |
int fd; |
int fd; |
|
char vtname[sizeof(VC_FORMAT) + 2]; |
|
59 |
|
|
60 |
if (argc < 3) { |
/* Do we, by chance, already have it? */ |
61 |
bb_show_usage(); |
for (fd = 0; fd < 3; fd++) |
62 |
|
if (!not_vt_fd(fd)) |
63 |
|
return fd; |
64 |
|
fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK); |
65 |
|
if (fd >= 0 && !not_vt_fd(fd)) |
66 |
|
return fd; |
67 |
|
bb_error_msg_and_die("can't find open VT"); |
68 |
|
} |
69 |
|
|
70 |
|
static int find_free_vtno(void) |
71 |
|
{ |
72 |
|
int vtno; |
73 |
|
int fd = get_vt_fd(); |
74 |
|
|
75 |
|
errno = 0; |
76 |
|
/*xfunc_error_retval = 3; - do we need compat? */ |
77 |
|
if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0) |
78 |
|
bb_perror_msg_and_die("can't find open VT"); |
79 |
|
// Not really needed, grep for DAEMON_ONLY_SANITIZE |
80 |
|
// if (fd > 2) |
81 |
|
// close(fd); |
82 |
|
return vtno; |
83 |
|
} |
84 |
|
|
85 |
|
/* vfork scares gcc, it generates bigger code. |
86 |
|
* Keep it away from main program. |
87 |
|
* TODO: move to libbb; or adapt existing libbb's spawn(). |
88 |
|
*/ |
89 |
|
static NOINLINE void vfork_child(char **argv) |
90 |
|
{ |
91 |
|
if (vfork() == 0) { |
92 |
|
/* CHILD */ |
93 |
|
/* Try to make this VT our controlling tty */ |
94 |
|
setsid(); /* lose old ctty */ |
95 |
|
ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */); |
96 |
|
//bb_error_msg("our sid %d", getsid(0)); |
97 |
|
//bb_error_msg("our pgrp %d", getpgrp()); |
98 |
|
//bb_error_msg("VT's sid %d", tcgetsid(0)); |
99 |
|
//bb_error_msg("VT's pgrp %d", tcgetpgrp(0)); |
100 |
|
BB_EXECVP(argv[0], argv); |
101 |
|
bb_perror_msg_and_die("exec %s", argv[0]); |
102 |
|
} |
103 |
|
} |
104 |
|
|
105 |
|
int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
106 |
|
int openvt_main(int argc UNUSED_PARAM, char **argv) |
107 |
|
{ |
108 |
|
char vtname[sizeof(VC_FORMAT) + sizeof(int)*3]; |
109 |
|
struct vt_stat vtstat; |
110 |
|
char *str_c; |
111 |
|
int vtno; |
112 |
|
int flags; |
113 |
|
enum { |
114 |
|
OPT_c = (1 << 0), |
115 |
|
OPT_w = (1 << 1), |
116 |
|
OPT_s = (1 << 2), |
117 |
|
OPT_l = (1 << 3), |
118 |
|
OPT_f = (1 << 4), |
119 |
|
OPT_v = (1 << 5), |
120 |
|
}; |
121 |
|
|
122 |
|
/* "+" - stop on first non-option */ |
123 |
|
flags = getopt32(argv, "+c:wslfv", &str_c); |
124 |
|
argv += optind; |
125 |
|
|
126 |
|
if (flags & OPT_c) { |
127 |
|
/* Check for illegal vt number: < 1 or > 63 */ |
128 |
|
vtno = xatou_range(str_c, 1, 63); |
129 |
|
} else { |
130 |
|
vtno = find_free_vtno(); |
131 |
|
} |
132 |
|
|
133 |
|
/* Grab new VT */ |
134 |
|
sprintf(vtname, VC_FORMAT, vtno); |
135 |
|
/* (Try to) clean up stray open fds above fd 2 */ |
136 |
|
bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL); |
137 |
|
close(STDIN_FILENO); |
138 |
|
/*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */ |
139 |
|
xopen(vtname, O_RDWR); |
140 |
|
xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat); |
141 |
|
|
142 |
|
if (flags & OPT_s) { |
143 |
|
console_make_active(STDIN_FILENO, vtno); |
144 |
|
} |
145 |
|
|
146 |
|
if (!argv[0]) { |
147 |
|
argv--; |
148 |
|
argv[0] = getenv("SHELL"); |
149 |
|
if (!argv[0]) |
150 |
|
argv[0] = (char *) DEFAULT_SHELL; |
151 |
|
/*argv[1] = NULL; - already is */ |
152 |
} |
} |
|
/* check for illegal vt number: < 1 or > 63 */ |
|
|
sprintf(vtname, VC_FORMAT, (int)xatoul_range(argv[1], 1, 63)); |
|
153 |
|
|
154 |
if (fork() == 0) { |
xdup2(STDIN_FILENO, STDOUT_FILENO); |
155 |
/* child */ |
xdup2(STDIN_FILENO, STDERR_FILENO); |
156 |
/* leave current vt (controlling tty) */ |
|
157 |
setsid(); |
#ifdef BLOAT |
158 |
/* and grab new one */ |
{ |
159 |
fd = xopen(vtname, O_RDWR); |
/* Handle -l (login shell) option */ |
160 |
/* Reassign stdin, stdout and sterr */ |
const char *prog = argv[0]; |
161 |
dup2(fd, STDIN_FILENO); |
if (flags & OPT_l) |
162 |
dup2(fd, STDOUT_FILENO); |
argv[0] = xasprintf("-%s", argv[0]); |
163 |
dup2(fd, STDERR_FILENO); |
} |
164 |
while (fd > 2) close(fd--); |
#endif |
165 |
|
|
166 |
execvp(argv[2], &argv[2]); |
vfork_child(argv); |
167 |
_exit(1); |
if (flags & OPT_w) { |
168 |
|
/* We have only one child, wait for it */ |
169 |
|
safe_waitpid(-1, NULL, 0); /* loops on EINTR */ |
170 |
|
if (flags & OPT_s) { |
171 |
|
console_make_active(STDIN_FILENO, vtstat.v_active); |
172 |
|
// Compat: even with -c N (try to) disallocate: |
173 |
|
// # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5 |
174 |
|
// openvt: could not deallocate console 9 |
175 |
|
xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno); |
176 |
|
} |
177 |
} |
} |
178 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
179 |
} |
} |