Home | History | Annotate | Download | only in consadm
      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, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include "utils.h"
     30 #include <locale.h>
     31 #include <poll.h>
     32 #include <setjmp.h>
     33 #include <signal.h>
     34 #include <strings.h>
     35 #include <stropts.h>
     36 #include <syslog.h>
     37 #include <sys/sysmsg_impl.h>
     38 #include <sys/stat.h>
     39 #include <sys/sysmacros.h>
     40 #include <sys/systeminfo.h>
     41 #include <sys/termios.h>
     42 #include <sys/types.h>
     43 
     44 #define	CONSADM			"/usr/sbin/consadm"
     45 #define	CONSADMD		"/usr/sbin/consadmd"
     46 #define	CONSADMLOCK		"/tmp/CoNsAdM.lck"
     47 #define	CONSDAEMON		"consadmd"
     48 #define	MSGLOG			"/dev/msglog"
     49 #define	CONSOLE			"/dev/console"
     50 #define	WSCONS			"/dev/wscons"
     51 #define	CONSCONFIG		"/etc/consadm.conf"
     52 #define	SETCONSOLEPID		"/etc/consadm.pid"
     53 
     54 #define	CONFIG			0
     55 #define	UNCONFIG		1
     56 #define	COMMENT			'#'
     57 #define	NEWLINE			'\n'
     58 #define	SPACE			' '
     59 #define	TAB			'	'
     60 
     61 #define	E_SUCCESS	0		/* Exit status for success */
     62 #define	E_ERROR		1		/* Exit status for error */
     63 #define	E_USAGE		2		/* Exit status for usage error */
     64 #define	E_NO_CARRIER	3		/* Exit status for no carrier */
     65 
     66 /* useful data structures for lock function */
     67 static struct flock fl;
     68 #define	LOCK_EX F_WRLCK
     69 
     70 static char usage[] =
     71 	"Usage:	\n"
     72 	"\tconsadm [ -p ] [ -a device ... ]\n"
     73 	"\tconsadm [ -p ] [ -d device ... ]\n"
     74 	"\tconsadm [ -p ]\n";
     75 
     76 /* data structures ... */
     77 static char conshdr[] =
     78 	"#\n# consadm.conf\n#"
     79 	"# Configuration parameters for console message redirection.\n"
     80 	"# Do NOT edit this file by hand -- use consadm(1m) instead.\n"
     81 	"#\n";
     82 const char *pname;		/* program name */
     83 static sigjmp_buf deadline;
     84 
     85 /* command line arguments */
     86 static int display;
     87 static int persist;
     88 static int addflag;
     89 static int deleteflag;
     90 
     91 /* function headers */
     92 static void setaux(char *);
     93 static void unsetaux(char *);
     94 static void getconsole(void);
     95 static boolean_t has_carrier(int fd);
     96 static boolean_t modem_support(int fd);
     97 static void setfallback(char *argv[]);
     98 static void removefallback(void);
     99 static void fallbackdaemon(void);
    100 static void persistlist(void);
    101 static int verifyarg(char *, int);
    102 static int safeopen(char *);
    103 static void catch_term(void);
    104 static void catch_alarm(void);
    105 static void catch_hup(void);
    106 static void cleanup_on_exit(void);
    107 static void addtolist(char *);
    108 static void removefromlist(char *);
    109 static int pathcmp(char *, char *);
    110 static int lckfunc(int, int);
    111 typedef void (*sig_handler_t)();
    112 static int getlock(void);
    113 
    114 /*
    115  * In main, return codes carry the following meaning:
    116  * 0 - successful
    117  * 1 - error during the command execution
    118  */
    119 
    120 int
    121 main(int argc, char *argv[])
    122 {
    123 	int	index;
    124 	struct	sigaction sa;
    125 	int	c;
    126 	char	*p = strrchr(argv[0], '/');
    127 
    128 	if (p == NULL)
    129 		p = argv[0];
    130 	else
    131 		p++;
    132 
    133 	pname = p;
    134 
    135 	(void) setlocale(LC_ALL, "");
    136 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
    137 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
    138 #endif
    139 	(void) textdomain(TEXT_DOMAIN);
    140 
    141 	if (getuid() != 0)
    142 		die(gettext("must be root to run this program\n"));
    143 
    144 	/*
    145 	 * Handle normal termination signals that may be received.
    146 	 */
    147 	sa.sa_handler = SIG_IGN;
    148 	sa.sa_flags = 0;
    149 	(void) sigemptyset(&sa.sa_mask);
    150 	(void) sigaction(SIGHUP, &sa, NULL);
    151 	(void) sigaction(SIGINT, &sa, NULL);
    152 	(void) sigaction(SIGQUIT, &sa, NULL);
    153 	(void) sigaction(SIGTERM, &sa, NULL);
    154 
    155 	/*
    156 	 * To make sure persistent state gets removed.
    157 	 */
    158 	sa.sa_handler = cleanup_on_exit;
    159 	sa.sa_flags = 0;
    160 	(void) sigemptyset(&sa.sa_mask);
    161 	(void) sigaction(SIGSEGV, &sa, NULL);
    162 	(void) sigaction(SIGILL, &sa, NULL);
    163 	(void) sigaction(SIGABRT, &sa, NULL);
    164 	(void) sigaction(SIGBUS, &sa, NULL);
    165 
    166 	if (strcmp(pname, CONSDAEMON) == 0) {
    167 		fallbackdaemon();
    168 		return (E_SUCCESS);
    169 	}
    170 
    171 	if (argc == 1)
    172 		display++;
    173 	else {
    174 		while ((c = getopt(argc, argv, "adp")) != EOF)  {
    175 			switch (c) {
    176 			case 'a':
    177 				addflag++;
    178 				break;
    179 			case 'd':
    180 				deleteflag++;
    181 				break;
    182 			case 'p':
    183 				persist++;
    184 				break;
    185 			default:
    186 				(void) fprintf(stderr, gettext(usage));
    187 				exit(E_USAGE);
    188 				/*NOTREACHED*/
    189 			}
    190 		}
    191 	}
    192 
    193 	if (display) {
    194 		getconsole();
    195 		return (E_SUCCESS);
    196 	}
    197 	if (addflag && deleteflag) {
    198 		(void) fprintf(stderr, gettext(usage));
    199 		return (E_ERROR);
    200 	}
    201 	if (addflag) {
    202 		if (optind == argc) {
    203 			(void) fprintf(stderr, gettext(usage));
    204 			return (E_ERROR);
    205 		}
    206 		/* separately check every device path specified */
    207 		for (index = optind; index < argc; index++) {
    208 			if (verifyarg(argv[index], addflag))
    209 				return (E_ERROR);
    210 		}
    211 
    212 		for (index = optind; index < argc; index++) {
    213 			setaux(argv[index]);
    214 			if (persist)
    215 				addtolist(argv[index]);
    216 		}
    217 
    218 		/*
    219 		 * start/restart daemon based on the auxilary
    220 		 * consoles at this time.
    221 		 */
    222 		setfallback(argv);
    223 		return (E_SUCCESS);
    224 	} else if (deleteflag) {
    225 		if (optind == argc) {
    226 			(void) fprintf(stderr, gettext(usage));
    227 			return (E_ERROR);
    228 		}
    229 		/* separately check every device path specified */
    230 		for (index = optind; index < argc; index++) {
    231 			if (verifyarg(argv[index], 0))
    232 				return (E_ERROR);
    233 		}
    234 
    235 		for (index = optind; index < argc; index++) {
    236 			unsetaux(argv[index]);
    237 			if (persist && deleteflag)
    238 				removefromlist(argv[index]);
    239 		}
    240 
    241 		/*
    242 		 * kill off daemon and restart with
    243 		 * new list of auxiliary consoles
    244 		 */
    245 		setfallback(argv);
    246 		return (E_SUCCESS);
    247 	} else if (persist) {
    248 		if (optind < argc) {
    249 			(void) fprintf(stderr, gettext(usage));
    250 			return (E_ERROR);
    251 		}
    252 
    253 		persistlist();
    254 		return (E_SUCCESS);
    255 	} else {
    256 		(void) fprintf(stderr, gettext(usage));
    257 		return (E_ERROR);
    258 	}
    259 } /* main */
    260 
    261 /* for daemon to handle termination from user command */
    262 static void
    263 catch_term()
    264 {
    265 	exit(E_SUCCESS);
    266 }
    267 
    268 /* handle lack of carrier on open */
    269 static void
    270 catch_alarm()
    271 {
    272 	siglongjmp(deadline, 1);
    273 }
    274 
    275 /* caught a sighup */
    276 static void
    277 catch_hup()
    278 {
    279 	/*
    280 	 * ttymon sends sighup to consadmd because it has the serial
    281 	 * port open.  We catch the signal here, but process it
    282 	 * within fallbackdaemon().  We ignore the signal if the
    283 	 * errno returned was EINTR.
    284 	 */
    285 }
    286 
    287 /* Remove persistent state on receiving signal. */
    288 static void
    289 cleanup_on_exit()
    290 {
    291 	(void) unlink(CONSADMLOCK);
    292 	exit(E_ERROR);
    293 }
    294 
    295 /*
    296  * send ioctl to /dev/sysmsg to route msgs of the device specified.
    297  */
    298 static void
    299 setaux(char *dev)
    300 {
    301 	int	fd;
    302 
    303 	if ((fd = safeopen(SYSMSG)) < 0)
    304 		die(gettext("%s is missing or not a valid device\n"), SYSMSG);
    305 
    306 	if (ioctl(fd, CIOCSETCONSOLE, dev) != 0) {
    307 		/*
    308 		 * Let setting duplicate device be warning, consadm
    309 		 * must proceed to set persistence if requested.
    310 		 */
    311 		if (errno == EBUSY)
    312 			die(gettext("%s is already the default console\n"),
    313 			    dev);
    314 		else if (errno != EEXIST)
    315 			die(gettext("cannot get table entry"));
    316 	}
    317 	syslog(LOG_WARNING, "%s: Added auxiliary device %s", CONSADM, dev);
    318 
    319 	(void) close(fd);
    320 }
    321 
    322 /*
    323  * Send ioctl to device specified and
    324  * Remove the entry from the list of auxiliary devices.
    325  */
    326 static void
    327 unsetaux(char *dev)
    328 {
    329 	int	fd;
    330 
    331 	if ((fd = safeopen(SYSMSG)) < 0)
    332 		die(gettext("%s is missing or not a valid device\n"), SYSMSG);
    333 
    334 	if (ioctl(fd, CIOCRMCONSOLE, dev) != 0) {
    335 		if (errno == EBUSY)
    336 			die(gettext("cannot unset the default console\n"));
    337 	} else
    338 		syslog(LOG_WARNING, "%s: Removed auxiliary device %s",
    339 		    CONSADM, dev);
    340 	(void) close(fd);
    341 }
    342 
    343 static int
    344 getlock(void)
    345 {
    346 	int lckfd;
    347 
    348 	if ((lckfd = open(CONSADMLOCK, O_CREAT | O_EXCL | O_WRONLY,
    349 	    S_IRUSR | S_IWUSR)) < 0) {
    350 		if (errno == EEXIST)
    351 			die(gettext("currently busy, try again later.\n"));
    352 		else
    353 			die(gettext("cannot open %s"), CONSADMLOCK);
    354 	}
    355 	if (lckfunc(lckfd, LOCK_EX) == -1) {
    356 		(void) close(lckfd);
    357 		(void) unlink(CONSADMLOCK);
    358 		die(gettext("fcntl operation failed"));
    359 	}
    360 	return (lckfd);
    361 }
    362 
    363 static void
    364 addtolist(char *dev)
    365 {
    366 	int	lckfd, fd;
    367 	FILE	*fp, *nfp;
    368 	char	newfile[MAXPATHLEN];
    369 	char	buf[MAXPATHLEN];
    370 	int	len;
    371 	boolean_t	found = B_FALSE;
    372 
    373 	/* update file of devices configured to get console msgs. */
    374 
    375 	lckfd = getlock();
    376 
    377 	/* Open new file */
    378 	(void) snprintf(newfile, sizeof (newfile), "%s%d",
    379 	    CONSCONFIG, (int)getpid());
    380 	if (((fd = creat(newfile, 0644)) < 0) ||
    381 	    ((nfp = fdopen(fd, "w")) == NULL)) {
    382 		(void) close(lckfd);
    383 		(void) unlink(CONSADMLOCK);
    384 		die(gettext("could not create new %s file"), CONSCONFIG);
    385 	}
    386 
    387 	/* Add header to new file */
    388 	(void) fprintf(nfp, "%s", conshdr);
    389 
    390 	/* Check that the file doesn't already exist */
    391 	if ((fp = fopen(CONSCONFIG, "r")) != NULL) {
    392 		while (fgets(buf, MAXPATHLEN, fp) != NULL) {
    393 			if (buf[0] == COMMENT || buf[0] == NEWLINE ||
    394 			    buf[0] == SPACE || buf[0] == TAB)
    395 				continue;
    396 			len = strlen(buf);
    397 			buf[len - 1] = NULL; /* Clear carriage return */
    398 			if (pathcmp(dev, buf) == 0) {
    399 				/* they match so use name passed in. */
    400 				(void) fprintf(nfp, "%s\n", dev);
    401 				found = B_TRUE;
    402 			} else
    403 				(void) fprintf(nfp, "%s\n", buf);
    404 		}
    405 	}
    406 	/* User specified persistent settings */
    407 	if (found == B_FALSE)
    408 		(void) fprintf(nfp, "%s\n", dev);
    409 
    410 	(void) fclose(fp);
    411 	(void) fclose(nfp);
    412 	(void) rename(newfile, CONSCONFIG);
    413 	(void) close(lckfd);
    414 	(void) unlink(CONSADMLOCK);
    415 }
    416 
    417 /* The list in CONSCONFIG gives the persistence capability in the proto */
    418 static void
    419 removefromlist(char *dev)
    420 {
    421 	int	lckfd;
    422 	FILE	*fp, *nfp;
    423 	char	newfile[MAXPATHLEN + 1];
    424 	char	len;
    425 	char	value[MAXPATHLEN + 1];
    426 	boolean_t	newcontents = B_FALSE;
    427 
    428 	/* update file of devices configured to get console msgs. */
    429 
    430 	lckfd = getlock();
    431 
    432 	if ((fp = fopen(CONSCONFIG, "r")) == NULL) {
    433 		(void) close(lckfd);
    434 		(void) unlink(CONSADMLOCK);
    435 		return;
    436 	}
    437 
    438 	/* Open new file */
    439 	(void) snprintf(newfile, sizeof (newfile), "%s%d",
    440 	    CONSCONFIG, (int)getpid());
    441 	if ((nfp = fopen(newfile, "w")) == NULL) {
    442 		(void) close(lckfd);
    443 		(void) unlink(CONSADMLOCK);
    444 		die(gettext("cannot create new %s file"), CONSCONFIG);
    445 	}
    446 
    447 	/* Add header to new file */
    448 	(void) fprintf(nfp, "%s", conshdr);
    449 
    450 	/*
    451 	 * Check whether the path duplicates what is already in the
    452 	 * file.
    453 	 */
    454 	while (fgets(value, MAXPATHLEN, fp) != NULL) {
    455 		/* skip comments */
    456 		if (value[0] == COMMENT || value[0] == NEWLINE ||
    457 		    value[0] == SPACE || value[0] == TAB)
    458 			continue;
    459 		len = strlen(value);
    460 		value[len - 1] = NULL; /* Clear carriage return */
    461 		if (pathcmp(dev, value) == 0) {
    462 			/* they match so don't write it */
    463 			continue;
    464 		}
    465 		(void) fprintf(nfp, "%s\n", value);
    466 		newcontents = B_TRUE;
    467 	}
    468 	(void) fclose(fp);
    469 	(void) fclose(nfp);
    470 	/* Remove the file if there aren't any auxiliary consoles */
    471 	if (newcontents)
    472 		(void) rename(newfile, CONSCONFIG);
    473 	else {
    474 		(void) unlink(CONSCONFIG);
    475 		(void) unlink(newfile);
    476 	}
    477 	(void) close(lckfd);
    478 	(void) unlink(CONSADMLOCK);
    479 }
    480 
    481 static int
    482 pathcmp(char *adev, char *bdev)
    483 {
    484 	struct stat	st1;
    485 	struct stat	st2;
    486 
    487 	if (strcmp(adev, bdev) == 0)
    488 		return (0);
    489 
    490 	if (stat(adev, &st1) != 0 || !S_ISCHR(st1.st_mode))
    491 		die(gettext("invalid device %s\n"), adev);
    492 
    493 	if (stat(bdev, &st2) != 0 || !S_ISCHR(st2.st_mode))
    494 		die(gettext("invalid device %s\n"), bdev);
    495 
    496 	if (st1.st_rdev == st2.st_rdev)
    497 		return (0);
    498 
    499 	return (1);
    500 }
    501 
    502 /*
    503  * Display configured consoles.
    504  */
    505 static void
    506 getconsole(void)
    507 {
    508 	int	fd;
    509 	int	bufsize = 0;		/* size of device cache */
    510 	char	*infop, *ptr, *p;	/* info structure for ioctl's */
    511 
    512 	if ((fd = safeopen(SYSMSG)) < 0)
    513 		die(gettext("%s is missing or not a valid device\n"), SYSMSG);
    514 
    515 	if ((bufsize = ioctl(fd, CIOCGETCONSOLE, NULL)) < 0)
    516 		die(gettext("cannot get table entry\n"));
    517 	if (bufsize == 0)
    518 		return;
    519 
    520 	if ((infop = calloc(bufsize, sizeof (char))) == NULL)
    521 		die(gettext("cannot allocate buffer"));
    522 
    523 	if (ioctl(fd, CIOCGETCONSOLE, infop) < 0)
    524 		die(gettext("cannot get table entry\n"));
    525 
    526 	ptr = infop;
    527 	while (ptr != NULL) {
    528 		p = strchr(ptr, ' ');
    529 		if (p == NULL) {
    530 			(void) printf("%s\n", ptr);
    531 			break;
    532 		}
    533 		*p++ = '\0';
    534 		(void) printf("%s\n", ptr);
    535 		ptr = p;
    536 	}
    537 	(void) close(fd);
    538 }
    539 
    540 /*
    541  * It is supposed that if the device supports TIOCMGET then it
    542  * might be a serial device.
    543  */
    544 static boolean_t
    545 modem_support(int fd)
    546 {
    547 	int	modem_state;
    548 
    549 	if (ioctl(fd, TIOCMGET, &modem_state) == 0)
    550 		return (B_TRUE);
    551 	else
    552 		return (B_FALSE);
    553 }
    554 
    555 static boolean_t
    556 has_carrier(int fd)
    557 {
    558 	int	modem_state;
    559 
    560 	if (ioctl(fd, TIOCMGET, &modem_state) == 0)
    561 		return ((modem_state & TIOCM_CAR) != 0);
    562 	else {
    563 		return (B_FALSE);
    564 	}
    565 }
    566 
    567 static void
    568 setfallback(char *argv[])
    569 {
    570 	pid_t	pid;
    571 	FILE	*fp;
    572 	char	*cmd = CONSADMD;
    573 	int	lckfd, fd;
    574 
    575 	lckfd = getlock();
    576 
    577 	/*
    578 	 * kill off any existing daemon
    579 	 * remove /etc/consadm.pid
    580 	 */
    581 	removefallback();
    582 
    583 	/* kick off a daemon */
    584 	if ((pid = fork()) == (pid_t)0) {
    585 		/* always fallback to /dev/console */
    586 		argv[0] = cmd;
    587 		argv[1] = NULL;
    588 		(void) close(0);
    589 		(void) close(1);
    590 		(void) close(2);
    591 		(void) close(lckfd);
    592 		if ((fd = open(MSGLOG, O_RDWR)) < 0)
    593 			die(gettext("cannot open %s"), MSGLOG);
    594 		(void) dup2(fd, 1);
    595 		(void) dup2(fd, 2);
    596 		(void) execv(cmd, argv);
    597 		exit(E_SUCCESS);
    598 	} else if (pid == -1)
    599 		die(gettext("%s not started"), CONSADMD);
    600 
    601 	if ((fp = fopen(SETCONSOLEPID, "w")) == NULL)
    602 		die(gettext("cannot open %s"), SETCONSOLEPID);
    603 	/* write daemon pid to file */
    604 	(void) fprintf(fp, "%d\n", (int)pid);
    605 	(void) fclose(fp);
    606 	(void) close(lckfd);
    607 	(void) unlink(CONSADMLOCK);
    608 }
    609 
    610 /*
    611  * Remove the daemon that would have implemented the automatic
    612  * fallback in event of carrier loss on the serial console.
    613  */
    614 static void
    615 removefallback(void)
    616 {
    617 	FILE	*fp;
    618 	int	pid;
    619 
    620 	if ((fp = fopen(SETCONSOLEPID, "r+")) == NULL)
    621 		/* file doesn't exist, so no work to do */
    622 		return;
    623 
    624 	if (fscanf(fp, "%d\n", &pid) <= 0) {
    625 		(void) fclose(fp);
    626 		(void) unlink(SETCONSOLEPID);
    627 		return;
    628 	}
    629 
    630 	/*
    631 	 * Don't shoot ourselves in the foot by killing init,
    632 	 * sched, pageout, or fsflush.
    633 	 */
    634 	if (pid == 0 || pid == 1 || pid == 2 || pid == 3) {
    635 		(void) unlink(SETCONSOLEPID);
    636 		return;
    637 	}
    638 	/*
    639 	 * kill off the existing daemon listed in
    640 	 * /etc/consadm.pid
    641 	 */
    642 	(void) kill((pid_t)pid, SIGTERM);
    643 
    644 	(void) fclose(fp);
    645 	(void) unlink(SETCONSOLEPID);
    646 }
    647 
    648 /*
    649  * Assume we always fall back to /dev/console.
    650  * parameter passed in will always be the auxiliary device.
    651  * The daemon will not start after the last device has been removed.
    652  */
    653 static void
    654 fallbackdaemon(void)
    655 {
    656 	int	fd, sysmfd, ret = 0;
    657 	char	**devpaths;
    658 	pollfd_t	*fds;
    659 	nfds_t	nfds = 0;
    660 	int	index;
    661 	int	pollagain;
    662 	struct	sigaction sa;
    663 	int	bufsize = 0;		/* length of device cache paths */
    664 	int	cachesize = 0;		/* size of device cache */
    665 	char	*infop, *ptr, *p;	/* info structure for ioctl's */
    666 
    667 	/*
    668 	 * catch SIGTERM cause it might be coming from user via consadm
    669 	 */
    670 	sa.sa_handler = catch_term;
    671 	sa.sa_flags = 0;
    672 	(void) sigemptyset(&sa.sa_mask);
    673 	(void) sigaction(SIGTERM, &sa, NULL);
    674 
    675 	/*
    676 	 * catch SIGHUP cause it might be coming from a disconnect
    677 	 */
    678 	sa.sa_handler = catch_hup;
    679 	sa.sa_flags = 0;
    680 	(void) sigemptyset(&sa.sa_mask);
    681 	(void) sigaction(SIGHUP, &sa, NULL);
    682 
    683 	if ((sysmfd = safeopen(SYSMSG)) < 0)
    684 		die(gettext("%s is missing or not a valid device\n"), SYSMSG);
    685 
    686 	if ((bufsize = ioctl(sysmfd, CIOCGETCONSOLE, NULL)) < 0)
    687 		die(gettext("cannot get table entry\n"));
    688 	if (bufsize == 0)
    689 		return;
    690 
    691 	if ((infop = calloc(bufsize, sizeof (char))) == NULL)
    692 		die(gettext("cannot allocate buffer"));
    693 
    694 	if (ioctl(sysmfd, CIOCGETCONSOLE, infop) < 0)
    695 		die(gettext("cannot get table entry\n"));
    696 
    697 	ptr = infop;
    698 	while (ptr != NULL) {
    699 		p = strchr(ptr, ' ');
    700 		if (p == NULL) {
    701 			cachesize++;
    702 			break;
    703 		}
    704 		p++;
    705 		cachesize++;
    706 		ptr = p;
    707 	}
    708 
    709 	if ((fds = calloc(cachesize, sizeof (struct pollfd))) == NULL)
    710 		die(gettext("cannot allocate buffer"));
    711 
    712 	if ((devpaths = calloc(cachesize, sizeof (char *))) == NULL)
    713 		die(gettext("cannot allocate buffer"));
    714 
    715 	ptr = infop;
    716 	while (ptr != NULL) {
    717 		p = strchr(ptr, ' ');
    718 		if (p == NULL) {
    719 			if ((fd = safeopen(ptr)) < 0) {
    720 				warn(gettext("cannot open %s, continuing"),
    721 				    ptr);
    722 				break;
    723 			}
    724 			if (!has_carrier(fd)) {
    725 				(void) close(fd);
    726 				warn(gettext(
    727 		    "no carrier on %s, device will not be monitored.\n"),
    728 				    ptr);
    729 				break;
    730 			} else {
    731 				fds[nfds].fd = fd;
    732 				fds[nfds].events = 0;
    733 
    734 				if ((devpaths[nfds] =
    735 				    malloc(strlen(ptr) + 1)) == NULL)
    736 					die(gettext("cannot allocate buffer"));
    737 
    738 				(void) strcpy(devpaths[nfds], ptr);
    739 				nfds++;
    740 				if (nfds >= cachesize)
    741 					break;
    742 			}
    743 			break;
    744 		}
    745 		*p++ = '\0';
    746 
    747 		if ((fd = safeopen(ptr)) < 0) {
    748 			warn(gettext("cannot open %s, continuing"), ptr);
    749 			ptr = p;
    750 			continue;
    751 		}
    752 		if (!has_carrier(fd)) {
    753 			(void) close(fd);
    754 			warn(gettext(
    755 		    "no carrier on %s, device will not be monitored.\n"),
    756 			    ptr);
    757 			ptr = p;
    758 			continue;
    759 		} else {
    760 			fds[nfds].fd = fd;
    761 			fds[nfds].events = 0;
    762 
    763 			if ((devpaths[nfds] = malloc(strlen(ptr) + 1)) == NULL)
    764 				die(gettext("cannot allocate buffer"));
    765 
    766 			(void) strcpy(devpaths[nfds], ptr);
    767 			nfds++;
    768 			if (nfds >= cachesize)
    769 				break;
    770 		}
    771 		ptr = p;
    772 	}
    773 	(void) close(sysmfd);
    774 
    775 	/* no point polling if no devices with carrier */
    776 	if (nfds == 0)
    777 		return;
    778 
    779 	for (;;) {
    780 		/* daemon sleeps waiting for a hangup on the console */
    781 		ret = poll(fds, nfds, INFTIM);
    782 		if (ret == -1) {
    783 			/* Check if ttymon is trying to get rid of us */
    784 			if (errno == EINTR)
    785 				continue;
    786 			warn(gettext("cannot poll device"));
    787 			return;
    788 		} else if (ret == 0) {
    789 			warn(gettext("timeout (%d milleseconds) occured\n"),
    790 			    INFTIM);
    791 			return;
    792 		} else {
    793 			/* Go through poll list looking for events. */
    794 			for (index = 0; index < nfds; index++) {
    795 				/* expected result */
    796 				if ((fds[index].revents & POLLHUP) ==
    797 				    POLLHUP) {
    798 					/*
    799 					 * unsetaux console.  Take out of list
    800 					 * of current auxiliary consoles.
    801 					 */
    802 					unsetaux((char *)devpaths[index]);
    803 					warn(gettext(
    804 				    "lost carrier, unsetting console %s\n"),
    805 					    devpaths[index]);
    806 					syslog(LOG_WARNING,
    807 			    "%s: lost carrier, unsetting auxiliary device %s",
    808 					    CONSADM, devpaths[index]);
    809 					free(devpaths[index]);
    810 					devpaths[index] = NULL;
    811 					(void) close(fds[index].fd);
    812 					fds[index].fd = -1;
    813 					fds[index].revents = 0;
    814 					continue;
    815 				}
    816 				if ((fds[index].revents & POLLERR) ==
    817 				    POLLERR) {
    818 					warn(gettext("poll error\n"));
    819 					continue;
    820 				} else if (fds[index].revents != 0) {
    821 					warn(gettext(
    822 					    "unexpected poll result 0x%x\n"),
    823 					    fds[index].revents);
    824 					continue;
    825 				}
    826 			}
    827 			/* check whether any left to poll */
    828 			pollagain = B_FALSE;
    829 			for (index = 0; index < nfds; index++)
    830 				if (fds[index].fd != -1)
    831 					pollagain = B_TRUE;
    832 			if (pollagain == B_TRUE)
    833 				continue;
    834 			else
    835 				return;
    836 		}
    837 	}
    838 }
    839 
    840 static void
    841 persistlist(void)
    842 {
    843 	FILE	*fp;
    844 	char	value[MAXPATHLEN + 1];
    845 	int	lckfd;
    846 
    847 	lckfd = getlock();
    848 
    849 	if ((fp = fopen(CONSCONFIG, "r")) != NULL) {
    850 		while (fgets(value, MAXPATHLEN, fp) != NULL) {
    851 			/* skip comments */
    852 			if (value[0] == COMMENT ||
    853 			    value[0] == NEWLINE ||
    854 			    value[0] == SPACE || value[0] == TAB)
    855 				continue;
    856 			(void) fprintf(stdout, "%s", value);
    857 		}
    858 		(void) fclose(fp);
    859 	}
    860 	(void) close(lckfd);
    861 	(void) unlink(CONSADMLOCK);
    862 }
    863 
    864 static int
    865 verifyarg(char *dev, int flag)
    866 {
    867 	struct stat	st;
    868 	int	fd;
    869 	int	ret = 0;
    870 
    871 	if (dev == NULL) {
    872 		warn(gettext("specify device(s)\n"));
    873 		ret = 1;
    874 		goto err_exit;
    875 	}
    876 
    877 	if (dev[0] != '/') {
    878 		warn(gettext("device name must begin with a '/'\n"));
    879 		ret = 1;
    880 		goto err_exit;
    881 	}
    882 
    883 	if ((pathcmp(dev, SYSMSG) == 0) ||
    884 	    (pathcmp(dev, WSCONS) == 0) ||
    885 	    (pathcmp(dev, CONSOLE) == 0)) {
    886 		/* they match */
    887 		warn(gettext("invalid device %s\n"), dev);
    888 		ret = 1;
    889 		goto err_exit;
    890 	}
    891 
    892 	if (stat(dev, &st) || ! S_ISCHR(st.st_mode)) {
    893 		warn(gettext("invalid device %s\n"), dev);
    894 		ret = 1;
    895 		goto err_exit;
    896 	}
    897 
    898 	/* Delete operation doesn't require this checking */
    899 	if ((fd = safeopen(dev)) < 0) {
    900 		if (flag) {
    901 			warn(gettext("invalid device %s\n"), dev);
    902 			ret = 1;
    903 		}
    904 		goto err_exit;
    905 	}
    906 	if (!modem_support(fd)) {
    907 		warn(gettext("invalid device %s\n"), dev);
    908 		(void) close(fd);
    909 		ret = 1;
    910 		goto err_exit;
    911 	}
    912 
    913 	/* Only verify carrier if it's an add operation */
    914 	if (flag) {
    915 		if (!has_carrier(fd)) {
    916 			warn(gettext("failure, no carrier on %s\n"), dev);
    917 			ret = 1;
    918 			goto err_exit;
    919 		}
    920 	}
    921 err_exit:
    922 	return (ret);
    923 }
    924 
    925 /*
    926  * Open the pseudo device, but be prepared to catch sigalarm if we block
    927  * cause there isn't any carrier present.
    928  */
    929 static int
    930 safeopen(char *devp)
    931 {
    932 	int	fd;
    933 	struct	sigaction sigact;
    934 
    935 	sigact.sa_flags = SA_RESETHAND | SA_NODEFER;
    936 	sigact.sa_handler = catch_alarm;
    937 	(void) sigemptyset(&sigact.sa_mask);
    938 	(void) sigaction(SIGALRM, &sigact, NULL);
    939 	if (sigsetjmp(deadline, 1) != 0)
    940 		return (-1);
    941 	(void) alarm(5);
    942 	/* The sysmsg driver sets NONBLOCK and NDELAY, but what the hell */
    943 	if ((fd = open(devp, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY)) < 0)
    944 		return (-1);
    945 	(void) alarm(0);
    946 	sigact.sa_flags = 0;
    947 	sigact.sa_handler = SIG_DFL;
    948 	(void) sigemptyset(&sigact.sa_mask);
    949 	(void) sigaction(SIGALRM, &sigact, NULL);
    950 	return (fd);
    951 }
    952 
    953 static int
    954 lckfunc(int fd, int flag)
    955 {
    956 	fl.l_type = flag;
    957 	return (fcntl(fd, F_SETLKW, &fl));
    958 }
    959