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