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