patch-2.3.14 linux/net/khttpd/main.c

Next file: linux/net/khttpd/make_times_h.c
Previous file: linux/net/khttpd/logging.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.13/linux/net/khttpd/main.c linux/net/khttpd/main.c
@@ -0,0 +1,394 @@
+/*
+
+kHTTPd -- the next generation
+
+Main program
+
+
+kHTTPd TNG consists of 1 thread, this main-thread handles ALL connections
+simultanious. It does this by keeping queues with the requests in different
+stages.
+
+The stages are
+
+<not accepted> 		-	TCP/IP connection is not accepted yet
+WaitForHeaders		-	Connection is accepted, waiting for headers
+DataSending		-	Headers decoded, sending file-data
+Userspace		-	Requires userspace daemon 
+Logging			-	The request is finished, cleanup and logging
+
+A typical flow for a request would be:
+
+<not accepted>
+WaitForHeaders
+DataSending
+Logging
+
+or
+
+<not accepted>
+WaitForHeaders
+Userspace
+
+
+
+*/
+/****************************************************************
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2, or (at your option)
+ *	any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include <asm/unistd.h>
+
+#include "structure.h"
+#include "prototypes.h"
+#include "sysctl.h"
+
+struct khttpd_threadinfo threadinfo[CONFIG_KHTTPD_NUMCPU];  /* The actual work-queues */
+
+
+atomic_t	ConnectCount;
+atomic_t	DaemonCount;
+
+static int	ActualThreads; /* The number of actual, active threads */
+
+
+static int ConnectionsPending(int CPUNR)
+{
+	if (threadinfo[CPUNR].DataSendingQueue!=NULL) return O_NONBLOCK;
+	if (threadinfo[CPUNR].WaitForHeaderQueue!=NULL) return O_NONBLOCK;
+	if (threadinfo[CPUNR].LoggingQueue!=NULL) return O_NONBLOCK;
+	if (threadinfo[CPUNR].UserspaceQueue!=NULL) return O_NONBLOCK;
+  return 0;
+}
+
+
+
+static wait_queue_head_t DummyWQ[CONFIG_KHTTPD_NUMCPU];
+static atomic_t Running[CONFIG_KHTTPD_NUMCPU]; 
+
+static int MainDaemon(void *cpu_pointer)
+{
+	int CPUNR;
+	sigset_t tmpsig;
+	
+	DECLARE_WAITQUEUE(main_wait,current);
+	
+	MOD_INC_USE_COUNT;
+
+	
+	current->state |= TASK_EXCLUSIVE;
+
+	CPUNR=0;
+	if (cpu_pointer!=NULL)
+	CPUNR=(int)*(int*)cpu_pointer;
+
+	sprintf(current->comm,"khttpd - %i",CPUNR);
+	lock_kernel();   /* This seems to be required for exit_mm */
+	exit_mm(current);
+	
+	init_waitqueue_head(&(DummyWQ[CPUNR]));
+	
+
+	/* Block all signals except SIGKILL, SIGSTOP and SIGHUP */
+	spin_lock_irq(&current->sigmask_lock);
+	tmpsig = current->blocked;
+	siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)| sigmask(SIGHUP));
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	
+	
+	if (MainSocket->sk==NULL)
+	 	return 0;
+	add_wait_queue_exclusive(MainSocket->sk->sleep,&(main_wait));
+	atomic_inc(&DaemonCount);
+	atomic_set(&Running[CPUNR],1);
+	
+	while (sysctl_khttpd_stop==0)
+	{
+		int changes = 0;
+				
+
+		
+		changes +=AcceptConnections(CPUNR,MainSocket);
+		if (ConnectionsPending(CPUNR))
+		{
+			changes +=WaitForHeaders(CPUNR);
+			changes +=DataSending(CPUNR);
+			changes +=Userspace(CPUNR);
+			changes +=Logging(CPUNR);
+			/* Test for incomming connections _again_, because it is possible
+			   one came in during the other steps, and the wakeup doesn't happen
+			   then.
+			*/
+			changes +=AcceptConnections(CPUNR,MainSocket);
+		}
+		
+		current->state = TASK_INTERRUPTIBLE|TASK_EXCLUSIVE;	
+		if (changes==0) 
+		{
+			(void)interruptible_sleep_on_timeout(&(DummyWQ[CPUNR]),1);	
+			if (CPUNR==0) 
+				UpdateCurrentDate();
+		}
+			
+		if (signal_pending(current)!=0)
+		{
+			(void)printk(KERN_NOTICE "kHTTPd: Ring Ring - signal received\n");
+			break;		  
+		}
+	
+	}
+	
+	remove_wait_queue(MainSocket->sk->sleep,&(main_wait));
+	
+	StopWaitingForHeaders(CPUNR);
+	StopDataSending(CPUNR);
+	StopUserspace(CPUNR);
+	StopLogging(CPUNR);
+	
+	atomic_set(&Running[CPUNR],0);
+	atomic_dec(&DaemonCount);
+	(void)printk(KERN_NOTICE "kHTTPd: Daemon %i has ended\n",CPUNR);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int CountBuf[CONFIG_KHTTPD_NUMCPU];
+
+static int errno;
+inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options);
+
+
+
+/*
+
+The ManagementDaemon has a very simple task: Start the real daemons when the user wants us
+to, and cleanup when the users wants to unload the module.
+
+Initially, kHTTPd didn't have this thread, but it is the only way to have "delayed activation",
+a feature required to prevent accidental activations resulting in unexpected backdoors.
+
+*/
+static int ManagementDaemon(void *unused)
+{
+	sigset_t tmpsig;
+	int waitpid_result;
+	
+	DECLARE_WAIT_QUEUE_HEAD(WQ);
+	
+	MOD_INC_USE_COUNT;
+	
+	sprintf(current->comm,"khttpd manager");
+	lock_kernel();   /* This seems to be required for exit_mm */
+	exit_mm(current);
+	
+
+	/* Block all signals except SIGKILL and SIGSTOP */
+	spin_lock_irq(&current->sigmask_lock);
+	tmpsig = current->blocked;
+	siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) );
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+
+	/* main loop */
+	while (sysctl_khttpd_unload==0)
+	{
+		int I;
+		
+		
+		/* First : wait for activation */
+		
+		sysctl_khttpd_start = 0;
+		
+		while ( (sysctl_khttpd_start==0) && (!signal_pending(current)) && (sysctl_khttpd_unload==0) )
+		{
+			current->state = TASK_INTERRUPTIBLE;
+			interruptible_sleep_on_timeout(&WQ,HZ); 
+		}
+		
+		if ( (signal_pending(current)) || (sysctl_khttpd_unload!=0) )
+		 	break;
+		 	
+		/* Then start listening and spawn the daemons */
+		 	
+		if (StartListening(sysctl_khttpd_serverport)==0)
+		{
+			continue;
+		}
+		
+		ActualThreads = sysctl_khttpd_threads;
+		if (ActualThreads<1) 
+			ActualThreads = 1;
+			
+		if (ActualThreads>CONFIG_KHTTPD_NUMCPU) 
+			ActualThreads = CONFIG_KHTTPD_NUMCPU;
+		
+		/* Write back the actual value */
+		
+		sysctl_khttpd_threads = ActualThreads;
+		
+		InitUserspace(ActualThreads);
+		
+		if (InitDataSending(ActualThreads)!=0)
+		{
+			StopListening();
+			continue;
+		}
+		if (InitWaitHeaders(ActualThreads)!=0)
+		{
+			I=0;
+			while (I<ActualThreads)
+			{
+				StopDataSending(I);
+				I++;
+			}
+			StopListening();
+			continue;
+		}
+	
+		/* Clean all queues */
+		memset(threadinfo, 0, sizeof(struct khttpd_threadinfo));
+
+
+		 	
+		I=0;
+		while (I<ActualThreads)
+		{
+			atomic_set(&Running[I],1);
+			(void)kernel_thread(MainDaemon,&(CountBuf[I]),0);
+			I++;
+		}
+		
+		/* Then wait for deactivation */
+		sysctl_khttpd_stop = 0;
+
+		while ( (sysctl_khttpd_stop==0) && (!signal_pending(current)) && (sysctl_khttpd_unload==0) )
+		{
+			if (atomic_read(&DaemonCount)<ActualThreads)
+			{
+				I=0;
+				while (I<ActualThreads)
+				{
+					if (atomic_read(&Running[I])==0)
+					{
+						atomic_set(&Running[I],1);
+						(void)kernel_thread(MainDaemon,&(CountBuf[I]),0);
+						(void)printk(KERN_CRIT "kHTTPd: Restarting daemon %i \n",I);
+					}
+					I++;
+				}
+			}
+			interruptible_sleep_on_timeout(&WQ,HZ);
+			
+			/* reap the daemons */
+			waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
+			
+		}
+		
+		
+		/* The user wants us to stop. So stop listening on the socket. */
+		if (sysctl_khttpd_stop!=0)	
+		{
+			/* Wait for the daemons to stop, one second per iteration */
+			while (atomic_read(&DaemonCount)>0)
+		 		interruptible_sleep_on_timeout(&WQ,HZ);
+			StopListening();
+		}
+
+		
+	
+	}
+	
+	sysctl_khttpd_stop = 1;
+
+	/* Wait for the daemons to stop, one second per iteration */
+	while (atomic_read(&DaemonCount)>0)
+ 		interruptible_sleep_on_timeout(&WQ,HZ);
+		
+		
+	waitpid_result = 1;
+	/* reap the zombie-daemons */
+	while (waitpid_result>0)
+		waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
+		
+	StopListening();
+	
+	
+	(void)printk(KERN_NOTICE "kHTTPd: Management daemon stopped. \n        You can unload the module now.\n");
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+int __init khttpd_init(void)
+{
+	int I;
+	
+	I=0;
+	while (I<CONFIG_KHTTPD_NUMCPU)
+	{
+		CountBuf[I]=I;
+		
+		I++;
+	}
+	
+	atomic_set(&ConnectCount,0);
+	atomic_set(&DaemonCount,0);
+	
+
+	/* Maybe the mime-types will be set-able through sysctl in the future */	   
+		
+ 	AddMimeType(".htm","text/html");
+ 	AddMimeType("html","text/html");
+ 	AddMimeType(".gif","image/gif");
+ 	AddMimeType(".jpg","image/jpeg");
+ 	AddMimeType(".png","image/png");
+ 	AddMimeType("tiff","image/tiff");
+ 	AddMimeType(".zip","application/zip");
+	AddMimeType(".pdf","application/pdf");
+ 	AddMimeType("r.gz","application/x-gtar");
+ 	AddMimeType(".tgz","application/x-gtar");
+	AddMimeType(".deb","application/x-debian-package");
+	AddMimeType("lass","application/x-java");
+	AddMimeType(".mp3","audio/mpeg");
+	AddMimeType(".txt","text/plain");
+	
+	AddDynamicString("..");
+	AddDynamicString("cgi-bin");
+
+	StartSysctl();
+	
+	(void)kernel_thread(ManagementDaemon,NULL,0);
+	
+	return 0;
+}
+
+void khttpd_cleanup(void)
+{
+	EndSysctl();
+}
+
+	module_init(khttpd_init)
+	module_exit(khttpd_cleanup)

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)