diff --git a/usr.sbin/ifwatchd/ifwatchd.8 b/usr.sbin/ifwatchd/ifwatchd.8 index 11e2fc3c6e4a..ec89994161fe 100644 --- a/usr.sbin/ifwatchd/ifwatchd.8 +++ b/usr.sbin/ifwatchd/ifwatchd.8 @@ -1,4 +1,4 @@ -.\" $NetBSD: ifwatchd.8,v 1.19 2003/07/04 12:48:30 wiz Exp $ +.\" $NetBSD: ifwatchd.8,v 1.20 2004/01/04 22:19:51 martin Exp $ .\" .\" Copyright (c) 2001-2003 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -34,7 +34,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd March 5, 2003 +.Dd January 4, 2004 .Os .Dt IFWATCHD 8 .Sh NAME @@ -47,11 +47,14 @@ .Op Fl D Ar departure-script .Op Fl d Ar down-script .Op Fl u Ar up-script +.Op Fl c Ar carrier-script +.Op Fl n Ar no-carrier-script .Ar ifname(s) .Sh DESCRIPTION .Nm is used to monitor dynamic interfaces (for example PPP interfaces) -for address changes. +for address changes, and to monitor static interfaces for carrier +changes. Sometimes these interfaces are accompanied by a daemon program, which can take care of running any necessary scripts (like .Xr pppd 8 @@ -61,13 +64,15 @@ but sometimes the interfaces run completely autonomously (like .Xr pppoe 4 ) . .Pp .Nm -provides a generic way to watch this type of changes. +provides a generic way to watch these types of changes. It works by monitoring the routing socket and interpreting .Ql RTM_NEWADDR -.Pq address added -and +.Pq address added , .Ql RTM_DELADDR .Pq address deleted +and +.Ql RTM_IFINFO +.Pq carrier detect or loss of carrier messages. It does not need special privileges to do this. The scripts called for up or down events are run with the same user @@ -80,6 +85,9 @@ The following options are available: .It Fl A Ar arrival-script Specify the command to invoke on arrival of new interfaces (like PCMCIA cards). +.It Fl c Ar carrier-script +Specify the command to invoke when the carrier status transitions from +no carrier to carrier. .It Fl D Ar departure-script Specify the command to invoke when an interface departs (for example a PCMCIA card is removed.) @@ -105,12 +113,11 @@ By default .Nm calls them on startup to account for this (and make the scripts easier.) +.It Fl n Ar no-carrier-script +Specify the command to invoke when the carrier status transitions from +carrier to no carrier. .It Fl q Be quiet and don't log non-error messages to syslog. -.It Ar ifname(s) -The name of the interface to watch. -Multiple interfaces may be specified. -Events for other interfaces are ignored. .It Fl u Ar up-script Specify the command to invoke on .Dq interface up @@ -120,11 +127,12 @@ Run in verbose debug mode and do not detach from the controlling terminal. Output verbose progress messages and flag errors ignored during normal operation. -Adding more -.Fl v -increases the verbosity. .Em You do not want to use this option in .Pa /etc/rc.conf ! +.It Ar ifname(s) +The name of the interface to watch. +Multiple interfaces may be specified. +Events for other interfaces are ignored. .El .Sh EXAMPLES .Bd -literal -offset indent @@ -159,6 +167,33 @@ Use in your .Pa /etc/ifconfig.pppoe0 file in the on-demand case. +.Pp +The next example is for dhclient users. +.Bd -literal -offset indent +# ifwatchd -i -c /etc/dhcp/carrier-detect tlp0 +.Ed +.Pp +With the above command, the carrier-detect script will be invoked when +a carrier is detected on the interface +.Ar tlp0 . +Note that the +.Ar -i +flag prevents any action based on the initial state. +A script like the following should work for most users, although it +will not work for machines with multiple interfaces running +.Cm dhclient . +.Bd -literal -offset indent +#! /bin/sh +# Arguments: ifname tty speed address destination +# If there is a dhclient already running, kill it. +# (This step could be put in a distinct no-carrier script, +# if desired.) +if [ -f /var/run/dhclient.pid ]; then + /bin/kill `/bin/cat /var/run/dhclient.pid` +fi +# Start dhclient again on this interface +/sbin/dhclient $1 +.Ed .Sh PARAMETERS PASSED TO SCRIPTS The invoked scripts get passed these parameters: .Bl -tag -width destination diff --git a/usr.sbin/ifwatchd/ifwatchd.c b/usr.sbin/ifwatchd/ifwatchd.c index 1ae6016ef3cf..61f7b2d1b727 100644 --- a/usr.sbin/ifwatchd/ifwatchd.c +++ b/usr.sbin/ifwatchd/ifwatchd.c @@ -1,4 +1,4 @@ -/* $NetBSD: ifwatchd.c,v 1.18 2003/12/27 00:05:46 martin Exp $ */ +/* $NetBSD: ifwatchd.c,v 1.19 2004/01/04 22:19:51 martin Exp $ */ /*- * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc. @@ -66,7 +66,8 @@ #include #include -enum event { ARRIVAL, DEPARTURE, UP, DOWN }; +enum event { ARRIVAL, DEPARTURE, UP, DOWN, CARRIER, NO_CARRIER }; + /* local functions */ static void usage(void); static void dispatch(void*, size_t); @@ -74,6 +75,7 @@ static void check_addrs(char *cp, int addrs, enum event ev); static void invoke_script(struct sockaddr *sa, struct sockaddr *dst, enum event ev, int ifindex, const char *ifname_hint); static void list_interfaces(const char *ifnames); static void check_announce(struct if_announcemsghdr *ifan); +static void check_carrier(int if_index, int carrier); static void rescan_interfaces(void); static void free_interfaces(void); static int find_interface(int index); @@ -100,18 +102,23 @@ static const char *arrival_script = NULL; static const char *departure_script = NULL; static const char *up_script = NULL; static const char *down_script = NULL; -static char DummyTTY[] = _PATH_DEVNULL; -static char DummySpeed[] = "9600"; +static const char *carrier_script = NULL; +static const char *no_carrier_script = NULL; +static const char DummyTTY[] = _PATH_DEVNULL; +static const char DummySpeed[] = "9600"; static const char **scripts[] = { &arrival_script, &departure_script, &up_script, - &down_script + &down_script, + &carrier_script, + &no_carrier_script }; struct interface_data { SLIST_ENTRY(interface_data) next; int index; + int last_carrier_status; char * ifname; }; SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs); @@ -124,21 +131,32 @@ main(int argc, char **argv) char msg[2048], *msgp; openlog(argv[0], LOG_PID|LOG_CONS, LOG_DAEMON); - while ((c = getopt(argc, argv, "qvhiu:d:A:D:")) != -1) + while ((c = getopt(argc, argv, "qvhic:n:u:d:A:D:")) != -1) switch (c) { case 'h': usage(); return 0; + case 'i': inhibit_initial = 1; break; + case 'v': verbose++; break; + case 'q': quiet = 1; break; + case 'c': + carrier_script = optarg; + break; + + case 'n': + no_carrier_script = optarg; + break; + case 'u': up_script = optarg; break; @@ -174,6 +192,8 @@ main(int argc, char **argv) up_script, down_script); printf("arrival_script: %s\ndeparture_script: %s\n", arrival_script, departure_script); + printf("carrier_script: %s\nno_carrier_script: %s\n", + carrier_script, no_carrier_script); printf("verbosity = %d\n", verbose); } @@ -218,11 +238,14 @@ usage() fprintf(stderr, "usage:\n" "\tifwatchd [-hiqv] [-A arrival-script] [-D departure-script]\n" - "\t\t [-d down-script] [-u up-script] ifname(s)\n" + "\t\t [-d down-script] [-u up-script]\n" + "\t\t [-c carrier-script] [-n no-carrier-script] ifname(s)\n" "\twhere:\n" "\t -A specify command to run on interface arrival event\n" + "\t -c specify command to run on interface carrier-detect event\n" "\t -D specify command to run on interface departure event\n" "\t -d specify command to run on interface down event\n" + "\t -n specify command to run on interface no-carrier-detect event\n" "\t -h show this help message\n" "\t -i no (!) initial run of the up script if the interface\n" "\t is already up on ifwatchd startup\n" @@ -236,6 +259,7 @@ static void dispatch(void *msg, size_t len) { struct rt_msghdr *hd = msg; + struct if_msghdr *ifmp; struct ifa_msghdr *ifam; enum event ev; @@ -250,6 +274,10 @@ dispatch(void *msg, size_t len) rescan_interfaces(); check_announce((struct if_announcemsghdr *)msg); return; + case RTM_IFINFO: + ifmp = (struct if_msghdr*)msg; + check_carrier(ifmp->ifm_index, ifmp->ifm_data.ifi_link_state); + return; } if (verbose) printf("unknown message ignored\n"); @@ -380,7 +408,8 @@ invoke_script(sa, dest, ev, ifindex, ifname_hint) } } -static void list_interfaces(const char *ifnames) +static void +list_interfaces(const char *ifnames) { char * names = strdup(ifnames); char * name, *lasts; @@ -392,6 +421,7 @@ static void list_interfaces(const char *ifnames) name = strtok_r(NULL, sep, &lasts)) { p = malloc(sizeof(*p)); SLIST_INSERT_HEAD(&ifs, p, next); + p->last_carrier_status = -1; p->ifname = strdup(name); p->index = if_nametoindex(p->ifname); if (!quiet) @@ -403,6 +433,45 @@ static void list_interfaces(const char *ifnames) free(names); } +static void +check_carrier(int if_index, int carrier_status) +{ + struct interface_data * p; + enum event ev; + + SLIST_FOREACH(p, &ifs, next) + if (p->index == if_index) + break; + + if (p == NULL) + return; + + /* + * Treat it as an event worth handling if: + * - the carrier status changed, or + * - this is the first time we've been called, and + * inhibit_initial is not set + */ + + if ((carrier_status != p->last_carrier_status) || + ((p->last_carrier_status == -1) && !inhibit_initial)) { + switch (carrier_status) { + case LINK_STATE_UP: + ev = CARRIER; + break; + case LINK_STATE_DOWN: + ev = NO_CARRIER; + break; + default: + if (verbose) + printf("unknown link status ignored\n"); + return; + } + invoke_script(NULL, NULL, ev, if_index, p->ifname); + p->last_carrier_status = carrier_status; + } +} + static void check_announce(struct if_announcemsghdr *ifan) {