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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
37 #include <sys/sysmsg_impl.h>
39 #include <sys/sysmacros.h>
40 #include <sys/systeminfo.h>
41 #include <sys/termios.h>
42 #include <sys/types.h>
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"
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 */
66 /* useful data structures for lock function */
67 static struct flock fl
;
68 #define LOCK_EX F_WRLCK
72 "\tconsadm [ -p ] [ -a device ... ]\n"
73 "\tconsadm [ -p ] [ -d device ... ]\n"
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"
82 const char *pname
; /* program name */
83 static sigjmp_buf deadline
;
85 /* command line arguments */
89 static int deleteflag
;
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);
115 * In main, return codes carry the following meaning:
117 * 1 - error during the command execution
121 main(int argc
, char *argv
[])
126 char *p
= strrchr(argv
[0], '/');
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 */
139 (void) textdomain(TEXT_DOMAIN
);
142 die(gettext("must be root to run this program\n"));
145 * Handle normal termination signals that may be received.
147 sa
.sa_handler
= SIG_IGN
;
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
);
156 * To make sure persistent state gets removed.
158 sa
.sa_handler
= cleanup_on_exit
;
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
);
166 if (strcmp(pname
, CONSDAEMON
) == 0) {
174 while ((c
= getopt(argc
, argv
, "adp")) != EOF
) {
186 (void) fprintf(stderr
, gettext(usage
));
197 if (addflag
&& deleteflag
) {
198 (void) fprintf(stderr
, gettext(usage
));
202 if (optind
== argc
) {
203 (void) fprintf(stderr
, gettext(usage
));
206 /* separately check every device path specified */
207 for (index
= optind
; index
< argc
; index
++) {
208 if (verifyarg(argv
[index
], addflag
))
212 for (index
= optind
; index
< argc
; index
++) {
215 addtolist(argv
[index
]);
219 * start/restart daemon based on the auxilary
220 * consoles at this time.
224 } else if (deleteflag
) {
225 if (optind
== argc
) {
226 (void) fprintf(stderr
, gettext(usage
));
229 /* separately check every device path specified */
230 for (index
= optind
; index
< argc
; index
++) {
231 if (verifyarg(argv
[index
], 0))
235 for (index
= optind
; index
< argc
; index
++) {
236 unsetaux(argv
[index
]);
237 if (persist
&& deleteflag
)
238 removefromlist(argv
[index
]);
242 * kill off daemon and restart with
243 * new list of auxiliary consoles
247 } else if (persist
) {
249 (void) fprintf(stderr
, gettext(usage
));
256 (void) fprintf(stderr
, gettext(usage
));
261 /* for daemon to handle termination from user command */
268 /* handle lack of carrier on open */
272 siglongjmp(deadline
, 1);
275 /* caught a sighup */
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.
287 /* Remove persistent state on receiving signal. */
291 (void) unlink(CONSADMLOCK
);
296 * send ioctl to /dev/sysmsg to route msgs of the device specified.
303 if ((fd
= safeopen(SYSMSG
)) < 0)
304 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
306 if (ioctl(fd
, CIOCSETCONSOLE
, dev
) != 0) {
308 * Let setting duplicate device be warning, consadm
309 * must proceed to set persistence if requested.
312 die(gettext("%s is already the default console\n"),
314 else if (errno
!= EEXIST
)
315 die(gettext("cannot get table entry"));
317 syslog(LOG_WARNING
, "%s: Added auxiliary device %s", CONSADM
, dev
);
323 * Send ioctl to device specified and
324 * Remove the entry from the list of auxiliary devices.
331 if ((fd
= safeopen(SYSMSG
)) < 0)
332 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
334 if (ioctl(fd
, CIOCRMCONSOLE
, dev
) != 0) {
336 die(gettext("cannot unset the default console\n"));
338 syslog(LOG_WARNING
, "%s: Removed auxiliary device %s",
348 if ((lckfd
= open(CONSADMLOCK
, O_CREAT
| O_EXCL
| O_WRONLY
,
349 S_IRUSR
| S_IWUSR
)) < 0) {
351 die(gettext("currently busy, try again later.\n"));
353 die(gettext("cannot open %s"), CONSADMLOCK
);
355 if (lckfunc(lckfd
, LOCK_EX
) == -1) {
357 (void) unlink(CONSADMLOCK
);
358 die(gettext("fcntl operation failed"));
368 char newfile
[MAXPATHLEN
];
369 char buf
[MAXPATHLEN
];
371 boolean_t found
= B_FALSE
;
373 /* update file of devices configured to get console msgs. */
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
)) {
383 (void) unlink(CONSADMLOCK
);
384 die(gettext("could not create new %s file"), CONSCONFIG
);
387 /* Add header to new file */
388 (void) fprintf(nfp
, "%s", conshdr
);
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
)
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
);
403 (void) fprintf(nfp
, "%s\n", buf
);
406 /* User specified persistent settings */
407 if (found
== B_FALSE
)
408 (void) fprintf(nfp
, "%s\n", dev
);
412 (void) rename(newfile
, CONSCONFIG
);
414 (void) unlink(CONSADMLOCK
);
417 /* The list in CONSCONFIG gives the persistence capability in the proto */
419 removefromlist(char *dev
)
423 char newfile
[MAXPATHLEN
+ 1];
425 char value
[MAXPATHLEN
+ 1];
426 boolean_t newcontents
= B_FALSE
;
428 /* update file of devices configured to get console msgs. */
432 if ((fp
= fopen(CONSCONFIG
, "r")) == NULL
) {
434 (void) unlink(CONSADMLOCK
);
439 (void) snprintf(newfile
, sizeof (newfile
), "%s%d",
440 CONSCONFIG
, (int)getpid());
441 if ((nfp
= fopen(newfile
, "w")) == NULL
) {
443 (void) unlink(CONSADMLOCK
);
444 die(gettext("cannot create new %s file"), CONSCONFIG
);
447 /* Add header to new file */
448 (void) fprintf(nfp
, "%s", conshdr
);
451 * Check whether the path duplicates what is already in the
454 while (fgets(value
, MAXPATHLEN
, fp
) != NULL
) {
456 if (value
[0] == COMMENT
|| value
[0] == NEWLINE
||
457 value
[0] == SPACE
|| value
[0] == TAB
)
460 value
[len
- 1] = NULL
; /* Clear carriage return */
461 if (pathcmp(dev
, value
) == 0) {
462 /* they match so don't write it */
465 (void) fprintf(nfp
, "%s\n", value
);
466 newcontents
= B_TRUE
;
470 /* Remove the file if there aren't any auxiliary consoles */
472 (void) rename(newfile
, CONSCONFIG
);
474 (void) unlink(CONSCONFIG
);
475 (void) unlink(newfile
);
478 (void) unlink(CONSADMLOCK
);
482 pathcmp(char *adev
, char *bdev
)
487 if (strcmp(adev
, bdev
) == 0)
490 if (stat(adev
, &st1
) != 0 || !S_ISCHR(st1
.st_mode
))
491 die(gettext("invalid device %s\n"), adev
);
493 if (stat(bdev
, &st2
) != 0 || !S_ISCHR(st2
.st_mode
))
494 die(gettext("invalid device %s\n"), bdev
);
496 if (st1
.st_rdev
== st2
.st_rdev
)
503 * Display configured consoles.
509 int bufsize
= 0; /* size of device cache */
510 char *infop
, *ptr
, *p
; /* info structure for ioctl's */
512 if ((fd
= safeopen(SYSMSG
)) < 0)
513 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
515 if ((bufsize
= ioctl(fd
, CIOCGETCONSOLE
, NULL
)) < 0)
516 die(gettext("cannot get table entry\n"));
520 if ((infop
= calloc(bufsize
, sizeof (char))) == NULL
)
521 die(gettext("cannot allocate buffer"));
523 if (ioctl(fd
, CIOCGETCONSOLE
, infop
) < 0)
524 die(gettext("cannot get table entry\n"));
527 while (ptr
!= NULL
) {
528 p
= strchr(ptr
, ' ');
530 (void) printf("%s\n", ptr
);
534 (void) printf("%s\n", ptr
);
541 * It is supposed that if the device supports TIOCMGET then it
542 * might be a serial device.
545 modem_support(int fd
)
549 if (ioctl(fd
, TIOCMGET
, &modem_state
) == 0)
560 if (ioctl(fd
, TIOCMGET
, &modem_state
) == 0)
561 return ((modem_state
& TIOCM_CAR
) != 0);
568 setfallback(char *argv
[])
572 char *cmd
= CONSADMD
;
578 * kill off any existing daemon
579 * remove /etc/consadm.pid
583 /* kick off a daemon */
584 if ((pid
= fork()) == (pid_t
)0) {
585 /* always fallback to /dev/console */
592 if ((fd
= open(MSGLOG
, O_RDWR
)) < 0)
593 die(gettext("cannot open %s"), MSGLOG
);
596 (void) execv(cmd
, argv
);
598 } else if (pid
== -1)
599 die(gettext("%s not started"), CONSADMD
);
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
);
607 (void) unlink(CONSADMLOCK
);
611 * Remove the daemon that would have implemented the automatic
612 * fallback in event of carrier loss on the serial console.
620 if ((fp
= fopen(SETCONSOLEPID
, "r+")) == NULL
)
621 /* file doesn't exist, so no work to do */
624 if (fscanf(fp
, "%d\n", &pid
) <= 0) {
626 (void) unlink(SETCONSOLEPID
);
631 * Don't shoot ourselves in the foot by killing init,
632 * sched, pageout, or fsflush.
634 if (pid
== 0 || pid
== 1 || pid
== 2 || pid
== 3) {
635 (void) unlink(SETCONSOLEPID
);
639 * kill off the existing daemon listed in
642 (void) kill((pid_t
)pid
, SIGTERM
);
645 (void) unlink(SETCONSOLEPID
);
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.
656 int fd
, sysmfd
, ret
= 0;
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 */
668 * catch SIGTERM cause it might be coming from user via consadm
670 sa
.sa_handler
= catch_term
;
672 (void) sigemptyset(&sa
.sa_mask
);
673 (void) sigaction(SIGTERM
, &sa
, NULL
);
676 * catch SIGHUP cause it might be coming from a disconnect
678 sa
.sa_handler
= catch_hup
;
680 (void) sigemptyset(&sa
.sa_mask
);
681 (void) sigaction(SIGHUP
, &sa
, NULL
);
683 if ((sysmfd
= safeopen(SYSMSG
)) < 0)
684 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
686 if ((bufsize
= ioctl(sysmfd
, CIOCGETCONSOLE
, NULL
)) < 0)
687 die(gettext("cannot get table entry\n"));
691 if ((infop
= calloc(bufsize
, sizeof (char))) == NULL
)
692 die(gettext("cannot allocate buffer"));
694 if (ioctl(sysmfd
, CIOCGETCONSOLE
, infop
) < 0)
695 die(gettext("cannot get table entry\n"));
698 while (ptr
!= NULL
) {
699 p
= strchr(ptr
, ' ');
709 if ((fds
= calloc(cachesize
, sizeof (struct pollfd
))) == NULL
)
710 die(gettext("cannot allocate buffer"));
712 if ((devpaths
= calloc(cachesize
, sizeof (char *))) == NULL
)
713 die(gettext("cannot allocate buffer"));
716 while (ptr
!= NULL
) {
717 p
= strchr(ptr
, ' ');
719 if ((fd
= safeopen(ptr
)) < 0) {
720 warn(gettext("cannot open %s, continuing"),
724 if (!has_carrier(fd
)) {
727 "no carrier on %s, device will not be monitored.\n"),
732 fds
[nfds
].events
= 0;
734 if ((devpaths
[nfds
] =
735 malloc(strlen(ptr
) + 1)) == NULL
)
736 die(gettext("cannot allocate buffer"));
738 (void) strcpy(devpaths
[nfds
], ptr
);
740 if (nfds
>= cachesize
)
747 if ((fd
= safeopen(ptr
)) < 0) {
748 warn(gettext("cannot open %s, continuing"), ptr
);
752 if (!has_carrier(fd
)) {
755 "no carrier on %s, device will not be monitored.\n"),
761 fds
[nfds
].events
= 0;
763 if ((devpaths
[nfds
] = malloc(strlen(ptr
) + 1)) == NULL
)
764 die(gettext("cannot allocate buffer"));
766 (void) strcpy(devpaths
[nfds
], ptr
);
768 if (nfds
>= cachesize
)
773 (void) close(sysmfd
);
775 /* no point polling if no devices with carrier */
780 /* daemon sleeps waiting for a hangup on the console */
781 ret
= poll(fds
, nfds
, INFTIM
);
783 /* Check if ttymon is trying to get rid of us */
786 warn(gettext("cannot poll device"));
788 } else if (ret
== 0) {
789 warn(gettext("timeout (%d milleseconds) occured\n"),
793 /* Go through poll list looking for events. */
794 for (index
= 0; index
< nfds
; index
++) {
795 /* expected result */
796 if ((fds
[index
].revents
& POLLHUP
) ==
799 * unsetaux console. Take out of list
800 * of current auxiliary consoles.
802 unsetaux((char *)devpaths
[index
]);
804 "lost carrier, unsetting console %s\n"),
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
);
813 fds
[index
].revents
= 0;
816 if ((fds
[index
].revents
& POLLERR
) ==
818 warn(gettext("poll error\n"));
820 } else if (fds
[index
].revents
!= 0) {
822 "unexpected poll result 0x%x\n"),
827 /* check whether any left to poll */
829 for (index
= 0; index
< nfds
; index
++)
830 if (fds
[index
].fd
!= -1)
832 if (pollagain
== B_TRUE
)
844 char value
[MAXPATHLEN
+ 1];
849 if ((fp
= fopen(CONSCONFIG
, "r")) != NULL
) {
850 while (fgets(value
, MAXPATHLEN
, fp
) != NULL
) {
852 if (value
[0] == COMMENT
||
853 value
[0] == NEWLINE
||
854 value
[0] == SPACE
|| value
[0] == TAB
)
856 (void) fprintf(stdout
, "%s", value
);
861 (void) unlink(CONSADMLOCK
);
865 verifyarg(char *dev
, int flag
)
872 warn(gettext("specify device(s)\n"));
878 warn(gettext("device name must begin with a '/'\n"));
883 if ((pathcmp(dev
, SYSMSG
) == 0) ||
884 (pathcmp(dev
, WSCONS
) == 0) ||
885 (pathcmp(dev
, CONSOLE
) == 0)) {
887 warn(gettext("invalid device %s\n"), dev
);
892 if (stat(dev
, &st
) || ! S_ISCHR(st
.st_mode
)) {
893 warn(gettext("invalid device %s\n"), dev
);
898 /* Delete operation doesn't require this checking */
899 if ((fd
= safeopen(dev
)) < 0) {
901 warn(gettext("invalid device %s\n"), dev
);
906 if (!modem_support(fd
)) {
907 warn(gettext("invalid device %s\n"), dev
);
913 /* Only verify carrier if it's an add operation */
915 if (!has_carrier(fd
)) {
916 warn(gettext("failure, no carrier on %s\n"), dev
);
926 * Open the pseudo device, but be prepared to catch sigalarm if we block
927 * cause there isn't any carrier present.
933 struct sigaction sigact
;
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)
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)
947 sigact
.sa_handler
= SIG_DFL
;
948 (void) sigemptyset(&sigact
.sa_mask
);
949 (void) sigaction(SIGALRM
, &sigact
, NULL
);
954 lckfunc(int fd
, int flag
)
957 return (fcntl(fd
, F_SETLKW
, &fl
));