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 */
67static struct flock fl;
68#define	LOCK_EX F_WRLCK
69
70static 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 ... */
77static 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";
82const char *pname;		/* program name */
83static sigjmp_buf deadline;
84
85/* command line arguments */
86static int display;
87static int persist;
88static int addflag;
89static int deleteflag;
90
91/* function headers */
92static void setaux(char *);
93static void unsetaux(char *);
94static void getconsole(void);
95static boolean_t has_carrier(int fd);
96static boolean_t modem_support(int fd);
97static void setfallback(char *argv[]);
98static void removefallback(void);
99static void fallbackdaemon(void);
100static void persistlist(void);
101static int verifyarg(char *, int);
102static int safeopen(char *);
103static void catch_term(void);
104static void catch_alarm(void);
105static void catch_hup(void);
106static void cleanup_on_exit(void);
107static void addtolist(char *);
108static void removefromlist(char *);
109static int pathcmp(char *, char *);
110static int lckfunc(int, int);
111typedef void (*sig_handler_t)();
112static 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
120int
121main(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 */
262static void
263catch_term()
264{
265	exit(E_SUCCESS);
266}
267
268/* handle lack of carrier on open */
269static void
270catch_alarm()
271{
272	siglongjmp(deadline, 1);
273}
274
275/* caught a sighup */
276static void
277catch_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. */
288static void
289cleanup_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 */
298static void
299setaux(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 */
326static void
327unsetaux(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
343static int
344getlock(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
363static void
364addtolist(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 */
418static void
419removefromlist(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
481static int
482pathcmp(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 */
505static void
506getconsole(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 */
544static boolean_t
545modem_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
555static boolean_t
556has_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
567static void
568setfallback(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 */
614static void
615removefallback(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 */
653static void
654fallbackdaemon(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
840static void
841persistlist(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
864static int
865verifyarg(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	}
921err_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 */
929static int
930safeopen(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
953static int
954lckfunc(int fd, int flag)
955{
956	fl.l_type = flag;
957	return (fcntl(fd, F_SETLKW, &fl));
958}
959