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