svn rev #24328: trunk/src/ include/ kadmin/server/ kdc/ lib/apputils/
ghudson@MIT.EDU
ghudson at MIT.EDU
Fri Sep 17 13:42:31 EDT 2010
http://src.mit.edu/fisheye/changelog/krb5/?cs=24328
Commit By: ghudson
Log Message:
ticket: 6783
subject: KDC worker processes feature
Add support for a krb5kdc -w option which causes the KDC to spawn
worker processes which can process requests in parallel. See also:
http://k5wiki.kerberos.org/wiki/Projects/Parallel_KDC
Changed Files:
U trunk/src/include/net-server.h
U trunk/src/kadmin/server/ovsec_kadmd.c
U trunk/src/kdc/Makefile.in
U trunk/src/kdc/krb5kdc.M
U trunk/src/kdc/main.c
A trunk/src/kdc/t_workers.py
U trunk/src/lib/apputils/net-server.c
Modified: trunk/src/include/net-server.h
===================================================================
--- trunk/src/include/net-server.h 2010-09-17 16:06:34 UTC (rev 24327)
+++ trunk/src/include/net-server.h 2010-09-17 17:42:31 UTC (rev 24328)
@@ -43,7 +43,7 @@
krb5_error_code add_tcp_port(int port);
krb5_error_code add_rpc_service(int port, u_long prognum, u_long versnum,
void (*dispatch)());
-krb5_error_code setup_network(void *handle, const char *prog);
+krb5_error_code setup_network(void *handle, const char *prog, int no_reconfig);
krb5_error_code listen_and_process(void *handle, const char *prog,
void (*reset)(void));
void closedown_network(void);
Modified: trunk/src/kadmin/server/ovsec_kadmd.c
===================================================================
--- trunk/src/kadmin/server/ovsec_kadmd.c 2010-09-17 16:06:34 UTC (rev 24327)
+++ trunk/src/kadmin/server/ovsec_kadmd.c 2010-09-17 17:42:31 UTC (rev 24328)
@@ -393,7 +393,7 @@
: 0)
#endif
#undef server_handle
- || (ret = setup_network(global_server_handle, whoami))) {
+ || (ret = setup_network(global_server_handle, whoami, 0))) {
const char *e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "%s: %s while initializing network, aborting",
whoami, e_txt);
Modified: trunk/src/kdc/Makefile.in
===================================================================
--- trunk/src/kdc/Makefile.in 2010-09-17 16:06:34 UTC (rev 24327)
+++ trunk/src/kdc/Makefile.in 2010-09-17 17:42:31 UTC (rev 24328)
@@ -70,6 +70,9 @@
cmp test.out $(srcdir)/rtest.good
$(RM) test.out
+check-pytests::
+ $(RUNPYTEST) $(srcdir)/t_workers.py $(PYTESTFLAGS)
+
install::
$(INSTALL_PROGRAM) krb5kdc ${DESTDIR}$(SERVER_BINDIR)/krb5kdc
$(INSTALL_DATA) $(srcdir)/krb5kdc.M ${DESTDIR}$(SERVER_MANDIR)/krb5kdc.8
Modified: trunk/src/kdc/krb5kdc.M
===================================================================
--- trunk/src/kdc/krb5kdc.M 2010-09-17 16:06:34 UTC (rev 24327)
+++ trunk/src/kdc/krb5kdc.M 2010-09-17 17:42:31 UTC (rev 24328)
@@ -49,6 +49,9 @@
] [
.B \-n
] [
+.B \-w
+.I numworkers
+] [
.B \-P
.I pid_file
]
@@ -138,7 +141,24 @@
the background.
.PP
The
+.B \-w
+.I numworkers
+option tells the KDC to fork
+.I numworkers
+processes to listen to the KDC ports and process requests in parallel.
+The top level KDC process (whose pid is recorded in the pid file if
+the
.B \-P
+option is also given) acts as a supervisor. The supervisor will relay
+SIGHUP signals to the worker subprocesses, and will terminate the
+worker subprocess if the it is itself terminated or if any other
+worker process exits. NOTE: on operating systems which do not have
+pktinfo support, using worker processes will prevent the KDC from
+listening for UDP packets on network interfaces created after the KDC
+starts.
+.PP
+The
+.B \-P
.I pid_file
option tells the KDC to write its PID (followed by a newline) into
.I pid_file
Modified: trunk/src/kdc/main.c
===================================================================
--- trunk/src/kdc/main.c 2010-09-17 16:06:34 UTC (rev 24327)
+++ trunk/src/kdc/main.c 2010-09-17 17:42:31 UTC (rev 24328)
@@ -61,6 +61,7 @@
#include <netdb.h>
#include <unistd.h>
#include <ctype.h>
+#include <sys/wait.h>
#include "k5-int.h"
#include "com_err.h"
@@ -93,6 +94,7 @@
static void finish_realms (void);
static int nofork = 0;
+static int workers = 0;
static const char *pid_file = NULL;
static int rkey_init_done = 0;
@@ -523,7 +525,100 @@
return;
}
+/*
+ * Kill the worker subprocesses given by pids[0..bound-1], skipping any which
+ * are set to -1, and wait for them to exit (so that we know the ports are no
+ * longer in use). num_active must be the number of active (i.e. not -1) pids
+ * in the array.
+ */
+static void
+terminate_workers(pid_t *pids, int bound, int num_active)
+{
+ int i, status;
+ pid_t pid;
+
+ /* Kill the active worker pids. */
+ for (i = 0; i < bound; i++) {
+ if (pids[i] != -1)
+ kill(pids[i], SIGTERM);
+ }
+
+ /* Wait for them to exit. */
+ while (num_active > 0) {
+ pid = wait(&status);
+ if (pid >= 0)
+ num_active--;
+ }
+}
+
+/*
+ * Create num worker processes and return successfully in each child. The
+ * parent process will act as a supervisor and will only return from this
+ * function in error cases.
+ */
static krb5_error_code
+create_workers(int num)
+{
+ int i, status, numleft;
+ pid_t pid, *pids;
+
+ /* Create child worker processes; return in each child. */
+ krb5_klog_syslog(LOG_INFO, "creating %d worker processes", num);
+ pids = malloc(num * sizeof(pid_t));
+ if (pids == NULL)
+ return ENOMEM;
+ for (i = 0; i < num; i++) {
+ pid = fork();
+ if (pid == 0)
+ return 0;
+ if (pid == -1) {
+ /* Couldn't fork enough times. */
+ status = errno;
+ terminate_workers(pids, i, i);
+ free(pids);
+ return status;
+ }
+ pids[i] = pid;
+ }
+
+ /* Supervise the child processes. */
+ numleft = num;
+ while (!signal_requests_exit) {
+ /* Wait until a child process exits or we get a signal. */
+ pid = wait(&status);
+ if (pid >= 0) {
+ krb5_klog_syslog(LOG_ERR, "worker %ld exited with status %d",
+ (long) pid, status);
+
+ /* Remove the pid from the table. */
+ for (i = 0; i < num; i++) {
+ if (pids[i] == pid)
+ pids[i] = -1;
+ }
+
+ /* When one process exits, terminate them all, so that KDC crashes
+ * behave similarly with or without worker processes. */
+ break;
+ }
+
+ /* Propagate HUP signal to worker processes if we received one. */
+ if (signal_requests_reset) {
+ for (i = 0; i < num; i++) {
+ if (pids[i] != -1)
+ kill(pids[i], SIGHUP);
+ }
+ signal_requests_reset = 0;
+ }
+ }
+ if (signal_requests_exit)
+ krb5_klog_syslog(LOG_INFO, "shutdown signal received in supervisor");
+
+ terminate_workers(pids, num, numleft);
+ free(pids);
+ exit(0);
+}
+
+static krb5_error_code
setup_sam(void)
{
return krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_MD5, &psr_key);
@@ -532,11 +627,17 @@
static void
usage(char *name)
{
- fprintf(stderr, "usage: %s [-x db_args]* [-d dbpathname] [-r dbrealmname]\n\t\t[-R replaycachename] [-m] [-k masterenctype] [-M masterkeyname]\n\t\t[-p port] [-P pid_file] [/]\n"
- "\nwhere,\n\t[-x db_args]* - Any number of database specific arguments. Look at\n"
- "\t\t\teach database module documentation for supported\n\t\t\targuments\n",
+ fprintf(stderr,
+ "usage: %s [-x db_args]* [-d dbpathname] [-r dbrealmname]\n"
+ "\t\t[-R replaycachename] [-m] [-k masterenctype]\n"
+ "\t\t[-M masterkeyname] [-p port] [-P pid_file]\n"
+ "\t\t[-n] [-w numworkers] [/]\n\n"
+ "where,\n"
+ "\t[-x db_args]* - Any number of database specific arguments.\n"
+ "\t\t\tLook at each database module documentation for supported\n"
+ "\t\t\targuments\n",
name);
- return;
+ exit(1);
}
@@ -609,7 +710,7 @@
* Loop through the option list. Each time we encounter a realm name,
* use the previously scanned options to fill in for defaults.
*/
- while ((c = getopt(argc, argv, "x:r:d:mM:k:R:e:P:p:s:n4:X3")) != -1) {
+ while ((c = getopt(argc, argv, "x:r:d:mM:k:R:e:P:p:s:nw:4:X3")) != -1) {
switch(c) {
case 'x':
db_args_size++;
@@ -691,6 +792,11 @@
case 'n':
nofork++; /* don't detach from terminal */
break;
+ case 'w': /* create multiple worker processes */
+ workers = atoi(optarg);
+ if (workers <= 0)
+ usage(argv[0]);
+ break;
case 'k': /* enctype for master key */
if (krb5_string_to_enctype(optarg, &menctype))
com_err(argv[0], 0, "invalid enctype %s", optarg);
@@ -722,7 +828,6 @@
case '?':
default:
usage(argv[0]);
- exit(1);
}
}
@@ -805,6 +910,7 @@
finish_realm(kdc_realmlist[i]);
kdc_realmlist[i] = 0;
}
+ kdc_numrealms = 0;
}
/*
@@ -921,7 +1027,13 @@
}
}
- if ((retval = setup_network(NULL, kdc_progname))) {
+ /*
+ * Setup network listeners. Disallow network reconfig in response to
+ * routing socket messages if we're using worker processes, since the
+ * children won't be able to re-open the listener sockets. Hopefully our
+ * platform has pktinfo support and doesn't need reconfigs.
+ */
+ if ((retval = setup_network(NULL, kdc_progname, (workers > 0)))) {
net_init_error:
kdc_err(kcontext, retval, "while initializing network");
finish_realms();
@@ -940,6 +1052,16 @@
return 1;
}
}
+ if (workers > 0) {
+ finish_realms();
+ retval = create_workers(workers);
+ if (retval) {
+ kdc_err(kcontext, errno, "creating worker processes");
+ return 1;
+ }
+ /* We get here only in a worker child process; re-initialize realms. */
+ initialize_realms(kcontext, argc, argv);
+ }
krb5_klog_syslog(LOG_INFO, "commencing operation");
if (nofork)
fprintf(stderr, "%s: starting...\n", kdc_progname);
Added: trunk/src/kdc/t_workers.py
===================================================================
--- trunk/src/kdc/t_workers.py (rev 0)
+++ trunk/src/kdc/t_workers.py 2010-09-17 17:42:31 UTC (rev 24328)
@@ -0,0 +1,8 @@
+#!/usr/bin/python
+from k5test import *
+
+realm = K5Realm(start_kdc=False, start_kadmind=False, create_host=False)
+realm.start_kdc(['-w', '3'])
+realm.kinit(realm.user_princ, password('user'))
+realm.klist(realm.user_princ)
+success('KDC worker processes.')
Modified: trunk/src/lib/apputils/net-server.c
===================================================================
--- trunk/src/lib/apputils/net-server.c 2010-09-17 16:06:34 UTC (rev 24327)
+++ trunk/src/lib/apputils/net-server.c 2010-09-17 17:42:31 UTC (rev 24328)
@@ -808,6 +808,7 @@
sock = create_server_socket(data, addr, SOCK_DGRAM);
if (sock == -1)
return 1;
+ setnbio(sock);
#if !(defined(CMSG_SPACE) && defined(HAVE_STRUCT_CMSGHDR) && \
(defined(IP_PKTINFO) || defined(IPV6_PKTINFO)))
@@ -1092,7 +1093,7 @@
extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t);
krb5_error_code
-setup_network(void *handle, const char *prog)
+setup_network(void *handle, const char *prog, int no_reconfig)
{
struct socksetup setup_data;
@@ -1108,7 +1109,8 @@
setup_data.retval = 0;
krb5_klog_syslog (LOG_INFO, "setting up network...");
#ifdef HAVE_STRUCT_RT_MSGHDR
- setup_routing_socket(&setup_data);
+ if (!no_reconfig)
+ setup_routing_socket(&setup_data);
#endif
/*
* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
@@ -1381,7 +1383,7 @@
(struct sockaddr *)&daddr, &daddr_len,
&auxaddr);
if (cc == -1) {
- if (errno != EINTR
+ if (errno != EINTR && errno != EAGAIN
/*
* This is how Linux indicates that a previous transmission was
* refused, e.g., if the client timed out before getting the
@@ -1837,7 +1839,7 @@
if (sret == 0 && netchanged) {
network_reconfiguration_needed = 0;
closedown_network_sockets();
- err = setup_network(handle, prog);
+ err = setup_network(handle, prog, 0);
if (err) {
com_err(prog, err, "while reinitializing network");
return err;
More information about the cvs-krb5
mailing list