/* 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 - 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: WaitForHeaders DataSending Logging or 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. * ****************************************************************/ static int errno; #define __KERNEL_SYSCALLS__ #include #include #include #include #include #include #include #include #include #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; CPUNR=0; if (cpu_pointer!=NULL) CPUNR=(int)*(int*)cpu_pointer; sprintf(current->comm,"khttpd - %i",CPUNR); daemonize(); init_waitqueue_head(&(DummyWQ[CPUNR])); /* Block all signals except SIGKILL, SIGSTOP and SIGHUP */ spin_lock_irq(¤t->sigmask_lock); tmpsig = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)| sigmask(SIGHUP)); recalc_sigpending(current); spin_unlock_irq(¤t->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 incoming connections _again_, because it is possible one came in during the other steps, and the wakeup doesn't happen then. */ changes +=AcceptConnections(CPUNR,MainSocket); } 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]; /* 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); sprintf(current->comm,"khttpd manager"); daemonize(); /* Block all signals except SIGKILL and SIGSTOP */ spin_lock_irq(¤t->sigmask_lock); tmpsig = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) ); recalc_sigpending(current); spin_unlock_irq(¤t->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 (I0) 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; MOD_INC_USE_COUNT; I=0; while (I