1 /*
2 
3 kHTTPd -- the next generation
4 
5 Main program
6 
7 
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
10 stages.
11 
12 The stages are
13 
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
19 
20 A typical flow for a request would be:
21 
22 <not accepted>
23 WaitForHeaders
24 DataSending
25 Logging
26 
27 or
28 
29 <not accepted>
30 WaitForHeaders
31 Userspace
32 
33 
34 
35 */
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)
40  *	any later version.
41  *
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.
46  *
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.
50  *
51  ****************************************************************/
52 
53 
54 static int errno;
55 #define __KERNEL_SYSCALLS__
56 
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>
66 
67 #include "structure.h"
68 #include "prototypes.h"
69 #include "sysctl.h"
70 
71 struct khttpd_threadinfo threadinfo[CONFIG_KHTTPD_NUMCPU];  /* The actual work-queues */
72 
73 
74 atomic_t	ConnectCount;
75 atomic_t	DaemonCount;
76 
77 static int	ActualThreads; /* The number of actual, active threads */
78 
79 
ConnectionsPending(int CPUNR)80 static int ConnectionsPending(int CPUNR)
81 {
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;
86   return 0;
87 }
88 
89 
90 
91 static wait_queue_head_t DummyWQ[CONFIG_KHTTPD_NUMCPU];
92 static atomic_t Running[CONFIG_KHTTPD_NUMCPU];
93 
MainDaemon(void * cpu_pointer)94 static int MainDaemon(void *cpu_pointer)
95 {
96 	int CPUNR;
97 	sigset_t tmpsig;
98 	int old_stop_count;
99 
100 	DECLARE_WAITQUEUE(main_wait,current);
101 
102 	MOD_INC_USE_COUNT;
103 
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
107 	 */
108 	old_stop_count = atomic_read(&khttpd_stopCount);
109 
110 	CPUNR=0;
111 	if (cpu_pointer!=NULL)
112 	CPUNR=(int)*(int*)cpu_pointer;
113 
114 	sprintf(current->comm,"khttpd - %i",CPUNR);
115 	daemonize();
116 
117 	init_waitqueue_head(&(DummyWQ[CPUNR]));
118 
119 
120 	/* Block all signals except SIGKILL, SIGSTOP and SIGHUP */
121 	spin_lock_irq(&current->sigmask_lock);
122 	tmpsig = current->blocked;
123 	siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)| sigmask(SIGHUP));
124 	recalc_sigpending(current);
125 	spin_unlock_irq(&current->sigmask_lock);
126 
127 
128 	if (MainSocket->sk==NULL)
129 	 	return 0;
130 	add_wait_queue_exclusive(MainSocket->sk->sleep,&(main_wait));
131 	atomic_inc(&DaemonCount);
132 	atomic_set(&Running[CPUNR],1);
133 
134 	while (old_stop_count == atomic_read(&khttpd_stopCount))
135 	{
136 		int changes = 0;
137 
138 		changes +=AcceptConnections(CPUNR,MainSocket);
139 		if (ConnectionsPending(CPUNR))
140 		{
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
147 			   then.
148 			*/
149 			changes +=AcceptConnections(CPUNR,MainSocket);
150 		}
151 
152 		if (changes==0)
153 		{
154 			(void)interruptible_sleep_on_timeout(&(DummyWQ[CPUNR]),1);
155 			if (CPUNR==0)
156 				UpdateCurrentDate();
157 		}
158 
159 		if (signal_pending(current)!=0)
160 		{
161 			(void)printk(KERN_NOTICE "kHTTPd: Ring Ring - signal received\n");
162 			break;
163 		}
164 
165 	}
166 
167 	remove_wait_queue(MainSocket->sk->sleep,&(main_wait));
168 
169 	StopWaitingForHeaders(CPUNR);
170 	StopDataSending(CPUNR);
171 	StopUserspace(CPUNR);
172 	StopLogging(CPUNR);
173 
174 	atomic_set(&Running[CPUNR],0);
175 	atomic_dec(&DaemonCount);
176 	(void)printk(KERN_NOTICE "kHTTPd: Daemon %i has ended\n",CPUNR);
177 	MOD_DEC_USE_COUNT;
178 	return 0;
179 }
180 
181 static int CountBuf[CONFIG_KHTTPD_NUMCPU];
182 
183 
184 
185 /*
186 
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.
189 
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.
192 
193 */
ManagementDaemon(void * unused)194 static int ManagementDaemon(void *unused)
195 {
196 	sigset_t tmpsig;
197 	int waitpid_result;
198 
199 	DECLARE_WAIT_QUEUE_HEAD(WQ);
200 
201 	sprintf(current->comm,"khttpd manager");
202 	daemonize();
203 
204 	/* Block all signals except SIGKILL and SIGSTOP */
205 	spin_lock_irq(&current->sigmask_lock);
206 	tmpsig = current->blocked;
207 	siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) );
208 	recalc_sigpending(current);
209 	spin_unlock_irq(&current->sigmask_lock);
210 
211 	/* main loop */
212 	while (sysctl_khttpd_unload==0)
213 	{
214 		int I;
215 		int old_stop_count;
216 
217 		/* First : wait for activation */
218 		while ( (sysctl_khttpd_start==0) && (!signal_pending(current)) && (sysctl_khttpd_unload==0) )
219 		{
220 			current->state = TASK_INTERRUPTIBLE;
221 			interruptible_sleep_on_timeout(&WQ,HZ);
222 		}
223 		if ( (signal_pending(current)) || (sysctl_khttpd_unload!=0) )
224 		 	break;
225 		sysctl_khttpd_stop = 0;
226 
227 		/* Then start listening and spawn the daemons */
228 		if (StartListening(sysctl_khttpd_serverport)==0)
229 		{
230 			sysctl_khttpd_start = 0;
231 			continue;
232 		}
233 
234 		ActualThreads = sysctl_khttpd_threads;
235 		if (ActualThreads<1)
236 			ActualThreads = 1;
237 		if (ActualThreads>CONFIG_KHTTPD_NUMCPU)
238 			ActualThreads = CONFIG_KHTTPD_NUMCPU;
239 		/* Write back the actual value */
240 		sysctl_khttpd_threads = ActualThreads;
241 
242 		InitUserspace(ActualThreads);
243 
244 		if (InitDataSending(ActualThreads)!=0)
245 		{
246 			StopListening();
247 			sysctl_khttpd_start = 0;
248 			continue;
249 		}
250 		if (InitWaitHeaders(ActualThreads)!=0)
251 		{
252 			for (I=0; I<ActualThreads; I++) {
253 				StopDataSending(I);
254 			}
255 			StopListening();
256 			sysctl_khttpd_start = 0;
257 			continue;
258 		}
259 
260 		/* Clean all queues */
261 		memset(threadinfo, 0, sizeof(struct khttpd_threadinfo));
262 
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);
266 		}
267 
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
272 		 */
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) )
277 		{
278 			/* Used to restart dead threads here, but it was buggy*/
279 			interruptible_sleep_on_timeout(&WQ,HZ);
280 		}
281 
282 		/* Wait for the daemons to stop, one second per iteration */
283 		while (atomic_read(&DaemonCount)>0)
284 			interruptible_sleep_on_timeout(&WQ,HZ);
285 		StopListening();
286 		sysctl_khttpd_start = 0;
287 		/* reap the zombie-daemons */
288 		do
289 			waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
290 		while (waitpid_result>0);
291 	}
292 	sysctl_khttpd_start = 0;
293 	sysctl_khttpd_stop = 1;
294 	atomic_inc(&khttpd_stopCount);
295 
296 	/* Wait for the daemons to stop, one second per iteration */
297 	while (atomic_read(&DaemonCount)>0)
298  		interruptible_sleep_on_timeout(&WQ,HZ);
299 	StopListening();
300 	/* reap the zombie-daemons */
301 	do
302 		waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
303 	while (waitpid_result>0);
304 
305 	(void)printk(KERN_NOTICE "kHTTPd: Management daemon stopped. \n        You can unload the module now.\n");
306 
307 	MOD_DEC_USE_COUNT;
308 
309 	return 0;
310 }
311 
khttpd_init(void)312 int __init khttpd_init(void)
313 {
314 	int I;
315 
316 	MOD_INC_USE_COUNT;
317 
318 	for (I=0; I<CONFIG_KHTTPD_NUMCPU; I++) {
319 		CountBuf[I]=I;
320 	}
321 
322 	atomic_set(&ConnectCount,0);
323 	atomic_set(&DaemonCount,0);
324 	atomic_set(&khttpd_stopCount,0);
325 
326 
327 	/* Maybe the mime-types will be set-able through sysctl in the future */
328 
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");
343 
344 	AddDynamicString("..");
345 	AddDynamicString("cgi-bin");
346 
347 	StartSysctl();
348 
349 	(void)kernel_thread(ManagementDaemon,NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
350 
351 	return 0;
352 }
353 
khttpd_cleanup(void)354 void khttpd_cleanup(void)
355 {
356 	EndSysctl();
357 }
358 
359 	module_init(khttpd_init)
360 	module_exit(khttpd_cleanup)
361 
362 	MODULE_LICENSE("GPL");
363