xref: /illumos-gate/usr/src/cmd/ttymon/tmexpress.c (revision 7c8de9202c10c8c49a901bff2e373864b545bd57)
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