2 * TUX - Integrated Application Protocols Layer and Object Cache
4 * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
6 * extcgi.c: dynamic TUX module which forks and starts an external CGI
9 #define __KERNEL_SYSCALLS__
10 #define __KERNEL_SYSCALLS_NO_ERRNO__
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)
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.
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.
30 ****************************************************************/
32 #define MAX_ENVLEN 1000
33 #define MAX_CGI_METAVARIABLES 32
34 #define CGI_CHUNK_SIZE 1024
35 #define MAX_CGI_COMMAND_LEN 256
38 #define PRINT_MESSAGE_LEFT \
39 Dprintk("CGI message left at %s:%d:\n--->{%s}<---\n", \
40 __FILE__, __LINE__, curr)
42 #define PRINT_MESSAGE_LEFT do {} while(0)
45 #define GOTO_INCOMPLETE do { Dprintk("invalid CGI reply at %s:%d.\n", __FILE__, __LINE__); goto invalid; } while (0)
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! :-)
52 #define CGI_SUCCESS2 "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: TUX/2.0 (Linux)\r\n"
54 static int handle_cgi_reply (tux_req_t *req)
61 buf = tux_kmalloc(CGI_CHUNK_SIZE+1);
65 oldmm = get_fs(); set_fs(KERNEL_DS);
66 send_sync_buf(NULL, req->sock, CGI_SUCCESS2, sizeof(CGI_SUCCESS2)-1, MSG_MORE);
71 * The new process is the new owner of the socket, it will
75 left = CGI_CHUNK_SIZE;
88 Dprintk("reading %d bytes via read().\n", left);
89 oldmm = get_fs(); set_fs(KERNEL_DS);
90 len = read(2, tmp, left);
92 Dprintk("got %d bytes from read() (total: %d).\n", len, total);
95 Dprintk("CGI reply: (%d bytes, total %d).\n", len, total);
96 if (len == -ERESTARTSYS) {
101 if (total > CGI_CHUNK_SIZE) {
102 printk(KERN_ERR "TUX: CGI weirdness. total: %d, len: %d, left: %d.\n", total, len, left);
105 Dprintk("CGI done reply chunk: (%d bytes last, total %d).\n", len, total);
109 oldmm = get_fs(); set_fs(KERNEL_DS);
111 send_sync_buf(NULL, req->sock, buf, total, 0);
113 send_sync_buf(NULL, req->sock, buf, total, MSG_MORE);
115 req->bytes_sent += total;
118 Dprintk("bytes_sent: %d\n", req->bytes_sent);
119 if ((total > 0) && first) {
124 tmp = strstr(buf, "\n\n");
126 req->bytes_sent -= (tmp-buf) + 2;
127 Dprintk("new bytes_sent: %d\n", req->bytes_sent);
134 Dprintk("sys_read returned with %d.\n", len);
142 add_req_to_workqueue(req);
148 static int exec_external_cgi (void *data)
151 tux_req_t *req = data;
152 char *envp[MAX_CGI_METAVARIABLES+1], **envp_p;
153 char *argv[] = { "extcgi", NULL};
156 struct k_sigaction *ka;
157 int in_pipe_fds[2], out_pipe_fds[2], err_pipe_fds[2], len, err;
161 len = strlen(tux_common_docroot);
162 if (req->objectname_len + len + 12 > MAX_CGI_COMMAND_LEN)
164 sprintf(current->comm,"cgimain - %d", current->pid);
165 host = inet_sk(req->sock->sk)->daddr;
167 envstr = tux_kmalloc(MAX_ENVLEN);
168 command = tux_kmalloc(MAX_CGI_COMMAND_LEN);
173 #define WRITE_ENV(str...) \
174 if (envp_p >= envp + MAX_CGI_METAVARIABLES) \
176 len = sprintf(tmp, str); \
179 if (tmp >= envstr + MAX_ENVLEN) \
182 #define WRITE_ENV_STR(str,field,len) \
186 offset = sizeof(str)-1; \
188 if (tmp - envstr + offset + len >= MAX_ENVLEN) \
190 if (envp_p >= envp + MAX_CGI_METAVARIABLES) \
192 memcpy(tmp, str, offset); \
193 memcpy(tmp + offset, field, len); \
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)");
207 WRITE_ENV("DOCUMENT_ROOT=/");
208 WRITE_ENV("PATH_INFO=/");
210 WRITE_ENV_STR("DOCUMENT_ROOT=", tux_common_docroot, len);
211 WRITE_ENV_STR("PATH_INFO=", tux_common_docroot, len);
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);
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);
226 WRITE_ENV_STR("SERVER_NAME=", req->host, req->host_len);
228 const char *host = "localhost";
229 WRITE_ENV_STR("SERVER_NAME=", host, strlen(host));
234 spin_lock_irq(¤t->sighand->siglock);
235 ka = current->sighand->action + SIGPIPE-1;
236 ka->sa.sa_handler = SIG_IGN;
237 siginitsetinv(¤t->blocked, sigmask(SIGCHLD));
239 spin_unlock_irq(¤t->sighand->siglock);
241 tux_close(0); tux_close(1);
242 tux_close(2); tux_close(3);
243 tux_close(4); tux_close(5);
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;
250 if (do_pipe(in_pipe_fds))
252 if (do_pipe(out_pipe_fds))
254 if (do_pipe(err_pipe_fds))
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();
264 if (req->virtual && req->host_len)
265 sprintf(command, "/%s/cgi-bin/%s", req->host, req->objectname);
267 sprintf(command, "/cgi-bin/%s", req->objectname);
268 Dprintk("before CGI exec.\n");
269 pid = tux_exec_process(command, argv, envp, 1, ¶m, 0);
270 Dprintk("after CGI exec.\n");
272 if (req->post_data_len) {
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);
280 Dprintk("write() returned: %d.\n", ret);
281 if (ret != req->post_data_len)
282 Dprintk("write() returned: %d.\n", ret);
288 handle_cgi_reply(req);
298 void start_external_cgi (tux_req_t *req)
303 pid = kernel_thread(exec_external_cgi, (void*) req, SIGCHLD);
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);
314 int query_extcgi (tux_req_t *req)
316 clear_keepalive(req);
317 start_external_cgi(req);
321 #define EXTCGI_INVALID_HEADER \
322 "HTTP/1.1 503 Service Unavailable\r\n" \
323 "Content-Length: 23\r\n\r\n"
325 #define EXTCGI_INVALID_BODY \
326 "TUX: invalid CGI reply."
328 #define EXTCGI_INVALID EXTCGI_INVALID_HEADER EXTCGI_INVALID_BODY