This commit was manufactured by cvs2svn to create branch 'fedora'.
[linux-2.6.git] / net / tux / extcgi.c
1 /*
2  * TUX - Integrated Application Protocols Layer and Object Cache
3  *
4  * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
5  *
6  * extcgi.c: dynamic TUX module which forks and starts an external CGI
7  */
8
9 #define __KERNEL_SYSCALLS__
10 #define __KERNEL_SYSCALLS_NO_ERRNO__
11
12 #include <net/tux.h>
13 #include "parser.h"
14
15 /****************************************************************
16  *      This program is free software; you can redistribute it and/or modify
17  *      it under the terms of the GNU General Public License as published by
18  *      the Free Software Foundation; either version 2, or (at your option)
19  *      any later version.
20  *
21  *      This program is distributed in the hope that it will be useful,
22  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
23  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  *      GNU General Public License for more details.
25  *
26  *      You should have received a copy of the GNU General Public License
27  *      along with this program; if not, write to the Free Software
28  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29  *
30  ****************************************************************/
31
32 #define MAX_ENVLEN 1000
33 #define MAX_CGI_METAVARIABLES 32
34 #define CGI_CHUNK_SIZE 1024 
35 #define MAX_CGI_COMMAND_LEN 256
36
37 #if CONFIG_TUX_DEBUG
38 #define PRINT_MESSAGE_LEFT \
39         Dprintk("CGI message left at %s:%d:\n--->{%s}<---\n", \
40                 __FILE__, __LINE__, curr)
41 #else
42 #define PRINT_MESSAGE_LEFT do {} while(0)
43 #endif
44
45 #define GOTO_INCOMPLETE do { Dprintk("invalid CGI reply at %s:%d.\n", __FILE__, __LINE__); goto invalid; } while (0)
46
47 /*
48  * Please acknowledge our hard work by not changing this define, or
49  * at least please acknowledge us by leaving "TUX/2.0 (Linux)" in
50  * the ID string. Thanks! :-)
51  */
52 #define CGI_SUCCESS2 "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: TUX/2.0 (Linux)\r\n"
53
54 static int handle_cgi_reply (tux_req_t *req)
55 {
56         int first = 1;
57         int len, left, total;
58         char *buf, *tmp;
59         mm_segment_t oldmm;
60
61         buf = tux_kmalloc(CGI_CHUNK_SIZE+1);
62         tux_close(3);
63         tux_close(4);
64         tux_close(5);
65         oldmm = get_fs(); set_fs(KERNEL_DS);
66         send_sync_buf(NULL, req->sock, CGI_SUCCESS2, sizeof(CGI_SUCCESS2)-1, MSG_MORE);
67         set_fs(oldmm);
68
69         req->bytes_sent = 0;
70         /*
71          * The new process is the new owner of the socket, it will
72          * close it.
73          */
74 repeat:
75         left = CGI_CHUNK_SIZE;
76         len = 0;
77         total = 0;
78         tmp = buf;
79         do {
80                 mm_segment_t oldmm;
81
82                 tmp += len;
83                 total += len;
84                 left -= len;
85                 if (!left)
86                         break;
87 repeat_read:
88                 Dprintk("reading %d bytes via read().\n", left);
89                 oldmm = get_fs(); set_fs(KERNEL_DS);
90                 len = read(2, tmp, left);
91                 set_fs(oldmm);
92                 Dprintk("got %d bytes from read() (total: %d).\n", len, total);
93                 if (len > 0)
94                         tmp[len] = 0;
95                 Dprintk("CGI reply: (%d bytes, total %d).\n", len, total);
96                 if (len == -ERESTARTSYS) {
97                         flush_all_signals();
98                         goto repeat_read;
99                 }
100         } while (len > 0);
101         if (total > CGI_CHUNK_SIZE) {
102                 printk(KERN_ERR "TUX: CGI weirdness. total: %d, len: %d, left: %d.\n", total, len, left);
103                 TUX_BUG();
104         }
105         Dprintk("CGI done reply chunk: (%d bytes last, total %d).\n", len, total);
106         if (total) {
107                 mm_segment_t oldmm;
108
109                 oldmm = get_fs(); set_fs(KERNEL_DS);
110                 if (!len)
111                         send_sync_buf(NULL, req->sock, buf, total, 0);
112                 else
113                         send_sync_buf(NULL, req->sock, buf, total, MSG_MORE);
114                 set_fs(oldmm);
115                 req->bytes_sent += total;
116         }
117
118         Dprintk("bytes_sent: %d\n", req->bytes_sent);
119         if ((total > 0) && first) {
120                 first = 0;
121
122                 if (buf[total])
123                         TUX_BUG();
124                 tmp = strstr(buf, "\n\n");
125                 if (tmp) {
126                         req->bytes_sent -= (tmp-buf) + 2;
127                         Dprintk("new bytes_sent: %d\n", req->bytes_sent);
128                 } else {
129                         req->bytes_sent = 0;
130                         req_err(req);
131                 }
132         }
133         if (len < 0)
134                 Dprintk("sys_read returned with %d.\n", len);
135         else {
136                 if (total > 0)
137                         goto repeat;
138         }
139         tux_close(2);
140
141         req->status = 200;
142         add_req_to_workqueue(req);
143         kfree(buf);
144
145         return -1;
146 }
147
148 static int exec_external_cgi (void *data)
149 {
150         exec_param_t param;
151         tux_req_t *req = data;
152         char *envp[MAX_CGI_METAVARIABLES+1], **envp_p;
153         char *argv[] = { "extcgi", NULL};
154         char *envstr, *tmp;
155         unsigned int host;
156         struct k_sigaction *ka;
157         int in_pipe_fds[2], out_pipe_fds[2], err_pipe_fds[2], len, err;
158         char *command;
159         pid_t pid;
160
161         len = strlen(tux_common_docroot);
162         if (req->objectname_len + len + 12 > MAX_CGI_COMMAND_LEN)
163                 return -ENOMEM;
164         sprintf(current->comm,"cgimain - %d", current->pid);
165         host = inet_sk(req->sock->sk)->daddr;
166
167         envstr = tux_kmalloc(MAX_ENVLEN);
168         command = tux_kmalloc(MAX_CGI_COMMAND_LEN);
169
170         tmp = envstr;
171         envp_p = envp;
172
173 #define WRITE_ENV(str...) \
174         if (envp_p >= envp + MAX_CGI_METAVARIABLES) \
175                 TUX_BUG(); \
176         len = sprintf(tmp, str); \
177         *envp_p++ = tmp; \
178         tmp += len + 1; \
179         if (tmp >= envstr + MAX_ENVLEN) \
180                 TUX_BUG();
181
182         #define WRITE_ENV_STR(str,field,len)                    \
183         do {                                                    \
184                 int offset;                                     \
185                                                                 \
186                 offset = sizeof(str)-1;                         \
187                 err = -EFAULT;                                  \
188                 if (tmp - envstr + offset + len >= MAX_ENVLEN)  \
189                         goto out;                               \
190                 if (envp_p >= envp + MAX_CGI_METAVARIABLES)     \
191                         TUX_BUG();                              \
192                 memcpy(tmp, str, offset);                       \
193                 memcpy(tmp + offset, field, len);               \
194                 offset += len;                                  \
195                 tmp[offset] = 0;                                \
196                 *envp_p++ = tmp;                                \
197                 tmp += offset + 1;                              \
198         } while (0)
199
200         WRITE_ENV("GATEWAY_INTERFACE=CGI/1.1");
201         WRITE_ENV("CONTENT_LENGTH=%d", req->post_data_len);
202         WRITE_ENV("REMOTE_ADDR=%d.%d.%d.%d", NIPQUAD(host));
203         WRITE_ENV("SERVER_PORT=%d", 80);
204         WRITE_ENV("SERVER_SOFTWARE=TUX/2.0 (Linux)");
205
206 #if 1
207         WRITE_ENV("DOCUMENT_ROOT=/");
208         WRITE_ENV("PATH_INFO=/");
209 #else
210         WRITE_ENV_STR("DOCUMENT_ROOT=", tux_common_docroot, len);
211         WRITE_ENV_STR("PATH_INFO=", tux_common_docroot, len);
212 #endif
213         WRITE_ENV_STR("QUERY_STRING=", req->query_str, req->query_len);
214         WRITE_ENV_STR("REQUEST_METHOD=", req->method_str, req->method_len);
215         WRITE_ENV_STR("SCRIPT_NAME=", req->objectname, req->objectname_len);
216         WRITE_ENV_STR("SERVER_PROTOCOL=", req->version_str, req->version_len);
217
218         if (req->content_type_len)
219                 WRITE_ENV_STR("CONTENT_TYPE=",
220                         req->content_type_str, req->content_type_len);
221         if (req->cookies_len)
222                 WRITE_ENV_STR("HTTP_COOKIE=",
223                         req->cookies_str, req->cookies_len);
224
225         if (req->host_len)
226                 WRITE_ENV_STR("SERVER_NAME=", req->host, req->host_len);
227         else {
228                 const char *host = "localhost";
229                 WRITE_ENV_STR("SERVER_NAME=", host, strlen(host));
230         }
231
232         *envp_p = NULL;
233
234         spin_lock_irq(&current->sighand->siglock);
235         ka = current->sighand->action + SIGPIPE-1;
236         ka->sa.sa_handler = SIG_IGN;
237         siginitsetinv(&current->blocked, sigmask(SIGCHLD));
238         recalc_sigpending();
239         spin_unlock_irq(&current->sighand->siglock);
240
241         tux_close(0); tux_close(1);
242         tux_close(2); tux_close(3);
243         tux_close(4); tux_close(5);
244
245         in_pipe_fds[0] = in_pipe_fds[1] = -1;
246         out_pipe_fds[0] = out_pipe_fds[1] = -1;
247         err_pipe_fds[0] = err_pipe_fds[1] = -1;
248
249         err = -ENFILE;
250         if (do_pipe(in_pipe_fds))
251                 goto out;
252         if (do_pipe(out_pipe_fds))
253                 goto out;
254         if (do_pipe(err_pipe_fds))
255                 goto out;
256
257         if (in_pipe_fds[0] != 0) TUX_BUG();
258         if (in_pipe_fds[1] != 1) TUX_BUG();
259         if (out_pipe_fds[0] != 2) TUX_BUG();
260         if (out_pipe_fds[1] != 3) TUX_BUG();
261         if (err_pipe_fds[0] != 4) TUX_BUG();
262         if (err_pipe_fds[1] != 5) TUX_BUG();
263
264         if (req->virtual && req->host_len)
265                 sprintf(command, "/%s/cgi-bin/%s", req->host, req->objectname);
266         else
267                 sprintf(command, "/cgi-bin/%s", req->objectname);
268         Dprintk("before CGI exec.\n");
269         pid = tux_exec_process(command, argv, envp, 1, &param, 0);
270         Dprintk("after CGI exec.\n");
271
272         if (req->post_data_len) {
273                 mm_segment_t oldmm;
274                 int ret;
275
276                 Dprintk("POST data to CGI:\n");
277                 oldmm = get_fs(); set_fs(KERNEL_DS);
278                 ret = write(1, req->post_data_str, req->post_data_len);
279                 set_fs(oldmm);
280                 Dprintk("write() returned: %d.\n", ret);
281                 if (ret != req->post_data_len)
282                         Dprintk("write() returned: %d.\n", ret);
283         }
284
285         tux_close(0);
286         tux_close(1);
287
288         handle_cgi_reply(req);
289         err = 0;
290
291 out:
292         kfree(envstr);
293         kfree(command);
294
295         return err;
296 }
297
298 void start_external_cgi (tux_req_t *req)
299 {
300         int pid;
301
302 repeat:
303         pid = kernel_thread(exec_external_cgi, (void*) req, SIGCHLD);
304         if (pid == -1)
305                 return;
306         if (pid < 0) {
307                 printk(KERN_INFO "TUX: Could not fork external CGI process due to %d, retrying!\n", pid);
308                 current->state = TASK_UNINTERRUPTIBLE;
309                 schedule_timeout(HZ);
310                 goto repeat;
311         }
312 }
313
314 int query_extcgi (tux_req_t *req)
315 {
316         clear_keepalive(req);
317         start_external_cgi(req);
318         return -1;
319 }
320
321 #define EXTCGI_INVALID_HEADER \
322         "HTTP/1.1 503 Service Unavailable\r\n" \
323         "Content-Length: 23\r\n\r\n"
324
325 #define EXTCGI_INVALID_BODY \
326         "TUX: invalid CGI reply."
327
328 #define EXTCGI_INVALID EXTCGI_INVALID_HEADER EXTCGI_INVALID_BODY
329