xref: /illumos-gate/usr/src/cmd/ttymon/tmexpress.c (revision 45d2468cd430f160914c353c714144054804373a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 #include	<stdio.h>
30 #include	<stdlib.h>
31 #include	<unistd.h>
32 #include	<fcntl.h>
33 #include	<errno.h>
34 #include	<ctype.h>
35 #include	<string.h>
36 #include	<signal.h>
37 #include	<sys/stat.h>
38 #include	<utmpx.h>
39 #include	<pwd.h>
40 #include	<dirent.h>
41 #include	<sys/param.h>
42 #include	<sys/acl.h>
43 #include	<sys/stat.h>
44 #include	<sys/types.h>
45 #include	<sys/console.h>
46 #include	"ttymon.h"
47 #include	"tmextern.h"
48 #include	"tmstruct.h"
49 
50 static	char	devbuf[BUFSIZ];
51 static	char	*devname;
52 
53 static	int	parse_args();
54 static	void	ttymon_options();
55 static	void	getty_options();
56 static	void	usage();
57 static	char	*find_ttyname();
58 
59 extern	void	tmchild();
60 extern	int	vml();
61 
62 void		revokedevaccess(char *, uid_t, gid_t, mode_t);
63 /* cannot include libdevinfo.h */
64 extern int di_devperm_logout(const char *);
65 
66 /*
67  * ttymon_express - This is call when ttymon is invoked with args
68  *		    or invoked as getty
69  *		  - This special version of ttymon will monitor
70  *		    one port only
71  *		  - It is intended to be used when some process
72  *		    wants to have a login session on the fly
73  */
74 void
75 ttymon_express(int argc, char **argv)
76 {
77 	struct	pmtab	*pmtab;
78 	struct	sigaction	sigact;
79 	extern	int	Retry;
80 	extern	void	open_device();
81 	extern	void	read_ttydefs();
82 	extern	int	checkut_line();
83 #ifdef	DEBUG
84 	extern	FILE	*Debugfp;
85 	extern	void	opendebug();
86 #endif
87 
88 #ifdef	DEBUG
89 	opendebug(TRUE);
90 #endif
91 
92 	sigact.sa_flags = 0;
93 	sigact.sa_handler = SIG_IGN;
94 	(void) sigemptyset(&sigact.sa_mask);
95 	(void) sigaction(SIGINT, &sigact, NULL);
96 
97 	if ((pmtab = ALLOC_PMTAB) == PNULL) {
98 		log("ttymon_express: ALLOC_PMTAB failed");
99 		exit(1);
100 	}
101 
102 	if (parse_args(argc, argv, pmtab) != 0) {
103 		log("ttymon_express: parse_args failed");
104 		exit(1);
105 	}
106 
107 	read_ttydefs(NULL, FALSE);
108 
109 	if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
110 		while (checkut_line(pmtab->p_device))
111 			sleep(15);
112 
113 	if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) {
114 		devname = find_ttyname(0);
115 		if ((devname == NULL) || (*devname == '\0')) {
116 			log("ttyname cannot find the device on fd 0");
117 			exit(1);
118 		}
119 		pmtab->p_device = devname;
120 #ifdef	DEBUG
121 		debug("ttymon_express: devname = %s", devname);
122 #endif
123 		/*
124 		 * become session leader
125 		 * fd 0 is closed and reopened just to make sure
126 		 * controlling tty is set up right
127 		 */
128 		(void) setsid();
129 		(void) close(0);
130 		revokedevaccess(pmtab->p_device, 0, 0, 0);
131 		if (open(pmtab->p_device, O_RDWR) < 0) {
132 			log("open %s failed: %s", pmtab->p_device,
133 			    strerror(errno));
134 			exit(1);
135 		}
136 		if ((pmtab->p_modules != NULL) &&
137 		    (*(pmtab->p_modules) != '\0')) {
138 			if (push_linedisc(0, pmtab->p_modules,
139 			    pmtab->p_device) == -1)
140 				exit(1);
141 		}
142 		if (initial_termio(0, pmtab) == -1)
143 			exit(1);
144 		di_devperm_logout((const char *)pmtab->p_device);
145 	} else {
146 		(void) setsid();
147 		(void) close(0);
148 		Retry = FALSE;
149 		open_device(pmtab);
150 		if (Retry)		/* open failed */
151 			exit(1);
152 	}
153 	tmchild(pmtab);
154 	exit(1);	/*NOTREACHED*/
155 }
156 
157 /*
158  * parse_arg	- parse cmd line arguments
159  */
160 static	int
161 parse_args(int argc, char **argv, struct pmtab *pmtab)
162 {
163 	static	char	p_server[] = "/usr/bin/login";
164 	extern	char	*lastname();
165 	extern	void	getty_account();
166 	static	char	termbuf[MAX_TERM_TYPE_LEN];
167 	static	struct	cons_getterm cnterm = {sizeof (termbuf), termbuf};
168 
169 	/* initialize fields to some default first */
170 	pmtab->p_tag = "";
171 	pmtab->p_flags = 0;
172 	pmtab->p_identity = "root";
173 	pmtab->p_res1 = "reserved";
174 	pmtab->p_res2 = "reserved";
175 	pmtab->p_res3 = "reserved";
176 	pmtab->p_uid = 0;
177 	pmtab->p_gid = 0;
178 	pmtab->p_dir = "/";
179 	pmtab->p_ttyflags = 0;
180 	pmtab->p_count = 0;
181 	pmtab->p_server = p_server;
182 	pmtab->p_timeout = 0;
183 	pmtab->p_modules = "";
184 	pmtab->p_prompt = "login: ";
185 	pmtab->p_dmsg = "";
186 	pmtab->p_termtype = "";
187 	pmtab->p_device = "";
188 	pmtab->p_status = GETTY;
189 	if (strcmp(lastname(argv[0]), "getty") == 0) {
190 		pmtab->p_ttylabel = "300";
191 		getty_options(argc, argv, pmtab);
192 	} else {
193 		int	cn_fd;
194 
195 		pmtab->p_ttylabel = "9600";
196 		ttymon_options(argc, argv, pmtab);
197 
198 		/*
199 		 * The following code is only reached if -g was specified.
200 		 * It attempts to determine a suitable terminal type for
201 		 * the console login process.
202 		 *
203 		 * If -d /dev/console also specified, we send an ioctl
204 		 * to the console device to query the TERM type.
205 		 *
206 		 * If any of the tests, system calls, or ioctls fail
207 		 * then pmtab->p_termtype retains its default value
208 		 * of "".  otherwise it is set to a term type value
209 		 * that was returned.
210 		 */
211 		if ((strlen(pmtab->p_termtype) == 0) &&
212 		    (strcmp(pmtab->p_device, "/dev/console") == 0) &&
213 		    ((cn_fd = open("/dev/console", O_RDONLY)) != -1)) {
214 
215 			if (ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1)
216 				pmtab->p_termtype = cnterm.cn_term_type;
217 			(void) close(cn_fd);
218 		}
219 	}
220 
221 	if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
222 		getty_account(pmtab->p_device); /* utmp accounting */
223 	return (0);
224 }
225 
226 
227 /*
228  * 	ttymon_options - scan and check args for ttymon express
229  */
230 
231 static	void
232 ttymon_options(int argc, char **argv, struct pmtab *pmtab)
233 {
234 	int 	c;			/* option letter */
235 	char 	*timeout;
236 	int  	gflag = 0;		/* -g seen */
237 	int	size = 0;
238 	char	tbuf[BUFSIZ];
239 
240 	extern	char	*optarg;
241 	extern	int	optind;
242 	extern	void	copystr();
243 	extern	char	*strsave();
244 	extern	char	*getword();
245 
246 	while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) {
247 		switch (c) {
248 		case 'g':
249 			gflag = 1;
250 			break;
251 		case 'd':
252 			pmtab->p_device = optarg;
253 			break;
254 		case 'h':
255 			pmtab->p_ttyflags &= ~H_FLAG;
256 			break;
257 
258 		case 'T':
259 			pmtab->p_termtype = optarg;
260 			break;
261 /*
262  *		case 'b':
263  *			pmtab->p_ttyflags |= B_FLAG;
264  *			pmtab->p_ttyflags |= R_FLAG;
265  *			break;
266  */
267 		case 't':
268 			timeout = optarg;
269 			while (*optarg) {
270 				if (!isdigit(*optarg++)) {
271 					log("Invalid argument for "
272 					    "\"-t\" -- number expected.");
273 					usage();
274 				}
275 			}
276 			pmtab->p_timeout = atoi(timeout);
277 			break;
278 		case 'p':
279 			copystr(tbuf, optarg);
280 			pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE));
281 			break;
282 		case 'm':
283 			pmtab->p_modules = optarg;
284 			if (vml(pmtab->p_modules) != 0)
285 				usage();
286 			break;
287 		case 'l':
288 			pmtab->p_ttylabel = optarg;
289 			break;
290 		case '?':
291 			usage();
292 			break;	/*NOTREACHED*/
293 		}
294 	}
295 	if (optind < argc)
296 		usage();
297 
298 	if (!gflag)
299 		usage();
300 }
301 
302 /*
303  * usage - print out a usage message
304  */
305 
306 static 	void
307 usage()
308 {
309 	char	*umsg = "Usage: ttymon\n  ttymon -g [-h] [-d device] "
310 	    "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n";
311 
312 	if (isatty(STDERR_FILENO))
313 		(void) fprintf(stderr, "%s", umsg);
314 	else
315 		cons_printf(umsg);
316 	exit(1);
317 }
318 
319 /*
320  *	getty_options	- this is cut from getty.c
321  *			- it scan getty cmd args
322  *			- modification is made to stuff args in pmtab
323  */
324 static	void
325 getty_options(argc, argv, pmtab)
326 int argc;
327 char **argv;
328 struct	pmtab	*pmtab;
329 {
330 	char	*ptr;
331 
332 	/*
333 	 * the pre-4.0 getty's hang_up_line() is a no-op.
334 	 * For compatibility, H_FLAG cannot be set for this "getty".
335 	 */
336 	pmtab->p_ttyflags &= ~(H_FLAG);
337 
338 	while (--argc && **++argv == '-') {
339 		for (ptr = *argv + 1; *ptr; ptr++)
340 		switch (*ptr) {
341 		case 'h':
342 			break;
343 		case 't':
344 			if (isdigit(*++ptr)) {
345 				(void) sscanf(ptr, "%d", &(pmtab->p_timeout));
346 				while (isdigit(*++ptr));
347 				ptr--;
348 			} else if (--argc) {
349 				if (isdigit(*(ptr = *++argv)))
350 					(void) sscanf(ptr, "%d",
351 					    &(pmtab->p_timeout));
352 				else {
353 					log("getty: timeout argument <%s> "
354 					    "invalid", *argv);
355 					exit(1);
356 				}
357 			}
358 			break;
359 
360 		case 'c':
361 			log("Use \"sttydefs -l\" to check /etc/ttydefs.");
362 			exit(0);
363 		default:
364 			break;
365 		}
366 	}
367 
368 	if (argc < 1) {
369 		log("getty: no terminal line specified.");
370 		exit(1);
371 	} else {
372 		(void) strcat(devbuf, "/dev/");
373 		(void) strcat(devbuf, *argv);
374 		pmtab->p_device = devbuf;
375 	}
376 
377 	if (--argc > 0) {
378 		pmtab->p_ttylabel = *++argv;
379 	}
380 
381 	/*
382 	 * every thing after this will be ignored
383 	 * i.e. termtype and linedisc are ignored
384 	 */
385 }
386 
387 /*
388  * find_ttyname(fd) 	- find the name of device associated with fd.
389  *			- it first tries utmpx to see if an entry exists
390  *			- with my pid and ut_line is defined. If ut_line
391  *			- is defined, it will see if the major and minor
392  *			- number of fd and devname from utmpx match.
393  *			- If utmpx search fails, ttyname(fd) will be called.
394  */
395 static	char	*
396 find_ttyname(fd)
397 int	fd;
398 {
399 	pid_t ownpid;
400 	struct utmpx *u;
401 	static	struct	stat	statf, statu;
402 	static	char	buf[BUFSIZ];
403 
404 	ownpid = getpid();
405 	setutxent();
406 	while ((u = getutxent()) != NULL) {
407 		if (u->ut_pid == ownpid) {
408 			if (strlen(u->ut_line) != 0) {
409 				if (*(u->ut_line) != '/') {
410 					(void) strcpy(buf, "/dev/");
411 					(void) strncat(buf, u->ut_line,
412 						sizeof (u->ut_line));
413 				} else {
414 					(void) strncat(buf, u->ut_line,
415 					    sizeof (u->ut_line));
416 				}
417 			}
418 			else
419 				u = NULL;
420 			break;
421 		}
422 	}
423 	endutxent();
424 	if ((u != NULL) &&
425 		(fstat(fd, &statf) == 0) &&
426 		(stat(buf, &statu) == 0) &&
427 		(statf.st_dev == statu.st_dev) &&
428 		(statf.st_rdev == statu.st_rdev)) {
429 #ifdef	DEBUG
430 			debug("ttymon_express: find device name from utmpx.");
431 #endif
432 			return (buf);
433 	} else {
434 #ifdef	DEBUG
435 		debug("ttymon_express: calling ttyname to find device name.");
436 #endif
437 		return (ttyname(fd));
438 	}
439 }
440 
441 /*
442  * Revoke all access to a device node and make sure that there are
443  * no interposed streams devices attached.  Must be called before a
444  * device is actually opened.
445  * When fdetach is called, the underlying device node is revealed; it
446  * will have the previous owner and that owner can re-attach; so we
447  * retry until we win.
448  * Ignore non-existent devices.
449  */
450 void
451 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode)
452 {
453 	do {
454 		if (chown(dev, uid, gid) == -1)
455 			return;
456 	} while (fdetach(dev) == 0);
457 
458 	/* Remove ACLs */
459 
460 	(void) acl_strip(dev, uid, gid, mode);
461 }
462