xref: /illumos-gate/usr/src/cmd/consadm/consadm.c (revision 7c478bd9)
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 2004 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) || (st1.st_mode & S_IFCHR) == 0)
491 		die(gettext("invalid device %s\n"), adev);
492 
493 	if (stat(bdev, &st2) || (st2.st_mode & S_IFCHR) == 0)
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