xref: /illumos-gate/usr/src/cmd/ttymon/tmexpress.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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	<utmpx.h>
38 #include	<pwd.h>
39 #include	<dirent.h>
40 #include	<sys/param.h>
41 #include	<sys/acl.h>
42 #include	<sys/stat.h>
43 #include	<sys/types.h>
44 #include	<sys/mkdev.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  * For serial device, return ttyX-mode property value.
147  */
148 static char *
149 get_ttymode_prop(dev_t rconsdev)
150 {
151 	char *rootpath = "/";
152 	char path[MAXPATHLEN];
153 	di_node_t root;
154 	char *propname, *v;
155 	struct stat st;
156 
157 	(void) snprintf(path, sizeof (path), "/dev/tty%c",
158 	    'a' + minor(rconsdev));
159 	if (stat(path, &st) < 0)
160 		return (NULL);
161 
162 	if (st.st_rdev != rconsdev)
163 		return (NULL);
164 
165 	if (asprintf(&propname, "%s-mode", path + 5) <= 0)
166 		return (NULL);
167 
168 	root = di_init(rootpath, DINFOPROP);
169 	if (root == DI_NODE_NIL) {
170 		free(propname);
171 		return (NULL);
172 	}
173 
174 	v = NULL;
175 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, root, propname, &v) > 0)
176 		v = strdup(v);
177 
178 	di_fini(root);
179 	free(propname);
180 	return (v);
181 }
182 
183 /*
184  * parse_arg	- parse cmd line arguments
185  */
186 static	int
187 parse_args(int argc, char **argv, struct pmtab *pmtab)
188 {
189 	static	char	p_server[] = "/usr/bin/login";
190 	static	char	termbuf[MAX_TERM_TYPE_LEN];
191 	static	struct	cons_getterm cnterm = {sizeof (termbuf), termbuf};
192 
193 	/* initialize fields to some default first */
194 	pmtab->p_tag = "";
195 	pmtab->p_flags = 0;
196 	pmtab->p_identity = "root";
197 	pmtab->p_res1 = "reserved";
198 	pmtab->p_res2 = "reserved";
199 	pmtab->p_res3 = "reserved";
200 	pmtab->p_uid = 0;
201 	pmtab->p_gid = 0;
202 	pmtab->p_dir = "/";
203 	pmtab->p_ttyflags = 0;
204 	pmtab->p_count = 0;
205 	pmtab->p_server = p_server;
206 	pmtab->p_timeout = 0;
207 	pmtab->p_modules = "";
208 	pmtab->p_prompt = "login: ";
209 	pmtab->p_dmsg = "";
210 	pmtab->p_termtype = "";
211 	pmtab->p_device = "";
212 	pmtab->p_status = GETTY;
213 	pmtab->p_ttymode = NULL;
214 	if (strcmp(lastname(argv[0]), "getty") == 0) {
215 		pmtab->p_ttylabel = "300";
216 		getty_options(argc, argv, pmtab);
217 	} else {
218 		int	cn_fd;
219 		struct	cons_getdev cnd;
220 
221 		pmtab->p_ttylabel = "9600";
222 		ttymon_options(argc, argv, pmtab);
223 
224 		/*
225 		 * The following code is only reached if -g was specified.
226 		 * It attempts to determine a suitable terminal type for
227 		 * the console login process, and in case we are using
228 		 * serial console, tty mode line.
229 		 *
230 		 * If -d /dev/console also specified, we send an ioctl
231 		 * to the console device to query the TERM type.
232 		 *
233 		 * If any of the tests, system calls, or ioctls fail
234 		 * then pmtab->p_termtype retains its default value
235 		 * of "".  otherwise it is set to a term type value
236 		 * that was returned.
237 		 */
238 		if (strcmp(pmtab->p_device, "/dev/console") == 0 &&
239 		    (cn_fd = open("/dev/console", O_RDONLY)) != -1) {
240 
241 			if (strlen(pmtab->p_termtype) == 0 &&
242 			    ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1) {
243 				pmtab->p_termtype = cnterm.cn_term_type;
244 			}
245 
246 			if (ioctl(cn_fd, CONS_GETDEV, &cnd) != -1)
247 				pmtab->p_ttymode =
248 				    get_ttymode_prop(cnd.cnd_rconsdev);
249 			(void) close(cn_fd);
250 		}
251 	}
252 
253 	if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
254 		getty_account(pmtab->p_device); /* utmp accounting */
255 	return (0);
256 }
257 
258 
259 /*
260  *	ttymon_options - scan and check args for ttymon express
261  */
262 
263 static	void
264 ttymon_options(int argc, char **argv, struct pmtab *pmtab)
265 {
266 	int	c;			/* option letter */
267 	char	*timeout;
268 	int	gflag = 0;		/* -g seen */
269 	int	size = 0;
270 	char	tbuf[BUFSIZ];
271 
272 	while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) {
273 		switch (c) {
274 		case 'g':
275 			gflag = 1;
276 			break;
277 		case 'd':
278 			pmtab->p_device = optarg;
279 			break;
280 		case 'h':
281 			pmtab->p_ttyflags &= ~H_FLAG;
282 			break;
283 
284 		case 'T':
285 			pmtab->p_termtype = optarg;
286 			break;
287 /*
288  *		case 'b':
289  *			pmtab->p_ttyflags |= B_FLAG;
290  *			pmtab->p_ttyflags |= R_FLAG;
291  *			break;
292  */
293 		case 't':
294 			timeout = optarg;
295 			while (*optarg) {
296 				if (!isdigit(*optarg++)) {
297 					log("Invalid argument for "
298 					    "\"-t\" -- number expected.");
299 					usage();
300 				}
301 			}
302 			pmtab->p_timeout = atoi(timeout);
303 			break;
304 		case 'p':
305 			copystr(tbuf, optarg);
306 			pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE));
307 			break;
308 		case 'm':
309 			pmtab->p_modules = optarg;
310 			if (vml(pmtab->p_modules) != 0)
311 				usage();
312 			break;
313 		case 'l':
314 			pmtab->p_ttylabel = optarg;
315 			break;
316 		case '?':
317 			usage();
318 			break;	/*NOTREACHED*/
319 		}
320 	}
321 	if (optind < argc)
322 		usage();
323 
324 	if (!gflag)
325 		usage();
326 }
327 
328 /*
329  * usage - print out a usage message
330  */
331 
332 static	void
333 usage(void)
334 {
335 	char	*umsg = "Usage: ttymon\n  ttymon -g [-h] [-d device] "
336 	    "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n";
337 
338 	if (isatty(STDERR_FILENO))
339 		(void) fprintf(stderr, "%s", umsg);
340 	else
341 		cons_printf(umsg);
342 	exit(1);
343 }
344 
345 /*
346  *	getty_options	- this is cut from getty.c
347  *			- it scan getty cmd args
348  *			- modification is made to stuff args in pmtab
349  */
350 static	void
351 getty_options(int argc, char **argv, struct pmtab *pmtab)
352 {
353 	char	*ptr;
354 
355 	/*
356 	 * the pre-4.0 getty's hang_up_line() is a no-op.
357 	 * For compatibility, H_FLAG cannot be set for this "getty".
358 	 */
359 	pmtab->p_ttyflags &= ~(H_FLAG);
360 
361 	while (--argc && **++argv == '-') {
362 		for (ptr = *argv + 1; *ptr; ptr++) {
363 			switch (*ptr) {
364 			case 'h':
365 				break;
366 			case 't':
367 				if (isdigit(*++ptr)) {
368 					(void) sscanf(ptr, "%d",
369 					    &(pmtab->p_timeout));
370 					while (isdigit(*++ptr))
371 						;
372 					ptr--;
373 				} else if (--argc) {
374 					if (isdigit(*(ptr = *++argv)))
375 						(void) sscanf(ptr, "%d",
376 						    &(pmtab->p_timeout));
377 					else {
378 						log("getty: timeout argument "
379 						    "<%s> invalid", *argv);
380 						exit(1);
381 					}
382 				}
383 				break;
384 
385 			case 'c':
386 				log("Use \"sttydefs -l\" to check "
387 				    "/etc/ttydefs.");
388 				exit(0);
389 			default:
390 				break;
391 			}
392 		}
393 	}
394 
395 	if (argc < 1) {
396 		log("getty: no terminal line specified.");
397 		exit(1);
398 	} else {
399 		(void) strcat(devbuf, "/dev/");
400 		(void) strcat(devbuf, *argv);
401 		pmtab->p_device = devbuf;
402 	}
403 
404 	if (--argc > 0) {
405 		pmtab->p_ttylabel = *++argv;
406 	}
407 
408 	/*
409 	 * every thing after this will be ignored
410 	 * i.e. termtype and linedisc are ignored
411 	 */
412 }
413 
414 /*
415  * find_ttyname(fd)	- find the name of device associated with fd.
416  *			- it first tries utmpx to see if an entry exists
417  *			- with my pid and ut_line is defined. If ut_line
418  *			- is defined, it will see if the major and minor
419  *			- number of fd and devname from utmpx match.
420  *			- If utmpx search fails, ttyname(fd) will be called.
421  */
422 static	char	*
423 find_ttyname(int fd)
424 {
425 	pid_t ownpid;
426 	struct utmpx *u;
427 	static	struct	stat	statf, statu;
428 	static	char	buf[BUFSIZ];
429 
430 	ownpid = getpid();
431 	setutxent();
432 	while ((u = getutxent()) != NULL) {
433 		if (u->ut_pid == ownpid) {
434 			if (strlen(u->ut_line) != 0) {
435 				if (*(u->ut_line) != '/') {
436 					(void) strcpy(buf, "/dev/");
437 					(void) strncat(buf, u->ut_line,
438 					    sizeof (u->ut_line));
439 				} else {
440 					(void) strncat(buf, u->ut_line,
441 					    sizeof (u->ut_line));
442 				}
443 			}
444 			else
445 				u = NULL;
446 			break;
447 		}
448 	}
449 	endutxent();
450 	if ((u != NULL) &&
451 	    (fstat(fd, &statf) == 0) &&
452 	    (stat(buf, &statu) == 0) &&
453 	    (statf.st_dev == statu.st_dev) &&
454 	    (statf.st_rdev == statu.st_rdev)) {
455 #ifdef	DEBUG
456 			debug("ttymon_express: find device name from utmpx.");
457 #endif
458 			return (buf);
459 	} else {
460 #ifdef	DEBUG
461 		debug("ttymon_express: calling ttyname to find device name.");
462 #endif
463 		return (ttyname(fd));
464 	}
465 }
466 
467 /*
468  * Revoke all access to a device node and make sure that there are
469  * no interposed streams devices attached.  Must be called before a
470  * device is actually opened.
471  * When fdetach is called, the underlying device node is revealed; it
472  * will have the previous owner and that owner can re-attach; so we
473  * retry until we win.
474  * Ignore non-existent devices.
475  */
476 void
477 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode)
478 {
479 	do {
480 		if (chown(dev, uid, gid) == -1)
481 			return;
482 	} while (fdetach(dev) == 0);
483 
484 	/* Remove ACLs */
485 
486 	(void) acl_strip(dev, uid, gid, mode);
487 }
488