Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / slave / kpropd.c
index 73e8da9..056c31a 100644 (file)
  * or implied warranty.
  */
 
-/*
- * XXX We need to modify the protocol so that an acknowledge is set
- * after each block, instead after the entire series is sent over.
- * The reason for this is so that error packets can get interpreted
- * right away.  If you don't do this, the sender may never get the
- * error packet, because it will die an EPIPE trying to complete the
- * write...
- */
 
+#include "k5-int.h"
+#include "com_err.h"
+#include "fake-addrinfo.h"
 
-#include <stdio.h>
+#include <locale.h>
 #include <ctype.h>
 #include <sys/file.h>
 #include <signal.h>
-#include <string.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <netdb.h>
 #include <syslog.h>
 
-#include "k5-int.h"
-#include "com_err.h"
-#include "fake-addrinfo.h"
-#include <errno.h>
-
 #include "kprop.h"
 #include <iprop_hdr.h>
 #include "iprop.h"
@@ -100,141 +89,247 @@ extern int daemon(int, int);
 #endif
 
 #define SYSLOG_CLASS LOG_DAEMON
-#define INITIAL_TIMER 10
 
-char *def_realm = NULL;
 int runonce = 0;
 
 /*
- * Global fd to close upon alarm time-out.
- */
-volatile int gfd = -1;
-
-/*
  * This struct simulates the use of _kadm5_server_handle_t
  *
  * This is a COPY of kadm5_server_handle_t from
  * lib/kadm5/clnt/client_internal.h!
  */
 typedef struct _kadm5_iprop_handle_t {
-    krb5_ui_4       magic_number;
-    krb5_ui_4       struct_version;
-    krb5_ui_4       api_version;
-    char            *cache_name;
-    int             destroy_cache;
-    CLIENT          *clnt;
-    krb5_context    context;
+    krb5_ui_4 magic_number;
+    krb5_ui_4 struct_version;
+    krb5_ui_4 api_version;
+    char *cache_name;
+    int destroy_cache;
+    CLIENT *clnt;
+    krb5_context context;
     kadm5_config_params params;
     struct _kadm5_iprop_handle_t *lhandle;
 } *kadm5_iprop_handle_t;
 
-
 static char *kprop_version = KPROP_PROT_VERSION;
 
-char    *progname;
-int     debug = 0;
-char    *srvtab = 0;
-int     standalone = 0;
-
-krb5_principal  server;         /* This is our server principal name */
-krb5_principal  client;         /* This is who we're talking to */
-krb5_context kpropd_context;
-krb5_auth_context auth_context;
-char    *realm = NULL;          /* Our realm */
-char    *file = KPROPD_DEFAULT_FILE;
-char    *temp_file_name;
-char    *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL;
-char    *kerb_database = NULL;
-char    *acl_file_name = KPROPD_ACL_FILE;
-
-krb5_address    *sender_addr;
-krb5_address    *receiver_addr;
-const char      *port = KPROP_SERVICE;
-
-char **db_args = NULL;
-int db_args_size = 0;
-
-void    PRS(char**);
-int     do_standalone(iprop_role iproprole);
-void    doit(int);
-krb5_error_code do_iprop(kdb_log_context *log_ctx);
-void    kerberos_authenticate(krb5_context, int, krb5_principal *,
-                              krb5_enctype *, struct sockaddr_storage *);
-krb5_boolean authorized_principal(krb5_context, krb5_principal, krb5_enctype);
-void    recv_database(krb5_context, int, int, krb5_data *);
-void    load_database(krb5_context, char *, char *);
-void    send_error(krb5_context, int, krb5_error_code, char *);
-void    recv_error(krb5_context, krb5_data *);
-unsigned int backoff_from_master(int *);
-
-static kadm5_ret_t
-kadm5_get_kiprop_host_srv_name(krb5_context context,
-                               const char *realm_name,
-                               char **host_service_name);
+static kadm5_config_params params;
+
+static char *progname;
+static int debug = 0;
+static int nodaemon = 0;
+static char *srvtab = NULL;
+static int standalone = 0;
+
+static pid_t fullprop_child = (pid_t)-1;
+
+static krb5_principal server;   /* This is our server principal name */
+static krb5_principal client;   /* This is who we're talking to */
+static krb5_context kpropd_context;
+static krb5_auth_context auth_context;
+static char *realm = NULL;      /* Our realm */
+static char *def_realm = NULL;  /* Ref pointer for default realm */
+static char *file = KPROPD_DEFAULT_FILE;
+static char *temp_file_name;
+static char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL;
+static char *kerb_database = NULL;
+static char *acl_file_name = KPROPD_ACL_FILE;
+
+static krb5_address *sender_addr;
+static krb5_address *receiver_addr;
+static const char *port = KPROP_SERVICE;
+
+static char **db_args = NULL;
+static int db_args_size = 0;
+
+static void parse_args(char **argv);
+static void do_standalone(void);
+static void doit(int fd);
+static krb5_error_code do_iprop(void);
+static void kerberos_authenticate(krb5_context context, int fd,
+                                  krb5_principal *clientp, krb5_enctype *etype,
+                                  struct sockaddr_storage *my_sin);
+static krb5_boolean authorized_principal(krb5_context context,
+                                         krb5_principal p,
+                                         krb5_enctype auth_etype);
+static void recv_database(krb5_context context, int fd, int database_fd,
+                          krb5_data *confmsg);
+static void load_database(krb5_context context, char *kdb_util,
+                          char *database_file_name);
+static void send_error(krb5_context context, int fd, krb5_error_code err_code,
+                       char *err_text);
+static void recv_error(krb5_context context, krb5_data *inbuf);
+static unsigned int backoff_from_master(int *cnt);
+static kadm5_ret_t kadm5_get_kiprop_host_srv_name(krb5_context context,
+                                                  const char *realm_name,
+                                                  char **host_service_name);
 
-static void usage()
+static void
+usage()
 {
     fprintf(stderr,
             _("\nUsage: %s [-r realm] [-s srvtab] [-dS] [-f slave_file]\n"),
             progname);
     fprintf(stderr, _("\t[-F kerberos_db_file ] [-p kdb5_util_pathname]\n"));
     fprintf(stderr, _("\t[-x db_args]* [-P port] [-a acl_file]\n"));
+    fprintf(stderr, _("\t[-A admin_server]\n"));
+    exit(1);
+}
+
+typedef void (*sig_handler_fn)(int sig);
+
+static void
+signal_wrapper(int sig, sig_handler_fn handler)
+{
+#ifdef POSIX_SIGNALS
+    struct sigaction s_action;
+
+    memset(&s_action, 0, sizeof(s_action));
+    sigemptyset(&s_action.sa_mask);
+    s_action.sa_handler = handler;
+    sigaction(sig, &s_action, NULL);
+#else
+    signal(sig, handler);
+#endif
+}
+
+static void
+alarm_handler(int sig)
+{
+    static char *timeout_msg = "Full propagation timed out\n";
+
+    write(STDERR_FILENO, timeout_msg, strlen(timeout_msg));
     exit(1);
 }
 
+static void
+usr1_handler(int sig)
+{
+    /* Nothing to do, just let the signal interrupt sleep(). */
+}
+
+static void
+kill_do_standalone(int sig)
+{
+    if (fullprop_child > 0) {
+        if (debug) {
+            fprintf(stderr, _("Killing fullprop child (%d)\n"),
+                    (int)fullprop_child);
+        }
+        kill(fullprop_child, sig);
+    }
+    /* Make sure our exit status code reflects our having been signaled */
+    signal_wrapper(sig, SIG_DFL);
+    kill(getpid(), sig);
+}
+
+static void
+atexit_kill_do_standalone(void)
+{
+    if (fullprop_child > 0)
+        kill(fullprop_child, SIGHUP);
+}
+
 int
-main(argc, argv)
-    int     argc;
-    char    **argv;
+main(int argc, char **argv)
 {
     krb5_error_code retval;
-    int ret = 0;
     kdb_log_context *log_ctx;
+    int devnull, sock;
+    struct stat st;
 
-    setlocale(LC_MESSAGES, "");
-    PRS(argv);
+    setlocale(LC_ALL, "");
+    parse_args(argv);
+
+    if (fstat(0, &st) == -1) {
+        com_err(progname, errno, _("while checking if stdin is a socket"));
+        exit(1);
+    }
+    /*
+     * Detect whether we're running from inetd; if not then we're in
+     * standalone mode.
+     */
+    standalone = !S_ISSOCK(st.st_mode);
 
     log_ctx = kpropd_context->kdblog_context;
 
-    {
-#ifdef POSIX_SIGNALS
-        struct sigaction s_action;
-        memset(&s_action, 0, sizeof(s_action));
-        sigemptyset(&s_action.sa_mask);
-        s_action.sa_handler = SIG_IGN;
-        sigaction(SIGPIPE, &s_action, NULL);
-#else
-        signal(SIGPIPE, SIG_IGN);
-#endif
-    }
+    signal_wrapper(SIGPIPE, SIG_IGN);
 
-    if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
+    if (standalone) {
+        /* "ready" is a sentinel for the test framework. */
+        if (!debug && !nodaemon) {
+            daemon(0, 0);
+        } else {
+            printf(_("ready\n"));
+            fflush(stdout);
+        }
+    } else {
         /*
-         * We wanna do iprop !
+         * We're an inetd nowait service.  Let's not risk anything
+         * read/write from/to the inetd socket unintentionally.
          */
-        retval = do_iprop(log_ctx);
-        if (retval) {
-            com_err(progname, retval,
-                    _("do_iprop failed.\n"));
+        devnull = open("/dev/null", O_RDWR);
+        if (devnull == -1) {
+            syslog(LOG_ERR, _("Could not open /dev/null: %s"),
+                   strerror(errno));
             exit(1);
         }
 
-    } else {
-        if (standalone)
-            ret = do_standalone(IPROP_NULL);
-        else
-            doit(0);
+        sock = dup(0);
+        if (sock == -1) {
+            syslog(LOG_ERR, _("Could not dup the inetd socket: %s"),
+                   strerror(errno));
+            exit(1);
+        }
+
+        dup2(devnull, STDIN_FILENO);
+        dup2(devnull, STDOUT_FILENO);
+        dup2(devnull, STDERR_FILENO);
+        close(devnull);
+        doit(sock);
+        exit(0);
     }
 
-    exit(ret);
-}
+    if (log_ctx == NULL || log_ctx->iproprole != IPROP_SLAVE) {
+        do_standalone();
+        /* do_standalone() should never return */
+        assert(0);
+    }
 
-static void resync_alarm(int sn)
-{
-    close (gfd);
-    if (debug)
-        fprintf(stderr, _("resync_alarm: closing fd: %d\n"), gfd);
-    gfd = -1;
+    /*
+     * This is the iprop case.  We'll fork a child to run do_standalone().  The
+     * parent will run do_iprop().  We try to kill the child if we get killed.
+     * Catch SIGUSR1, which can be used to interrupt the sleep timer and force
+     * an iprop request.
+     */
+    signal_wrapper(SIGHUP, kill_do_standalone);
+    signal_wrapper(SIGINT, kill_do_standalone);
+    signal_wrapper(SIGQUIT, kill_do_standalone);
+    signal_wrapper(SIGTERM, kill_do_standalone);
+    signal_wrapper(SIGSEGV, kill_do_standalone);
+    signal_wrapper(SIGUSR1, usr1_handler);
+    atexit(atexit_kill_do_standalone);
+    fullprop_child = fork();
+    switch (fullprop_child) {
+    case -1:
+        com_err(progname, errno, _("do_iprop failed.\n"));
+        break;
+    case 0:
+        do_standalone();
+        /* do_standalone() should never return */
+        /* NOTREACHED */
+        break;
+    default:
+        retval = do_iprop();
+        /* do_iprop() can return due to failures and runonce. */
+        kill(fullprop_child, SIGHUP);
+        wait(NULL);
+        if (retval)
+            com_err(progname, retval, _("do_iprop failed.\n"));
+        else
+            exit(0);
+    }
+
+    exit(1);
 }
 
 /* Use getaddrinfo to determine a wildcard listener address, preferring
@@ -248,33 +343,27 @@ get_wildcard_addr(struct addrinfo **res)
     memset(&hints, 0, sizeof(hints));
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
-#ifdef KRB5_USE_INET6
     hints.ai_family = AF_INET6;
     error = getaddrinfo(NULL, port, &hints, res);
     if (error == 0)
         return 0;
-#endif
     hints.ai_family = AF_INET;
     return getaddrinfo(NULL, port, &hints, res);
 }
 
-int do_standalone(iprop_role iproprole)
+static void
+do_standalone()
 {
-    struct  sockaddr_in     frominet;
+    struct sockaddr_in frominet;
     struct addrinfo *res;
-    int     finet, s;
     GETPEERNAME_ARG3_TYPE fromlen;
-    int ret, error, val;
-    /*
-     * Timer for accept/read calls, in case of network type errors.
-     */
-    int backoff_timer = INITIAL_TIMER;
-
-retry:
+    int finet, s, ret, error, val, status;
+    pid_t child_pid;
+    pid_t wait_pid;
 
     error = get_wildcard_addr(&res);
     if (error != 0) {
-        (void) fprintf(stderr, _("getaddrinfo: %s\n"), gai_strerror(error));
+        fprintf(stderr, _("getaddrinfo: %s\n"), gai_strerror(error));
         exit(1);
     }
 
@@ -288,7 +377,7 @@ retry:
     if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
         com_err(progname, errno, _("while setting SO_REUSEADDR option"));
 
-#if defined(KRB5_USE_INET6) && defined(IPV6_V6ONLY)
+#if defined(IPV6_V6ONLY)
     /* Make sure dual-stack support is enabled on IPv6 listener sockets if
      * possible. */
     val = 0;
@@ -297,60 +386,16 @@ retry:
         com_err(progname, errno, _("while unsetting IPV6_V6ONLY option"));
 #endif
 
-    /*
-     * We need to close the socket immediately if iprop is enabled,
-     * since back-to-back full resyncs are possible, so we do not
-     * linger around for too long
-     */
-    if (iproprole == IPROP_SLAVE) {
-        struct linger linger;
-
-        linger.l_onoff = 1;
-        linger.l_linger = 2;
-        if (setsockopt(finet, SOL_SOCKET, SO_LINGER,
-                       (void *)&linger, sizeof(linger)) < 0)
-            com_err(progname, errno,
-                    _("while setting socket option (SO_LINGER)"));
-        /*
-         * We also want to set a timer so that the slave is not waiting
-         * until infinity for an update from the master.
-         */
-        gfd = finet;
-        signal(SIGALRM, resync_alarm);
-        if (debug) {
-            fprintf(stderr, "do_standalone: setting resync alarm to %d\n",
-                    backoff_timer);
-        }
-        if (alarm(backoff_timer) != 0) {
-            if (debug) {
-                fprintf(stderr,
-                        _("%s: alarm already set\n"), progname);
-            }
-        }
-        backoff_timer *= 2;
-    }
-    if ((ret = bind(finet, res->ai_addr, res->ai_addrlen)) < 0) {
+    ret = bind(finet, res->ai_addr, res->ai_addrlen);
+    if (ret < 0) {
         com_err(progname, errno, _("while binding listener socket"));
         exit(1);
     }
-    if (!debug && iproprole != IPROP_SLAVE)
-        daemon(1, 0);
-#ifdef PID_FILE
-    if ((pidfile = fopen(PID_FILE, "w")) != NULL) {
-        fprintf(pidfile, "%d\n", getpid());
-        fclose(pidfile);
-    } else
-        com_err(progname, errno,
-                _("while opening pid file %s for writing"), PID_FILE);
-#endif
     if (listen(finet, 5) < 0) {
         com_err(progname, errno, "in listen call");
         exit(1);
     }
-    while (1) {
-        int child_pid;
-        int status;
-
+    for (;;) {
         memset(&frominet, 0, sizeof(frominet));
         fromlen = sizeof(frominet);
         if (debug)
@@ -360,72 +405,54 @@ retry:
         if (s < 0) {
             int e = errno;
             if (e != EINTR) {
-                com_err(progname, e,
-                        _("while accepting connection"));
-                if (e != EBADF)
-                    backoff_timer = INITIAL_TIMER;
+                com_err(progname, e, _("while accepting connection"));
             }
-            /*
-             * If we got EBADF, an alarm signal handler closed
-             * the file descriptor on us.
-             */
-            if (e != EBADF)
-                close(finet);
-            /*
-             * An alarm could have been set and the fd closed, we
-             * should retry in case of transient network error for
-             * up to a couple of minutes.
-             */
-            if (backoff_timer > 120)
-                return EINTR;
-            goto retry;
         }
-        alarm(0);
-        gfd = -1;
-        if (debug && iproprole != IPROP_SLAVE)
-            child_pid = 0;
-        else
-            child_pid = fork();
+        child_pid = fork();
         switch (child_pid) {
         case -1:
             com_err(progname, errno, _("while forking"));
             exit(1);
         case 0:
-            (void) close(finet);
+            close(finet);
 
             doit(s);
             close(s);
             _exit(0);
         default:
-            /*
-             * Errors should not be considered fatal in the
-             * iprop case as we could have transient type
-             * errors, such as network outage, etc.  Sleeping
-             * 3s for 2s linger interval.
-             */
-            if (wait(&status) < 0) {
+            do {
+                wait_pid = waitpid(child_pid, &status, 0);
+            } while (wait_pid == -1 && errno == EINTR);
+            if (wait_pid == -1) {
+                /* Something bad happened; panic. */
+                if (debug) {
+                    fprintf(stderr, _("waitpid() failed to wait for doit() "
+                                      "(%d %s)\n"), errno, strerror(errno));
+                }
                 com_err(progname, errno,
                         _("while waiting to receive database"));
-                if (iproprole != IPROP_SLAVE)
-                    exit(1);
-                sleep(3);
+                exit(1);
+            }
+            if (debug) {
+                fprintf(stderr, _("Database load process for full propagation "
+                                  "completed.\n"));
             }
 
             close(s);
-            if (iproprole == IPROP_SLAVE) {
-                close(finet);
-                if ((ret = WEXITSTATUS(status)) != 0)
-                    return (ret);
-            }
+
+            /* If we are the fullprop child in iprop mode, notify the parent
+             * process that it should poll for incremental updates. */
+            if (fullprop_child == 0)
+                kill(getppid(), SIGUSR1);
+            else if (runonce)
+                exit(0);
         }
-        if (iproprole == IPROP_SLAVE)
-            break;
     }
-    return 0;
+    exit(0);
 }
 
-void doit(fd)
-    int     fd;
+static void
+doit(int fd)
 {
     struct sockaddr_storage from;
     int on = 1;
@@ -436,27 +463,12 @@ void doit(fd)
     mode_t omask;
     krb5_enctype etype;
     int database_fd;
-    char host[INET6_ADDRSTRLEN+1];
+    char host[INET6_ADDRSTRLEN + 1];
 
-    if (kpropd_context->kdblog_context &&
-        kpropd_context->kdblog_context->iproprole == IPROP_SLAVE) {
-        /*
-         * We also want to set a timer so that the slave is not waiting
-         * until infinity for an update from the master.
-         */
-        if (debug)
-            fprintf(stderr, "doit: setting resync alarm to 5s\n");
-        signal(SIGALRM, resync_alarm);
-        gfd = fd;
-        if (alarm(INITIAL_TIMER) != 0) {
-            if (debug) {
-                fprintf(stderr,
-                        _("%s: alarm already set\n"), progname);
-            }
-        }
-    }
-    fromlen = sizeof (from);
-    if (getpeername(fd, (struct sockaddr *) &from, &fromlen) < 0) {
+    signal_wrapper(SIGALRM, alarm_handler);
+    alarm(params.iprop_resync_timeout);
+    fromlen = sizeof(from);
+    if (getpeername(fd, (struct sockaddr *)&from, &fromlen) < 0) {
 #ifdef ENOTSOCK
         if (errno == ENOTSOCK && fd == 0 && !standalone) {
             fprintf(stderr,
@@ -472,7 +484,7 @@ void doit(fd)
         exit(1);
     }
     if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (caddr_t) &on,
-                   sizeof (on)) < 0) {
+                   sizeof(on)) < 0) {
         com_err(progname, errno,
                 _("while attempting setsockopt (SO_KEEPALIVE)"));
     }
@@ -481,7 +493,7 @@ void doit(fd)
                     host, sizeof(host), NULL, 0, 0) == 0) {
         syslog(LOG_INFO, _("Connection from %s"), host);
         if (debug)
-            printf("Connection from %s\n", host);
+            fprintf(stderr, "Connection from %s\n", host);
     }
 
     /*
@@ -489,19 +501,12 @@ void doit(fd)
      */
     kerberos_authenticate(kpropd_context, fd, &client, &etype, &from);
 
-    /*
-     * Turn off alarm upon successful authentication from master.
-     */
-    alarm(0);
-    gfd = -1;
-
     if (!authorized_principal(kpropd_context, client, etype)) {
-        char    *name;
+        char *name;
 
         retval = krb5_unparse_name(kpropd_context, client, &name);
         if (retval) {
-            com_err(progname, retval,
-                    "While unparsing client name");
+            com_err(progname, retval, "While unparsing client name");
             exit(1);
         }
         if (debug) {
@@ -516,17 +521,17 @@ void doit(fd)
         exit(1);
     }
     omask = umask(077);
-    lock_fd = open(temp_file_name, O_RDWR|O_CREAT, 0600);
-    (void) umask(omask);
+    lock_fd = open(temp_file_name, O_RDWR | O_CREAT, 0600);
+    (void)umask(omask);
     retval = krb5_lock_file(kpropd_context, lock_fd,
-                            KRB5_LOCKMODE_EXCLUSIVE|KRB5_LOCKMODE_DONTBLOCK);
+                            KRB5_LOCKMODE_EXCLUSIVE | KRB5_LOCKMODE_DONTBLOCK);
     if (retval) {
         com_err(progname, retval, _("while trying to lock '%s'"),
                 temp_file_name);
         exit(1);
     }
-    if ((database_fd = open(temp_file_name,
-                            O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
+    database_fd = open(temp_file_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    if (database_fd < 0) {
         com_err(progname, errno, _("while opening database file, '%s'"),
                 temp_file_name);
         exit(1);
@@ -549,13 +554,13 @@ void doit(fd)
         com_err(progname, retval, _("while unlocking '%s'"), temp_file_name);
         exit(1);
     }
-    (void)close(lock_fd);
+    close(lock_fd);
 
     /*
      * Send the acknowledgement message generated in
      * recv_database, then close the socket.
      */
-    retval = krb5_write_message(kpropd_context, (void *) &fd, &confmsg);
+    retval = krb5_write_message(kpropd_context, &fd, &confmsg);
     if (retval) {
         krb5_free_data_contents(kpropd_context, &confmsg);
         com_err(progname, retval, _("while sending # of received bytes"));
@@ -583,125 +588,76 @@ full_resync(CLIENT *clnt)
 
     memset(&clnt_res, 0, sizeof(clnt_res));
 
-    status = clnt_call (clnt, IPROP_FULL_RESYNC_EXT,
-                        (xdrproc_t) xdr_u_int32,
-                        (caddr_t) &vers,
-                        (xdrproc_t) xdr_kdb_fullresync_result_t,
-                        (caddr_t) &clnt_res,
-                        full_resync_timeout);
+    status = clnt_call(clnt, IPROP_FULL_RESYNC_EXT, (xdrproc_t)xdr_u_int32,
+                       (caddr_t)&vers, (xdrproc_t)xdr_kdb_fullresync_result_t,
+                       (caddr_t)&clnt_res, full_resync_timeout);
     if (status == RPC_PROCUNAVAIL) {
-        status = clnt_call (clnt, IPROP_FULL_RESYNC,
-                            (xdrproc_t) xdr_void,
-                            (caddr_t *)&vers,
-                            (xdrproc_t) xdr_kdb_fullresync_result_t,
-                            (caddr_t) &clnt_res,
-                            full_resync_timeout);
+        status = clnt_call(clnt, IPROP_FULL_RESYNC, (xdrproc_t)xdr_void,
+                           (caddr_t *)&vers,
+                           (xdrproc_t)xdr_kdb_fullresync_result_t,
+                           (caddr_t)&clnt_res, full_resync_timeout);
     }
 
     return (status == RPC_SUCCESS) ? &clnt_res : NULL;
 }
 
 /*
- * Routine to handle incremental update transfer(s) from master KDC
+ * Beg for incrementals from the KDC.
+ *
+ * Returns 0 on success IFF runonce is true.
+ * Returns non-zero on failure due to errors.
  */
-kadm5_config_params params;
-krb5_error_code do_iprop(kdb_log_context *log_ctx)
+krb5_error_code
+do_iprop()
 {
     kadm5_ret_t retval;
-    krb5_ccache cc;
     krb5_principal iprop_svc_principal;
     void *server_handle = NULL;
-    char *iprop_svc_princstr = NULL;
-    char *master_svc_princstr = NULL;
+    char *iprop_svc_princstr = NULL, *master_svc_princstr = NULL;
     unsigned int pollin, backoff_time;
-    int backoff_cnt = 0;
-    int reinit_cnt = 0;
-    int ret;
-    int frdone = 0;
-
+    int backoff_cnt = 0, reinit_cnt = 0;
+    struct timeval iprop_start, iprop_end;
+    unsigned long usec;
+    time_t frrequested = 0, now;
     kdb_incr_result_t *incr_ret;
-    static kdb_last_t mylast;
-
+    kdb_last_t mylast;
     kdb_fullresync_result_t *full_ret;
-
     kadm5_iprop_handle_t handle;
-    kdb_hlog_t *ulog;
 
-    if (!debug)
-        daemon(0, 0);
-
-    ulog = log_ctx->ulog;
+    if (debug)
+        fprintf(stderr, _("Incremental propagation enabled\n"));
 
     pollin = params.iprop_poll_time;
-    if (pollin < 10)
+    if (pollin == 0)
         pollin = 10;
 
-    /*
-     * Grab the realm info and check if iprop is enabled.
-     */
-    if (def_realm == NULL) {
-        retval = krb5_get_default_realm(kpropd_context, &def_realm);
-        if (retval) {
-            com_err(progname, retval,
-                    _("Unable to get default realm"));
-            exit(1);
-        }
-    }
-
-    params.mask |= KADM5_CONFIG_REALM;
-    params.realm = def_realm;
-
     if (master_svc_princstr == NULL) {
-        if ((retval = kadm5_get_kiprop_host_srv_name(kpropd_context,
-                                                     def_realm,
-                                                     &master_svc_princstr))) {
+        retval = kadm5_get_kiprop_host_srv_name(kpropd_context, realm,
+                                                &master_svc_princstr);
+        if (retval) {
             com_err(progname, retval,
                     _("%s: unable to get kiprop host based "
                       "service name for realm %s\n"),
-                    progname, def_realm);
-            exit(1);
+                    progname, realm);
+            return retval;
         }
     }
 
-    /*
-     * Set cc to the default credentials cache
-     */
-    if ((retval = krb5_cc_default(kpropd_context, &cc))) {
-        com_err(progname, retval,
-                _("while opening default "
-                  "credentials cache"));
-        exit(1);
-    }
-
-    retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME,
-                                     KRB5_NT_SRV_HST, &iprop_svc_principal);
+    retval = sn2princ_realm(kpropd_context, NULL, KIPROP_SVC_NAME, realm,
+                            &iprop_svc_principal);
     if (retval) {
         com_err(progname, retval,
                 _("while trying to construct host service principal"));
-        exit(1);
+        return retval;
     }
 
-    /* XXX referrals? */
-    if (krb5_is_referral_realm(krb5_princ_realm(kpropd_context,
-                                                iprop_svc_principal))) {
-        krb5_data *r = krb5_princ_realm(kpropd_context,
-                                        iprop_svc_principal);
-        assert(def_realm != NULL);
-        r->length = strlen(def_realm);
-        r->data = strdup(def_realm);
-        if (r->data == NULL) {
-            com_err(progname, retval,
-                    _("while determining local service principal name"));
-            exit(1);
-        }
-        /* XXX Memory leak: Old r->data value.  */
-    }
-    if ((retval = krb5_unparse_name(kpropd_context, iprop_svc_principal,
-                                    &iprop_svc_princstr))) {
+    retval = krb5_unparse_name(kpropd_context, iprop_svc_principal,
+                               &iprop_svc_princstr);
+    if (retval) {
         com_err(progname, retval,
                 _("while canonicalizing principal name"));
         krb5_free_principal(kpropd_context, iprop_svc_principal);
-        exit(1);
+        return retval;
     }
     krb5_free_principal(kpropd_context, iprop_svc_principal);
 
@@ -709,28 +665,38 @@ reinit:
     /*
      * Authentication, initialize rpcsec_gss handle etc.
      */
+    if (debug) {
+        fprintf(stderr, _("Initializing kadm5 as client %s\n"),
+                iprop_svc_princstr);
+    }
     retval = kadm5_init_with_skey(kpropd_context, iprop_svc_princstr,
                                   srvtab,
                                   master_svc_princstr,
                                   &params,
                                   KADM5_STRUCT_VERSION,
-                                  KADM5_API_VERSION_3,
+                                  KADM5_API_VERSION_4,
                                   db_args,
                                   &server_handle);
 
     if (retval) {
+        if (debug)
+            fprintf(stderr, _("kadm5 initialization failed!\n"));
         if (retval == KADM5_RPC_ERROR) {
             reinit_cnt++;
             if (server_handle)
-                kadm5_destroy((void *) server_handle);
-            server_handle = (void *)NULL;
-            handle = (kadm5_iprop_handle_t)NULL;
+                kadm5_destroy(server_handle);
+            server_handle = NULL;
+            handle = NULL;
 
             com_err(progname, retval, _(
                         "while attempting to connect"
                         " to master KDC ... retrying"));
             backoff_time = backoff_from_master(&reinit_cnt);
-            (void) sleep(backoff_time);
+            if (debug) {
+                fprintf(stderr, _("Sleeping %d seconds to re-initialize "
+                                  "kadm5 (RPC ERROR)\n"), backoff_time);
+            }
+            sleep(backoff_time);
             goto reinit;
         } else {
             if (retval == KADM5_BAD_CLIENT_PARAMS ||
@@ -746,11 +712,19 @@ reinit:
                     _("while initializing %s interface, retrying"),
                     progname);
             backoff_time = backoff_from_master(&reinit_cnt);
+            if (debug) {
+                fprintf(stderr, _("Sleeping %d seconds to re-initialize "
+                                  "kadm5 (krb5kdc not running?)\n"),
+                        backoff_time);
+            }
             sleep(backoff_time);
             goto reinit;
         }
     }
 
+    if (debug)
+        fprintf(stderr, _("kadm5 initialization succeeded\n"));
+
     /*
      * Reset re-initialization count to zero now.
      */
@@ -769,8 +743,11 @@ reinit:
          * Get the most recent ulog entry sno + ts, which
          * we package in the request to the master KDC
          */
-        mylast.last_sno = ulog->kdb_last_sno;
-        mylast.last_time = ulog->kdb_last_time;
+        retval = ulog_get_last(kpropd_context, &mylast);
+        if (retval) {
+            com_err(progname, retval, _("reading update log header"));
+            goto done;
+        }
 
         /*
          * Loop continuously on an iprop_get_updates_1(),
@@ -778,14 +755,26 @@ reinit:
          * or (if needed) do a full resync of the krb5 db.
          */
 
+        if (debug) {
+            fprintf(stderr, _("Calling iprop_get_updates_1 "
+                              "(sno=%u sec=%u usec=%u)\n"),
+                    (unsigned int)mylast.last_sno,
+                    (unsigned int)mylast.last_time.seconds,
+                    (unsigned int)mylast.last_time.useconds);
+        }
+        gettimeofday(&iprop_start, NULL);
         incr_ret = iprop_get_updates_1(&mylast, handle->clnt);
         if (incr_ret == (kdb_incr_result_t *)NULL) {
             clnt_perror(handle->clnt,
                         _("iprop_get_updates call failed"));
             if (server_handle)
-                kadm5_destroy((void *)server_handle);
-            server_handle = (void *)NULL;
+                kadm5_destroy(server_handle);
+            server_handle = NULL;
             handle = (kadm5_iprop_handle_t)NULL;
+            if (debug) {
+                fprintf(stderr, _("Reinitializing iprop because get updates "
+                                  "failed\n"));
+            }
             goto reinit;
         }
 
@@ -793,123 +782,139 @@ reinit:
 
         case UPDATE_FULL_RESYNC_NEEDED:
             /*
-             * We dont do a full resync again, if the last
-             * X'fer was a resync and if the master sno is
-             * still "0", i.e. no updates so far.
+             * If we're already asked for a full resync and we still
+             * need one and the last one hasn't timed out then just keep
+             * asking for updates as eventually the resync will finish
+             * (or, if it times out we'll just try again).  Note that
+             * doit() also applies a timeout to the full resync, thus
+             * it's OK for us to do the same here.
              */
-            if ((frdone == 1) && (incr_ret->lastentry.last_sno
-                                  == 0)) {
+            now = time(NULL);
+            if (frrequested &&
+                (now - frrequested) < params.iprop_resync_timeout) {
+                if (debug)
+                    fprintf(stderr, _("Still waiting for full resync\n"));
                 break;
             } else {
+                frrequested = now;
+                if (debug)
+                    fprintf(stderr, _("Full resync needed\n"));
+                syslog(LOG_INFO, _("kpropd: Full resync needed."));
+
                 full_ret = full_resync(handle->clnt);
-                if (full_ret == (kdb_fullresync_result_t *)
-                    NULL) {
+                if (full_ret == NULL) {
                     clnt_perror(handle->clnt,
                                 _("iprop_full_resync call failed"));
-                    if (server_handle)
-                        kadm5_destroy((void *)
-                                      server_handle);
-                    server_handle = (void *)NULL;
-                    handle = (kadm5_iprop_handle_t)NULL;
+                    kadm5_destroy(server_handle);
+                    server_handle = NULL;
+                    handle = NULL;
                     goto reinit;
                 }
             }
 
             switch (full_ret->ret) {
             case UPDATE_OK:
+                if (debug)
+                    fprintf(stderr, _("Full resync request granted\n"));
+                syslog(LOG_INFO, _("Full resync request granted."));
                 backoff_cnt = 0;
-                /*
-                 * We now listen on the kprop port for
-                 * the full dump
-                 */
-                ret = do_standalone(log_ctx->iproprole);
-                if (debug) {
-                    if (ret)
-                        fprintf(stderr,
-                                _("Full resync "
-                                  "was unsuccessful\n"));
-                    else
-                        fprintf(stderr,
-                                _("Full resync "
-                                  "was successful\n"));
-                }
-                if (ret) {
-                    syslog(LOG_WARNING,
-                           _("kpropd: Full resync, invalid return."));
-                    frdone = 0;
-                    backoff_cnt++;
-                } else
-                    frdone = 1;
                 break;
 
             case UPDATE_BUSY:
                 /*
                  * Exponential backoff
                  */
+                if (debug)
+                    fprintf(stderr, _("Exponential backoff\n"));
                 backoff_cnt++;
                 break;
 
-            case UPDATE_FULL_RESYNC_NEEDED:
-            case UPDATE_NIL:
-            default:
-                backoff_cnt = 0;
-                frdone = 0;
-                syslog(LOG_ERR, _("kpropd: Full resync,"
-                                  " invalid return from master KDC."));
-                break;
-
             case UPDATE_PERM_DENIED:
-                syslog(LOG_ERR, _("kpropd: Full resync,"
-                                  " permission denied."));
+                if (debug)
+                    fprintf(stderr, _("Full resync permission denied\n"));
+                syslog(LOG_ERR, _("Full resync, permission denied."));
                 goto error;
 
             case UPDATE_ERROR:
-                syslog(LOG_ERR, _("kpropd: Full resync,"
-                                  " error returned from master KDC."));
+                if (debug)
+                    fprintf(stderr, _("Full resync error from master\n"));
+                syslog(LOG_ERR, _(" Full resync, "
+                       "error returned from master KDC."));
                 goto error;
+
+            default:
+                backoff_cnt = 0;
+                if (debug) {
+                    fprintf(stderr,
+                            _("Full resync invalid result from master\n"));
+                }
+                syslog(LOG_ERR, _("Full resync, "
+                                  "invalid return from master KDC."));
+                break;
             }
             break;
 
         case UPDATE_OK:
             backoff_cnt = 0;
-            frdone = 0;
+            frrequested = 0;
 
             /*
              * ulog_replay() will convert the ulog updates to db
              * entries using the kdb conv api and will commit
              * the entries to the slave kdc database
              */
-            retval = ulog_replay(kpropd_context, incr_ret,
-                                 db_args);
+            if (debug) {
+                fprintf(stderr, _("Got incremental updates "
+                                  "(sno=%u sec=%u usec=%u)\n"),
+                        (unsigned int)incr_ret->lastentry.last_sno,
+                        (unsigned int)incr_ret->lastentry.last_time.seconds,
+                        (unsigned int)incr_ret->lastentry.last_time.useconds);
+            }
+            retval = ulog_replay(kpropd_context, incr_ret, db_args);
 
             if (retval) {
                 const char *msg =
                     krb5_get_error_message(kpropd_context, retval);
-                syslog(LOG_ERR,
-                       _("kpropd: ulog_replay failed (%s), updates not registered."), msg);
+                if (debug) {
+                    fprintf(stderr, _("ulog_replay failed (%s), updates not "
+                                      "registered\n"), msg);
+                }
+                syslog(LOG_ERR, _("ulog_replay failed (%s), updates "
+                       "not registered."), msg);
                 krb5_free_error_message(kpropd_context, msg);
                 break;
             }
 
-            if (debug)
-                fprintf(stderr, _("Update transfer "
-                                  "from master was OK\n"));
+            gettimeofday(&iprop_end, NULL);
+            usec = (iprop_end.tv_sec - iprop_start.tv_sec) * 1000000 +
+                iprop_end.tv_usec - iprop_start.tv_usec;
+            syslog(LOG_INFO, _("Incremental updates: %d updates / %lu us"),
+                   incr_ret->updates.kdb_ulog_t_len, usec);
+            if (debug) {
+                fprintf(stderr, _("Incremental updates: %d updates / "
+                                  "%lu us\n"),
+                        incr_ret->updates.kdb_ulog_t_len, usec);
+            }
             break;
 
         case UPDATE_PERM_DENIED:
-            syslog(LOG_ERR, _("kpropd: get_updates,"
-                              " permission denied."));
+            if (debug)
+                fprintf(stderr, _("get_updates permission denied\n"));
+            syslog(LOG_ERR, _("get_updates, permission denied."));
             goto error;
 
         case UPDATE_ERROR:
-            syslog(LOG_ERR, _("kpropd: get_updates, error "
-                              "returned from master KDC."));
+            if (debug)
+                fprintf(stderr, _("get_updates error from master\n"));
+            syslog(LOG_ERR, _("get_updates, error returned from master KDC."));
             goto error;
 
         case UPDATE_BUSY:
             /*
              * Exponential backoff
              */
+            if (debug)
+                fprintf(stderr, _("get_updates master busy; backoff\n"));
             backoff_cnt++;
             break;
 
@@ -918,20 +923,20 @@ reinit:
              * Master-slave are in sync
              */
             if (debug)
-                fprintf(stderr, _("Master, slave KDC's "
-                                  "are in-sync, no updates\n"));
+                fprintf(stderr, _("KDC is synchronized with master.\n"));
             backoff_cnt = 0;
-            frdone = 0;
+            frrequested = 0;
             break;
 
         default:
             backoff_cnt = 0;
-            syslog(LOG_ERR, _("kpropd: get_updates,"
-                              " invalid return from master KDC."));
+            if (debug)
+                fprintf(stderr, _("get_updates invalid result from master\n"));
+            syslog(LOG_ERR, _("get_updates, invalid return from master KDC."));
             break;
         }
 
-        if (runonce == 1)
+        if (runonce == 1 && incr_ret->ret != UPDATE_FULL_RESYNC_NEEDED)
             goto done;
 
         /*
@@ -941,14 +946,19 @@ reinit:
          */
         if (backoff_cnt > 0) {
             backoff_time = backoff_from_master(&backoff_cnt);
-            if (debug)
+            if (debug) {
                 fprintf(stderr, _("Busy signal received "
                                   "from master, backoff for %d secs\n"),
                         backoff_time);
-            (void) sleep(backoff_time);
+            }
+            sleep(backoff_time);
+        } else {
+            if (debug) {
+                fprintf(stderr, _("Waiting for %d seconds before checking "
+                                  "for updates again\n"), pollin);
+            }
+            sleep(pollin);
         }
-        else
-            (void) sleep(pollin);
 
     }
 
@@ -956,36 +966,24 @@ reinit:
 error:
     if (debug)
         fprintf(stderr, _("ERROR returned by master, bailing\n"));
-    syslog(LOG_ERR, _("kpropd: ERROR returned by master KDC,"
-                      " bailing.\n"));
+    syslog(LOG_ERR, _("ERROR returned by master KDC, bailing.\n"));
 done:
-    if(iprop_svc_princstr)
-        free(iprop_svc_princstr);
-    if (master_svc_princstr)
-        free(master_svc_princstr);
-    if ((retval = krb5_cc_close(kpropd_context, cc))) {
-        com_err(progname, retval,
-                _("while closing default ccache"));
-        exit(1);
-    }
-    if (def_realm && kpropd_context)
-        krb5_free_default_realm(kpropd_context, def_realm);
-    if (server_handle)
-        kadm5_destroy((void *)server_handle);
-    if (kpropd_context)
-        krb5_free_context(kpropd_context);
-
-    if (runonce == 1)
-        return (0);
-    else
-        exit(1);
+    free(iprop_svc_princstr);
+    free(master_svc_princstr);
+    krb5_free_default_realm(kpropd_context, def_realm);
+    kadm5_destroy(server_handle);
+    krb5_db_fini(kpropd_context);
+    ulog_fini(kpropd_context);
+    krb5_free_context(kpropd_context);
+
+    return (runonce == 1) ? 0 : 1;
 }
 
 
-/*
- * Do exponential backoff, since master KDC is BUSY or down
- */
-unsigned int backoff_from_master(int *cnt) {
+/* Do exponential backoff, since master KDC is BUSY or down. */
+static unsigned int
+backoff_from_master(int *cnt)
+{
     unsigned int btime;
 
     btime = (unsigned int)(2<<(*cnt));
@@ -994,13 +992,11 @@ unsigned int backoff_from_master(int *cnt) {
         (*cnt)--;
     }
 
-    return (btime);
+    return btime;
 }
 
 static void
-kpropd_com_err_proc(const char *whoami,
-                    long code,
-                    const char *fmt,
+kpropd_com_err_proc(const char *whoami, long code, const char *fmt,
                     va_list args)
 #if !defined(__cplusplus) && (__GNUC__ > 2)
     __attribute__((__format__(__printf__, 3, 0)))
@@ -1008,12 +1004,10 @@ kpropd_com_err_proc(const char *whoami,
     ;
 
 static void
-kpropd_com_err_proc(const char *whoami,
-                    long code,
-                    const char *fmt,
+kpropd_com_err_proc(const char *whoami, long code, const char *fmt,
                     va_list args)
 {
-    char    error_buf[8096];
+    char error_buf[8096];
 
     error_buf[0] = '\0';
     if (fmt)
@@ -1022,15 +1016,13 @@ kpropd_com_err_proc(const char *whoami,
            code ? error_message(code) : "", code ? " " : "", error_buf);
 }
 
-void PRS(argv)
-    char    **argv;
+static void
+parse_args(char **argv)
 {
-    register char   *word, ch;
+    char **newargs, *word, ch;
     krb5_error_code retval;
-    static const char       tmp[] = ".temp";
-    kdb_log_context *log_ctx;
 
-    (void) memset(&params, 0, sizeof (params));
+    memset(&params, 0, sizeof(params));
 
     /* Since we may modify the KDB with ulog_replay(), we must read the KDC
      * profile. */
@@ -1041,153 +1033,138 @@ void PRS(argv)
     }
 
     progname = *argv++;
-    while ((word = *argv++)) {
-        if (*word == '-') {
-            word++;
-            while (word && (ch = *word++)) {
-                switch(ch){
-                case 'f':
-                    if (*word)
-                        file = word;
-                    else
-                        file = *argv++;
-                    if (!file)
-                        usage();
-                    word = 0;
-                    break;
-                case 'F':
-                    if (*word)
-                        kerb_database = word;
-                    else
-                        kerb_database = *argv++;
-                    if (!kerb_database)
-                        usage();
-                    word = 0;
-                    break;
-                case 'p':
-                    if (*word)
-                        kdb5_util = word;
-                    else
-                        kdb5_util = *argv++;
-                    if (!kdb5_util)
-                        usage();
-                    word = 0;
-                    break;
-                case 'P':
-                    port = (*word != '\0') ? word : *argv++;
-                    if (port == NULL)
-                        usage();
-                    word = 0;
-                    break;
-                case 'r':
-                    if (*word)
-                        realm = word;
-                    else
-                        realm = *argv++;
-                    if (!realm)
-                        usage();
-                    word = 0;
-                    break;
-                case 's':
-                    if (*word)
-                        srvtab = word;
-                    else
-                        srvtab = *argv++;
-                    if (!srvtab)
-                        usage();
-                    word = 0;
-                    break;
-                case 'd':
-                    debug++;
-                    break;
-                case 'S':
-                    standalone++;
-                    break;
-                case 'a':
-                    if (*word)
-                        acl_file_name = word;
-                    else
-                        acl_file_name = *argv++;
-                    if (!acl_file_name)
-                        usage();
-                    word = 0;
-                    break;
-
-                case 't':
-                    /*
-                     * Undocumented option - for testing only.
-                     *
-                     * Option to run the kpropd server exactly
-                     * once (this is true only if iprop is enabled).
-                     */
-                    runonce = 1;
-                    break;
-
-                case 'x':
-                {
-                    char **new_db_args;
-                    new_db_args = realloc(db_args,
-                                          (db_args_size+2)*sizeof(*db_args));
-                    if (new_db_args == NULL) {
-                        com_err(argv[0], errno, _("copying db args"));
-                        exit(1);
-                    }
-                    db_args = new_db_args;
-                    if (*word)
-                        db_args[db_args_size] = word;
-                    else
-                        db_args[db_args_size] = *argv++;
-                    word = 0;
-                    if (db_args[db_args_size] == NULL)
-                        usage();
-                    db_args[db_args_size+1] = NULL;
-                    db_args_size++;
-                }
+    while ((word = *argv++) != NULL) {
+        /* We don't take any arguments, only options */
+        if (*word != '-')
+            usage();
 
-                default:
+        word++;
+        while (word != NULL && (ch = *word++) != '\0') {
+            switch (ch) {
+            case 'A':
+                params.mask |= KADM5_CONFIG_ADMIN_SERVER;
+                params.admin_server = (*word != '\0') ? word : *argv++;
+                if (params.admin_server == NULL)
                     usage();
+                word = NULL;
+                break;
+            case 'f':
+                file = (*word != '\0') ? word : *argv++;
+                if (file == NULL)
+                    usage();
+                word = NULL;
+                break;
+            case 'F':
+                kerb_database = (*word != '\0') ? word : *argv++;
+                if (kerb_database == NULL)
+                    usage();
+                word = NULL;
+                break;
+            case 'p':
+                kdb5_util = (*word != '\0') ? word : *argv++;
+                if (kdb5_util == NULL)
+                    usage();
+                word = NULL;
+                break;
+            case 'P':
+                port = (*word != '\0') ? word : *argv++;
+                if (port == NULL)
+                    usage();
+                word = NULL;
+                break;
+            case 'r':
+                realm = (*word != '\0') ? word : *argv++;
+                if (realm == NULL)
+                    usage();
+                word = NULL;
+                break;
+            case 's':
+                srvtab = (*word != '\0') ? word : *argv++;
+                if (srvtab == NULL)
+                    usage();
+                word = NULL;
+                break;
+            case 'D':
+                nodaemon++;
+                break;
+            case 'd':
+                debug++;
+                break;
+            case 'S':
+                /* Standalone mode is now auto-detected; see main(). */
+                break;
+            case 'a':
+                acl_file_name = (*word != '\0') ? word : *argv++;
+                if (acl_file_name == NULL)
+                    usage();
+                word = NULL;
+                break;
+
+            case 't':
+                /* Undocumented option - for testing only.  Run the kpropd
+                 * server exactly once. */
+                runonce = 1;
+                break;
+
+            case 'x':
+                newargs = realloc(db_args,
+                                  (db_args_size + 2) * sizeof(*db_args));
+                if (newargs == NULL) {
+                    com_err(argv[0], errno, _("copying db args"));
+                    exit(1);
                 }
+                db_args = newargs;
+                db_args[db_args_size] = (*word != '\0') ? word : *argv++;
+                if (db_args[db_args_size] == NULL)
+                    usage();
+                word = NULL;
+                db_args[db_args_size + 1] = NULL;
+                db_args_size++;
+                break;
 
+            default:
+                usage();
             }
-        } else
-            /* We don't take any arguments, only options */
-            usage();
+        }
     }
-    /*
-     * If not in debug mode, switch com_err reporting to syslog
-     */
-    if (! debug) {
-        openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS);
+
+    openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS);
+    if (!debug)
         set_com_err_hook(kpropd_com_err_proc);
+
+    if (realm == NULL) {
+        retval = krb5_get_default_realm(kpropd_context, &def_realm);
+        if (retval) {
+            com_err(progname, retval, _("Unable to get default realm"));
+            exit(1);
+        }
+        realm = def_realm;
+    } else {
+        retval = krb5_set_default_realm(kpropd_context, realm);
+        if (retval) {
+            com_err(progname, retval, _("Unable to set default realm"));
+            exit(1);
+        }
     }
-    /*
-     * Get my hostname, so we can construct my service name
-     */
-    retval = krb5_sname_to_principal(kpropd_context,
-                                     NULL, KPROP_SERVICE_NAME,
-                                     KRB5_NT_SRV_HST, &server);
+
+    /* Construct service name from local hostname. */
+    retval = sn2princ_realm(kpropd_context, NULL, KPROP_SERVICE_NAME, realm,
+                            &server);
     if (retval) {
         com_err(progname, retval,
                 _("while trying to construct my service name"));
         exit(1);
     }
-    if (realm) {
-        retval = krb5_set_principal_realm(kpropd_context, server, realm);
-        if (retval) {
-            com_err(progname, errno,
-                    _("while constructing my service realm"));
-            exit(1);
-        }
-    }
-    /*
-     * Construct the name of the temporary file.
-     */
-    if (asprintf(&temp_file_name, "%s%s", file, tmp) < 0) {
+
+    /* Construct the name of the temporary file. */
+    if (asprintf(&temp_file_name, "%s.temp", file) < 0) {
         com_err(progname, ENOMEM,
                 _("while allocating filename for temp file"));
         exit(1);
     }
 
+    params.realm = realm;
+    params.mask |= KADM5_CONFIG_REALM;
     retval = kadm5_get_config_params(kpropd_context, 1, &params, &params);
     if (retval) {
         com_err(progname, retval, _("while initializing"));
@@ -1197,58 +1174,48 @@ void PRS(argv)
         ulog_set_role(kpropd_context, IPROP_SLAVE);
 
         if (ulog_map(kpropd_context, params.iprop_logfile,
-                     params.iprop_ulogsize, FKPROPD, db_args)) {
-            com_err(progname, errno,
-                    _("Unable to map log!\n"));
+                     params.iprop_ulogsize)) {
+            com_err(progname, errno, _("Unable to map log!\n"));
             exit(1);
         }
     }
-    log_ctx = kpropd_context->kdblog_context;
-    if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE))
-        ulog_set_role(kpropd_context, IPROP_SLAVE);
 }
 
 /*
  * Figure out who's calling on the other end of the connection....
  */
-void
-kerberos_authenticate(context, fd, clientp, etype, my_sin)
-    krb5_context          context;
-    int                   fd;
-    krb5_principal      * clientp;
-    krb5_enctype        * etype;
-    struct sockaddr_storage * my_sin;
+static void
+kerberos_authenticate(krb5_context context, int fd, krb5_principal *clientp,
+                      krb5_enctype *etype, struct sockaddr_storage *my_sin)
 {
-    krb5_error_code       retval;
-    krb5_ticket         * ticket;
-    struct sockaddr_storage  r_sin;
+    krb5_error_code retval;
+    krb5_ticket *ticket;
+    struct sockaddr_storage r_sin;
     GETSOCKNAME_ARG3_TYPE sin_length;
-    krb5_keytab           keytab = NULL;
+    krb5_keytab keytab = NULL;
+    char *name, etypebuf[100];
 
-    /*
-     * Set recv_addr and send_addr
-     */
-    sockaddr2krbaddr(context, my_sin->ss_family, (struct sockaddr *) my_sin,
+    /* Set recv_addr and send_addr. */
+    sockaddr2krbaddr(context, my_sin->ss_family, (struct sockaddr *)my_sin,
                      &sender_addr);
 
     sin_length = sizeof(r_sin);
-    if (getsockname(fd, (struct sockaddr *) &r_sin, &sin_length)) {
+    if (getsockname(fd, (struct sockaddr *)&r_sin, &sin_length)) {
         com_err(progname, errno, _("while getting local socket address"));
         exit(1);
     }
 
-    sockaddr2krbaddr(context, r_sin.ss_family, (struct sockaddr *) &r_sin,
+    sockaddr2krbaddr(context, r_sin.ss_family, (struct sockaddr *)&r_sin,
                      &receiver_addr);
 
     if (debug) {
-        char *name;
-
         retval = krb5_unparse_name(context, server, &name);
         if (retval) {
             com_err(progname, retval, _("while unparsing client name"));
             exit(1);
         }
-        printf("krb5_recvauth(%d, %s, %s, ...)\n", fd, kprop_version, name);
+        fprintf(stderr, "krb5_recvauth(%d, %s, %s, ...)\n", fd, kprop_version,
+                name);
         free(name);
     }
 
@@ -1275,7 +1242,7 @@ kerberos_authenticate(context, fd, clientp, etype, my_sin)
         exit(1);
     }
 
-    if (srvtab) {
+    if (srvtab != NULL) {
         retval = krb5_kt_resolve(context, srvtab, &keytab);
         if (retval) {
             syslog(LOG_ERR, _("Error in krb5_kt_resolve: %s"),
@@ -1284,8 +1251,8 @@ kerberos_authenticate(context, fd, clientp, etype, my_sin)
         }
     }
 
-    retval = krb5_recvauth(context, &auth_context, (void *) &fd,
-                           kprop_version, server, 0, keytab, &ticket);
+    retval = krb5_recvauth(context, &auth_context, &fd, kprop_version, server,
+                           0, keytab, &ticket);
     if (retval) {
         syslog(LOG_ERR, _("Error in krb5_recvauth: %s"),
                error_message(retval));
@@ -1302,9 +1269,6 @@ kerberos_authenticate(context, fd, clientp, etype, my_sin)
     *etype = ticket->enc_part.enctype;
 
     if (debug) {
-        char * name;
-        char etypebuf[100];
-
         retval = krb5_unparse_name(context, *clientp, &name);
         if (retval) {
             com_err(progname, retval, _("while unparsing client name"));
@@ -1317,32 +1281,30 @@ kerberos_authenticate(context, fd, clientp, etype, my_sin)
             exit(1);
         }
 
-        printf(_("authenticated client: %s (etype == %s)\n"), name, etypebuf);
+        fprintf(stderr, _("authenticated client: %s (etype == %s)\n"),
+                name, etypebuf);
         free(name);
     }
 
     krb5_free_ticket(context, ticket);
 }
 
-krb5_boolean
-authorized_principal(context, p, auth_etype)
-    krb5_context context;
-    krb5_principal p;
-    krb5_enctype auth_etype;
+static krb5_boolean
+authorized_principal(krb5_context context, krb5_principal p,
+                     krb5_enctype auth_etype)
 {
-    char                *name, *ptr;
-    char                buf[1024];
-    krb5_error_code     retval;
-    FILE                *acl_file;
-    int                 end;
-    krb5_enctype        acl_etype;
+    char *name, *ptr, buf[1024];
+    krb5_error_code retval;
+    FILE *acl_file;
+    int end;
+    krb5_enctype acl_etype;
 
     retval = krb5_unparse_name(context, p, &name);
     if (retval)
         return FALSE;
 
     acl_file = fopen(acl_file_name, "r");
-    if (!acl_file)
+    if (acl_file == NULL)
         return FALSE;
 
     while (!feof(acl_file)) {
@@ -1352,22 +1314,23 @@ authorized_principal(context, p, auth_etype)
         if (buf[end] == '\n')
             buf[end] = '\0';
         if (!strncmp(name, buf, strlen(name))) {
-            ptr = buf+strlen(name);
+            ptr = buf + strlen(name);
 
-            /* if the next character is not whitespace or nul, then
-               the match is only partial.  continue on to new lines. */
-            if (*ptr && !isspace((int) *ptr))
+            /* If the next character is not whitespace or null, then the match
+             * is only partial.  Continue on to new lines. */
+            if (*ptr != '\0' && !isspace((int)*ptr))
                 continue;
 
-            /* otherwise, skip trailing whitespace */
-            for (; *ptr && isspace((int) *ptr); ptr++) ;
-
-            /* now, look for an etype string. if there isn't one,
-               return true.  if there is an invalid string, continue.
-               If there is a valid string, return true only if it
-               matches the etype passed in, otherwise continue */
+            /* Otherwise, skip trailing whitespace. */
+            for (; *ptr != '\0' && isspace((int)*ptr); ptr++) ;
 
-            if ((*ptr) &&
+            /*
+             * Now, look for an etype string.  If there isn't one, return true.
+             * If there is an invalid string, continue.  If there is a valid
+             * string, return true only if it matches the etype passed in,
+             * otherwise continue.
+             */
+            if (*ptr != '\0' &&
                 ((retval = krb5_string_to_enctype(ptr, &acl_etype)) ||
                  (acl_etype != auth_etype)))
                 continue;
@@ -1382,23 +1345,18 @@ authorized_principal(context, p, auth_etype)
     return FALSE;
 }
 
-void
-recv_database(context, fd, database_fd, confmsg)
-    krb5_context context;
-    int fd;
-    int database_fd;
-    krb5_data *confmsg;
+static void
+recv_database(krb5_context context, int fd, int database_fd,
+              krb5_data *confmsg)
 {
-    krb5_ui_4       database_size; /* This must be 4 bytes */
-    int     received_size, n;
-    char            buf[1024];
-    krb5_data       inbuf, outbuf;
+    krb5_ui_4 database_size, received_size;
+    int n;
+    char buf[1024];
+    krb5_data inbuf, outbuf;
     krb5_error_code retval;
 
-    /*
-     * Receive and decode size from client
-     */
-    retval = krb5_read_message(context, (void *) &fd, &inbuf);
+    /* Receive and decode size from client. */
+    retval = krb5_read_message(context, &fd, &inbuf);
     if (retval) {
         send_error(context, fd, retval, "while reading database size");
         com_err(progname, retval,
@@ -1409,8 +1367,7 @@ recv_database(context, fd, database_fd, confmsg)
         recv_error(context, &inbuf);
     retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL);
     if (retval) {
-        send_error(context, fd, retval,
-                   "while decoding database size");
+        send_error(context, fd, retval, "while decoding database size");
         krb5_free_data_contents(context, &inbuf);
         com_err(progname, retval,
                 _("while decoding database size from client"));
@@ -1421,9 +1378,7 @@ recv_database(context, fd, database_fd, confmsg)
     krb5_free_data_contents(context, &outbuf);
     database_size = ntohl(database_size);
 
-    /*
-     * Initialize the initial vector.
-     */
+    /* Initialize the initial vector. */
     retval = krb5_auth_con_initivector(context, auth_context);
     if (retval) {
         send_error(context, fd, retval,
@@ -1432,12 +1387,13 @@ recv_database(context, fd, database_fd, confmsg)
         exit(1);
     }
 
-    /*
-     * Now start receiving the database from the net
-     */
+    if (debug)
+        fprintf(stderr, _("Full propagation transfer started.\n"));
+
+    /* Now start receiving the database from the net. */
     received_size = 0;
     while (received_size < database_size) {
-        retval = krb5_read_message(context, (void *) &fd, &inbuf);
+        retval = krb5_read_message(context, &fd, &inbuf);
         if (retval) {
             snprintf(buf, sizeof(buf),
                      "while reading database block starting at offset %d",
@@ -1448,8 +1404,7 @@ recv_database(context, fd, database_fd, confmsg)
         }
         if (krb5_is_krb_error(&inbuf))
             recv_error(context, &inbuf);
-        retval = krb5_rd_priv(context, auth_context, &inbuf,
-                              &outbuf, NULL);
+        retval = krb5_rd_priv(context, auth_context, &inbuf, &outbuf, NULL);
         if (retval) {
             snprintf(buf, sizeof(buf),
                      "while decoding database block starting at offset %d",
@@ -1467,69 +1422,63 @@ recv_database(context, fd, database_fd, confmsg)
                      "while writing database block starting at offset %d",
                      received_size);
             send_error(context, fd, errno, buf);
-        } else if (n != outbuf.length) {
+        } else if ((unsigned int)n != outbuf.length) {
             snprintf(buf, sizeof(buf),
-                     "incomplete write while writing database block starting at \noffset %d (%d written, %d expected)",
+                     "incomplete write while writing database block starting "
+                     "at \noffset %d (%d written, %d expected)",
                      received_size, n, outbuf.length);
             send_error(context, fd, KRB5KRB_ERR_GENERIC, buf);
         }
         received_size += outbuf.length;
     }
-    /*
-     * OK, we've seen the entire file.  Did we get too many bytes?
-     */
+
+    /* OK, we've seen the entire file.  Did we get too many bytes? */
     if (received_size > database_size) {
         snprintf(buf, sizeof(buf),
                  "Received %d bytes, expected %d bytes for database file",
                  received_size, database_size);
         send_error(context, fd, KRB5KRB_ERR_GENERIC, buf);
     }
-    /*
-     * Create message acknowledging number of bytes received, but
-     * don't send it until kdb5_util returns successfully.
-     */
+
+    if (debug)
+        fprintf(stderr, _("Full propagation transfer finished.\n"));
+
+    /* Create message acknowledging number of bytes received, but
+     * don't send it until kdb5_util returns successfully. */
     database_size = htonl(database_size);
-    inbuf.data = (char *) &database_size;
+    inbuf.data = (char *)&database_size;
     inbuf.length = sizeof(database_size);
     retval = krb5_mk_safe(context,auth_context,&inbuf,confmsg,NULL);
     if (retval) {
-        com_err(progname, retval,
-                "while encoding # of receieved bytes");
-        send_error(context, fd, retval,
-                   "while encoding # of received bytes");
+        com_err(progname, retval, "while encoding # of receieved bytes");
+        send_error(context, fd, retval, "while encoding # of received bytes");
         exit(1);
     }
 }
 
 
-void
-send_error(context, fd, err_code, err_text)
-    krb5_context context;
-    int fd;
-    krb5_error_code     err_code;
-    char        *err_text;
+static void
+send_error(krb5_context context, int fd, krb5_error_code err_code,
+           char *err_text)
 {
-    krb5_error      error;
-    const char      *text;
-    krb5_data       outbuf;
-    char            buf[1024];
+    krb5_error error;
+    const char *text;
+    krb5_data outbuf;
+    char buf[1024];
 
     memset(&error, 0, sizeof(error));
     krb5_us_timeofday(context, &error.stime, &error.susec);
     error.server = server;
     error.client = client;
 
-    if (err_text)
-        text = err_text;
-    else
-        text = error_message(err_code);
+    text = (err_text != NULL) ? err_text : error_message(err_code);
 
     error.error = err_code - ERROR_TABLE_BASE_krb5;
     if (error.error > 127) {
         error.error = KRB_ERR_GENERIC;
         if (err_text) {
-            snprintf(buf, sizeof(buf), "%s %s",
-                     error_message(err_code), err_text);
+            snprintf(buf, sizeof(buf), "%s %s", error_message(err_code),
+                     err_text);
             text = buf;
         }
     }
@@ -1537,7 +1486,7 @@ send_error(context, fd, err_code, err_text)
     error.text.data = strdup(text);
     if (error.text.data) {
         if (!krb5_mk_error(context, &error, &outbuf)) {
-            (void) krb5_write_message(context, (void *)&fd,&outbuf);
+            (void)krb5_write_message(context, &fd, &outbuf);
             krb5_free_data_contents(context, &outbuf);
         }
         free(error.text.data);
@@ -1545,11 +1494,9 @@ send_error(context, fd, err_code, err_text)
 }
 
 void
-recv_error(context, inbuf)
-    krb5_context context;
-    krb5_data   *inbuf;
+recv_error(krb5_context context, krb5_data *inbuf)
 {
-    krb5_error      *error;
+    krb5_error *error;
     krb5_error_code retval;
 
     retval = krb5_rd_error(context, inbuf, &error);
@@ -1563,7 +1510,7 @@ recv_error(context, inbuf)
             fprintf(stderr, _("Generic remote error: %s\n"), error->text.data);
     } else if (error->error) {
         com_err(progname,
-                (krb5_error_code) error->error + ERROR_TABLE_BASE_krb5,
+                (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5,
                 _("signaled from server"));
         if (error->text.data) {
             fprintf(stderr, _("Error text from client: %s\n"),
@@ -1574,32 +1521,26 @@ recv_error(context, inbuf)
     exit(1);
 }
 
-void
-load_database(context, kdb_util, database_file_name)
-    krb5_context context;
-    char *kdb_util;
-    char *database_file_name;
+static void
+load_database(krb5_context context, char *kdb_util, char *database_file_name)
 {
-    static char     *edit_av[10];
-    int     error_ret, save_stderr = -1;
-    int     child_pid;
-    int     count;
+    static char *edit_av[10];
+    int error_ret, child_pid, count;
 
     /* <sys/param.h> has been included, so BSD will be defined on
-       BSD systems */
+     * BSD systems. */
 #if BSD > 0 && BSD <= 43
 #ifndef WEXITSTATUS
 #define WEXITSTATUS(w) (w).w_retcode
 #endif
-    union wait      waitb;
+    union wait waitb;
 #else
-    int     waitb;
+    int waitb;
 #endif
-    krb5_error_code retval;
     kdb_log_context *log_ctx;
 
     if (debug)
-        printf("calling kdb5_util to load database\n");
+        fprintf(stderr, "calling kdb5_util to load database\n");
 
     log_ctx = context->kdblog_context;
 
@@ -1614,45 +1555,34 @@ load_database(context, kdb_util, database_file_name)
         edit_av[count++] = "-d";
         edit_av[count++] = kerb_database;
     }
-    if (log_ctx && log_ctx->iproprole == IPROP_SLAVE) {
+    if (log_ctx && log_ctx->iproprole == IPROP_SLAVE)
         edit_av[count++] = "-i";
-    }
     edit_av[count++] = database_file_name;
     edit_av[count++] = NULL;
 
-    switch(child_pid = fork()) {
+    switch (child_pid = fork()) {
     case -1:
         com_err(progname, errno, _("while trying to fork %s"), kdb_util);
         exit(1);
     case 0:
-        if (!debug) {
-            save_stderr = dup(2);
-            close(0);
-            close(1);
-            close(2);
-            open("/dev/null", O_RDWR);
-            dup(0);
-            dup(0);
-        }
-
-        if (execv(kdb_util, edit_av) < 0)
-            retval = errno;
-        else
-            retval = 0;
-        if (!debug)
-            dup2(save_stderr, 2);
-        com_err(progname, retval, _("while trying to exec %s"), kdb_util);
+        execv(kdb_util, edit_av);
+        com_err(progname, errno, _("while trying to exec %s"), kdb_util);
         _exit(1);
         /*NOTREACHED*/
     default:
         if (debug)
-            printf("Child PID is %d\n", child_pid);
+            fprintf(stderr, "Load PID is %d\n", child_pid);
         if (wait(&waitb) < 0) {
             com_err(progname, errno, _("while waiting for %s"), kdb_util);
             exit(1);
         }
     }
 
+    if (!WIFEXITED(waitb)) {
+        com_err(progname, 0, _("%s load terminated"), kdb_util);
+        exit(1);
+    }
+
     error_ret = WEXITSTATUS(waitb);
     if (error_ret) {
         com_err(progname, 0, _("%s returned a bad exit status (%d)"),
@@ -1668,20 +1598,17 @@ load_database(context, kdb_util, database_file_name)
  * for host_service_name.
  */
 static kadm5_ret_t
-kadm5_get_kiprop_host_srv_name(krb5_context context,
-                               const char *realm_name,
+kadm5_get_kiprop_host_srv_name(krb5_context context, const char *realm_name,
                                char **host_service_name)
 {
-    char *name;
-    char *host;
+    char *name, *host;
 
     host = params.admin_server; /* XXX */
-
     if (asprintf(&name, "%s/%s", KADM5_KIPROP_HOST_SERVICE, host) < 0) {
         free(host);
-        return (ENOMEM);
+        return ENOMEM;
     }
     *host_service_name = name;
 
-    return (KADM5_OK);
+    return KADM5_OK;
 }