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
main(int argc,char * argv[])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
catch_term()263 catch_term()
264 {
265 exit(E_SUCCESS);
266 }
267
268 /* handle lack of carrier on open */
269 static void
catch_alarm()270 catch_alarm()
271 {
272 siglongjmp(deadline, 1);
273 }
274
275 /* caught a sighup */
276 static void
catch_hup()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
cleanup_on_exit()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
setaux(char * dev)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
unsetaux(char * dev)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
getlock(void)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
addtolist(char * dev)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
removefromlist(char * dev)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
pathcmp(char * adev,char * bdev)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
getconsole(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
modem_support(int fd)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
has_carrier(int fd)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
setfallback(char * argv[])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
removefallback(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
fallbackdaemon(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
persistlist(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
verifyarg(char * dev,int flag)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
safeopen(char * devp)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
lckfunc(int fd,int flag)954 lckfunc(int fd, int flag)
955 {
956 fl.l_type = flag;
957 return (fcntl(fd, F_SETLKW, &fl));
958 }
959