3 kHTTPd -- the next generation
8 kHTTPd TNG consists of 1 thread, this main-thread handles ALL connections
9 simultanious. It does this by keeping queues with the requests in different
14 <not accepted> - TCP/IP connection is not accepted yet
15 WaitForHeaders - Connection is accepted, waiting for headers
16 DataSending - Headers decoded, sending file-data
17 Userspace - Requires userspace daemon
18 Logging - The request is finished, cleanup and logging
20 A typical flow for a request would be:
36 /****************************************************************
37 * This program is free software; you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation; either version 2, or (at your option)
42 * This program is distributed in the hope that it will be useful,
43 * but WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 * GNU General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program; if not, write to the Free Software
49 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
51 ****************************************************************/
55 #define __KERNEL_SYSCALLS__
57 #include <linux/config.h>
58 #include <linux/module.h>
59 #include <linux/kernel.h>
60 #include <linux/sched.h>
61 #include <linux/signal.h>
62 #include <linux/init.h>
63 #include <linux/wait.h>
64 #include <linux/smp_lock.h>
65 #include <asm/unistd.h>
67 #include "structure.h"
68 #include "prototypes.h"
71 struct khttpd_threadinfo threadinfo[CONFIG_KHTTPD_NUMCPU]; /* The actual work-queues */
74 atomic_t ConnectCount;
77 static int ActualThreads; /* The number of actual, active threads */
80 static int ConnectionsPending(int CPUNR)
82 if (threadinfo[CPUNR].DataSendingQueue!=NULL) return O_NONBLOCK;
83 if (threadinfo[CPUNR].WaitForHeaderQueue!=NULL) return O_NONBLOCK;
84 if (threadinfo[CPUNR].LoggingQueue!=NULL) return O_NONBLOCK;
85 if (threadinfo[CPUNR].UserspaceQueue!=NULL) return O_NONBLOCK;
91 static wait_queue_head_t DummyWQ[CONFIG_KHTTPD_NUMCPU];
92 static atomic_t Running[CONFIG_KHTTPD_NUMCPU];
94 static int MainDaemon(void *cpu_pointer)
100 DECLARE_WAITQUEUE(main_wait,current);
104 /* Remember value of stop count. If it changes, user must have
105 * asked us to stop. Sensing this is much less racy than
106 * directly sensing sysctl_khttpd_stop. - dank
108 old_stop_count = atomic_read(&khttpd_stopCount);
111 if (cpu_pointer!=NULL)
112 CPUNR=(int)*(int*)cpu_pointer;
114 sprintf(current->comm,"khttpd - %i",CPUNR);
117 init_waitqueue_head(&(DummyWQ[CPUNR]));
120 /* Block all signals except SIGKILL, SIGSTOP and SIGHUP */
121 spin_lock_irq(¤t->sigmask_lock);
122 tmpsig = current->blocked;
123 siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)| sigmask(SIGHUP));
124 recalc_sigpending(current);
125 spin_unlock_irq(¤t->sigmask_lock);
128 if (MainSocket->sk==NULL)
130 add_wait_queue_exclusive(MainSocket->sk->sleep,&(main_wait));
131 atomic_inc(&DaemonCount);
132 atomic_set(&Running[CPUNR],1);
134 while (old_stop_count == atomic_read(&khttpd_stopCount))
138 changes +=AcceptConnections(CPUNR,MainSocket);
139 if (ConnectionsPending(CPUNR))
141 changes +=WaitForHeaders(CPUNR);
142 changes +=DataSending(CPUNR);
143 changes +=Userspace(CPUNR);
144 changes +=Logging(CPUNR);
145 /* Test for incoming connections _again_, because it is possible
146 one came in during the other steps, and the wakeup doesn't happen
149 changes +=AcceptConnections(CPUNR,MainSocket);
154 (void)interruptible_sleep_on_timeout(&(DummyWQ[CPUNR]),1);
159 if (signal_pending(current)!=0)
161 (void)printk(KERN_NOTICE "kHTTPd: Ring Ring - signal received\n");
167 remove_wait_queue(MainSocket->sk->sleep,&(main_wait));
169 StopWaitingForHeaders(CPUNR);
170 StopDataSending(CPUNR);
171 StopUserspace(CPUNR);
174 atomic_set(&Running[CPUNR],0);
175 atomic_dec(&DaemonCount);
176 (void)printk(KERN_NOTICE "kHTTPd: Daemon %i has ended\n",CPUNR);
181 static int CountBuf[CONFIG_KHTTPD_NUMCPU];
187 The ManagementDaemon has a very simple task: Start the real daemons when the user wants us
188 to, and cleanup when the users wants to unload the module.
190 Initially, kHTTPd didn't have this thread, but it is the only way to have "delayed activation",
191 a feature required to prevent accidental activations resulting in unexpected backdoors.
194 static int ManagementDaemon(void *unused)
199 DECLARE_WAIT_QUEUE_HEAD(WQ);
201 sprintf(current->comm,"khttpd manager");
204 /* Block all signals except SIGKILL and SIGSTOP */
205 spin_lock_irq(¤t->sigmask_lock);
206 tmpsig = current->blocked;
207 siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) );
208 recalc_sigpending(current);
209 spin_unlock_irq(¤t->sigmask_lock);
212 while (sysctl_khttpd_unload==0)
217 /* First : wait for activation */
218 while ( (sysctl_khttpd_start==0) && (!signal_pending(current)) && (sysctl_khttpd_unload==0) )
220 current->state = TASK_INTERRUPTIBLE;
221 interruptible_sleep_on_timeout(&WQ,HZ);
223 if ( (signal_pending(current)) || (sysctl_khttpd_unload!=0) )
225 sysctl_khttpd_stop = 0;
227 /* Then start listening and spawn the daemons */
228 if (StartListening(sysctl_khttpd_serverport)==0)
230 sysctl_khttpd_start = 0;
234 ActualThreads = sysctl_khttpd_threads;
237 if (ActualThreads>CONFIG_KHTTPD_NUMCPU)
238 ActualThreads = CONFIG_KHTTPD_NUMCPU;
239 /* Write back the actual value */
240 sysctl_khttpd_threads = ActualThreads;
242 InitUserspace(ActualThreads);
244 if (InitDataSending(ActualThreads)!=0)
247 sysctl_khttpd_start = 0;
250 if (InitWaitHeaders(ActualThreads)!=0)
252 for (I=0; I<ActualThreads; I++) {
256 sysctl_khttpd_start = 0;
260 /* Clean all queues */
261 memset(threadinfo, 0, sizeof(struct khttpd_threadinfo));
263 for (I=0; I<ActualThreads; I++) {
264 atomic_set(&Running[I],1);
265 (void)kernel_thread(MainDaemon,&(CountBuf[I]), CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
268 /* Then wait for deactivation */
269 /* Remember value of stop count. If it changes, user must
270 * have asked us to stop. Sensing this is much less racy
271 * than directly sensing sysctl_khttpd_stop. - dank
273 old_stop_count = atomic_read(&khttpd_stopCount);
274 while ( ( old_stop_count == atomic_read(&khttpd_stopCount))
275 && (!signal_pending(current))
276 && (sysctl_khttpd_unload==0) )
278 /* Used to restart dead threads here, but it was buggy*/
279 interruptible_sleep_on_timeout(&WQ,HZ);
282 /* Wait for the daemons to stop, one second per iteration */
283 while (atomic_read(&DaemonCount)>0)
284 interruptible_sleep_on_timeout(&WQ,HZ);
286 sysctl_khttpd_start = 0;
287 /* reap the zombie-daemons */
289 waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
290 while (waitpid_result>0);
292 sysctl_khttpd_start = 0;
293 sysctl_khttpd_stop = 1;
294 atomic_inc(&khttpd_stopCount);
296 /* Wait for the daemons to stop, one second per iteration */
297 while (atomic_read(&DaemonCount)>0)
298 interruptible_sleep_on_timeout(&WQ,HZ);
300 /* reap the zombie-daemons */
302 waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
303 while (waitpid_result>0);
305 (void)printk(KERN_NOTICE "kHTTPd: Management daemon stopped. \n You can unload the module now.\n");
312 int __init khttpd_init(void)
318 for (I=0; I<CONFIG_KHTTPD_NUMCPU; I++) {
322 atomic_set(&ConnectCount,0);
323 atomic_set(&DaemonCount,0);
324 atomic_set(&khttpd_stopCount,0);
327 /* Maybe the mime-types will be set-able through sysctl in the future */
329 AddMimeType(".htm","text/html");
330 AddMimeType("html","text/html");
331 AddMimeType(".gif","image/gif");
332 AddMimeType(".jpg","image/jpeg");
333 AddMimeType(".png","image/png");
334 AddMimeType("tiff","image/tiff");
335 AddMimeType(".zip","application/zip");
336 AddMimeType(".pdf","application/pdf");
337 AddMimeType("r.gz","application/x-gtar");
338 AddMimeType(".tgz","application/x-gtar");
339 AddMimeType(".deb","application/x-debian-package");
340 AddMimeType("lass","application/x-java");
341 AddMimeType(".mp3","audio/mpeg");
342 AddMimeType(".txt","text/plain");
344 AddDynamicString("..");
345 AddDynamicString("cgi-bin");
349 (void)kernel_thread(ManagementDaemon,NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
354 void khttpd_cleanup(void)
359 module_init(khttpd_init)
360 module_exit(khttpd_cleanup)
362 MODULE_LICENSE("GPL");