/* * open.c open a vt to run a new command (or shell). * * Copyright (c) 1994 by Jon Tombs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include "open.h" const char *OPENversion = "open: 1.4 (c) Jon Tombs 1994"; #ifndef VTNAME #error vt device name must be defined in open.h #endif int main(int argc, char *argv[]) { int fd = 0; int opt, pid; #if defined(__FreeBSD__) int vt_active; #else struct vt_stat vt; #define vt_active vt.v_active #endif struct passwd *pwnam=NULL; int vtno = -1; char show = FALSE; char login = FALSE; char verbose = FALSE; char do_wait = FALSE; char as_user= FALSE; char vtname[sizeof VTNAME + 2]; /* allow 999 possible VTs */ char *cmd = NULL, *def_cmd = NULL; /* * I don't like using getopt for this, but otherwise this gets messy. * POSIX/Gnu getopt forces the use of -- to separate child/program * options. RTFM. */ while ((opt = getopt(argc, argv, "c:lsvuw")) != -1) { switch (opt) { case 'c': vtno = (int) atol(optarg); #if defined(__FreeBSD__) if (vtno <= 0 || vtno > 99) { #else if (vtno < 0 || vtno > 99) { #endif fprintf(stderr, "open: %s illegal vt number\n", optarg); return 5; } /* close security holes - until we can do this safely */ (void) setuid(getuid()); break; case 'l': login = TRUE; break; case 's': show = TRUE; break; case 'v': verbose = TRUE; break; case 'w': do_wait = TRUE; break; case 'u': /* we'll let 'em get away with the meaningless -ul combo */ if(getuid()) { fprintf(stderr,"%s: only root can use the -u flag.\n",argv[0]); exit(1); } as_user = TRUE; break; default: usage(1); } } if (vtno == -1) { if ((fd = open("/dev/console",O_WRONLY,0)) < 0) { perror("open: Failed to open /dev/console\n"); return(2); } if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) || (vtno == -1)) { perror("open: Cannot find a free VT\n"); close(fd); return(3); } #if defined(__FreeBSD__) if (ioctl(fd, VT_GETACTIVE, &vt_active) < 0) { perror("open: can't get active VT\n"); #else if (ioctl(fd, VT_GETSTATE, &vt) < 0) { perror("open: can't get VTstate\n"); #endif close(fd); return(4); } } #if defined(__FreeBSD__) sprintf(vtname, VTNAME, vtno - 1); #else sprintf(vtname, VTNAME, vtno); #endif /* support for Spawn_Console; running from init added by Joshua Spoerri, Thu Jul 18 21:13:16 EDT 1996 */ if (as_user) { DIR *dp; struct dirent *dentp; struct stat buf; dev_t console_dev; ino_t console_ino; uid_t console_uid; char filename[sizeof VTNAME + 2]; if (!(dp=opendir("/proc"))) { perror("/proc"); exit(1); } /* get the current tty */ #if defined(__FreeBSD__) sprintf(filename,VTNAME,vt_active - 1); #else sprintf(filename,VTNAME,vt_active); #endif if (stat(filename,&buf)) { perror(filename); exit(1); } console_dev=buf.st_dev; console_ino=buf.st_ino; console_uid=buf.st_uid; /* get the owner of current tty */ if (!(pwnam=getpwuid(console_uid))) { perror("can't getpwuid"); exit(1); } /* check to make sure that user has process on that tty */ while ((dentp=readdir(dp))) { sprintf(filename,"/proc/%s/fd/0",dentp->d_name); if (stat(filename,&buf)) /*stat will fail if prcs lacks stdin*/ continue; if(buf.st_dev == console_dev && buf.st_ino == console_ino && buf.st_uid == console_uid) break; } if(!(buf.st_dev == console_dev && buf.st_ino == console_ino && buf.st_uid == console_uid)) { fprintf(stderr,"couldn't find owner of current tty!\n"); exit(1); } } else { if (!geteuid()) { uid_t uid = getuid(); chown(vtname, uid, getgid()); setuid(uid); } } if (verbose) fprintf(stderr, "open: using VT %s\n", vtname); if(!as_user) { if (!(argc > optind)) { def_cmd = getenv("SHELL"); if (def_cmd == NULL) usage(0); cmd = malloc(strlen(def_cmd + 2)); } else { cmd = malloc(strlen(argv[optind] + 2)); } if (login) strcpy(cmd, "-"); else cmd[0] = '\0'; if (def_cmd) strcat(cmd, def_cmd); else strcat(cmd, argv[optind]); if (login) argv[optind] = cmd++; } if((pid=fork()) == 0) { /* leave current vt */ #ifdef ESIX_5_3_2_D if (setpgrp() < 0) { #else if (setsid() < 0) { #endif fprintf(stderr, "open: Unable to set new session (%s)\n", strerror(errno)); } close(0); close(1); close(2); close(fd); /* and grab new one */ if ((fd = open(vtname, O_RDWR)) == -1) { /* Shouldn't happen */ _exit (4); /* silently die */ } dup(fd); dup(fd); if (ioctl(fd, TIOCSCTTY, NULL) < 0) _exit(4); if (show) { /* * Can't tell anyone if any of these fail, so throw away * the return values */ if (ioctl(fd, VT_ACTIVATE, vtno) < 0) _exit(4); /* wait to be really sure we have switched */ if (ioctl(fd, VT_WAITACTIVE, vtno) < 0) _exit(4); } if(as_user) execlp("login","login","-f",pwnam->pw_name,NULL); else if (def_cmd) execlp(cmd, def_cmd, NULL); else execvp(cmd, &argv[optind]); } if ( pid < 0 ) { perror("open: fork() error"); return(6); } if ( do_wait ) { wait(NULL); if (show) { /* Switch back... */ if (ioctl(fd, VT_ACTIVATE, vt_active) < 0) _exit(4); /* wait to be really sure we have switched */ if (ioctl(fd, VT_WAITACTIVE, vt_active) < 0) _exit(4); } } close (fd); return 0; } void usage(int stat) { fprintf(stderr, "Usage: open [-c vtnumer ][-l] [-u] [-s] [-v] -- command_line\n"); exit (stat); }