Add to git.
[rws.git] / exec.c
1 /* CGI scripts
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
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.
8  *
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.
13  *
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.
17  *
18  * $Id: exec.c,v 1.6 2003/02/05 23:02:51 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif
37
38 #ifdef HAVE_SYS_SOCKET_H
39 #include <sys/socket.h>
40 #endif
41
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45
46 #ifdef HAVE_ARPA_INET_H
47 #include <arpa/inet.h>
48 #endif
49
50 #include <pool.h>
51 #include <pstring.h>
52
53 #include <pthr_http.h>
54
55 #include "process_rq.h"
56 #include "errors.h"
57 #include "exec.h"
58
59 #ifndef HAVE_SETENV
60 #ifdef HAVE_PUTENV
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.
64  */
65 static inline void
66 setenv (const char *name, const char *value, int overwrite)
67 {
68   int len = strlen (name) + strlen (value) + 2;
69   char *str = malloc (len);
70
71   snprintf (str, len, "%s=%s", name, value);
72   putenv (str);
73 }
74 #else
75 #error "no setenv or putenv in your libc"
76 #endif
77 #endif
78
79 /* Note: For performance reasons and because I wanted to simplify the
80  * server, this code only handles NPH scripts.
81  *
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,
84  * do this:
85  *
86  * use CGI qw(:standard -nph);
87  */
88 int
89 exec_file (process_rq p)
90 {
91   int pid, to_script[2], from_script[2], len, i;
92   io_handle to_io, from_io;
93   const char *content_length;
94
95   content_length
96     = http_request_get_header (p->http_request, "Content-Length");
97
98   /* Set up two pipes between us and the script, one for reading, one
99    * for writing.
100    */
101   if (pipe (to_script) == -1 || pipe (from_script) == -1)
102     return bad_request_error (p, "cannot create pipes to script");
103
104   /* Fork off a process to run the request. */
105   pid = fork ();
106   if (pid == -1)
107     {
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");
111     }
112
113   if (pid == 0)                 /* Child process -- runs the script. */
114     {
115       int j;
116       const char *query_string, *content_type;
117       int major, minor, method;
118       char *header, *env;
119       vector headers;
120       struct sockaddr_in addr;
121       socklen_t addrlen;
122
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.
126        */
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.
130        */
131       close (to_script[1]);
132       if (to_script[0] != 0)
133         {
134           dup2 (to_script[0], 0);
135           close (to_script[0]);
136         }
137       close (from_script[0]);
138       if (from_script[1] != 1)
139         {
140           dup2 (from_script[1], 1);
141           close (from_script[1]);
142         }
143
144       /* Query string environment variable. */
145       query_string = http_request_query_string (p->http_request);
146       if (query_string)
147         setenv ("QUERY_STRING", query_string, 1);
148
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);
153
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" :
160                  "unknown"))), 1);
161
162       /* Content length, content type. */
163       if (content_length) setenv ("CONTENT_LENGTH", content_length, 1);
164       content_type
165         = http_request_get_header (p->http_request, "Content-Type");
166       if (content_type) setenv ("CONTENT_TYPE", content_type, 1);
167
168       /* Get peer address. */
169       addrlen = sizeof addr;
170       getpeername (p->sock, (struct sockaddr *) &addr, &addrlen);
171
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);
181
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)
185         {
186           vector_get (headers, i, header);
187           env = pstrdup (p->pool, header);
188           pstrupr (env);
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);
193         }
194
195       /* Run the CGI script. */
196       execl (p->file_path, p->file_path, 0);
197
198       perror ("exec");
199       exit (1);
200     }
201
202   /* Close the unneeded halves of each pipe. */
203   close (to_script[0]);
204   close (from_script[1]);
205
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); }
210
211   /* Associate the ends of the pipe with IO handles. This will also
212    * close them automagically in case of error.
213    */
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");
218
219   /* If this is a POST method, copy the required amount of data
220    * to the CGI script.
221    */
222   if (http_request_method (p->http_request) == HTTP_METHOD_POST)
223     {
224       /* How much to copy? Is content-length set? */
225       len = -1;
226       if (content_length) sscanf (content_length, "%d", &len);
227
228       /* Copy the data to the script. */
229       io_copy (p->io, to_io, len);
230     }
231
232   /* Read data back from the script and out to the client. */
233   io_copy (from_io, p->io, -1);
234
235   /* Force us to close the connection back to the client now. */
236   return 1;
237 }