]> git.meshlink.io Git - catta/blobdiff - avahi-autoipd/main.c
remove remnants of a todo list
[catta] / avahi-autoipd / main.c
index 617a54727f6a422debe44801e50140fb3d81376d..72ee904e740ca375fac212c6be663027c4a0af50 100644 (file)
@@ -43,6 +43,8 @@
 #include <getopt.h>
 #include <signal.h>
 #include <sys/wait.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include <avahi-common/malloc.h>
 #include <avahi-common/timeval.h>
@@ -79,6 +81,7 @@
 #define IPV4LL_NETWORK 0xA9FE0000L
 #define IPV4LL_NETMASK 0xFFFF0000L
 #define IPV4LL_HOSTMASK 0x0000FFFFL
+#define IPV4LL_BROADCAST 0xA9FEFFFFL
 
 #define ETHER_ADDRLEN 6
 #define ARP_PACKET_SIZE (8+4+4+2*ETHER_ADDRLEN)
@@ -108,6 +111,12 @@ static int wait_for_address = 0;
 static int use_syslog = 0;
 static int debug = 0;
 static int modify_proc_title = 1;
+static int force_bind = 0;
+#ifdef HAVE_CHROOT
+static int no_chroot = 0;
+#endif
+static int no_drop_root = 0;
+static int wrote_pid_file = 0;
 
 static enum {
     DAEMON_RUN,
@@ -126,6 +135,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 {\
@@ -164,11 +186,69 @@ static uint32_t pick_addr(uint32_t old_addr) {
         
         addr = htonl(IPV4LL_NETWORK | (uint32_t) r);
 
-    } while (addr == old_addr);
+    } while (addr == old_addr || !is_ll_address(addr));
 
     return addr;
 }
 
+static int load_address(const char *fn, uint32_t *addr) {
+    FILE *f;
+    unsigned a, b, c, d;
+
+    assert(fn);
+    assert(addr);
+    
+    if (!(f = fopen(fn, "r"))) {
+
+        if (errno == ENOENT) {
+            *addr = 0;
+            return 0;
+        }
+        
+        daemon_log(LOG_ERR, "fopen() failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    if (fscanf(f, "%u.%u.%u.%u\n", &a, &b, &c, &d) != 4) {
+        daemon_log(LOG_ERR, "Parse failure");
+        goto fail;
+    }
+
+    fclose(f);
+
+    *addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
+    return 0;
+    
+fail:
+    if (f)
+        fclose(f);
+
+    return -1;
+}
+
+static int save_address(const char *fn, uint32_t addr) {
+    FILE *f;
+    char buf[32];
+
+    assert(fn);
+    
+    if (!(f = fopen(fn, "w"))) {
+        daemon_log(LOG_ERR, "fopen() failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    fprintf(f, "%s\n", inet_ntop(AF_INET, &addr, buf, sizeof (buf)));
+    fclose(f);
+
+    return 0;
+    
+fail:
+    if (f)
+        fclose(f);
+
+    return -1;
+}
+
 static void* packet_new(const ArpPacketInfo *info, size_t *packet_len) {
     uint8_t *r;
 
@@ -242,7 +322,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",
@@ -264,16 +344,14 @@ static void set_state(State st, int reset_counter, uint32_t address) {
         n_iteration = 0;
     }
 
-    if (modify_proc_title) {
-        if (state == STATE_SLEEPING) 
-            avahi_set_proc_title("%s: sleeping", argv0);
-        else if (state == STATE_ANNOUNCING)
-            avahi_set_proc_title("%s: announcing %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
-        else if (state == STATE_RUNNING)
-            avahi_set_proc_title("%s: bound %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
-        else
-            avahi_set_proc_title("%s: probing %s", argv0, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
-    }
+    if (state == STATE_SLEEPING) 
+        avahi_set_proc_title(argv0, "%s: [%s] sleeping", argv0, interface_name);
+    else if (state == STATE_ANNOUNCING)
+        avahi_set_proc_title(argv0, "%s: [%s] announcing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+    else if (state == STATE_RUNNING)
+        avahi_set_proc_title(argv0, "%s: [%s] bound %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
+    else
+        avahi_set_proc_title(argv0, "%s: [%s] probing %s", argv0, interface_name, inet_ntop(AF_INET, &address, buf, sizeof(buf)));
 }
 
 static int interface_up(int iface) {
@@ -314,23 +392,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;
@@ -440,7 +501,10 @@ fail:
 }
  
 int is_ll_address(uint32_t addr) {
-    return (ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK;
+    return
+        (ntohl(addr) & IPV4LL_NETMASK) == IPV4LL_NETWORK &&
+        ntohl(addr) != IPV4LL_NETWORK &&
+        ntohl(addr) != IPV4LL_BROADCAST;
 }
 
 static struct timeval *elapse_time(struct timeval *tv, unsigned msec, unsigned jitter) {
@@ -457,6 +521,253 @@ 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);
+
+#ifdef HAVE_CHROOT
+        /* If the main process is trapped inside a chroot() we have to
+         * remove the PID file for it */
+        
+        if (!no_chroot && wrote_pid_file)
+            daemon_pid_file_remove();
+#endif
+        
+        _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;
+}
+
+#define set_env(key, value) putenv(avahi_strdup_printf("%s=%s", (key), (value)))
+
+static int drop_privs(void) {
+    struct passwd *pw;
+    struct group * gr;
+    int r;
+    mode_t u;
+
+    /* Get user/group ID */
+    
+    if (!no_drop_root) {
+    
+        if (!(pw = getpwnam(AVAHI_AUTOIPD_USER))) {
+            daemon_log(LOG_ERR, "Failed to find user '"AVAHI_AUTOIPD_USER"'.");
+            return -1;
+        }
+        
+        if (!(gr = getgrnam(AVAHI_AUTOIPD_GROUP))) {
+            daemon_log(LOG_ERR, "Failed to find group '"AVAHI_AUTOIPD_GROUP"'.");
+            return -1;
+        }
+        
+        daemon_log(LOG_INFO, "Found user '"AVAHI_AUTOIPD_USER"' (UID %lu) and group '"AVAHI_AUTOIPD_GROUP"' (GID %lu).", (unsigned long) pw->pw_uid, (unsigned long) gr->gr_gid);
+    }
+
+    /* Create directory */
+    u = umask(0000);
+    r = mkdir(AVAHI_IPDATA_DIR, 0755);
+    umask(u);
+    
+    if (r < 0 && errno != EEXIST) {
+        daemon_log(LOG_ERR, "mkdir(\""AVAHI_IPDATA_DIR"\"): %s", strerror(errno));
+        return -1;
+    }
+
+    /* Convey working directory */
+    
+    if (!no_drop_root) {
+        struct stat st;
+        
+        chown(AVAHI_IPDATA_DIR, pw->pw_uid, gr->gr_gid);
+        
+        if (stat(AVAHI_IPDATA_DIR, &st) < 0) {
+            daemon_log(LOG_ERR, "stat(): %s\n", strerror(errno));
+            return -1;
+        }
+        
+        if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) {
+            daemon_log(LOG_ERR, "Failed to create runtime directory "AVAHI_IPDATA_DIR".");
+            return -1;
+        }
+    }
+
+#ifdef HAVE_CHROOT
+
+    if (!no_chroot) {
+        if (chroot(AVAHI_IPDATA_DIR) < 0) {
+            daemon_log(LOG_ERR, "Failed to chroot(): %s", strerror(errno));
+            return -1;
+        }
+
+        daemon_log(LOG_INFO, "Successfully called chroot().");
+        chdir("/");
+
+        /* Since we are now trapped inside a chroot we cannot remove
+         * the pid file anymore, the helper process will do that for us. */
+        wrote_pid_file = 0;
+    }
+    
+#endif
+
+    if (!no_drop_root) {
+
+        if (initgroups(AVAHI_AUTOIPD_USER, gr->gr_gid) != 0) {
+            daemon_log(LOG_ERR, "Failed to change group list: %s", strerror(errno));
+            return -1;
+        }
+        
+#if defined(HAVE_SETRESGID)
+        r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
+#elif defined(HAVE_SETEGID)
+        if ((r = setgid(gr->gr_gid)) >= 0)
+            r = setegid(gr->gr_gid);
+#elif defined(HAVE_SETREGID)
+        r = setregid(gr->gr_gid, gr->gr_gid);
+#else
+#error "No API to drop priviliges"
+#endif
+
+        if (r < 0) {
+            daemon_log(LOG_ERR, "Failed to change GID: %s", strerror(errno));
+            return -1;
+        }
+        
+#if defined(HAVE_SETRESUID)
+        r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
+#elif defined(HAVE_SETEUID)
+        if ((r = setuid(pw->pw_uid)) >= 0)
+            r = seteuid(pw->pw_uid);
+#elif defined(HAVE_SETREUID)
+        r = setreuid(pw->pw_uid, pw->pw_uid);
+#else
+#error "No API to drop priviliges"
+#endif
+        
+        if (r < 0) {
+            daemon_log(LOG_ERR, "Failed to change UID: %s", strerror(errno));
+            return -1;
+        }
+
+        set_env("USER", pw->pw_name);
+        set_env("LOGNAME", pw->pw_name);
+        set_env("HOME", pw->pw_dir);
+        
+        daemon_log(LOG_ERR, "Successfully dropped root privileges.");
+    }
+    
+    return 0;
+}
+
 static int loop(int iface, uint32_t addr) {
     enum {
         FD_ARP,
@@ -479,18 +790,45 @@ static int loop(int iface, uint32_t addr) {
     Event event = EVENT_NULL;
     int retval_sent = !daemonize;
     State st;
+    FILE *dispatcher = NULL;
+    char *address_fn = NULL;
+    const char *p;
 
     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 (iface_get_initial_state(&st) < 0)
+    if (drop_privs() < 0)
         goto fail;
 
+    if (force_bind)
+        st = STATE_START;
+    else if (iface_get_initial_state(&st) < 0)
+        goto fail;
+
+#ifdef HAVE_CHROOT
+    if (!no_chroot)
+        p = "";
+    else
+#endif
+        p = AVAHI_IPDATA_DIR;
+    
+    address_fn = avahi_strdup_printf(
+            "%s/%02x:%02x:%02x:%02x:%02x:%02x", p,
+            hw_address[0], hw_address[1],
+            hw_address[2], hw_address[3],
+            hw_address[4], hw_address[5]);
+    
+    if (!addr)
+        load_address(address_fn, &addr);
+
     if (addr && !is_ll_address(addr)) {
         daemon_log(LOG_WARNING, "Requested address %s is not from IPv4LL range 169.254/16, ignoring.", inet_ntop(AF_INET, &addr, buf, sizeof(buf)));
         addr = 0;
@@ -568,13 +906,10 @@ 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) {
-                    daemon_retval_send(0);
-                    retval_sent = 1;
-                }
             }
 
         } else if ((state == STATE_ANNOUNCING && event == EVENT_TIMEOUT && n_iteration >= ANNOUNCE_NUM-1)) {
@@ -583,7 +918,14 @@ static int loop(int iface, uint32_t addr) {
             set_state(STATE_RUNNING, 0, addr);
 
             next_wakeup_valid = 0;
+
+            save_address(address_fn, addr);
             
+            if (!retval_sent) {
+                daemon_retval_send(0);
+                retval_sent = 1;
+            }
+
         } else if (event == EVENT_PACKET) {
             ArpPacketInfo info;
 
@@ -607,7 +949,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);
@@ -634,7 +977,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);
@@ -644,7 +988,7 @@ static int loop(int iface, uint32_t addr) {
             set_state(STATE_SLEEPING, 1, addr);
             next_wakeup_valid = 0;
             
-        } else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING) {
+        } else if (event == EVENT_ROUTABLE_ADDR_UNCONFIGURED && state == STATE_SLEEPING && !force_bind) {
 
             daemon_log(LOG_INFO, "No longer a routable address configured, restarting probe process.");
 
@@ -653,7 +997,7 @@ static int loop(int iface, uint32_t addr) {
             elapse_time(&next_wakeup, 0, PROBE_WAIT*1000);
             next_wakeup_valid = 1;
 
-        } else if (event == EVENT_REFRESH_REQUEST && state == STATE_RUNNING) {
+        } else if (event == EVENT_REFRESH_REQUEST && state == STATE_RUNNING && !force_bind) {
 
             /* The user requested a reannouncing of the address by a SIGHUP */
             daemon_log(LOG_INFO, "Reannouncing address.");
@@ -772,7 +1116,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);
@@ -785,6 +1129,12 @@ fail:
 
     if (daemonize && !retval_sent)
         daemon_retval_send(ret);
+
+    if (dispatcher)
+        fclose(dispatcher);
+
+    if (address_fn)
+        avahi_free(address_fn);
     
     return ret;
 }
@@ -800,8 +1150,16 @@ static void help(FILE *f, const char *a0) {
             "    -r --refresh        Request a running daemon to refresh it's IP address\n"
             "    -c --check          Return 0 if a daemon is already running\n"
             "    -V --version        Show version\n"
-            "    -S --start=ADDRESS  Start with this address from the IPv4LL range 169.254.0.0/16\n"
-            "    -w --wait           Wait until an address has been acquired before daemonizing\n"
+            "    -S --start=ADDRESS  Start with this address from the IPv4LL range\n"
+            "                        169.254.0.0/16\n"
+            "    -w --wait           Wait until an address has been acquired before\n"
+            "                        daemonizing\n"
+            "       --force-bind     Assign an IPv4LL address even if routable address\n"
+            "                        is already assigned\n"
+            "       --no-drop-root   Don't drop privileges\n"
+#ifdef HAVE_CHROOT            
+            "       --no-chroot      Don't chroot()\n"
+#endif            
             "       --no-proc-title  Don't modify process title\n"
             "       --debug          Increase verbosity\n",
             a0);
@@ -812,7 +1170,12 @@ static int parse_command_line(int argc, char *argv[]) {
     
     enum {
         OPTION_NO_PROC_TITLE = 256,
-        OPTION_DEBUG
+        OPTION_FORCE_BIND,
+        OPTION_DEBUG,
+        OPTION_NO_DROP_ROOT,
+#ifdef HAVE_CHROOT
+        OPTION_NO_CHROOT
+#endif        
     };
     
     static const struct option long_options[] = {
@@ -825,13 +1188,18 @@ static int parse_command_line(int argc, char *argv[]) {
         { "version",       no_argument,       NULL, 'V' },
         { "start",         required_argument, NULL, 'S' },
         { "wait",          no_argument,       NULL, 'w' },
+        { "force-bind",    no_argument,       NULL, OPTION_FORCE_BIND },
+        { "no-drop-root",  no_argument,       NULL, OPTION_NO_DROP_ROOT },
+#ifdef HAVE_CHROOT            
+        { "no-chroot",     no_argument,       NULL, OPTION_NO_CHROOT },
+#endif        
         { "no-proc-title", no_argument,       NULL, OPTION_NO_PROC_TITLE },
         { "debug",         no_argument,       NULL, OPTION_DEBUG },
         { NULL, 0, NULL, 0 }
     };
 
     opterr = 0;
-    while ((c = getopt_long(argc, argv, "hDkVrcS:", long_options, NULL)) >= 0) {
+    while ((c = getopt_long(argc, argv, "hDskrcVS:w", long_options, NULL)) >= 0) {
 
         switch(c) {
             case 's':
@@ -874,6 +1242,20 @@ static int parse_command_line(int argc, char *argv[]) {
                 debug = 1;
                 break;
 
+            case OPTION_FORCE_BIND:
+                force_bind = 1;
+                break;
+
+            case OPTION_NO_DROP_ROOT:
+                no_drop_root = 1;
+                break;
+
+#ifdef HAVE_CHROOT
+            case OPTION_NO_CHROOT:
+                no_chroot = 1;
+                break;
+#endif
+
             default:
                 fprintf(stderr, "Invalid command line argument: %c\n", c);
                 return -1;
@@ -890,7 +1272,7 @@ static int parse_command_line(int argc, char *argv[]) {
             return -1;
         }
 
-        interface_name = argv[optind++];
+        interface_name = avahi_strdup(argv[optind++]);
     }
 
     if (optind != argc) {
@@ -907,21 +1289,25 @@ static const char* pid_file_proc(void) {
 
 int main(int argc, char*argv[]) {
     int r = 1;
-    int wrote_pid_file = 0;
-
-    avahi_init_proc_title(argc, argv);
+    char *log_ident = NULL;
 
+    signal(SIGPIPE, SIG_IGN);
+    
     if ((argv0 = strrchr(argv[0], '/')))
-        argv0++;
+        argv0 = avahi_strdup(argv0 + 1);
     else
-        argv0 = argv[0];
+        argv0 = avahi_strdup(argv[0]);
 
-    daemon_pid_file_ident = daemon_log_ident = argv0;
-    daemon_pid_file_proc = pid_file_proc;
+    daemon_log_ident = argv0;
     
     if (parse_command_line(argc, argv) < 0)
         goto finish;
 
+    if (modify_proc_title)
+        avahi_init_proc_title(argc, argv);
+
+    daemon_log_ident = log_ident = avahi_strdup_printf("%s(%s)", argv0, interface_name);
+    daemon_pid_file_proc = pid_file_proc;
     pid_file_name = avahi_strdup_printf(AVAHI_RUNTIME_DIR"/avahi-autoipd.%s.pid", interface_name);
 
     if (command == DAEMON_RUN) {
@@ -980,6 +1366,8 @@ int main(int argc, char*argv[]) {
         } else
             wrote_pid_file = 1;
 
+        avahi_set_proc_title(argv0, "%s: [%s] starting up", argv0, interface_name);
+        
         if (loop(ifindex, start_address) < 0)
             goto finish;
 
@@ -1018,14 +1406,10 @@ finish:
     if (wrote_pid_file)
         daemon_pid_file_remove();
 
+    avahi_free(log_ident);
+    avahi_free(pid_file_name);
+    avahi_free(argv0);
+    avahi_free(interface_name);
+
     return r;
 }
-
-/* TODO:
-
-- chroot/drop privs/caps
-- user script
-- store last used address
-- man page
-
-*/