19 #include "compat/linux-limits.h"
22 // There must be a universal way to find these!
27 #define VTBASE "/dev/vt%02d"
30 // Where your VTs are hidden
32 #define VTNAME "/dev/tty%d"
33 #define VTNAME2 "/dev/vc/%d"
37 #error vt device name must be defined
41 __attribute__ ((noreturn))
44 printf(_("Usage: %s [OPTIONS] -- command\n"
46 "This utility help you to start a program on a new virtual terminal (VT).\n"
49 " -c, --console=NUM use the given VT number;\n"
50 " -e, --exec execute the command, without forking;\n"
51 " -f, --force force opening a VT without checking;\n"
52 " -l, --login make the command a login shell;\n"
53 " -u, --user figure out the owner of the current VT;\n"
54 " -s, --switch switch to the new VT;\n"
55 " -w, --wait wait for command to complete;\n"
56 " -v, --verbose print a message for each action;\n"
57 " -V, --version print program version and exit;\n"
58 " -h, --help output a brief help message.\n"
64 __attribute__ ((format (printf, 1, 2)))
65 openvt_message(const char *fmt, ...) {
68 fprintf(stderr, "%s: ", progname);
69 vfprintf(stderr, fmt, ap);
70 fprintf(stderr, "\n");
76 __attribute__ ((noreturn))
77 __attribute__ ((format (printf, 3, 4)))
78 openvt_fatal(const int exitnum, const int errnum, const char *fmt, ...) {
81 fprintf(stderr, "%s: ", progname);
82 vfprintf(stderr, fmt, ap);
85 fprintf(stderr, ": %s", strerror(errnum));
86 fprintf(stderr, "\n");
92 * Support for Spawn_Console: openvt running from init
93 * added by Joshua Spoerri, Thu Jul 18 21:13:16 EDT 1996
95 * -u Figure out the owner of the current VT, and run
96 * login as that user. Suitable to be called by init.
97 * Shouldn't be used with -c or -l.
98 * Sample inittab line:
99 * kb::kbrequest:/usr/bin/openvt -us
101 * It is the job of authenticate_user() to find out who
102 * produced this keyboard signal. It is called only as root.
104 * Note that there is a race condition: curvt may not be the vt
105 * from which the keyboard signal was produced.
106 * (Possibly the signal was not produced at the keyboard at all,
107 * but by a "kill -SIG 1". However, only root can do this.)
109 * Conclusion: do not use this in high security environments.
110 * Or fix the code below to be more suspicious.
112 * Maybe it is better to just start a login at the new vt,
113 * instead of pre-authenticating the user with "login -f".
117 authenticate_user(int curvt)
120 struct dirent *dentp;
125 char filename[NAME_MAX + 12];
126 struct passwd *pwnam;
128 if (!(dp = opendir("/proc")))
129 openvt_fatal(1, errno, "opendir(/proc)");
131 /* get the current tty */
132 /* try /dev/ttyN, then /dev/vc/N */
133 sprintf(filename, VTNAME, curvt);
134 if (stat(filename, &buf)) {
136 sprintf(filename, VTNAME2, curvt);
137 if (stat(filename, &buf)) {
138 /* give error message for first attempt */
139 sprintf(filename, VTNAME, curvt);
140 openvt_fatal(1, errsv, "%s", filename);
143 console_dev = buf.st_dev;
144 console_ino = buf.st_ino;
145 console_uid = buf.st_uid;
147 /* get the owner of current tty */
148 if (!(pwnam = getpwuid(console_uid)))
149 openvt_fatal(1, errno, "getpwuid");
151 /* check to make sure that user has a process on that tty */
152 /* this will fail for example when X is running on the tty */
153 while ((dentp = readdir(dp))) {
154 sprintf(filename, "/proc/%s/fd/0", dentp->d_name);
156 if (stat(filename, &buf))
159 if (buf.st_dev == console_dev && buf.st_ino == console_ino && buf.st_uid == console_uid)
163 openvt_fatal(1, 0, _("Couldn't find owner of current tty!"));
166 return pwnam->pw_name;
170 open_vt(char *vtname, int force)
174 if ((fd = open(vtname, O_RDWR)) == -1)
177 if (ioctl(fd, TIOCSCTTY, force) == -1) {
186 main(int argc, char *argv[])
189 struct vt_stat vtstat;
197 char verbose = FALSE;
198 char direct_exec = FALSE;
199 char do_wait = FALSE;
200 char as_user = FALSE;
201 char vtname[sizeof(VTNAME) + 2]; /* allow 999 possible VTs */
202 char *cmd = NULL, *def_cmd = NULL, *username = NULL;
204 struct option long_options[] = {
205 { "help", no_argument, 0, 'h' },
206 { "version", no_argument, 0, 'V' },
207 { "verbose", no_argument, 0, 'v' },
208 { "exec", no_argument, 0, 'e' },
209 { "force", no_argument, 0, 'f' },
210 { "login", no_argument, 0, 'l' },
211 { "user", no_argument, 0, 'u' },
212 { "switch", no_argument, 0, 's' },
213 { "wait", no_argument, 0, 'w' },
214 { "console", required_argument, 0, 'c' },
218 set_progname(argv[0]);
220 setlocale(LC_ALL, "");
221 bindtextdomain(PACKAGE_NAME, LOCALEDIR);
222 textdomain(PACKAGE_NAME);
224 while ((opt = getopt_long(argc, argv, "c:lsfuewhvV", long_options, NULL)) != -1) {
227 optc = 1; /* vtno was specified by the user */
228 vtno = (int)atol(optarg);
230 if (vtno <= 0 || vtno > 63)
231 openvt_fatal(5, 0, _("%s: Illegal vt number"), optarg);
233 /* close security holes - until we can do this safely */
234 (void)setuid(getuid());
255 /* we'll let 'em get away with the meaningless -ul combo */
257 openvt_fatal(EXIT_FAILURE, 0, _("Only root can use the -u flag."));
262 print_version_and_exit();
266 print_help(EXIT_SUCCESS);
271 for (i = 0; i < 3; i++) {
274 if (fstat(i, &st) == -1 && open("/dev/null", O_RDWR) == -1)
275 openvt_fatal(EXIT_FAILURE, errno, "open(/dev/null)");
278 if ((consfd = getfd(NULL)) < 0)
279 openvt_fatal(2, 0, _("Couldn't get a file descriptor referring to the console"));
281 if (ioctl(consfd, VT_GETSTATE, &vtstat) < 0)
282 openvt_fatal(4, errno, "ioctl(VT_GETSTATE)");
285 if (ioctl(consfd, VT_OPENQRY, &vtno) < 0 || vtno == -1)
286 openvt_fatal(3, errno, _("Cannot find a free vt"));
290 openvt_fatal(7, 0, _("Cannot check whether vt %d is free; use `%s -f' to force."),
293 if (vtstat.v_state & (1 << vtno))
294 openvt_fatal(7, 0, _("vt %d is in use; command aborted; use `%s -f' to force."),
299 username = authenticate_user(vtstat.v_active);
301 if (!(argc > optind)) {
302 def_cmd = getenv("SHELL");
304 openvt_fatal(7, 0, _("Unable to find command."));
305 cmd = xmalloc(strlen(def_cmd) + 2);
307 cmd = xmalloc(strlen(argv[optind]) + 2);
316 strcat(cmd, def_cmd);
318 strcat(cmd, argv[optind]);
321 argv[optind] = cmd++;
324 if (direct_exec || ((pid = fork()) == 0)) {
325 /* leave current vt */
332 #endif /* HAVE_SETPGRP */
335 #endif /* ESIX_5_3_2_D */
336 openvt_fatal(5, errno, _("Unable to set new session"));
339 sprintf(vtname, VTNAME, vtno);
341 /* Can we open the vt we want? */
342 if ((fd = open_vt(vtname, force)) == -1) {
345 /* We found vtno ourselves - it is free according
346 to the kernel, but we cannot open it. Maybe X
347 used it and did a chown. Try a few vt's more
348 before giving up. Note: the 16 is a kernel limitation. */
349 for (i = vtno + 1; i < 16; i++) {
350 if ((vtstat.v_state & (1 << i)) == 0) {
351 sprintf(vtname, VTNAME, i);
352 if ((fd = open_vt(vtname, force)) >= 0) {
358 sprintf(vtname, VTNAME, vtno);
360 openvt_fatal(5, errsv, _("Unable to open %s"), vtname);
364 openvt_message(_("Using VT %s"), vtname);
366 /* Maybe we are suid root, and the -c option was given.
367 Check that the real user can access this VT.
368 We assume getty has made any in use VT non accessable */
369 if (access(vtname, R_OK | W_OK) < 0)
370 openvt_fatal(5, errno, _("Cannot open %s read/write"), vtname);
372 if (!as_user && !geteuid()) {
373 uid_t uid = getuid();
374 if (chown(vtname, uid, getgid()) == -1)
375 openvt_fatal(5, errno, "chown");
380 if (ioctl(fd, VT_ACTIVATE, vtno))
381 openvt_fatal(1, errno, _("Couldn't activate vt %d"), vtno);
383 if (ioctl(fd, VT_WAITACTIVE, vtno))
384 openvt_fatal(1, errno, _("Activation interrupted?"));
391 if ((dup2(fd, 0) == -1) || (dup2(fd, 1) == -1) || (dup2(fd, 2) == -1))
392 openvt_fatal(1, errno, "dup");
394 /* slight problem: after "openvt -su" has finished, the
395 utmp entry is not removed */
397 execlp("login", "login", "-f", username, NULL);
399 execlp(cmd, def_cmd, NULL);
401 execvp(cmd, &argv[optind]);
403 openvt_fatal(127, errno, "exec");
407 openvt_fatal(6, errno, "fork");
410 int retval = 0; /* actual value returned form process */
413 waitpid(pid, &retval, 0);
415 if (show) { /* Switch back... */
416 if (ioctl(consfd, VT_ACTIVATE, vtstat.v_active))
417 openvt_fatal(8, errno, "ioctl(VT_ACTIVATE)");
419 /* wait to be really sure we have switched */
420 if (ioctl(consfd, VT_WAITACTIVE, vtstat.v_active))
421 openvt_fatal(8, errno, "ioctl(VT_WAITACTIVE)");
423 if (ioctl(consfd, VT_DISALLOCATE, vtno))
424 openvt_fatal(8, 0, _("Couldn't deallocate console %d"), vtno);
427 /* if all our stuff went right, we want to return the exit code of the command we ran
428 super vital for scripting loops etc */