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.
35 #include <sys/sysmsg_impl.h>
37 #include <sys/sysmacros.h>
38 #include <sys/systeminfo.h>
39 #include <sys/termios.h>
40 #include <sys/types.h>
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"
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 */
64 /* useful data structures for lock function */
65 static struct flock fl
;
66 #define LOCK_EX F_WRLCK
70 "\tconsadm [ -p ] [ -a device ... ]\n"
71 "\tconsadm [ -p ] [ -d device ... ]\n"
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(1m) instead.\n"
80 const char *pname
; /* program name */
81 static sigjmp_buf deadline
;
83 /* command line arguments */
87 static int deleteflag
;
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);
113 * In main, return codes carry the following meaning:
115 * 1 - error during the command execution
119 main(int argc
, char *argv
[])
124 char *p
= strrchr(argv
[0], '/');
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 */
137 (void) textdomain(TEXT_DOMAIN
);
140 die(gettext("must be root to run this program\n"));
143 * Handle normal termination signals that may be received.
145 sa
.sa_handler
= SIG_IGN
;
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
);
154 * To make sure persistent state gets removed.
156 sa
.sa_handler
= cleanup_on_exit
;
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
);
164 if (strcmp(pname
, CONSDAEMON
) == 0) {
172 while ((c
= getopt(argc
, argv
, "adp")) != EOF
) {
184 (void) fprintf(stderr
, gettext(usage
));
195 if (addflag
&& deleteflag
) {
196 (void) fprintf(stderr
, gettext(usage
));
200 if (optind
== argc
) {
201 (void) fprintf(stderr
, gettext(usage
));
204 /* separately check every device path specified */
205 for (index
= optind
; index
< argc
; index
++) {
206 if (verifyarg(argv
[index
], addflag
))
210 for (index
= optind
; index
< argc
; index
++) {
213 addtolist(argv
[index
]);
217 * start/restart daemon based on the auxilary
218 * consoles at this time.
222 } else if (deleteflag
) {
223 if (optind
== argc
) {
224 (void) fprintf(stderr
, gettext(usage
));
227 /* separately check every device path specified */
228 for (index
= optind
; index
< argc
; index
++) {
229 if (verifyarg(argv
[index
], 0))
233 for (index
= optind
; index
< argc
; index
++) {
234 unsetaux(argv
[index
]);
235 if (persist
&& deleteflag
)
236 removefromlist(argv
[index
]);
240 * kill off daemon and restart with
241 * new list of auxiliary consoles
245 } else if (persist
) {
247 (void) fprintf(stderr
, gettext(usage
));
254 (void) fprintf(stderr
, gettext(usage
));
259 /* for daemon to handle termination from user command */
266 /* handle lack of carrier on open */
270 siglongjmp(deadline
, 1);
273 /* caught a sighup */
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.
285 /* Remove persistent state on receiving signal. */
289 (void) unlink(CONSADMLOCK
);
294 * send ioctl to /dev/sysmsg to route msgs of the device specified.
301 if ((fd
= safeopen(SYSMSG
)) < 0)
302 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
304 if (ioctl(fd
, CIOCSETCONSOLE
, dev
) != 0) {
306 * Let setting duplicate device be warning, consadm
307 * must proceed to set persistence if requested.
310 die(gettext("%s is already the default console\n"),
312 else if (errno
!= EEXIST
)
313 die(gettext("cannot get table entry"));
315 syslog(LOG_WARNING
, "%s: Added auxiliary device %s", CONSADM
, dev
);
321 * Send ioctl to device specified and
322 * Remove the entry from the list of auxiliary devices.
329 if ((fd
= safeopen(SYSMSG
)) < 0)
330 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
332 if (ioctl(fd
, CIOCRMCONSOLE
, dev
) != 0) {
334 die(gettext("cannot unset the default console\n"));
336 syslog(LOG_WARNING
, "%s: Removed auxiliary device %s",
346 if ((lckfd
= open(CONSADMLOCK
, O_CREAT
| O_EXCL
| O_WRONLY
,
347 S_IRUSR
| S_IWUSR
)) < 0) {
349 die(gettext("currently busy, try again later.\n"));
351 die(gettext("cannot open %s"), CONSADMLOCK
);
353 if (lckfunc(lckfd
, LOCK_EX
) == -1) {
355 (void) unlink(CONSADMLOCK
);
356 die(gettext("fcntl operation failed"));
366 char newfile
[MAXPATHLEN
];
367 char buf
[MAXPATHLEN
];
369 boolean_t found
= B_FALSE
;
371 /* update file of devices configured to get console msgs. */
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
)) {
381 (void) unlink(CONSADMLOCK
);
382 die(gettext("could not create new %s file"), CONSCONFIG
);
385 /* Add header to new file */
386 (void) fprintf(nfp
, "%s", conshdr
);
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
)
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
);
401 (void) fprintf(nfp
, "%s\n", buf
);
404 /* User specified persistent settings */
405 if (found
== B_FALSE
)
406 (void) fprintf(nfp
, "%s\n", dev
);
410 (void) rename(newfile
, CONSCONFIG
);
412 (void) unlink(CONSADMLOCK
);
415 /* The list in CONSCONFIG gives the persistence capability in the proto */
417 removefromlist(char *dev
)
421 char newfile
[MAXPATHLEN
+ 1];
423 char value
[MAXPATHLEN
+ 1];
424 boolean_t newcontents
= B_FALSE
;
426 /* update file of devices configured to get console msgs. */
430 if ((fp
= fopen(CONSCONFIG
, "r")) == NULL
) {
432 (void) unlink(CONSADMLOCK
);
437 (void) snprintf(newfile
, sizeof (newfile
), "%s%d",
438 CONSCONFIG
, (int)getpid());
439 if ((nfp
= fopen(newfile
, "w")) == NULL
) {
441 (void) unlink(CONSADMLOCK
);
442 die(gettext("cannot create new %s file"), CONSCONFIG
);
445 /* Add header to new file */
446 (void) fprintf(nfp
, "%s", conshdr
);
449 * Check whether the path duplicates what is already in the
452 while (fgets(value
, MAXPATHLEN
, fp
) != NULL
) {
454 if (value
[0] == COMMENT
|| value
[0] == NEWLINE
||
455 value
[0] == SPACE
|| value
[0] == TAB
)
458 value
[len
- 1] = '\0'; /* Clear carriage return */
459 if (pathcmp(dev
, value
) == 0) {
460 /* they match so don't write it */
463 (void) fprintf(nfp
, "%s\n", value
);
464 newcontents
= B_TRUE
;
468 /* Remove the file if there aren't any auxiliary consoles */
470 (void) rename(newfile
, CONSCONFIG
);
472 (void) unlink(CONSCONFIG
);
473 (void) unlink(newfile
);
476 (void) unlink(CONSADMLOCK
);
480 pathcmp(char *adev
, char *bdev
)
485 if (strcmp(adev
, bdev
) == 0)
488 if (stat(adev
, &st1
) != 0 || !S_ISCHR(st1
.st_mode
))
489 die(gettext("invalid device %s\n"), adev
);
491 if (stat(bdev
, &st2
) != 0 || !S_ISCHR(st2
.st_mode
))
492 die(gettext("invalid device %s\n"), bdev
);
494 if (st1
.st_rdev
== st2
.st_rdev
)
501 * Display configured consoles.
507 int bufsize
= 0; /* size of device cache */
508 char *infop
, *ptr
, *p
; /* info structure for ioctl's */
510 if ((fd
= safeopen(SYSMSG
)) < 0)
511 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
513 if ((bufsize
= ioctl(fd
, CIOCGETCONSOLE
, NULL
)) < 0)
514 die(gettext("cannot get table entry\n"));
518 if ((infop
= calloc(bufsize
, sizeof (char))) == NULL
)
519 die(gettext("cannot allocate buffer"));
521 if (ioctl(fd
, CIOCGETCONSOLE
, infop
) < 0)
522 die(gettext("cannot get table entry\n"));
525 while (ptr
!= NULL
) {
526 p
= strchr(ptr
, ' ');
528 (void) printf("%s\n", ptr
);
532 (void) printf("%s\n", ptr
);
539 * It is supposed that if the device supports TIOCMGET then it
540 * might be a serial device.
543 modem_support(int fd
)
547 if (ioctl(fd
, TIOCMGET
, &modem_state
) == 0)
558 if (ioctl(fd
, TIOCMGET
, &modem_state
) == 0)
559 return ((modem_state
& TIOCM_CAR
) != 0);
566 setfallback(char *argv
[])
570 char *cmd
= CONSADMD
;
576 * kill off any existing daemon
577 * remove /etc/consadm.pid
581 /* kick off a daemon */
582 if ((pid
= fork()) == (pid_t
)0) {
583 /* always fallback to /dev/console */
590 if ((fd
= open(MSGLOG
, O_RDWR
)) < 0)
591 die(gettext("cannot open %s"), MSGLOG
);
594 (void) execv(cmd
, argv
);
596 } else if (pid
== -1)
597 die(gettext("%s not started"), CONSADMD
);
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
);
605 (void) unlink(CONSADMLOCK
);
609 * Remove the daemon that would have implemented the automatic
610 * fallback in event of carrier loss on the serial console.
618 if ((fp
= fopen(SETCONSOLEPID
, "r+")) == NULL
)
619 /* file doesn't exist, so no work to do */
622 if (fscanf(fp
, "%d\n", &pid
) <= 0) {
624 (void) unlink(SETCONSOLEPID
);
629 * Don't shoot ourselves in the foot by killing init,
630 * sched, pageout, or fsflush.
632 if (pid
== 0 || pid
== 1 || pid
== 2 || pid
== 3) {
633 (void) unlink(SETCONSOLEPID
);
637 * kill off the existing daemon listed in
640 (void) kill((pid_t
)pid
, SIGTERM
);
643 (void) unlink(SETCONSOLEPID
);
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.
654 int fd
, sysmfd
, ret
= 0;
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 */
666 * catch SIGTERM cause it might be coming from user via consadm
668 sa
.sa_handler
= catch_term
;
670 (void) sigemptyset(&sa
.sa_mask
);
671 (void) sigaction(SIGTERM
, &sa
, NULL
);
674 * catch SIGHUP cause it might be coming from a disconnect
676 sa
.sa_handler
= catch_hup
;
678 (void) sigemptyset(&sa
.sa_mask
);
679 (void) sigaction(SIGHUP
, &sa
, NULL
);
681 if ((sysmfd
= safeopen(SYSMSG
)) < 0)
682 die(gettext("%s is missing or not a valid device\n"), SYSMSG
);
684 if ((bufsize
= ioctl(sysmfd
, CIOCGETCONSOLE
, NULL
)) < 0)
685 die(gettext("cannot get table entry\n"));
689 if ((infop
= calloc(bufsize
, sizeof (char))) == NULL
)
690 die(gettext("cannot allocate buffer"));
692 if (ioctl(sysmfd
, CIOCGETCONSOLE
, infop
) < 0)
693 die(gettext("cannot get table entry\n"));
696 while (ptr
!= NULL
) {
697 p
= strchr(ptr
, ' ');
707 if ((fds
= calloc(cachesize
, sizeof (struct pollfd
))) == NULL
)
708 die(gettext("cannot allocate buffer"));
710 if ((devpaths
= calloc(cachesize
, sizeof (char *))) == NULL
)
711 die(gettext("cannot allocate buffer"));
714 while (ptr
!= NULL
) {
715 p
= strchr(ptr
, ' ');
717 if ((fd
= safeopen(ptr
)) < 0) {
718 warn(gettext("cannot open %s, continuing"),
722 if (!has_carrier(fd
)) {
725 "no carrier on %s, device will not be monitored.\n"),
730 fds
[nfds
].events
= 0;
732 if ((devpaths
[nfds
] =
733 malloc(strlen(ptr
) + 1)) == NULL
)
734 die(gettext("cannot allocate buffer"));
736 (void) strcpy(devpaths
[nfds
], ptr
);
738 if (nfds
>= cachesize
)
745 if ((fd
= safeopen(ptr
)) < 0) {
746 warn(gettext("cannot open %s, continuing"), ptr
);
750 if (!has_carrier(fd
)) {
753 "no carrier on %s, device will not be monitored.\n"),
759 fds
[nfds
].events
= 0;
761 if ((devpaths
[nfds
] = malloc(strlen(ptr
) + 1)) == NULL
)
762 die(gettext("cannot allocate buffer"));
764 (void) strcpy(devpaths
[nfds
], ptr
);
766 if (nfds
>= cachesize
)
771 (void) close(sysmfd
);
773 /* no point polling if no devices with carrier */
778 /* daemon sleeps waiting for a hangup on the console */
779 ret
= poll(fds
, nfds
, INFTIM
);
781 /* Check if ttymon is trying to get rid of us */
784 warn(gettext("cannot poll device"));
786 } else if (ret
== 0) {
787 warn(gettext("timeout (%d milleseconds) occured\n"),
791 /* Go through poll list looking for events. */
792 for (index
= 0; index
< nfds
; index
++) {
793 /* expected result */
794 if ((fds
[index
].revents
& POLLHUP
) ==
797 * unsetaux console. Take out of list
798 * of current auxiliary consoles.
800 unsetaux((char *)devpaths
[index
]);
802 "lost carrier, unsetting console %s\n"),
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
);
811 fds
[index
].revents
= 0;
814 if ((fds
[index
].revents
& POLLERR
) ==
816 warn(gettext("poll error\n"));
818 } else if (fds
[index
].revents
!= 0) {
820 "unexpected poll result 0x%x\n"),
825 /* check whether any left to poll */
827 for (index
= 0; index
< nfds
; index
++)
828 if (fds
[index
].fd
!= -1)
830 if (pollagain
== B_TRUE
)
842 char value
[MAXPATHLEN
+ 1];
847 if ((fp
= fopen(CONSCONFIG
, "r")) != NULL
) {
848 while (fgets(value
, MAXPATHLEN
, fp
) != NULL
) {
850 if (value
[0] == COMMENT
||
851 value
[0] == NEWLINE
||
852 value
[0] == SPACE
|| value
[0] == TAB
)
854 (void) fprintf(stdout
, "%s", value
);
859 (void) unlink(CONSADMLOCK
);
863 verifyarg(char *dev
, int flag
)
870 warn(gettext("specify device(s)\n"));
876 warn(gettext("device name must begin with a '/'\n"));
881 if ((pathcmp(dev
, SYSMSG
) == 0) ||
882 (pathcmp(dev
, WSCONS
) == 0) ||
883 (pathcmp(dev
, CONSOLE
) == 0)) {
885 warn(gettext("invalid device %s\n"), dev
);
890 if (stat(dev
, &st
) || ! S_ISCHR(st
.st_mode
)) {
891 warn(gettext("invalid device %s\n"), dev
);
896 /* Delete operation doesn't require this checking */
897 if ((fd
= safeopen(dev
)) < 0) {
899 warn(gettext("invalid device %s\n"), dev
);
904 if (!modem_support(fd
)) {
905 warn(gettext("invalid device %s\n"), dev
);
911 /* Only verify carrier if it's an add operation */
913 if (!has_carrier(fd
)) {
914 warn(gettext("failure, no carrier on %s\n"), dev
);
924 * Open the pseudo device, but be prepared to catch sigalarm if we block
925 * cause there isn't any carrier present.
931 struct sigaction sigact
;
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)
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)
945 sigact
.sa_handler
= SIG_DFL
;
946 (void) sigemptyset(&sigact
.sa_mask
);
947 (void) sigaction(SIGALRM
, &sigact
, NULL
);
952 lckfunc(int fd
, int flag
)
955 return (fcntl(fd
, F_SETLKW
, &fl
));