OpenSolaris_b135/cmd/ttymon/tmexpress.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

#include	<stdio.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<ctype.h>
#include	<string.h>
#include	<signal.h>
#include	<sys/stat.h>
#include	<utmpx.h>
#include	<pwd.h>
#include	<dirent.h>
#include	<sys/param.h>
#include	<sys/acl.h>
#include	<sys/stat.h>
#include	<sys/types.h>
#include	<sys/console.h>
#include	"ttymon.h"
#include	"tmextern.h"
#include	"tmstruct.h"

static	char	devbuf[BUFSIZ];
static	char	*devname;

static	int	parse_args();
static	void	ttymon_options();
static	void	getty_options();
static	void	usage();
static	char	*find_ttyname();

extern	void	tmchild();
extern	int	vml();

void		revokedevaccess(char *, uid_t, gid_t, mode_t);
/* cannot include libdevinfo.h */
extern int di_devperm_logout(const char *);

/*
 * ttymon_express - This is call when ttymon is invoked with args
 *		    or invoked as getty
 *		  - This special version of ttymon will monitor
 *		    one port only
 *		  - It is intended to be used when some process
 *		    wants to have a login session on the fly
 */
void
ttymon_express(int argc, char **argv)
{
	struct	pmtab	*pmtab;
	struct	sigaction	sigact;
	extern	int	Retry;
	extern	void	open_device();
	extern	void	read_ttydefs();
	extern	int	checkut_line();
#ifdef	DEBUG
	extern	FILE	*Debugfp;
	extern	void	opendebug();
#endif

#ifdef	DEBUG
	opendebug(TRUE);
#endif

	sigact.sa_flags = 0;
	sigact.sa_handler = SIG_IGN;
	(void) sigemptyset(&sigact.sa_mask);
	(void) sigaction(SIGINT, &sigact, NULL);

	if ((pmtab = ALLOC_PMTAB) == PNULL) {
		log("ttymon_express: ALLOC_PMTAB failed");
		exit(1);
	}

	if (parse_args(argc, argv, pmtab) != 0) {
		log("ttymon_express: parse_args failed");
		exit(1);
	}

	read_ttydefs(NULL, FALSE);

	if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
		while (checkut_line(pmtab->p_device))
			sleep(15);

	if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) {
		devname = find_ttyname(0);
		if ((devname == NULL) || (*devname == '\0')) {
			log("ttyname cannot find the device on fd 0");
			exit(1);
		}
		pmtab->p_device = devname;
#ifdef	DEBUG
		debug("ttymon_express: devname = %s", devname);
#endif
		/*
		 * become session leader
		 * fd 0 is closed and reopened just to make sure
		 * controlling tty is set up right
		 */
		(void) setsid();
		(void) close(0);
		revokedevaccess(pmtab->p_device, 0, 0, 0);
		if (open(pmtab->p_device, O_RDWR) < 0) {
			log("open %s failed: %s", pmtab->p_device,
			    strerror(errno));
			exit(1);
		}
		if ((pmtab->p_modules != NULL) &&
		    (*(pmtab->p_modules) != '\0')) {
			if (push_linedisc(0, pmtab->p_modules,
			    pmtab->p_device) == -1)
				exit(1);
		}
		if (initial_termio(0, pmtab) == -1)
			exit(1);
		di_devperm_logout((const char *)pmtab->p_device);
	} else {
		(void) setsid();
		(void) close(0);
		Retry = FALSE;
		open_device(pmtab);
		if (Retry)		/* open failed */
			exit(1);
	}
	tmchild(pmtab);
	exit(1);	/*NOTREACHED*/
}

/*
 * parse_arg	- parse cmd line arguments
 */
static	int
parse_args(int argc, char **argv, struct pmtab *pmtab)
{
	static	char	p_server[] = "/usr/bin/login";
	extern	char	*lastname();
	extern	void	getty_account();
	static	char	termbuf[MAX_TERM_TYPE_LEN];
	static	struct	cons_getterm cnterm = {sizeof (termbuf), termbuf};

	/* initialize fields to some default first */
	pmtab->p_tag = "";
	pmtab->p_flags = 0;
	pmtab->p_identity = "root";
	pmtab->p_res1 = "reserved";
	pmtab->p_res2 = "reserved";
	pmtab->p_res3 = "reserved";
	pmtab->p_uid = 0;
	pmtab->p_gid = 0;
	pmtab->p_dir = "/";
	pmtab->p_ttyflags = 0;
	pmtab->p_count = 0;
	pmtab->p_server = p_server;
	pmtab->p_timeout = 0;
	pmtab->p_modules = "";
	pmtab->p_prompt = "login: ";
	pmtab->p_dmsg = "";
	pmtab->p_termtype = "";
	pmtab->p_device = "";
	pmtab->p_status = GETTY;
	if (strcmp(lastname(argv[0]), "getty") == 0) {
		pmtab->p_ttylabel = "300";
		getty_options(argc, argv, pmtab);
	} else {
		int	cn_fd;

		pmtab->p_ttylabel = "9600";
		ttymon_options(argc, argv, pmtab);

		/*
		 * The following code is only reached if -g was specified.
		 * It attempts to determine a suitable terminal type for
		 * the console login process.
		 *
		 * If -d /dev/console also specified, we send an ioctl
		 * to the console device to query the TERM type.
		 *
		 * If any of the tests, system calls, or ioctls fail
		 * then pmtab->p_termtype retains its default value
		 * of "".  otherwise it is set to a term type value
		 * that was returned.
		 */
		if ((strlen(pmtab->p_termtype) == 0) &&
		    (strcmp(pmtab->p_device, "/dev/console") == 0) &&
		    ((cn_fd = open("/dev/console", O_RDONLY)) != -1)) {

			if (ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1)
				pmtab->p_termtype = cnterm.cn_term_type;
			(void) close(cn_fd);
		}
	}

	if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
		getty_account(pmtab->p_device); /* utmp accounting */
	return (0);
}


/*
 * 	ttymon_options - scan and check args for ttymon express
 */

static	void
ttymon_options(int argc, char **argv, struct pmtab *pmtab)
{
	int 	c;			/* option letter */
	char 	*timeout;
	int  	gflag = 0;		/* -g seen */
	int	size = 0;
	char	tbuf[BUFSIZ];

	extern	char	*optarg;
	extern	int	optind;
	extern	void	copystr();
	extern	char	*strsave();
	extern	char	*getword();

	while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) {
		switch (c) {
		case 'g':
			gflag = 1;
			break;
		case 'd':
			pmtab->p_device = optarg;
			break;
		case 'h':
			pmtab->p_ttyflags &= ~H_FLAG;
			break;

		case 'T':
			pmtab->p_termtype = optarg;
			break;
/*
 *		case 'b':
 *			pmtab->p_ttyflags |= B_FLAG;
 *			pmtab->p_ttyflags |= R_FLAG;
 *			break;
 */
		case 't':
			timeout = optarg;
			while (*optarg) {
				if (!isdigit(*optarg++)) {
					log("Invalid argument for "
					    "\"-t\" -- number expected.");
					usage();
				}
			}
			pmtab->p_timeout = atoi(timeout);
			break;
		case 'p':
			copystr(tbuf, optarg);
			pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE));
			break;
		case 'm':
			pmtab->p_modules = optarg;
			if (vml(pmtab->p_modules) != 0)
				usage();
			break;
		case 'l':
			pmtab->p_ttylabel = optarg;
			break;
		case '?':
			usage();
			break;	/*NOTREACHED*/
		}
	}
	if (optind < argc)
		usage();

	if (!gflag)
		usage();
}

/*
 * usage - print out a usage message
 */

static 	void
usage()
{
	char	*umsg = "Usage: ttymon\n  ttymon -g [-h] [-d device] "
	    "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n";

	if (isatty(STDERR_FILENO))
		(void) fprintf(stderr, "%s", umsg);
	else
		cons_printf(umsg);
	exit(1);
}

/*
 *	getty_options	- this is cut from getty.c
 *			- it scan getty cmd args
 *			- modification is made to stuff args in pmtab
 */
static	void
getty_options(argc, argv, pmtab)
int argc;
char **argv;
struct	pmtab	*pmtab;
{
	char	*ptr;

	/*
	 * the pre-4.0 getty's hang_up_line() is a no-op.
	 * For compatibility, H_FLAG cannot be set for this "getty".
	 */
	pmtab->p_ttyflags &= ~(H_FLAG);

	while (--argc && **++argv == '-') {
		for (ptr = *argv + 1; *ptr; ptr++)
		switch (*ptr) {
		case 'h':
			break;
		case 't':
			if (isdigit(*++ptr)) {
				(void) sscanf(ptr, "%d", &(pmtab->p_timeout));
				while (isdigit(*++ptr));
				ptr--;
			} else if (--argc) {
				if (isdigit(*(ptr = *++argv)))
					(void) sscanf(ptr, "%d",
					    &(pmtab->p_timeout));
				else {
					log("getty: timeout argument <%s> "
					    "invalid", *argv);
					exit(1);
				}
			}
			break;

		case 'c':
			log("Use \"sttydefs -l\" to check /etc/ttydefs.");
			exit(0);
		default:
			break;
		}
	}

	if (argc < 1) {
		log("getty: no terminal line specified.");
		exit(1);
	} else {
		(void) strcat(devbuf, "/dev/");
		(void) strcat(devbuf, *argv);
		pmtab->p_device = devbuf;
	}

	if (--argc > 0) {
		pmtab->p_ttylabel = *++argv;
	}

	/*
	 * every thing after this will be ignored
	 * i.e. termtype and linedisc are ignored
	 */
}

/*
 * find_ttyname(fd) 	- find the name of device associated with fd.
 *			- it first tries utmpx to see if an entry exists
 *			- with my pid and ut_line is defined. If ut_line
 *			- is defined, it will see if the major and minor
 *			- number of fd and devname from utmpx match.
 *			- If utmpx search fails, ttyname(fd) will be called.
 */
static	char	*
find_ttyname(fd)
int	fd;
{
	pid_t ownpid;
	struct utmpx *u;
	static	struct	stat	statf, statu;
	static	char	buf[BUFSIZ];

	ownpid = getpid();
	setutxent();
	while ((u = getutxent()) != NULL) {
		if (u->ut_pid == ownpid) {
			if (strlen(u->ut_line) != 0) {
				if (*(u->ut_line) != '/') {
					(void) strcpy(buf, "/dev/");
					(void) strncat(buf, u->ut_line,
						sizeof (u->ut_line));
				} else {
					(void) strncat(buf, u->ut_line,
					    sizeof (u->ut_line));
				}
			}
			else
				u = NULL;
			break;
		}
	}
	endutxent();
	if ((u != NULL) &&
		(fstat(fd, &statf) == 0) &&
		(stat(buf, &statu) == 0) &&
		(statf.st_dev == statu.st_dev) &&
		(statf.st_rdev == statu.st_rdev)) {
#ifdef	DEBUG
			debug("ttymon_express: find device name from utmpx.");
#endif
			return (buf);
	} else {
#ifdef	DEBUG
		debug("ttymon_express: calling ttyname to find device name.");
#endif
		return (ttyname(fd));
	}
}

/*
 * Revoke all access to a device node and make sure that there are
 * no interposed streams devices attached.  Must be called before a
 * device is actually opened.
 * When fdetach is called, the underlying device node is revealed; it
 * will have the previous owner and that owner can re-attach; so we
 * retry until we win.
 * Ignore non-existent devices.
 */
void
revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode)
{
	do {
		if (chown(dev, uid, gid) == -1)
			return;
	} while (fdetach(dev) == 0);

	/* Remove ACLs */

	(void) acl_strip(dev, uid, gid, mode);
}