You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Wojtek <so...@nemezis.ipan.lublin.pl> on 1999/04/08 17:31:00 UTC

no-loosing-listen-sockets-restart-patch

Hey !

I'm sending in this patch again. It seems that it went unnoticed.. please
answer me (I'm not subscribed to this list so send any replies to
sopel@nemezis.ipan.lublin.pl please).

The patch adds a '-i' option to httpd which allows to restart the daemon
without loosing the listen sockets. That way if You recompile httpd, You
can safely run the new one and not break any connections. The new httpd simply
inherits all the listen sockets from the old one. All old children are allowed
to finish their current tasks and then exit.

Please send Your comments on my patch and give me directions how to modify
the documentation, so that the patch can be inserted into the Apache dist.

Greetings,
Wojtek Sobczuk

the patch follows:

diff -N -up apache_1.3.6/src/main/Makefile.tmpl modded_apache_1.3.6/src/main/Makefile.tmpl
--- apache_1.3.6/src/main/Makefile.tmpl	Tue Jan 12 15:47:00 1999
+++ modded_apache_1.3.6/src/main/Makefile.tmpl	Wed Mar 31 12:46:34 1999
@@ -11,7 +11,7 @@ OBJS= alloc.o buff.o \
       http_config.o http_core.o http_log.o \
       http_main.o http_protocol.o http_request.o http_vhost.o \
       util.o util_date.o util_script.o util_uri.o util_md5.o \
-      rfc1413.o
+      rfc1413.o mycode.o
 
 .c.o:
 	$(CC) -c $(INCLUDES) $(CFLAGS) $<
diff -N -up apache_1.3.6/src/main/http_main.c modded_apache_1.3.6/src/main/http_main.c
--- apache_1.3.6/src/main/http_main.c	Wed Mar 17 23:05:43 1999
+++ modded_apache_1.3.6/src/main/http_main.c	Sat Apr  3 16:41:35 1999
@@ -90,6 +90,7 @@ int ap_main(int argc, char *argv[]);
 
 #define CORE_PRIVATE
 
+#include "mycode.h"
 #include "httpd.h"
 #include "http_main.h"
 #include "http_log.h"
@@ -285,6 +286,17 @@ array_header *ap_server_pre_read_config;
 array_header *ap_server_post_read_config;
 array_header *ap_server_config_defines;
 
+/* The pid from which we are inheriting open listen sockets */
+int	inherit_from_pid = 0;
+/* If set to != 0, the parent sends it's sockets, sends USR1 to children and
+ * cleanly exits.
+ */
+int	send_my_fds_and_die = 0;
+/* Used in the children. Set if we catch SIGUSR1 while processing some request
+ * (previously SIGUSR1 was simply ignored then).
+ */
+int	got_usr1_while_processing = 0;
+
 /* *Non*-shared http_main globals... */
 
 static server_rec *server_conf;
@@ -996,6 +1008,7 @@ static void usage(char *bin)
     fprintf(stderr, "  -v               : show version number\n");
     fprintf(stderr, "  -V               : show compile settings\n");
     fprintf(stderr, "  -h               : list available command line options (this page)\n");
+    fprintf(stderr, "  -i pid           : inherit sockets from a running apache\n");
     fprintf(stderr, "  -l               : list compiled-in modules\n");
     fprintf(stderr, "  -L               : list available configuration directives\n");
     fprintf(stderr, "  -S               : show parsed settings (currently only vhost settings)\n");
@@ -2751,6 +2764,11 @@ static void set_signals(void)
     if (sigaction(SIGPIPE, &sa, NULL) < 0)
 	ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)");
 #endif
+#ifdef SIGUSR2
+    sa.sa_handler = sigusr2_handler;
+    if (sigaction(SIGUSR2, &sa, NULL) < 0)
+	ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR2)");
+#endif
 
     /* we want to ignore HUPs and USR1 while we're busy processing one */
     sigaddset(&sa.sa_mask, SIGHUP);
@@ -3177,6 +3195,10 @@ static void copy_listeners(pool *p)
     listen_rec *lr;
 
     ap_assert(old_listeners == NULL);
+    if (inherit_from_pid) {
+	inherit_sockets_and_create_listeners (p, inherit_from_pid, server_conf);
+	inherit_from_pid = 0;
+    }
     if (ap_listeners == NULL) {
 	return;
     }
@@ -3765,8 +3787,9 @@ static void child_main(int child_num_arg
 
 	/* We've got a socket, let's at least process one request off the
 	 * socket before we accept a graceful restart request.
+	 * (We now note that SIGUSR1 was sent and handle it later)
 	 */
-	signal(SIGUSR1, SIG_IGN);
+	signal(SIGUSR1, processing_time_usr1_handler);
 
 	ap_note_cleanups_for_fd(ptrans, csd);
 
@@ -3884,6 +3907,15 @@ static void child_main(int child_num_arg
 	}
 
 	/*
+	 * If SIGUSR1 was caught while the request was being processed, then
+	 * it means that this child should die.
+	 */
+	if (got_usr1_while_processing) {
+		/* Maybe some exit code for this will be invented ? */
+		clean_child_exit (0);
+	}
+
+	/*
 	 * Close the connection, being careful to send out whatever is still
 	 * in our buffers.  If possible, try to avoid a hard close until the
 	 * client has ACKed our FIN and/or has stopped sending us data.
@@ -4154,6 +4186,20 @@ static void perform_idle_server_maintena
     else {
 	idle_spawn_rate = 1;
     }
+
+    if (send_my_fds_and_die) {
+	/* Send the file descriptors */
+	send_sockets_and_listeners (server_conf);
+	/* Tells the children to exit nicely */
+	if (ap_killpg (pgrp, SIGUSR1) < 0) {
+		ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+				"killpg SIGUSR1");
+	}
+	/* Exit myself */
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+			"caught SIGUSR2, passed descriptors and exiting");
+	clean_parent_exit(0);
+    }
 }
 
 
@@ -4499,7 +4545,7 @@ int REALMAIN(int argc, char *argv[])
 
 #ifndef TPF
     while ((c = getopt(argc, argv,
-				    "D:C:c:Xd:f:vVlLR:Sth"
+				    "D:C:c:Xd:f:vVlLR:Sthi:"
 #ifdef DEBUG_SIGSTOP
 				    "Z:"
 #endif
@@ -4523,6 +4569,10 @@ int REALMAIN(int argc, char *argv[])
 	    break;
 	case 'f':
 	    ap_cpystrn(ap_server_confname, optarg, sizeof(ap_server_confname));
+	    break;
+	case 'i':
+	    /* Get the fds from a httpd which is already running */
+	    inherit_from_pid = atoi (optarg);
 	    break;
 	case 'v':
 	    ap_set_version();
diff -N -up apache_1.3.6/src/main/http_protocol.c modded_apache_1.3.6/src/main/http_protocol.c
--- apache_1.3.6/src/main/http_protocol.c	Wed Mar 10 17:42:42 1999
+++ modded_apache_1.3.6/src/main/http_protocol.c	Wed Mar 31 14:00:09 1999
@@ -76,6 +76,8 @@
 #include <stdarg.h>
 #include "http_conf_globals.h"
 
+#include "mycode.h"
+
 #define SET_BYTES_SENT(r) \
   do { if (r->sent_bodyct) \
           ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
@@ -796,7 +798,7 @@ static int read_request_line(request_rec
     }
     /* we've probably got something to do, ignore graceful restart requests */
 #ifdef SIGUSR1
-    signal(SIGUSR1, SIG_IGN);
+    signal(SIGUSR1, processing_time_usr1_handler);
 #endif
 
     ap_bsetflag(conn->client, B_SAFEREAD, 0);
diff -N -up apache_1.3.6/src/main/mycode.c modded_apache_1.3.6/src/main/mycode.c
--- apache_1.3.6/src/main/mycode.c	Thu Jan  1 00:00:00 1970
+++ modded_apache_1.3.6/src/main/mycode.c	Sat Apr  3 16:34:39 1999
@@ -0,0 +1,311 @@
+/* created by Wojciech Sobczuk <so...@hack.dk> */
+/* code for passing file descriptors between processes */
+#include "mycode.h"
+
+/* Variables defined in http_main.c */
+extern int	send_my_fds_and_die;
+extern int	got_usr1_while_processing;
+
+/* Some dummy string which needs to be sent in the iov substructure used by
+ * sendmsg/recvmsg
+ */
+char	*dummyiov = "yes!";
+
+/*
+ * Handles SIGUSR1 when we are currently processing a connection, so that
+ * if SIGUSR1 is received it is recorded and can be handled later on.
+ */
+void
+processing_time_usr1_handler (int signo)
+{
+	got_usr1_while_processing = 1;
+}
+
+/*
+ * Handles SIGUSR2 - this notifies the server to call 
+ * send_sockets_and_listeners ().
+ */
+void
+sigusr2_handler (int signo)
+{
+	send_my_fds_and_die = 1;
+}
+
+/* Sends the file descriptor subject_fd through to_fd */
+int
+sm_send_fd (int to_fd, int subject_fd)
+{
+struct iovec	iov[1];
+struct msghdr	msg;
+char		cmsgbuf[sizeof (struct cmsghdr) + sizeof (int)];
+struct cmsghdr	*cmsg = (struct cmsghdr *) cmsgbuf;
+
+	iov[0].iov_len 		= strlen (dummyiov) + 1;
+	iov[0].iov_base 	= (char *) dummyiov;
+	bzero (&msg, sizeof (msg));
+	msg.msg_iov 		= iov;
+	msg.msg_iovlen 		= 1;
+
+	cmsg->cmsg_len		= sizeof (cmsgbuf);
+	cmsg->cmsg_level	= SOL_SOCKET;
+	cmsg->cmsg_type		= SCM_RIGHTS;
+	*(int *)CMSG_DATA(cmsg) = subject_fd;
+
+	msg.msg_control		= cmsgbuf;
+	msg.msg_controllen	= sizeof (cmsgbuf);
+
+	if (sendmsg (to_fd, &msg, 0) < 0)
+		return 1;
+
+	return 0;
+}
+
+/* Receives a file descriptor from from_fd */
+int
+sm_read_fd (int from_fd)
+{
+int		new_fd;
+struct iovec	iov[1];
+struct msghdr	msg;
+char		cmsgbuf[sizeof (struct cmsghdr) + sizeof (int)];
+struct cmsghdr	*cmsg = (struct cmsghdr *) cmsgbuf;
+
+	iov[0].iov_len 		= strlen (dummyiov) + 1;
+	iov[0].iov_base 	= (char *) malloc (iov[0].iov_len);
+
+	bzero (cmsgbuf, sizeof (cmsgbuf));
+	cmsg->cmsg_len		= sizeof (cmsgbuf);
+	cmsg->cmsg_level	= SOL_SOCKET;
+	cmsg->cmsg_type		= SCM_RIGHTS;
+
+	bzero (&msg, sizeof (msg));
+	msg.msg_iov 		= iov;
+	msg.msg_iovlen 		= 1;
+	msg.msg_control		= cmsgbuf;
+	msg.msg_controllen	= sizeof (cmsgbuf);
+
+	if (recvmsg (from_fd, &msg, 0) < 0)
+		return -1;
+
+	new_fd = *(int *)CMSG_DATA(cmsg);
+
+	if (cmsg->cmsg_type != SCM_RIGHTS)
+		return -2;
+
+	free (iov[0].iov_base);
+	return new_fd;
+}
+
+/*
+ * Inherits sockets from pid oldpid. Uses memory pool p for allocation, and
+ * server_conf when logging errors.
+ */
+void
+inherit_sockets_and_create_listeners (pool *p, int oldpid,
+				      server_rec *server_conf)
+{
+int			fd, orig_fd, listen_fd, i, saddr_siz;
+char			c, transfer_socket_path [MAXPATHLEN];
+struct sockaddr_un	saddr;
+listen_rec		*lr;
+
+	ap_log_error (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+			"attempting to receive sockets");
+
+	/* Set up the socket's identity */
+	bzero ((char *) &saddr, sizeof (saddr));
+	saddr.sun_family = PF_LOCAL;
+	snprintf (transfer_socket_path, MAXPATHLEN, "%s/httpd.trans.%d",
+					TRANSFER_SOCKET_DIR, oldpid);
+	strcpy (saddr.sun_path, transfer_socket_path);
+
+	saddr_siz = sizeof (saddr);
+
+	/* Creating the socket we will use for reading/writing stuff */
+	if ((orig_fd = socket (PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+						"inherit_sockets: socket ()");
+		exit (1);
+	}
+
+	if (bind (orig_fd, (struct sockaddr *) &saddr, sizeof (saddr))) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+						"inherit_sockets: bind ()");
+		exit (1);
+	}
+
+	if (listen (orig_fd, 5) < 0) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+						"inherit_sockets: listen ()");
+		exit (1);
+	}
+
+	/* Sending USR2 to the old httpd */
+	if (kill (oldpid, SIGUSR2) < 0) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+						"inherit_sockets: kill ()");
+		exit (1);
+	}
+
+	/* Wait for the old httpd to connect for the transfer */
+	if ((fd = accept (orig_fd, (struct sockaddr *) &saddr, &saddr_siz))
+									< 0) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+						"inherit_sockets: accept ()");
+		exit (1);
+	}
+
+	/* Read the fds and listen_rec structures */
+	for (i = 0; ; i++) {
+		/* Check the status */
+		if (read (fd, &c, 1) != 1) {
+			ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+					"inherit_sockets: read ()");
+			exit (1);
+		}
+		if (c == TRANSFER_END) {
+			break;
+		} else if (c != TRANSFER_OK) {
+			ap_log_error (APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, server_conf,
+					"read unknown TRANSFER char");
+			exit (1);
+		}
+
+		/* Get the listener fd (must be done separately) */
+		if ((listen_fd = sm_read_fd (fd)) < 0) {
+			ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+					"inherit_sockets: read ()");
+			exit (1);
+		}
+
+		/* Get his full struct */
+		lr = ap_pcalloc(p, sizeof(listen_rec));
+		if (read (fd, (void *) lr, sizeof (listen_rec)) !=
+							sizeof (listen_rec)) {
+			ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+						"inherit_sockets: read ()");
+			exit (1);
+		}
+		lr->fd = listen_fd;
+
+		/* Add the new listener to the ring */
+		if (!ap_listeners) {
+			ap_listeners = lr;
+			ap_listeners->next = ap_listeners;
+		} else {
+			lr->next = ap_listeners->next;
+			ap_listeners->next = lr;
+		}
+	}
+
+	/* Write the confirmation */
+	c = TRANSFER_OK;
+	if (write (fd, &c, 1) != 1) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"inherit_sockets: write ()");
+		exit (1);
+	}
+
+	close (fd);
+	unlink (transfer_socket_path);
+
+	ap_log_error (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+			"sockets successfuly received");
+}
+
+/* Sends the sockets and listeners records to the open unix socket */
+void
+send_sockets_and_listeners (server_rec *server_conf)
+{
+int			fd;
+char			c;
+struct sockaddr_un	saddr;
+listen_rec		*lr;
+/* struct linger		myling; */
+
+	ap_log_error (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+			"attempting to send sockets");
+
+	/* Set up the socket's identity */
+	bzero ((char *) &saddr, sizeof (saddr));
+	saddr.sun_family = PF_LOCAL;
+	snprintf (saddr.sun_path, MAXPATHLEN, "%s/httpd.trans.%d",
+					TRANSFER_SOCKET_DIR, getpid ());
+
+	/* Creating the socket we will use for reading/writing stuff */
+	if ((fd = socket (PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"send_sockets_and_listeners: socket ()");
+		exit (1);
+	}
+
+/* Someday I might put this back..
+	myling.l_onoff = 1;
+	myling.l_linger = 10;
+	if (setsockopt (fd, SOL_SOCKET, SO_LINGER, &myling, sizeof (myling))
+									< 0) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"send_sockets_and_listeners: setsockopt ()");
+		exit (1);
+	}
+*/
+	if (connect (fd, (struct sockaddr *) &saddr, sizeof (saddr))) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"send_sockets_and_listeners: connect ()");
+		exit (1);
+	}
+
+	c = TRANSFER_OK;
+	/* send the descriptors */
+	for (lr = ap_listeners; ; lr = lr->next) {
+		if (!lr)
+			break;
+
+		/* Send confirmation of successful transfer (c is set to
+		 * TRANSFER_OK earlier).
+		 */
+		if (write (fd, &c, 1) != 1) {
+			ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"send_sockets_and_listeners: connect ()");
+			exit (1);
+		}
+
+		/* Send the descriptor */
+		if (sm_send_fd (fd, lr->fd) < 0) {
+			ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"send_sockets_and_listeners: write ()");
+			exit (1);
+		}
+
+		/* Write the whole listener struct */
+		if (write (fd, lr, sizeof (listen_rec)) != sizeof (listen_rec)){
+			ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+					"send_sockets_and_listeners: write ()");
+			exit (1);
+		}
+
+		/* Last in the ring - we break */
+		if (lr->next == ap_listeners)
+			break;
+	}
+
+	/* Now we write that this is the end of data */
+	c = TRANSFER_END;
+	if (write (fd, &c, 1) != 1) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"send_sockets_and_listeners: write ()");
+		exit (1);
+	}
+
+	/* Read the confirmation */
+	if (read (fd, &c, 1) != 1) {
+		ap_log_error (APLOG_MARK, APLOG_CRIT, server_conf,
+				"send_sockets_and_listeners: read ()");
+		exit (1);
+	}
+
+	close (fd);
+
+	ap_log_error (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+			"sockets successfuly sent");
+}
diff -N -up apache_1.3.6/src/main/mycode.h modded_apache_1.3.6/src/main/mycode.h
--- apache_1.3.6/src/main/mycode.h	Thu Jan  1 00:00:00 1970
+++ modded_apache_1.3.6/src/main/mycode.h	Wed Mar 31 14:40:00 1999
@@ -0,0 +1,35 @@
+#ifndef __mkinc_mycode_H__
+#define __mkinc_mycode_H__
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h"        /* for read_config */
+#include "http_protocol.h"      /* for read_request */
+#include "http_request.h"       /* for process_request */
+#include "http_conf_globals.h"
+#include "http_core.h"          /* for get_remote_host */
+#include "http_vhost.h"
+#include "util_script.h"        /* to force util_script.c linking */
+#include "util_uri.h"
+#include "scoreboard.h"
+#include "multithread.h"
+#include <sys/stat.h>
+
+#define TRANSFER_SOCKET_DIR	"/var/run"
+
+#define TRANSFER_OK		'o'
+#define TRANSFER_END		'e'
+
+void processing_time_usr1_handler (int signo) ;
+void sigusr2_handler (int signo) ;
+int sm_send_fd (int to_fd, int subject_fd) ;
+int sm_read_fd (int from_fd) ;
+void inherit_sockets_and_create_listeners (pool *p, int oldpid, server_rec *server_conf) ;
+void send_sockets_and_listeners (server_rec *server_conf) ;
+
+#endif /* __mkinc_mycode_H__ */

EOT