xref: /illumos-gate/usr/src/cmd/ttymon/tmexpress.c (revision dd72704bd9e794056c558153663c739e2012d721)
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.
228 		 *
229 		 * If -d /dev/console also specified, we send an ioctl
230 		 * to the console device to query the TERM type.
231 		 *
232 		 * If any of the tests, system calls, or ioctls fail
233 		 * then pmtab->p_termtype retains its default value
234 		 * of "".  otherwise it is set to a term type value
235 		 * that was returned.
236 		 */
237 		if ((strlen(pmtab->p_termtype) == 0) &&
238 		    (strcmp(pmtab->p_device, "/dev/console") == 0) &&
239 		    ((cn_fd = open("/dev/console", O_RDONLY)) != -1)) {
240 
241 			if (ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1)
242 				pmtab->p_termtype = cnterm.cn_term_type;
243 
244 			if (ioctl(cn_fd, CONS_GETDEV, &cnd) != -1)
245 				pmtab->p_ttymode =
246 				    get_ttymode_prop(cnd.cnd_rconsdev);
247 			(void) close(cn_fd);
248 		}
249 	}
250 
251 	if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
252 		getty_account(pmtab->p_device); /* utmp accounting */
253 	return (0);
254 }
255 
256 
257 /*
258  *	ttymon_options - scan and check args for ttymon express
259  */
260 
261 static	void
262 ttymon_options(int argc, char **argv, struct pmtab *pmtab)
263 {
264 	int	c;			/* option letter */
265 	char	*timeout;
266 	int	gflag = 0;		/* -g seen */
267 	int	size = 0;
268 	char	tbuf[BUFSIZ];
269 
270 	while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) {
271 		switch (c) {
272 		case 'g':
273 			gflag = 1;
274 			break;
275 		case 'd':
276 			pmtab->p_device = optarg;
277 			break;
278 		case 'h':
279 			pmtab->p_ttyflags &= ~H_FLAG;
280 			break;
281 
282 		case 'T':
283 			pmtab->p_termtype = optarg;
284 			break;
285 /*
286  *		case 'b':
287  *			pmtab->p_ttyflags |= B_FLAG;
288  *			pmtab->p_ttyflags |= R_FLAG;
289  *			break;
290  */
291 		case 't':
292 			timeout = optarg;
293 			while (*optarg) {
294 				if (!isdigit(*optarg++)) {
295 					log("Invalid argument for "
296 					    "\"-t\" -- number expected.");
297 					usage();
298 				}
299 			}
300 			pmtab->p_timeout = atoi(timeout);
301 			break;
302 		case 'p':
303 			copystr(tbuf, optarg);
304 			pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE));
305 			break;
306 		case 'm':
307 			pmtab->p_modules = optarg;
308 			if (vml(pmtab->p_modules) != 0)
309 				usage();
310 			break;
311 		case 'l':
312 			pmtab->p_ttylabel = optarg;
313 			break;
314 		case '?':
315 			usage();
316 			break;	/*NOTREACHED*/
317 		}
318 	}
319 	if (optind < argc)
320 		usage();
321 
322 	if (!gflag)
323 		usage();
324 }
325 
326 /*
327  * usage - print out a usage message
328  */
329 
330 static	void
331 usage(void)
332 {
333 	char	*umsg = "Usage: ttymon\n  ttymon -g [-h] [-d device] "
334 	    "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n";
335 
336 	if (isatty(STDERR_FILENO))
337 		(void) fprintf(stderr, "%s", umsg);
338 	else
339 		cons_printf(umsg);
340 	exit(1);
341 }
342 
343 /*
344  *	getty_options	- this is cut from getty.c
345  *			- it scan getty cmd args
346  *			- modification is made to stuff args in pmtab
347  */
348 static	void
349 getty_options(int argc, char **argv, struct pmtab *pmtab)
350 {
351 	char	*ptr;
352 
353 	/*
354 	 * the pre-4.0 getty's hang_up_line() is a no-op.
355 	 * For compatibility, H_FLAG cannot be set for this "getty".
356 	 */
357 	pmtab->p_ttyflags &= ~(H_FLAG);
358 
359 	while (--argc && **++argv == '-') {
360 		for (ptr = *argv + 1; *ptr; ptr++) {
361 			switch (*ptr) {
362 			case 'h':
363 				break;
364 			case 't':
365 				if (isdigit(*++ptr)) {
366 					(void) sscanf(ptr, "%d",
367 					    &(pmtab->p_timeout));
368 					while (isdigit(*++ptr))
369 						;
370 					ptr--;
371 				} else if (--argc) {
372 					if (isdigit(*(ptr = *++argv)))
373 						(void) sscanf(ptr, "%d",
374 						    &(pmtab->p_timeout));
375 					else {
376 						log("getty: timeout argument "
377 						    "<%s> invalid", *argv);
378 						exit(1);
379 					}
380 				}
381 				break;
382 
383 			case 'c':
384 				log("Use \"sttydefs -l\" to check "
385 				    "/etc/ttydefs.");
386 				exit(0);
387 			default:
388 				break;
389 			}
390 		}
391 	}
392 
393 	if (argc < 1) {
394 		log("getty: no terminal line specified.");
395 		exit(1);
396 	} else {
397 		(void) strcat(devbuf, "/dev/");
398 		(void) strcat(devbuf, *argv);
399 		pmtab->p_device = devbuf;
400 	}
401 
402 	if (--argc > 0) {
403 		pmtab->p_ttylabel = *++argv;
404 	}
405 
406 	/*
407 	 * every thing after this will be ignored
408 	 * i.e. termtype and linedisc are ignored
409 	 */
410 }
411 
412 /*
413  * find_ttyname(fd)	- find the name of device associated with fd.
414  *			- it first tries utmpx to see if an entry exists
415  *			- with my pid and ut_line is defined. If ut_line
416  *			- is defined, it will see if the major and minor
417  *			- number of fd and devname from utmpx match.
418  *			- If utmpx search fails, ttyname(fd) will be called.
419  */
420 static	char	*
421 find_ttyname(int fd)
422 {
423 	pid_t ownpid;
424 	struct utmpx *u;
425 	static	struct	stat	statf, statu;
426 	static	char	buf[BUFSIZ];
427 
428 	ownpid = getpid();
429 	setutxent();
430 	while ((u = getutxent()) != NULL) {
431 		if (u->ut_pid == ownpid) {
432 			if (strlen(u->ut_line) != 0) {
433 				if (*(u->ut_line) != '/') {
434 					(void) strcpy(buf, "/dev/");
435 					(void) strncat(buf, u->ut_line,
436 					    sizeof (u->ut_line));
437 				} else {
438 					(void) strncat(buf, u->ut_line,
439 					    sizeof (u->ut_line));
440 				}
441 			}
442 			else
443 				u = NULL;
444 			break;
445 		}
446 	}
447 	endutxent();
448 	if ((u != NULL) &&
449 	    (fstat(fd, &statf) == 0) &&
450 	    (stat(buf, &statu) == 0) &&
451 	    (statf.st_dev == statu.st_dev) &&
452 	    (statf.st_rdev == statu.st_rdev)) {
453 #ifdef	DEBUG
454 			debug("ttymon_express: find device name from utmpx.");
455 #endif
456 			return (buf);
457 	} else {
458 #ifdef	DEBUG
459 		debug("ttymon_express: calling ttyname to find device name.");
460 #endif
461 		return (ttyname(fd));
462 	}
463 }
464 
465 /*
466  * Revoke all access to a device node and make sure that there are
467  * no interposed streams devices attached.  Must be called before a
468  * device is actually opened.
469  * When fdetach is called, the underlying device node is revealed; it
470  * will have the previous owner and that owner can re-attach; so we
471  * retry until we win.
472  * Ignore non-existent devices.
473  */
474 void
475 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode)
476 {
477 	do {
478 		if (chown(dev, uid, gid) == -1)
479 			return;
480 	} while (fdetach(dev) == 0);
481 
482 	/* Remove ACLs */
483 
484 	(void) acl_strip(dev, uid, gid, mode);
485 }
486