1 /*
2  *       open.c open a vt to run a new command (or shell).
3  *
4  *	 Copyright (c) 1994 by Jon Tombs <jon@gtex02.us.es>
5  *
6  *       This program is free software; you can redistribute it and/or
7  *       modify it under the terms of the GNU General Public License
8  *       as published by the Free Software Foundation; either version
9  *       2 of the License, or (at your option) any later version.
10  */
11 
12 #include "open.h"
13 
14 const char *OPENversion = "open: 1.4 (c) Jon Tombs 1994";
15 
16 #ifndef VTNAME
17 #error vt device name must be defined in open.h
18 #endif
19 
20 
21 int
main(int argc,char * argv[])22 main(int argc, char *argv[])
23 {
24 
25    int fd = 0;
26    int opt, pid;
27 #if defined(__FreeBSD__)
28    int vt_active;
29 #else
30    struct vt_stat vt;
31 #define vt_active vt.v_active
32 #endif
33    struct passwd *pwnam=NULL;
34    int vtno     = -1;
35    char show    = FALSE;
36    char login   = FALSE;
37    char verbose = FALSE;
38    char do_wait	= FALSE;
39    char as_user= FALSE;
40    char vtname[sizeof VTNAME + 2]; /* allow 999 possible VTs */
41    char *cmd = NULL, *def_cmd = NULL;
42 
43    /*
44     * I don't like using getopt for this, but otherwise this gets messy.
45     * POSIX/Gnu getopt forces the use of -- to separate child/program
46     * options. RTFM.
47     */
48    while ((opt = getopt(argc, argv, "c:lsvuw")) != -1) {
49       switch (opt) {
50 	case 'c':
51 	  vtno = (int) atol(optarg);
52 #if defined(__FreeBSD__)
53 	  if (vtno <= 0 || vtno > 99) {
54 #else
55 	  if (vtno < 0 || vtno > 99) {
56 #endif
57 	    fprintf(stderr, "open: %s illegal vt number\n", optarg);
58 	    return 5;
59 	  }
60 	  /* close security holes - until we can do this safely */
61 	  (void) setuid(getuid());
62 	  break;
63 	case 'l':
64 	  login = TRUE;
65 	  break;
66 	case 's':
67 	  show = TRUE;
68 	  break;
69 	case 'v':
70 	  verbose = TRUE;
71 	  break;
72 	case 'w':
73 	  do_wait = TRUE;
74 	  break;
75 	case 'u':
76           /* we'll let 'em get away with the meaningless -ul combo */
77           if(getuid()) {
78 		fprintf(stderr,"%s: only root can use the -u flag.\n",argv[0]);
79 		exit(1);
80           }
81 	  as_user = TRUE;
82 	  break;
83 	default:
84 	  usage(1);
85 
86       }
87    }
88 
89 
90 
91    if (vtno == -1) {
92      if ((fd = open("/dev/console",O_WRONLY,0)) < 0) {
93 	perror("open: Failed to open /dev/console\n");
94 	return(2);
95      }
96 
97 
98      if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) || (vtno == -1)) {
99         perror("open: Cannot find a free VT\n");
100         close(fd);
101         return(3);
102      }
103 
104 #if defined(__FreeBSD__)
105      if (ioctl(fd, VT_GETACTIVE, &vt_active) < 0) {
106 	perror("open: can't get active VT\n");
107 #else
108      if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
109 	perror("open: can't get VTstate\n");
110 #endif
111         close(fd);
112         return(4);
113      }
114    }
115 
116 #if defined(__FreeBSD__)
117    sprintf(vtname, VTNAME, vtno - 1);
118 #else
119    sprintf(vtname, VTNAME, vtno);
120 #endif
121 
122 /* support for Spawn_Console; running from init
123 added by Joshua Spoerri, Thu Jul 18 21:13:16 EDT 1996 */
124 if (as_user) {
125 	DIR *dp;
126 	struct dirent *dentp;
127 	struct stat buf;
128 	dev_t console_dev;
129 	ino_t console_ino;
130 	uid_t console_uid;
131 	char filename[sizeof VTNAME + 2];
132 
133 	if (!(dp=opendir("/proc"))) {
134 		perror("/proc");
135 		exit(1);
136 	}
137 
138 	/* get the current tty */
139 #if defined(__FreeBSD__)
140 	sprintf(filename,VTNAME,vt_active - 1);
141 #else
142 	sprintf(filename,VTNAME,vt_active);
143 #endif
144 	if (stat(filename,&buf)) {
145 		perror(filename);
146 		exit(1);
147 	}
148 	console_dev=buf.st_dev;
149 	console_ino=buf.st_ino;
150 	console_uid=buf.st_uid;
151 
152 	/* get the owner of current tty */
153 	if (!(pwnam=getpwuid(console_uid))) {
154 		perror("can't getpwuid");
155 		exit(1);
156 	}
157 
158 	/* check to make sure that user has process on that tty */
159 	while ((dentp=readdir(dp))) {
160 		sprintf(filename,"/proc/%s/fd/0",dentp->d_name);
161 		if (stat(filename,&buf)) /*stat will fail if prcs lacks stdin*/
162 			continue;
163 		if(buf.st_dev == console_dev && buf.st_ino == console_ino
164 				&& buf.st_uid == console_uid)
165 			break;
166 	}
167 
168 	if(!(buf.st_dev == console_dev && buf.st_ino == console_ino
169 				&& buf.st_uid == console_uid)) {
170 		fprintf(stderr,"couldn't find owner of current tty!\n");
171 		exit(1);
172 	}
173 
174 } else {
175    if (!geteuid()) {
176       uid_t uid = getuid();
177       chown(vtname, uid, getgid());
178       setuid(uid);
179    }
180 }
181 
182    if (verbose)
183 	fprintf(stderr,	"open: using VT %s\n", vtname);
184 
185 if(!as_user) {
186    if (!(argc > optind)) {
187       def_cmd = getenv("SHELL");
188       if (def_cmd == NULL)
189 	usage(0);
190       cmd = malloc(strlen(def_cmd + 2));
191    } else {
192       cmd = malloc(strlen(argv[optind] + 2));
193    }
194 
195    if (login)
196       strcpy(cmd, "-");
197    else
198       cmd[0] = '\0';
199 
200    if (def_cmd)
201       strcat(cmd, def_cmd);
202    else
203       strcat(cmd, argv[optind]);
204 
205    if (login)
206       argv[optind] = cmd++;
207 }
208 
209    if((pid=fork()) == 0) {
210       /* leave current vt */
211 #ifdef   ESIX_5_3_2_D
212       if (setpgrp() < 0) {
213 #else
214       if (setsid() < 0) {
215 #endif
216         fprintf(stderr, "open: Unable to set new session (%s)\n",
217 	strerror(errno));
218       }
219       close(0);
220       close(1);
221       close(2);
222       close(fd);
223 
224       /* and grab new one */
225       if ((fd = open(vtname, O_RDWR)) == -1) { /* Shouldn't happen */
226         _exit (4); /* silently die */
227       }
228       dup(fd); dup(fd);
229 	if (ioctl(fd, TIOCSCTTY, NULL) < 0)
230 	  _exit(4);
231       if (show) {
232 	/*
233          * Can't tell anyone if any of these fail, so throw away
234 	 * the return values
235          */
236         if (ioctl(fd, VT_ACTIVATE, vtno) < 0)
237 	    _exit(4);
238         /* wait to be really sure we have switched */
239 	if (ioctl(fd, VT_WAITACTIVE, vtno) < 0)
240 	    _exit(4);
241       }
242       if(as_user)
243 	 execlp("login","login","-f",pwnam->pw_name,NULL);
244       else if (def_cmd)
245          execlp(cmd, def_cmd, NULL);
246       else
247 	 execvp(cmd, &argv[optind]);
248    }
249    if ( pid < 0 ) {
250       perror("open: fork() error");
251       return(6);
252    }
253 
254 
255    if ( do_wait ) {
256       wait(NULL);
257       if (show) { /* Switch back... */
258 	 if (ioctl(fd, VT_ACTIVATE, vt_active) < 0)
259 	    _exit(4);
260 	 /* wait to be really sure we have switched */
261 	 if (ioctl(fd, VT_WAITACTIVE, vt_active) < 0)
262 	    _exit(4);
263       }
264    }
265 
266    close (fd);
267    return 0;
268 }
269 
270 
271 void usage(int stat)
272 {
273    fprintf(stderr,
274       "Usage: open [-c vtnumer ][-l] [-u] [-s] [-v] -- command_line\n");
275    exit (stat);
276 }
277 
278 
279