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