2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: exec.c,v 1.6 2003/02/05 23:02:51 rich Exp $
38 #ifdef HAVE_SYS_SOCKET_H
39 #include <sys/socket.h>
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
46 #ifdef HAVE_ARPA_INET_H
47 #include <arpa/inet.h>
53 #include <pthr_http.h>
55 #include "process_rq.h"
61 /* setenv implementation for architectures which only have putenv. The
62 * apparent memory leak in this code does not actually matter because
63 * this is only called in the child process just before invoking exec.
66 setenv (const char *name, const char *value, int overwrite)
68 int len = strlen (name) + strlen (value) + 2;
69 char *str = malloc (len);
71 snprintf (str, len, "%s=%s", name, value);
75 #error "no setenv or putenv in your libc"
79 /* Note: For performance reasons and because I wanted to simplify the
80 * server, this code only handles NPH scripts.
82 * You must ensure that your CGI program can generate full NPH headers.
83 * This is generally quite simple. For example, with Perl's CGI.pm,
86 * use CGI qw(:standard -nph);
89 exec_file (process_rq p)
91 int pid, to_script[2], from_script[2], len, i;
92 io_handle to_io, from_io;
93 const char *content_length;
96 = http_request_get_header (p->http_request, "Content-Length");
98 /* Set up two pipes between us and the script, one for reading, one
101 if (pipe (to_script) == -1 || pipe (from_script) == -1)
102 return bad_request_error (p, "cannot create pipes to script");
104 /* Fork off a process to run the request. */
108 close (to_script[0]); close (to_script[1]);
109 close (from_script[0]); close (from_script[1]);
110 return bad_request_error (p, "cannot fork");
113 if (pid == 0) /* Child process -- runs the script. */
116 const char *query_string, *content_type;
117 int major, minor, method;
120 struct sockaddr_in addr;
123 /* XXX Currently all fds will be correctly closed over the exec
124 * except the accepting socket. This requires a small change to
125 * pthrlib to fix. Ignore it for now.
127 /* Set up fds 0 and 1 to point to the pipes connecting us to
128 * the main rwsd process. Fd 2 points to the error log, so just
129 * leave that one alone.
131 close (to_script[1]);
132 if (to_script[0] != 0)
134 dup2 (to_script[0], 0);
135 close (to_script[0]);
137 close (from_script[0]);
138 if (from_script[1] != 1)
140 dup2 (from_script[1], 1);
141 close (from_script[1]);
144 /* Query string environment variable. */
145 query_string = http_request_query_string (p->http_request);
147 setenv ("QUERY_STRING", query_string, 1);
149 /* Set server protocol. */
150 http_request_version (p->http_request, &major, &minor);
151 setenv ("SERVER_PROTOCOL",
152 psprintf (p->pool, "HTTP/%d.%d", major, minor), 1);
154 /* Set request method. */
155 method = http_request_method (p->http_request);
156 setenv ("REQUEST_METHOD",
157 (method == HTTP_METHOD_GET ? "GET" :
158 (method == HTTP_METHOD_POST ? "POST" :
159 (method == HTTP_METHOD_HEAD ? "HEAD" :
162 /* Content length, content type. */
163 if (content_length) setenv ("CONTENT_LENGTH", content_length, 1);
165 = http_request_get_header (p->http_request, "Content-Type");
166 if (content_type) setenv ("CONTENT_TYPE", content_type, 1);
168 /* Get peer address. */
169 addrlen = sizeof addr;
170 getpeername (p->sock, (struct sockaddr *) &addr, &addrlen);
172 /* General CGI environment variables. */
173 setenv ("SERVER_SOFTWARE", http_get_servername (), 1);
174 setenv ("SERVER_NAME", p->host_header, 1);
175 setenv ("GATEWAY_INTERFACE", "CGI/1.1", 1);
176 /*setenv ("SERVER_PORT", pitoa (p->pool, port), 1); XXX */
177 setenv ("PATH_INFO", p->canonical_path, 1);
178 setenv ("PATH_TRANSLATED", p->file_path, 1);
179 setenv ("SCRIPT_NAME", p->canonical_path, 1);
180 setenv ("REMOTE_ADDR", inet_ntoa (addr.sin_addr), 1);
182 /* Convert any other headers into HTTP_* environment variables. */
183 headers = http_request_get_headers (p->http_request);
184 for (i = 0; i < vector_size (headers); ++i)
186 vector_get (headers, i, header);
187 env = pstrdup (p->pool, header);
189 for (j = 0; j < strlen (env); ++j)
190 if (env[j] == '-') env[j] = '_';
191 env = psprintf (p->pool, "HTTP_%s", env);
192 setenv (env, http_request_get_header (p->http_request, header), 1);
195 /* Run the CGI script. */
196 execl (p->file_path, p->file_path, 0);
202 /* Close the unneeded halves of each pipe. */
203 close (to_script[0]);
204 close (from_script[1]);
206 /* Set the ends of the pipes to non-blocking mode. */
207 if (fcntl (to_script[1], F_SETFL, O_NONBLOCK) < 0 ||
208 fcntl (from_script[0], F_SETFL, O_NONBLOCK) < 0)
209 { perror ("fcntl"); exit (1); }
211 /* Associate the ends of the pipe with IO handles. This will also
212 * close them automagically in case of error.
214 to_io = io_fdopen (to_script[1]);
215 from_io = io_fdopen (from_script[0]);
216 if (to_io == 0 || from_io == 0)
217 return bad_request_error (p, "error associating pipes with IO handles");
219 /* If this is a POST method, copy the required amount of data
222 if (http_request_method (p->http_request) == HTTP_METHOD_POST)
224 /* How much to copy? Is content-length set? */
226 if (content_length) sscanf (content_length, "%d", &len);
228 /* Copy the data to the script. */
229 io_copy (p->io, to_io, len);
232 /* Read data back from the script and out to the client. */
233 io_copy (from_io, p->io, -1);
235 /* Force us to close the connection back to the client now. */