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