aboutsummaryrefslogtreecommitdiff
path: root/src/slave
diff options
context:
space:
mode:
authorKen Raeburn <raeburn@mit.edu>2008-06-24 05:04:29 +0000
committerKen Raeburn <raeburn@mit.edu>2008-06-24 05:04:29 +0000
commit5661d1290f74312a405db970aea097da77706f71 (patch)
tree0ab69c8078ef3275b99a3ad27f3592b607e43f70 /src/slave
parent6879f371402854465e5276d36e4792938906097f (diff)
downloadkrb5-5661d1290f74312a405db970aea097da77706f71.zip
krb5-5661d1290f74312a405db970aea097da77706f71.tar.gz
krb5-5661d1290f74312a405db970aea097da77706f71.tar.bz2
Merge from branch sun-iprop
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20465 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/slave')
-rw-r--r--src/slave/Makefile.in71
-rw-r--r--src/slave/kpropd.M30
-rw-r--r--src/slave/kpropd.c630
-rw-r--r--src/slave/kpropd_rpc.c55
-rw-r--r--src/slave/kproplog.M96
-rw-r--r--src/slave/kproplog.c327
6 files changed, 1176 insertions, 33 deletions
diff --git a/src/slave/Makefile.in b/src/slave/Makefile.in
index 5e4e9cb..2437f84 100644
--- a/src/slave/Makefile.in
+++ b/src/slave/Makefile.in
@@ -6,25 +6,31 @@ PROG_LIBPATH=-L$(TOPLIBD)
PROG_RPATH=$(KRB5_LIBDIR)
DEFS=
-all:: kprop kpropd
+all:: kprop kpropd kproplog
CLIENTSRCS= $(srcdir)/kprop.c
CLIENTOBJS= kprop.o
-SERVERSRCS= $(srcdir)/kpropd.c
-SERVEROBJS= kpropd.o
+SERVERSRCS= $(srcdir)/kpropd.c $(srcdir)/kpropd_rpc.c
+SERVEROBJS= kpropd.o kpropd_rpc.o
-SRCS= $(CLIENTSRCS) $(SERVERSRCS)
+LOGSRCS= $(srcdir)/kproplog.c
+LOGOBJS= kproplog.o
+
+SRCS= $(CLIENTSRCS) $(SERVERSRCS) $(LOGSRCS)
kprop: $(CLIENTOBJS) $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o kprop $(CLIENTOBJS) $(KRB5_BASE_LIBS) @LIBUTIL@
-kpropd: $(SERVEROBJS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB)
- $(CC_LINK) -o kpropd $(SERVEROBJS) $(KRB5_BASE_LIBS) $(APPUTILS_LIB) @LIBUTIL@
+kpropd: $(SERVEROBJS) $(KADMCLNT_DEPLIBS) $(KDB5_DEPLIB) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB)
+ $(CC_LINK) -o kpropd $(SERVEROBJS) $(KADMCLNT_LIBS) $(KDB5_LIB) $(KRB5_BASE_LIBS) $(APPUTILS_LIB) @LIBUTIL@
+
+kproplog: $(LOGOBJS)
+ $(CC_LINK) -o kproplog $(LOGOBJS) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS)
install::
- for f in kprop kpropd; do \
+ for f in kprop kpropd kproplog; do \
$(INSTALL_PROGRAM) $$f \
$(DESTDIR)$(SERVER_BINDIR)/`echo $$f|sed '$(transform)'`; \
$(INSTALL_DATA) $(srcdir)/$$f.M \
@@ -52,11 +58,46 @@ $(OUTPRE)kprop.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
kprop.c kprop.h
$(OUTPRE)kpropd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
- $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
- $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h \
- $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
- $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
- $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
- $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
- $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
- kprop.h kpropd.c
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/gssrpc/auth.h $(SRCTOP)/include/gssrpc/auth_gss.h \
+ $(SRCTOP)/include/gssrpc/auth_unix.h $(SRCTOP)/include/gssrpc/clnt.h \
+ $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
+ $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
+ $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+ $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
+ $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h kprop.h kpropd.c
+$(OUTPRE)kpropd_rpc.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssrpc/types.h $(SRCTOP)/include/gssrpc/auth.h \
+ $(SRCTOP)/include/gssrpc/auth_gss.h $(SRCTOP)/include/gssrpc/auth_unix.h \
+ $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
+ $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
+ $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
+ $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+ kpropd_rpc.c
+$(OUTPRE)kproplog.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/gssrpc/auth.h $(SRCTOP)/include/gssrpc/auth_gss.h \
+ $(SRCTOP)/include/gssrpc/auth_unix.h $(SRCTOP)/include/gssrpc/clnt.h \
+ $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
+ $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
+ $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+ $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
+ $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h kproplog.c
diff --git a/src/slave/kpropd.M b/src/slave/kpropd.M
index 41791f2..e2d0e76 100644
--- a/src/slave/kpropd.M
+++ b/src/slave/kpropd.M
@@ -1,6 +1,6 @@
.\" slave/kpropd.M
.\"
-.\" Copyright 1992 by the Massachusetts Institute of Technology.
+.\" Copyright 1992, 2008 by the Massachusetts Institute of Technology.
.\"
.\" Export of this software from the United States of America may
.\" require a specific license from the United States Government.
@@ -49,10 +49,15 @@ kpropd \- Kerberos V5 slave KDC update server
]
.br
.SH DESCRIPTION
+The
.I kpropd
-is the server which accepts connections from the
+command runs on the slave KDC server. It listens for update requests
+made by the
.IR kprop (8)
-program.
+program, and periodically requests incremental updates from the
+master KDC.
+
+When the slave receives a kprop request from the master,
.I kpropd
accepts the dumped KDC database and places it in a file, and then runs
.IR kdb5_util (8)
@@ -76,6 +81,25 @@ However, kpropd can also run as a standalone deamon, if the
option is turned on. This is done for debugging purposes, or if for
some reason the system administrator just doesn't want to run it out of
.IR inetd (8).
+
+When the slave periodically requests incremental updates,
+.I kpropd
+updates its
+.I principal.ulog
+file with any updates from the master.
+.IR kproplog (8)
+can be used to view a summary of the update entry log on the slave
+KDC. Incremental propagation is not enabled by default; it can be
+enabled using the
+.I iprop_enable
+and
+.I iprop_slave_poll
+settings in
+.IR kdc.conf (5).
+The principal "kiprop/slavehostname@REALM" (where "slavehostname" is
+the name of the slave KDC host, and "REALM" is the name of the
+Kerberos realm) must be present in the slave's keytab file.
+
.SH OPTIONS
.TP
\fB\-r\fP \fIrealm\fP
diff --git a/src/slave/kpropd.c b/src/slave/kpropd.c
index d790055..fd46819 100644
--- a/src/slave/kpropd.c
+++ b/src/slave/kpropd.c
@@ -81,6 +81,10 @@
#include <errno.h>
#include "kprop.h"
+#include <iprop_hdr.h>
+#include "iprop.h"
+#include <kadm5/admin.h>
+#include <kdb_log.h>
#ifndef GETSOCKNAME_ARG3_TYPE
#define GETSOCKNAME_ARG3_TYPE unsigned int
@@ -95,6 +99,28 @@ extern int daemon(int, int);
#define SYSLOG_CLASS LOG_DAEMON
+char *def_realm = NULL;
+int runonce = 0;
+
+/*
+ * 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;
+ kadm5_config_params params;
+ struct _kadm5_iprop_handle_t *lhandle;
+} *kadm5_iprop_handle_t;
+
+
static char *kprop_version = KPROP_PROT_VERSION;
char *progname;
@@ -117,12 +143,16 @@ krb5_address sender_addr;
krb5_address receiver_addr;
short port = 0;
+char **db_args = NULL;
+int db_args_size = 0;
+
void PRS
(char**);
-void do_standalone
- (void);
+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,
@@ -150,6 +180,12 @@ void send_error
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,
+ char **host_service_name);
static void usage()
{
@@ -157,7 +193,7 @@ static void usage()
"\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[-P port] [-a acl_file]\n");
+ fprintf(stderr, "\t[-x db_args]* [-P port] [-a acl_file]\n");
exit(1);
}
@@ -166,23 +202,55 @@ main(argc, argv)
int argc;
char **argv;
{
- PRS(argv);
+ krb5_error_code retval;
+ int ret = 0;
+ kdb_log_context *log_ctx;
+
+ PRS(argv);
+
+ 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
+ }
+
+ if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
+ /*
+ * We wanna do iprop !
+ */
+ retval = do_iprop(log_ctx);
+ if (retval) {
+ com_err(progname, retval,
+ _("do_iprop failed.\n"));
+ exit(1);
+ }
+ } else {
if (standalone)
- do_standalone();
+ ret = do_standalone(IPROP_NULL);
else
- doit(0);
- exit(0);
+ doit(0);
+ }
+
+ exit(ret);
}
-void do_standalone()
+int do_standalone(iprop_role iproprole)
{
struct sockaddr_in my_sin, frominet;
struct servent *sp;
int finet, s;
GETPEERNAME_ARG3_TYPE fromlen;
int ret;
-
+
finet = socket(AF_INET, SOCK_STREAM, 0);
if (finet < 0) {
com_err(progname, errno, "while obtaining socket");
@@ -200,6 +268,27 @@ void do_standalone()
my_sin.sin_port = port;
}
my_sin.sin_family = AF_INET;
+
+ /*
+ * 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) {
+ int on = 1;
+ struct linger linger;
+
+ if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0)
+ com_err(progname, errno,
+ _("in setsockopt(SO_REUSEADDR)"));
+ linger.l_onoff = 1;
+ linger.l_linger = 2;
+ if (setsockopt(finet, SOL_SOCKET, SO_LINGER,
+ (void *)&linger, sizeof(linger)) < 0)
+ com_err(progname, errno,
+ _("in setsockopt(SO_LINGER)"));
+ }
if ((ret = bind(finet, (struct sockaddr *) &my_sin, sizeof(my_sin))) < 0) {
if (debug) {
int on = 1;
@@ -217,7 +306,7 @@ void do_standalone()
exit(1);
}
}
- if (!debug)
+ if (!debug && iproprole != IPROP_SLAVE)
daemon(1, 0);
#ifdef PID_FILE
if ((pidfile = fopen(PID_FILE, "w")) != NULL) {
@@ -233,9 +322,12 @@ void do_standalone()
}
while (1) {
int child_pid;
+ int status;
memset((char *)&frominet, 0, sizeof(frominet));
fromlen = sizeof(frominet);
+ if (debug)
+ fprintf(stderr, "waiting for a kprop connection\n");
s = accept(finet, (struct sockaddr *) &frominet, &fromlen);
if (s < 0) {
@@ -244,7 +336,7 @@ void do_standalone()
"from accept system call");
continue;
}
- if (debug)
+ if (debug && iproprole != IPROP_SLAVE)
child_pid = 0;
else
child_pid = fork();
@@ -259,11 +351,23 @@ void do_standalone()
close(s);
_exit(0);
default:
- wait(0);
- close(s);
-
+ if (wait(&status) < 0) {
+ com_err(progname, errno,
+ _("while waiting to receive database"));
+ exit(1);
+ }
+
+ close(s);
+ if (iproprole == IPROP_SLAVE)
+ close(finet);
+
+ if ((ret = WEXITSTATUS(status)) != 0)
+ return (ret);
}
+ if (iproprole == IPROP_SLAVE)
+ break;
}
+ return 0;
}
void doit(fd)
@@ -328,6 +432,10 @@ void doit(fd)
"While unparsing client name");
exit(1);
}
+ if (debug)
+ fprintf(stderr,
+ "Rejected connection from unauthorized principal %s\n",
+ name);
syslog(LOG_WARNING,
"Rejected connection from unauthorized principal %s",
name);
@@ -392,6 +500,411 @@ void doit(fd)
exit(0);
}
+/*
+ * Routine to handle incremental update transfer(s) from master KDC
+ */
+kadm5_config_params params;
+krb5_error_code do_iprop(kdb_log_context *log_ctx)
+{
+ 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 *keytab_name = NULL;
+ unsigned int pollin, backoff_time;
+ int backoff_cnt = 0;
+ int reinit_cnt = 0;
+ int ret;
+ int frdone = 0;
+
+ kdb_incr_result_t *incr_ret;
+ static kdb_last_t mylast;
+
+ kdb_fullresync_result_t *full_ret;
+ char *full_resync_arg = NULL;
+
+ kadm5_iprop_handle_t handle;
+ kdb_hlog_t *ulog;
+
+ if (!debug)
+ daemon(0, 0);
+
+ ulog = log_ctx->ulog;
+
+ pollin = params.iprop_poll_time;
+ if (pollin < 10)
+ 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)) {
+ com_err(progname, retval,
+ _("%s: unable to get kiprop host based "
+ "service name for realm %s\n"),
+ progname, def_realm);
+ exit(1);
+ }
+ }
+
+ /*
+ * 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);
+ if (retval) {
+ com_err(progname, retval,
+ _("while trying to construct host service principal"));
+ exit(1);
+ }
+
+ /* 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)) {
+ com_err(progname, retval,
+ _("while canonicalizing principal name"));
+ krb5_free_principal(kpropd_context, iprop_svc_principal);
+ exit(1);
+ }
+ krb5_free_principal(kpropd_context, iprop_svc_principal);
+
+reinit:
+ /*
+ * Authentication, initialize rpcsec_gss handle etc.
+ */
+ retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name,
+ master_svc_princstr,
+ &params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ db_args,
+ &server_handle);
+
+ if (retval) {
+ 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;
+
+ com_err(progname, retval, _(
+ "while attempting to connect"
+ " to master KDC ... retrying"));
+ backoff_time = backoff_from_master(&reinit_cnt);
+ (void) sleep(backoff_time);
+ goto reinit;
+ } else {
+ if (retval == KADM5_BAD_CLIENT_PARAMS ||
+ retval == KADM5_BAD_SERVER_PARAMS) {
+ com_err(progname, retval,
+ _("while initializing %s interface"),
+ progname);
+
+ usage();
+ }
+ reinit_cnt++;
+ com_err(progname, retval,
+ _("while initializing %s interface, retrying"),
+ progname);
+ backoff_time = backoff_from_master(&reinit_cnt);
+ sleep(backoff_time);
+ goto reinit;
+ }
+ }
+
+ /*
+ * Reset re-initialization count to zero now.
+ */
+ reinit_cnt = backoff_time = 0;
+
+ /*
+ * Reset the handle to the correct type for the RPC call
+ */
+ handle = server_handle;
+
+ for (;;) {
+ incr_ret = NULL;
+ full_ret = NULL;
+
+ /*
+ * 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;
+
+ /*
+ * Loop continuously on an iprop_get_updates_1(),
+ * so that we can keep probing the master for updates
+ * or (if needed) do a full resync of the krb5 db.
+ */
+
+ 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;
+ handle = (kadm5_iprop_handle_t)NULL;
+ goto reinit;
+ }
+
+ switch (incr_ret->ret) {
+
+ 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 ((frdone == 1) && (incr_ret->lastentry.last_sno
+ == 0)) {
+ break;
+ } else {
+
+ full_ret = iprop_full_resync_1((void *)
+ &full_resync_arg, handle->clnt);
+
+ if (full_ret == (kdb_fullresync_result_t *)
+ 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;
+ goto reinit;
+ }
+ }
+
+ switch (full_ret->ret) {
+ case UPDATE_OK:
+ backoff_cnt = 0;
+ /*
+ * We now listen on the kprop port for
+ * the full dump
+ */
+ ret = do_standalone(log_ctx->iproprole);
+ if (ret)
+ syslog(LOG_WARNING,
+ _("kpropd: Full resync, "
+ "invalid return."));
+ if (debug) {
+ if (ret)
+ fprintf(stderr,
+ _("Full resync "
+ "was unsuccessful\n"));
+ else
+ fprintf(stderr,
+ _("Full resync "
+ "was successful\n"));
+ }
+ frdone = 1;
+ break;
+
+ case UPDATE_BUSY:
+ /*
+ * Exponential backoff
+ */
+ 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."));
+ goto error;
+
+ case UPDATE_ERROR:
+ syslog(LOG_ERR, _("kpropd: Full resync,"
+ " error returned from master KDC."));
+ goto error;
+ }
+ break;
+
+ case UPDATE_OK:
+ backoff_cnt = 0;
+ frdone = 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 (retval) {
+ syslog(LOG_ERR, _("kpropd: ulog_replay"
+ " failed, updates not registered."));
+ break;
+ }
+
+ if (debug)
+ fprintf(stderr, _("Update transfer "
+ "from master was OK\n"));
+ break;
+
+ case UPDATE_PERM_DENIED:
+ syslog(LOG_ERR, _("kpropd: get_updates,"
+ " permission denied."));
+ goto error;
+
+ case UPDATE_ERROR:
+ syslog(LOG_ERR, _("kpropd: get_updates, error "
+ "returned from master KDC."));
+ goto error;
+
+ case UPDATE_BUSY:
+ /*
+ * Exponential backoff
+ */
+ backoff_cnt++;
+ break;
+
+ case UPDATE_NIL:
+ /*
+ * Master-slave are in sync
+ */
+ if (debug)
+ fprintf(stderr, _("Master, slave KDC's "
+ "are in-sync, no updates\n"));
+ backoff_cnt = 0;
+ frdone = 0;
+ break;
+
+ default:
+ backoff_cnt = 0;
+ syslog(LOG_ERR, _("kpropd: get_updates,"
+ " invalid return from master KDC."));
+ break;
+ }
+
+ if (runonce == 1)
+ goto done;
+
+ /*
+ * Sleep for the specified poll interval (Default is 2 mts),
+ * or do a binary exponential backoff if we get an
+ * UPDATE_BUSY signal
+ */
+ if (backoff_cnt > 0) {
+ backoff_time = backoff_from_master(&backoff_cnt);
+ if (debug)
+ fprintf(stderr, _("Busy signal received "
+ "from master, backoff for %d secs\n"),
+ backoff_time);
+ (void) sleep(backoff_time);
+ }
+ else
+ (void) sleep(pollin);
+
+ }
+
+
+error:
+ if (debug)
+ fprintf(stderr, _("ERROR returned by master, bailing\n"));
+ syslog(LOG_ERR, _("kpropd: 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)
+ free(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);
+}
+
+
+/*
+ * Do exponential backoff, since master KDC is BUSY or down
+ */
+unsigned int backoff_from_master(int *cnt) {
+ unsigned int btime;
+
+ btime = (unsigned int)(2<<(*cnt));
+ if (btime > MAX_BACKOFF) {
+ btime = MAX_BACKOFF;
+ *cnt--;
+ }
+
+ return (btime);
+}
+
+
+static char *
+copy_leading_substring(char *src, size_t len)
+{
+ char *result;
+ result = malloc((len + 1) * sizeof(char));
+ (void) strncpy(result, src, len+1);
+ result[len] = 0;
+ return result;
+}
+
static void
kpropd_com_err_proc(whoami, code, fmt, args)
const char *whoami;
@@ -414,7 +927,10 @@ void PRS(argv)
register char *word, ch;
krb5_error_code retval;
static const char tmp[] = ".temp";
-
+ kdb_log_context *log_ctx;
+
+ (void) memset((char *)&params, 0, sizeof (params));
+
retval = krb5_init_context(&kpropd_context);
if (retval) {
com_err(argv[0], retval, "while initializing krb5");
@@ -496,6 +1012,38 @@ void PRS(argv)
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++;
+ }
+
default:
usage();
}
@@ -542,6 +1090,25 @@ void PRS(argv)
}
strcpy(temp_file_name, file);
strcat(temp_file_name, tmp);
+
+ retval = kadm5_get_config_params(kpropd_context, 1, &params, &params);
+ if (retval) {
+ com_err(progname, retval, _("while initializing"));
+ exit(1);
+ }
+ if (params.iprop_enabled == TRUE) {
+ 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"));
+ exit(1);
+ }
+ }
+ log_ctx = kpropd_context->kdblog_context;
+ if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE))
+ ulog_set_role(kpropd_context, IPROP_SLAVE);
}
/*
@@ -939,10 +1506,13 @@ load_database(context, kdb_util, database_file_name)
int waitb;
#endif
krb5_error_code retval;
+ kdb_log_context *log_ctx;
if (debug)
printf("calling kdb5_util to load database\n");
+ log_ctx = context->kdblog_context;
+
edit_av[0] = kdb_util;
count = 1;
if (realm) {
@@ -954,6 +1524,9 @@ 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) {
+ edit_av[count++] = "-i";
+ }
edit_av[count++] = database_file_name;
edit_av[count++] = NULL;
@@ -999,3 +1572,30 @@ load_database(context, kdb_util, database_file_name)
}
return;
}
+
+/*
+ * Get the host base service name for the kiprop principal. Returns
+ * KADM5_OK on success. Caller must free the storage allocated
+ * for host_service_name.
+ */
+static kadm5_ret_t
+kadm5_get_kiprop_host_srv_name(krb5_context context,
+ const char *realm,
+ char **host_service_name)
+{
+ kadm5_ret_t ret;
+ char *name;
+ char *host;
+
+ host = params.admin_server; /* XXX */
+
+ name = malloc(strlen(KADM5_KIPROP_HOST_SERVICE) + strlen(host) + 2);
+ if (name == NULL) {
+ free(host);
+ return (ENOMEM);
+ }
+ sprintf(name, "%s/%s", KADM5_KIPROP_HOST_SERVICE, host);
+ *host_service_name = name;
+
+ return (KADM5_OK);
+}
diff --git a/src/slave/kpropd_rpc.c b/src/slave/kpropd_rpc.c
new file mode 100644
index 0000000..e5713b2
--- /dev/null
+++ b/src/slave/kpropd_rpc.c
@@ -0,0 +1,55 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include <memory.h> /* for memset */
+#include "iprop.h"
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+void *
+iprop_null_1(void *argp, CLIENT *clnt)
+{
+ static char clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, IPROP_NULL,
+ (xdrproc_t) xdr_void, (caddr_t) argp,
+ (xdrproc_t) xdr_void, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&clnt_res);
+}
+
+kdb_incr_result_t *
+iprop_get_updates_1(kdb_last_t *argp, CLIENT *clnt)
+{
+ static kdb_incr_result_t clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, IPROP_GET_UPDATES,
+ (xdrproc_t) xdr_kdb_last_t, (caddr_t) argp,
+ (xdrproc_t) xdr_kdb_incr_result_t, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+kdb_fullresync_result_t *
+iprop_full_resync_1(void *argp, CLIENT *clnt)
+{
+ static kdb_fullresync_result_t clnt_res;
+
+ memset((char *)&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call (clnt, IPROP_FULL_RESYNC,
+ (xdrproc_t) xdr_void, (caddr_t) argp,
+ (xdrproc_t) xdr_kdb_fullresync_result_t, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
diff --git a/src/slave/kproplog.M b/src/slave/kproplog.M
new file mode 100644
index 0000000..b7081a9
--- /dev/null
+++ b/src/slave/kproplog.M
@@ -0,0 +1,96 @@
+.\" slave/kprop.M
+.\"
+.\" Copyright 2008 by the Massachusetts Institute of Technology.
+.\"
+.\" Export of this software from the United States of America may
+.\" require a specific license from the United States Government.
+.\" It is the responsibility of any person or organization contemplating
+.\" export to obtain such a license before exporting.
+.\"
+.\" WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+.\" distribute this software and its documentation for any purpose and
+.\" without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" the name of M.I.T. not be used in advertising or publicity pertaining
+.\" to distribution of the software without specific, written prior
+.\" permission. Furthermore if you modify this software you must label
+.\" your software as modified software and not distribute it in such a
+.\" fashion that it might be confused with the original M.I.T. software.
+.\" M.I.T. makes no representations about the suitability of
+.\" this software for any purpose. It is provided "as is" without express
+.\" or implied warranty.
+.\"
+.\"
+.\" Copyright (c) 2003, Sun Microsystems, Inc. All Rights Reserved
+.\"
+.TH KPROPLOG 1
+.SH NAME
+kproplog \- display the contents of the Kerberos principal update log
+.SH SYNOPSIS
+.B kproplog
+[\fB\-h\fP] [\fB\-e\fP \fInum\fP]
+.br
+.SH DESCRIPTION
+The
+.I kproplog
+command displays the contents of the Kerberos principal update log to
+standard output. It can be used to keep track of the incremental
+updates to the principal database, when enabled. The update log
+file contains the update log maintained by the
+.I kadmind
+process on the master KDC server and the kpropd process on the slave
+KDC servers. When updates occur, they are logged to this
+file. Subsequently any KDC slave configured for incremental updates
+will request the current data from the master KDC and update their
+.I principal.ulog
+file with any updates returned.
+
+The
+.I kproplog
+command can only be run on a KDC server by someone with privileges
+comparable to the superuser. It will display update entries for that
+server only.
+
+If no options are specified, the summary of the update log is
+displayed. If invoked on the master, all of the update entries are
+also displayed. When invoked on a slave KDC server, only a summary of
+the updates are displayed, which includes the serial number of the
+last update received and the associated time stamp of the last update.
+
+.SH OPTIONS
+.TP
+\fB\-h\fP
+Display a summary of the update log. This information includes the
+database version number, state of the database, the number of updates
+in the log, the time stamp of the first and last update, and the
+version number of the first and last update entry.
+.TP
+\fB\-e\fP \fInum\fP
+Display the last
+.I num
+update entries in the log. This is useful when debugging
+synchronization between KDC servers.
+.TP
+\fB\-v\fP
+Display individual attributes per update.
+An example of the output generated for one entry:
+.nf
+ Update Entry
+ Update serial # : 4
+ Update operation : Add
+ Update principal : test@EXAMPLE.COM
+ Update size : 424
+ Update committed : True
+ Update time stamp : Fri Feb 20 23:37:42 2004
+ Attributes changed : 6
+ Principal
+ Key data
+ Password last changed
+ Modifying principal
+ Modification time
+ TL data
+.fi
+
+.SH SEE ALSO
+kpropd(8)
diff --git a/src/slave/kproplog.c b/src/slave/kproplog.c
new file mode 100644
index 0000000..58ce70f
--- /dev/null
+++ b/src/slave/kproplog.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident "@(#)kproplog.c 1.4 04/03/19 SMI" */
+
+/*
+ * This module will parse the update logs on the master or slave servers.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <limits.h>
+#include <locale.h>
+#include <syslog.h>
+#include "k5-int.h"
+#include <kdb_log.h>
+#include <kadm5/admin.h>
+
+#ifndef gettext
+#define textdomain(X) 0
+#endif
+
+static char *progname;
+
+static void
+usage()
+{
+ (void) fprintf(stderr, _("\nUsage: %s [-h] [-v] [-e num]\n\n"),
+ progname);
+ exit(1);
+}
+
+/*
+ * Print the individual types if verbose mode was specified.
+ */
+static void
+print_attr(kdbe_attr_type_t type)
+{
+ switch (type) {
+ case AT_ATTRFLAGS:
+ (void) printf(_("\t\tAttribute flags\n"));
+ break;
+ case AT_MAX_LIFE:
+ (void) printf(_("\t\tMaximum ticket life\n"));
+ break;
+ case AT_MAX_RENEW_LIFE:
+ (void) printf(_("\t\tMaximum renewable life\n"));
+ break;
+ case AT_EXP:
+ (void) printf(_("\t\tPrincipal expiration\n"));
+ break;
+ case AT_PW_EXP:
+ (void) printf(_("\t\tPassword expiration\n"));
+ break;
+ case AT_LAST_SUCCESS:
+ (void) printf(_("\t\tLast successful auth\n"));
+ break;
+ case AT_LAST_FAILED:
+ (void) printf(_("\t\tLast failed auth\n"));
+ break;
+ case AT_FAIL_AUTH_COUNT:
+ (void) printf(_("\t\tFailed passwd attempt\n"));
+ break;
+ case AT_PRINC:
+ (void) printf(_("\t\tPrincipal\n"));
+ break;
+ case AT_KEYDATA:
+ (void) printf(_("\t\tKey data\n"));
+ break;
+ case AT_TL_DATA:
+ (void) printf(_("\t\tTL data\n"));
+ break;
+ case AT_LEN:
+ (void) printf(_("\t\tLength\n"));
+ break;
+ case AT_MOD_PRINC:
+ (void) printf(_("\t\tModifying principal\n"));
+ break;
+ case AT_MOD_TIME:
+ (void) printf(_("\t\tModification time\n"));
+ break;
+ case AT_MOD_WHERE:
+ (void) printf(_("\t\tModified where\n"));
+ break;
+ case AT_PW_LAST_CHANGE:
+ (void) printf(_("\t\tPassword last changed\n"));
+ break;
+ case AT_PW_POLICY:
+ (void) printf(_("\t\tPassword policy\n"));
+ break;
+ case AT_PW_POLICY_SWITCH:
+ (void) printf(_("\t\tPassword policy switch\n"));
+ break;
+ case AT_PW_HIST_KVNO:
+ (void) printf(_("\t\tPassword history KVNO\n"));
+ break;
+ case AT_PW_HIST:
+ (void) printf(_("\t\tPassword history\n"));
+ break;
+ } /* switch */
+
+}
+/*
+ * Print the update entry information
+ */
+static void
+print_update(kdb_hlog_t *ulog, uint32_t entry, bool_t verbose)
+{
+ XDR xdrs;
+ uint32_t start_sno, i, j, indx;
+ char *dbprinc;
+ kdb_ent_header_t *indx_log;
+ kdb_incr_update_t upd;
+
+ if (entry && (entry < ulog->kdb_num))
+ start_sno = ulog->kdb_last_sno - entry;
+ else
+ start_sno = ulog->kdb_first_sno - 1;
+
+ for (i = start_sno; i < ulog->kdb_last_sno; i++) {
+ indx = i % ulog->kdb_num;
+
+ indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
+
+ /*
+ * Check for corrupt update entry
+ */
+ if (indx_log->kdb_umagic != KDB_ULOG_MAGIC) {
+ (void) fprintf(stderr,
+ _("Corrupt update entry\n\n"));
+ exit(1);
+ }
+
+ (void) memset((char *)&upd, 0, sizeof (kdb_incr_update_t));
+ xdrmem_create(&xdrs, (char *)indx_log->entry_data,
+ indx_log->kdb_entry_size, XDR_DECODE);
+ if (!xdr_kdb_incr_update_t(&xdrs, &upd)) {
+ (void) printf(_("Entry data decode failure\n\n"));
+ exit(1);
+ }
+
+ (void) printf("---\n");
+ (void) printf(_("Update Entry\n"));
+
+ (void) printf(_("\tUpdate serial # : %u\n"),
+ indx_log->kdb_entry_sno);
+
+ (void) printf(_("\tUpdate operation : "));
+ if (upd.kdb_deleted)
+ (void) printf(_("Delete\n"));
+ else
+ (void) printf(_("Add\n"));
+
+ dbprinc = malloc(upd.kdb_princ_name.utf8str_t_len + 1);
+ if (dbprinc == NULL) {
+ (void) printf(_("Could not allocate "
+ "principal name\n\n"));
+ exit(1);
+ }
+ (void) strncpy(dbprinc, upd.kdb_princ_name.utf8str_t_val,
+ (upd.kdb_princ_name.utf8str_t_len + 1));
+ dbprinc[upd.kdb_princ_name.utf8str_t_len] = 0;
+ (void) printf(_("\tUpdate principal : %s\n"), dbprinc);
+
+ (void) printf(_("\tUpdate size : %u\n"),
+ indx_log->kdb_entry_size);
+
+ (void) printf(_("\tUpdate committed : %s\n"),
+ indx_log->kdb_commit ? "True" : "False");
+
+ if (indx_log->kdb_time.seconds == 0L)
+ (void) printf(_("\tUpdate time stamp : None\n"));
+ else
+ (void) printf(_("\tUpdate time stamp : %s"),
+ ctime((time_t *)&(indx_log->kdb_time.seconds)));
+
+ (void) printf(_("\tAttributes changed : %d\n"),
+ upd.kdb_update.kdbe_t_len);
+
+ if (verbose)
+ for (j = 0; j < upd.kdb_update.kdbe_t_len; j++)
+ print_attr(
+ upd.kdb_update.kdbe_t_val[j].av_type);
+
+ xdr_free(xdr_kdb_incr_update_t, (char *)&upd);
+ free(dbprinc);
+ } /* for */
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ bool_t verbose = FALSE;
+ bool_t headeronly = FALSE;
+ uint32_t entry = 0;
+ krb5_context context;
+ kadm5_config_params params;
+ kdb_log_context *log_ctx;
+ kdb_hlog_t *ulog = NULL;
+ char **db_args = NULL; /* XXX */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif /* TEXT_DOMAIN */
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ progname = argv[0];
+
+ while ((c = getopt(argc, argv, "vhe:")) != -1) {
+ switch (c) {
+ case 'h':
+ headeronly = TRUE;
+ break;
+ case 'e':
+ entry = atoi(optarg);
+ break;
+ case 'v':
+ verbose = TRUE;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (krb5_init_context(&context)) {
+ (void) fprintf(stderr,
+ _("Unable to initialize Kerberos\n\n"));
+ exit(1);
+ }
+
+ (void) memset((char *)&params, 0, sizeof (params));
+
+ if (kadm5_get_config_params(context, 1, &params, &params)) {
+ (void) fprintf(stderr,
+ _("Couldn't read database_name\n\n"));
+ exit(1);
+ }
+
+ (void) printf(_("\nKerberos update log (%s.ulog)\n"),
+ params.dbname);
+
+ if (ulog_map(context, params.iprop_logfile, 0, FKPROPLOG, db_args)) {
+ (void) fprintf(stderr, _("Unable to map log file "
+ "%s.ulog\n\n"), params.dbname);
+ exit(1);
+ }
+
+ log_ctx = context->kdblog_context;
+ if (log_ctx)
+ ulog = log_ctx->ulog;
+ else {
+ (void) fprintf(stderr, _("Unable to map log file "
+ "%s.ulog\n\n"), params.dbname);
+ exit(1);
+ }
+
+ if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC) {
+ (void) fprintf(stderr,
+ _("Corrupt header log, exiting\n\n"));
+ exit(1);
+ }
+
+ (void) printf(_("Update log dump :\n"));
+ (void) printf(_("\tLog version # : %u\n"), ulog->db_version_num);
+ (void) printf(_("\tLog state : "));
+ switch (ulog->kdb_state) {
+ case KDB_STABLE:
+ (void) printf(_("Stable\n"));
+ break;
+ case KDB_UNSTABLE:
+ (void) printf(_("Unstable\n"));
+ break;
+ case KDB_CORRUPT:
+ (void) printf(_("Corrupt\n"));
+ break;
+ default:
+ (void) printf(_("Unknown state: %d\n"),
+ ulog->kdb_state);
+ break;
+ }
+ (void) printf(_("\tEntry block size : %u\n"), ulog->kdb_block);
+ (void) printf(_("\tNumber of entries : %u\n"), ulog->kdb_num);
+
+ if (ulog->kdb_last_sno == 0)
+ (void) printf(_("\tLast serial # : None\n"));
+ else {
+ if (ulog->kdb_first_sno == 0)
+ (void) printf(_("\tFirst serial # : None\n"));
+ else {
+ (void) printf(_("\tFirst serial # : "));
+ (void) printf("%u\n", ulog->kdb_first_sno);
+ }
+
+ (void) printf(_("\tLast serial # : "));
+ (void) printf("%u\n", ulog->kdb_last_sno);
+ }
+
+ if (ulog->kdb_last_time.seconds == 0L) {
+ (void) printf(_("\tLast time stamp : None\n"));
+ } else {
+ if (ulog->kdb_first_time.seconds == 0L)
+ (void) printf(_("\tFirst time stamp : None\n"));
+ else {
+ (void) printf(_("\tFirst time stamp : %s"),
+ ctime((time_t *)
+ &(ulog->kdb_first_time.seconds)));
+ }
+
+ (void) printf(_("\tLast time stamp : %s\n"),
+ ctime((time_t *)&(ulog->kdb_last_time.seconds)));
+ }
+
+ if ((!headeronly) && ulog->kdb_num) {
+ print_update(ulog, entry, verbose);
+ }
+
+ (void) printf("\n");
+
+ return (0);
+}