xref: /illumos-gate/usr/src/cmd/ttymon/tmexpress.c (revision e4586ebf2f01666696316c178da243993b1a0c04)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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	"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 	    strcmp(pmtab->p_device, "/dev/console") == 0) {
111 		while (checkut_line(pmtab->p_device))
112 			sleep(15);
113 	}
114 
115 	if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) {
116 		devname = find_ttyname(0);
117 		if ((devname == NULL) || (*devname == '\0')) {
118 			log("ttyname cannot find the device on fd 0");
119 			exit(1);
120 		}
121 		pmtab->p_device = devname;
122 #ifdef	DEBUG
123 		debug("ttymon_express: devname = %s", devname);
124 #endif
125 		/*
126 		 * become session leader
127 		 * fd 0 is closed and reopened just to make sure
128 		 * controlling tty is set up right
129 		 */
130 		(void) setsid();
131 		(void) close(0);
132 		revokedevaccess(pmtab->p_device, 0, 0, 0);
133 		if (open(pmtab->p_device, O_RDWR) < 0) {
134 			log("open %s failed: %s", pmtab->p_device,
135 			    strerror(errno));
136 			exit(1);
137 		}
138 		if ((pmtab->p_modules != NULL) &&
139 		    (*(pmtab->p_modules) != '\0')) {
140 			if (push_linedisc(0, pmtab->p_modules,
141 			    pmtab->p_device) == -1)
142 				exit(1);
143 		}
144 		if (initial_termio(0, pmtab) == -1)
145 			exit(1);
146 		di_devperm_logout((const char *)pmtab->p_device);
147 	} else {
148 		(void) setsid();
149 		(void) close(0);
150 		Retry = FALSE;
151 		open_device(pmtab);
152 		if (Retry)		/* open failed */
153 			exit(1);
154 	}
155 	tmchild(pmtab);
156 	exit(1);	/*NOTREACHED*/
157 }
158 
159 /*
160  * parse_arg	- parse cmd line arguments
161  */
162 static	int
163 parse_args(int argc, char **argv, struct pmtab *pmtab)
164 {
165 	static	char	p_server[] = "/usr/bin/login";
166 	extern	char	*lastname();
167 	extern	void	getty_account();
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 		pmtab->p_ttylabel = "9600";
194 		ttymon_options(argc, argv, pmtab);
195 	}
196 	if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
197 		getty_account(pmtab->p_device); /* utmp accounting */
198 	return (0);
199 }
200 
201 
202 /*
203  * 	ttymon_options - scan and check args for ttymon express
204  */
205 
206 static	void
207 ttymon_options(int argc, char **argv, struct pmtab *pmtab)
208 {
209 	int 	c;			/* option letter */
210 	char 	*timeout;
211 	int  	gflag = 0;		/* -g seen */
212 	int	size = 0;
213 	char	tbuf[BUFSIZ];
214 
215 	extern	char	*optarg;
216 	extern	int	optind;
217 	extern	void	copystr();
218 	extern	char	*strsave();
219 	extern	char	*getword();
220 
221 	while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) {
222 		switch (c) {
223 		case 'g':
224 			gflag = 1;
225 			break;
226 		case 'd':
227 			pmtab->p_device = optarg;
228 			break;
229 		case 'h':
230 			pmtab->p_ttyflags &= ~H_FLAG;
231 			break;
232 
233 		case 'T':
234 			pmtab->p_termtype = optarg;
235 			break;
236 /*
237  *		case 'b':
238  *			pmtab->p_ttyflags |= B_FLAG;
239  *			pmtab->p_ttyflags |= R_FLAG;
240  *			break;
241  */
242 		case 't':
243 			timeout = optarg;
244 			while (*optarg) {
245 				if (!isdigit(*optarg++)) {
246 					log("Invalid argument for "
247 					    "\"-t\" -- number expected.");
248 					usage();
249 				}
250 			}
251 			pmtab->p_timeout = atoi(timeout);
252 			break;
253 		case 'p':
254 			copystr(tbuf, optarg);
255 			pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE));
256 			break;
257 		case 'm':
258 			pmtab->p_modules = optarg;
259 			if (vml(pmtab->p_modules) != 0)
260 				usage();
261 			break;
262 		case 'l':
263 			pmtab->p_ttylabel = optarg;
264 			break;
265 		case '?':
266 			usage();
267 			break;	/*NOTREACHED*/
268 		}
269 	}
270 	if (optind < argc)
271 		usage();
272 
273 	if (!gflag)
274 		usage();
275 }
276 
277 /*
278  * usage - print out a usage message
279  */
280 
281 static 	void
282 usage()
283 {
284 	char	*umsg = "Usage: ttymon\n  ttymon -g [-h] [-d device] "
285 	    "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n";
286 
287 	if (isatty(STDERR_FILENO))
288 		(void) fprintf(stderr, "%s", umsg);
289 	else
290 		cons_printf(umsg);
291 	exit(1);
292 }
293 
294 /*
295  *	getty_options	- this is cut from getty.c
296  *			- it scan getty cmd args
297  *			- modification is made to stuff args in pmtab
298  */
299 static	void
300 getty_options(argc, argv, pmtab)
301 int argc;
302 char **argv;
303 struct	pmtab	*pmtab;
304 {
305 	char	*ptr;
306 
307 	/*
308 	 * the pre-4.0 getty's hang_up_line() is a no-op.
309 	 * For compatibility, H_FLAG cannot be set for this "getty".
310 	 */
311 	pmtab->p_ttyflags &= ~(H_FLAG);
312 
313 	while (--argc && **++argv == '-') {
314 		for (ptr = *argv + 1; *ptr; ptr++)
315 		switch (*ptr) {
316 		case 'h':
317 			break;
318 		case 't':
319 			if (isdigit(*++ptr)) {
320 				(void) sscanf(ptr, "%d", &(pmtab->p_timeout));
321 				while (isdigit(*++ptr));
322 				ptr--;
323 			} else if (--argc) {
324 				if (isdigit(*(ptr = *++argv)))
325 					(void) sscanf(ptr, "%d",
326 					    &(pmtab->p_timeout));
327 				else {
328 					log("getty: timeout argument <%s> "
329 					    "invalid", *argv);
330 					exit(1);
331 				}
332 			}
333 			break;
334 
335 		case 'c':
336 			log("Use \"sttydefs -l\" to check /etc/ttydefs.");
337 			exit(0);
338 		default:
339 			break;
340 		}
341 	}
342 
343 	if (argc < 1) {
344 		log("getty: no terminal line specified.");
345 		exit(1);
346 	} else {
347 		(void) strcat(devbuf, "/dev/");
348 		(void) strcat(devbuf, *argv);
349 		pmtab->p_device = devbuf;
350 	}
351 
352 	if (--argc > 0) {
353 		pmtab->p_ttylabel = *++argv;
354 	}
355 
356 	/*
357 	 * every thing after this will be ignored
358 	 * i.e. termtype and linedisc are ignored
359 	 */
360 }
361 
362 /*
363  * find_ttyname(fd) 	- find the name of device associated with fd.
364  *			- it first tries utmpx to see if an entry exists
365  *			- with my pid and ut_line is defined. If ut_line
366  *			- is defined, it will see if the major and minor
367  *			- number of fd and devname from utmpx match.
368  *			- If utmpx search fails, ttyname(fd) will be called.
369  */
370 static	char	*
371 find_ttyname(fd)
372 int	fd;
373 {
374 	pid_t ownpid;
375 	struct utmpx *u;
376 	static	struct	stat	statf, statu;
377 	static	char	buf[BUFSIZ];
378 
379 	ownpid = getpid();
380 	setutxent();
381 	while ((u = getutxent()) != NULL) {
382 		if (u->ut_pid == ownpid) {
383 			if (strlen(u->ut_line) != 0) {
384 				if (*(u->ut_line) != '/') {
385 					(void) strcpy(buf, "/dev/");
386 					(void) strncat(buf, u->ut_line,
387 						sizeof (u->ut_line));
388 				} else {
389 					(void) strncat(buf, u->ut_line,
390 					    sizeof (u->ut_line));
391 				}
392 			}
393 			else
394 				u = NULL;
395 			break;
396 		}
397 	}
398 	endutxent();
399 	if ((u != NULL) &&
400 		(fstat(fd, &statf) == 0) &&
401 		(stat(buf, &statu) == 0) &&
402 		(statf.st_dev == statu.st_dev) &&
403 		(statf.st_rdev == statu.st_rdev)) {
404 #ifdef	DEBUG
405 			debug("ttymon_express: find device name from utmpx.");
406 #endif
407 			return (buf);
408 	} else {
409 #ifdef	DEBUG
410 		debug("ttymon_express: calling ttyname to find device name.");
411 #endif
412 		return (ttyname(fd));
413 	}
414 }
415 
416 /*
417  * Revoke all access to a device node and make sure that there are
418  * no interposed streams devices attached.  Must be called before a
419  * device is actually opened.
420  * When fdetach is called, the underlying device node is revealed; it
421  * will have the previous owner and that owner can re-attach; so we
422  * retry until we win.
423  * Ignore non-existent devices.
424  */
425 void
426 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode)
427 {
428 	do {
429 		if (chown(dev, uid, gid) == -1)
430 			return;
431 	} while (fdetach(dev) == 0);
432 
433 	/* Remove ACLs */
434 
435 	(void) acl_strip(dev, uid, gid, mode);
436 }
437