From 7bf6b7b95b7c3327000794a24e104242598a9f3f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 31 Aug 2006 19:48:42 +0000 Subject: add support for interface configuration with user supplied event script git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1301 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-autoipd/Makefile.am | 4 +- avahi-autoipd/avahi-autoipd.action | 77 ++++++++++++++++ avahi-autoipd/main.c | 176 ++++++++++++++++++++++++++++++++----- 3 files changed, 233 insertions(+), 24 deletions(-) create mode 100755 avahi-autoipd/avahi-autoipd.action diff --git a/avahi-autoipd/Makefile.am b/avahi-autoipd/Makefile.am index c553470..3a6bb2a 100644 --- a/avahi-autoipd/Makefile.am +++ b/avahi-autoipd/Makefile.am @@ -20,13 +20,15 @@ if ENABLE_AUTOIPD if HAVE_LIBDAEMON +pkgsysconfdir=$(sysconfdir)/avahi + AM_CFLAGS= \ -I$(top_srcdir) # This cool debug trap works on i386/gcc only AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' \ -DAVAHI_RUNTIME_DIR=\"$(avahi_runtime_dir)/\" \ - -DAVAHI_DNSCONF_SCRIPT=\"$(pkgsysconfdir)/avahi-autoipd.action\" + -DAVAHI_IPCONF_SCRIPT=\"$(pkgsysconfdir)/avahi-autoipd.action\" sbin_PROGRAMS = avahi-autoipd diff --git a/avahi-autoipd/avahi-autoipd.action b/avahi-autoipd/avahi-autoipd.action new file mode 100755 index 0000000..45ce1b5 --- /dev/null +++ b/avahi-autoipd/avahi-autoipd.action @@ -0,0 +1,77 @@ +#!/bin/sh + +# $Id$ +# +# This file is part of avahi. +# +# avahi is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# avahi is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with avahi; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +set -e + +# Command line arguments: +# $1 event that happened: +# BIND: Successfully claimed address +# CONFLICT: An IP address conflict happened +# UNBIND: The IP address is no longer needed +# STOP: The daemon is terminating +# $2 interface name +# $3 IP adddress + +if [ -x /bin/ip -o -x /sbin/ip ] ; then + + # We have the Linux ip tool from the iproute package + + case "$1" in + BIND) + ip addr add "$3"/16 label "$2:avahi" scope link dev "$2" + ;; + + CONFLICT|UNBIND|STOP) + ip addr del "$3"/16 label "$2:avahi" scope link dev "$2" + ;; + + *) + echo "Unknown event $1" >&2 + exit 1 + ;; + esac + +elif [ -x /bin/ifconfig -o -x /sbin/ifconfig ] ; then + + # We have the old ifconfig tool + + case "$1" in + BIND) + ifconfig "$2" inet "$3" netmask 255.255.0.0 + ;; + + CONFLICT|UNBIND|STOP) + ifconfig "$2" inet 0 + ;; + + *) + echo "Unknown event $1" >&2 + exit 1 + ;; + esac +else + + echo "No network configuration tool found." >&2 + exit 1 + +fi + +exit 0 diff --git a/avahi-autoipd/main.c b/avahi-autoipd/main.c index 6d83d3a..82dba88 100644 --- a/avahi-autoipd/main.c +++ b/avahi-autoipd/main.c @@ -127,6 +127,19 @@ typedef enum CalloutEvent { CALLOUT_MAX } CalloutEvent; +static const char * const callout_event_table[CALLOUT_MAX] = { + [CALLOUT_BIND] = "BIND", + [CALLOUT_CONFLICT] = "CONFLICT", + [CALLOUT_UNBIND] = "UNBIND", + [CALLOUT_STOP] = "STOP" +}; + +typedef struct CalloutEventInfo { + CalloutEvent event; + uint32_t address; + int ifindex; +} CalloutEventInfo; + #define RANDOM_DEVICE "/dev/urandom" #define DEBUG(x) do {\ @@ -243,7 +256,7 @@ static int packet_parse(const void *data, size_t packet_len, ArpPacketInfo *info } static void set_state(State st, int reset_counter, uint32_t address) { - const char* const state_table[] = { + static const char* const state_table[] = { [STATE_START] = "START", [STATE_WAITING_PROBE] = "WAITING_PROBE", [STATE_PROBING] = "PROBING", @@ -315,23 +328,6 @@ fail: return -1; } -static int do_callout(CalloutEvent event, int iface, uint32_t addr) { - char buf[64], ifname[IFNAMSIZ]; - const char * const event_table[CALLOUT_MAX] = { - [CALLOUT_BIND] = "BIND", - [CALLOUT_CONFLICT] = "CONFLICT", - [CALLOUT_UNBIND] = "UNBIND", - [CALLOUT_STOP] = "STOP" - }; - - daemon_log(LOG_INFO, "Callout %s, address %s on interface %s", - event_table[event], - inet_ntop(AF_INET, &addr, buf, sizeof(buf)), - if_indextoname(iface, ifname)); - - return 0; -} - static int open_socket(int iface, uint8_t *hw_address) { int fd = -1; struct sockaddr_ll sa; @@ -458,6 +454,125 @@ static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned j return tv; } +static FILE* fork_dispatcher(void) { + FILE *ret; + int fds[2]; + pid_t pid; + + if (pipe(fds) < 0) { + daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); + goto fail; + } + + if ((pid = fork()) < 0) + goto fail; + else if (pid == 0) { + FILE *f = NULL; + int r = 1; + + /* Please note that the signal pipe is not closed at this + * point, signals will thus be dispatched in the main + * process. */ + + daemon_retval_done(); + + setsid(); + + avahi_set_proc_title(argv0, "%s(%s): callout dispatcher", argv0, interface_name); + + close(fds[1]); + + if (!(f = fdopen(fds[0], "r"))) { + daemon_log(LOG_ERR, "fdopen() failed: %s", strerror(errno)); + goto dispatcher_fail; + } + + for (;;) { + CalloutEventInfo info; + char name[IFNAMSIZ], buf[64]; + int k; + + if (fread(&info, sizeof(info), 1, f) != 1) { + if (feof(f)) + break; + + daemon_log(LOG_ERR, "fread() failed: %s", strerror(errno)); + goto dispatcher_fail; + } + + assert(info.event <= CALLOUT_MAX); + + if (!if_indextoname(info.ifindex, name)) { + daemon_log(LOG_ERR, "if_indextoname() failed: %s", strerror(errno)); + continue; + } + + if (daemon_exec("/", &k, + AVAHI_IPCONF_SCRIPT, AVAHI_IPCONF_SCRIPT, + callout_event_table[info.event], + name, + inet_ntop(AF_INET, &info.address, buf, sizeof(buf)), NULL) < 0) { + + daemon_log(LOG_ERR, "Failed to run script: %s", strerror(errno)); + continue; + } + + if (k != 0) + daemon_log(LOG_WARNING, "Script execution failed with return value %i", k); + } + + r = 0; + + dispatcher_fail: + + if (f) + fclose(f); + + _exit(r); + } + + /* parent */ + + close(fds[0]); + fds[0] = -1; + + if (!(ret = fdopen(fds[1], "w"))) { + daemon_log(LOG_ERR, "fdopen() failed: %s", strerror(errno)); + goto fail; + } + + return ret; + +fail: + if (fds[0] >= 0) + close(fds[0]); + if (fds[1] >= 0) + close(fds[1]); + + return NULL; +} + +static int do_callout(FILE *f, CalloutEvent event, int iface, uint32_t addr) { + CalloutEventInfo info; + char buf[64], ifname[IFNAMSIZ]; + + daemon_log(LOG_INFO, "Callout %s, address %s on interface %s", + callout_event_table[event], + inet_ntop(AF_INET, &addr, buf, sizeof(buf)), + if_indextoname(iface, ifname)); + + info.event = event; + info.ifindex = iface; + info.address = addr; + + if (fwrite(&info, sizeof(info), 1, f) != 1 || fflush(f) != 0) { + daemon_log(LOG_ERR, "Failed to write callout event: %s", strerror(errno)); + return -1; + } + + return 0; +} + static int loop(int iface, uint32_t addr) { enum { FD_ARP, @@ -480,15 +595,22 @@ static int loop(int iface, uint32_t addr) { Event event = EVENT_NULL; int retval_sent = !daemonize; State st; + FILE *dispatcher = NULL; daemon_signal_init(SIGINT, SIGTERM, SIGCHLD, SIGHUP,0); + if (!(dispatcher = fork_dispatcher())) + goto fail; + if ((fd = open_socket(iface, hw_address)) < 0) goto fail; if ((iface_fd = iface_init(iface)) < 0) goto fail; +/* if (drop_privs() < 0) */ +/* goto fail; */ + if (force_bind) st = STATE_START; else if (iface_get_initial_state(&st) < 0) @@ -571,7 +693,9 @@ static int loop(int iface, uint32_t addr) { next_wakeup_valid = 1; if (n_iteration == 0) { - do_callout(CALLOUT_BIND, iface, addr); + if (do_callout(dispatcher, CALLOUT_BIND, iface, addr) < 0) + goto fail; + n_conflict = 0; if (!retval_sent) { @@ -610,7 +734,8 @@ static int loop(int iface, uint32_t addr) { if (conflict) { if (state == STATE_RUNNING || state == STATE_ANNOUNCING) - do_callout(CALLOUT_CONFLICT, iface, addr); + if (do_callout(dispatcher, CALLOUT_CONFLICT, iface, addr) < 0) + goto fail; /* Pick a new address */ addr = pick_addr(addr); @@ -637,7 +762,8 @@ static int loop(int iface, uint32_t addr) { daemon_log(LOG_INFO, "A routable address has been configured."); if (state == STATE_RUNNING || state == STATE_ANNOUNCING) - do_callout(CALLOUT_UNBIND, iface, addr); + if (do_callout(dispatcher, CALLOUT_UNBIND, iface, addr) < 0) + goto fail; if (!retval_sent) { daemon_retval_send(0); @@ -775,7 +901,7 @@ static int loop(int iface, uint32_t addr) { fail: if (state == STATE_RUNNING || state == STATE_ANNOUNCING) - do_callout(CALLOUT_STOP, iface, addr); + do_callout(dispatcher, CALLOUT_STOP, iface, addr); avahi_free(out_packet); avahi_free(in_packet); @@ -788,6 +914,9 @@ fail: if (daemonize && !retval_sent) daemon_retval_send(ret); + + if (dispatcher) + fclose(dispatcher); return ret; } @@ -925,6 +1054,8 @@ int main(int argc, char*argv[]) { avahi_init_proc_title(argc, argv); + signal(SIGPIPE, SIG_IGN); + if ((argv0 = strrchr(argv[0], '/'))) argv0++; else @@ -1048,7 +1179,6 @@ finish: /* TODO: - chroot/drop privs/caps -- user script - store last used address - man page -- cgit