--- /dev/null
+#!/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
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 {\
}
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",
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;
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,
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)
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) {
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);
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);
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);
if (daemonize && !retval_sent)
daemon_retval_send(ret);
+
+ if (dispatcher)
+ fclose(dispatcher);
return ret;
}
avahi_init_proc_title(argc, argv);
+ signal(SIGPIPE, SIG_IGN);
+
if ((argv0 = strrchr(argv[0], '/')))
argv0++;
else
/* TODO:
- chroot/drop privs/caps
-- user script
- store last used address
- man page