src/server/safs/cgi.c

changeset 430
83560f32e7d5
parent 415
d938228c382e
child 431
032b0ad35ee3
equal deleted inserted replaced
429:25c8e8021156 430:83560f32e7d5
40 40
41 #include "../util/util.h" 41 #include "../util/util.h"
42 #include "../util/pblock.h" 42 #include "../util/pblock.h"
43 #include "../daemon/netsite.h" 43 #include "../daemon/netsite.h"
44 #include "../util/io.h" 44 #include "../util/io.h"
45 #include "../daemon/event.h"
45 46
46 #include "cgiutils.h" 47 #include "cgiutils.h"
47 48
48 #define CGI_VARS 32 49 #define CGI_VARS 32
49 50
78 79
79 param_free(pblock_remove("content-type", rq->srvhdrs)); 80 param_free(pblock_remove("content-type", rq->srvhdrs));
80 81
81 const char *args = pblock_findval("query", rq->reqpb); 82 const char *args = pblock_findval("query", rq->reqpb);
82 char **argv = cgi_create_argv(path, NULL, args); 83 char **argv = cgi_create_argv(path, NULL, args);
84 if(!argv) {
85 return REQ_ABORTED;
86 }
83 87
84 char **env = http_hdrs2env(rq->headers); 88 char **env = http_hdrs2env(rq->headers);
85 env = cgi_common_vars(sn, rq, env); 89 env = cgi_common_vars(sn, rq, env);
86 env = cgi_specific_vars(sn, rq, args, env, 1); 90 env = cgi_specific_vars(sn, rq, args, env, 1);
87 91
88 CGIProcess cgip; 92 // event handler object for non-blocking io event handler
89 int ret = cgi_start(&cgip, path, argv, env); 93 CGIHandler *handler = pool_malloc(sn->pool, sizeof(CGIHandler));
94 if(!handler) {
95 return REQ_ABORTED;
96 }
97 ZERO(handler, sizeof(CGIHandler));
98 handler->path = path;
99
100 int ret = cgi_start(&handler->process, path, argv, env);
90 if(ret != REQ_PROCEED) { 101 if(ret != REQ_PROCEED) {
91 util_env_free(env); 102 util_env_free(env);
92 cgi_free_argv(argv); 103 cgi_free_argv(argv);
93 return ret; 104 return ret;
94 } 105 }
95 106
96 util_env_free(env); 107 util_env_free(env);
97 cgi_free_argv(argv); 108 cgi_free_argv(argv);
98 109
99 char buf[4096]; // I/O buffer 110 char buf[4096]; // I/O buffer
100 ssize_t r; 111 ssize_t r;
101 112
102 if(content_length > 0) { 113 if(content_length > 0) {
103 ssize_t n = 0; 114 ssize_t n = 0;
107 // TODO: handle error 118 // TODO: handle error
108 log_ereport( 119 log_ereport(
109 LOG_FAILURE, 120 LOG_FAILURE,
110 "send-cgi: script: %s: cannot read request body", 121 "send-cgi: script: %s: cannot read request body",
111 path); 122 path);
112 kill(cgip.pid, SIGTERM); 123 kill(handler->process.pid, SIGTERM);
113 cgi_close(&cgip); 124 cgi_close(&handler->process);
114 return REQ_ABORTED; 125 return REQ_ABORTED;
115 } 126 }
116 ssize_t w = write(cgip.in[1], buf, r); 127 ssize_t w = write(handler->process.in[1], buf, r);
117 if(w <= 0) { 128 if(w <= 0) {
118 // TODO: handle error 129 // TODO: handle error
119 log_ereport( 130 log_ereport(
120 LOG_FAILURE, 131 LOG_FAILURE,
121 "send-cgi: script: %s: cannot send request body to cgi process", 132 "send-cgi: script: %s: cannot send request body to cgi process",
122 path); 133 path);
123 kill(cgip.pid, SIGKILL); 134 kill(handler->process.pid, SIGKILL);
124 cgi_close(&cgip); 135 cgi_close(&handler->process);
125 return REQ_ABORTED; 136 return REQ_ABORTED;
126 } 137 }
127 n += r; 138 n += r;
128 } 139 }
129 } 140 }
130 system_close(cgip.in[1]); 141 system_close(handler->process.in[1]);
131 cgip.in[1] = -1; 142 handler->process.in[1] = -1;
132 143
133 // read from child 144 handler->parser = cgi_parser_new(sn, rq);
134 CGIResponseParser *parser = cgi_parser_new(sn, rq); 145
135 WSBool cgiheader = TRUE; 146 Event *readev = pool_malloc(sn->pool, sizeof(Event));
147 ZERO(readev, sizeof(Event));
148 readev->cookie = handler;
149 readev->fn = cgi_stdout_readevent;
150 readev->finish = cgi_event_finish;
151
152 Event *stderr_readev = pool_malloc(sn->pool, sizeof(Event));
153 ZERO(stderr_readev, sizeof(Event));
154 stderr_readev->cookie = handler;
155 stderr_readev->fn = cgi_stderr_readevent;
156 stderr_readev->finish = NULL;
157
158 Event *writeev = pool_malloc(sn->pool, sizeof(Event));
159 ZERO(writeev, sizeof(Event));
160 writeev->cookie = handler;
161 // TODO: fn
162
163 handler->writeev = writeev;
164
165 // add poll events for cgi stdout/stderr
166 int error = 0;
167 if(ev_pollin(sn->ev, handler->process.err[0], stderr_readev)) {
168 log_ereport(LOG_FAILURE, "send-cgi: stderr ev_pollin failed");
169 error = 1;
170 }
171 if(ev_pollin(sn->ev, handler->process.out[0], readev)) {
172 log_ereport(LOG_FAILURE, "send-cgi: stdout ev_pollin failed");
173 error = 1;
174 }
175
176 if(error) {
177 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", path);
178 kill(handler->process.pid, SIGKILL);
179 cgi_parser_free(handler->parser);
180 return REQ_ABORTED;
181 }
182
183 return REQ_PROCESSING;
184 }
185
186 int cgi_stdout_readevent(EventHandler *ev, Event *event) {
187 CGIHandler *handler = event->cookie;
188 CGIResponseParser *parser = handler->parser;
189 Session *sn = parser->sn;
190 Request *rq = parser->rq;
191
192 char buf[4096]; // I/O buffer
193 ssize_t r;
136 ssize_t wr = 0; 194 ssize_t wr = 0;
137 int result = REQ_PROCEED; 195
138 size_t response_length = 0; 196 handler->result = REQ_PROCEED;
139 while((r = read(cgip.out[0], buf, 4096)) > 0) { 197 while((r = read(handler->process.out[0], buf, 4096)) > 0) {
140 if(cgiheader) { 198 if(parser->cgiheader) {
141 size_t pos; 199 size_t pos;
142 ret = cgi_parse_response(parser, buf, r, &pos); 200 int ret = cgi_parse_response(parser, buf, r, &pos);
143 if(ret == -1) { 201 if(ret == -1) {
144 log_ereport( 202 log_ereport(
145 LOG_FAILURE, 203 LOG_FAILURE,
146 "broken cgi script response: path: %s", path); 204 "broken cgi script response: path: %s", handler->path);
147 protocol_status(sn, rq, 500, NULL); 205 protocol_status(sn, rq, 500, NULL);
148 result = REQ_ABORTED; 206 handler->result = REQ_ABORTED;
149 break; 207 break;
150 } else if(ret == 1) { 208 } else if(ret == 1) {
151 cgiheader = FALSE; 209 parser->cgiheader = FALSE;
152 if(parser->status > 0) { 210 if(parser->status > 0) {
153 protocol_status(sn, rq, parser->status, parser->msg); 211 protocol_status(sn, rq, parser->status, parser->msg);
154 } 212 }
155 http_start_response(sn, rq); 213 http_start_response(sn, rq);
156 if(pos < r) { 214 if(pos < r) {
157 response_length += r-pos; 215 parser->response_length += r-pos;
158 wr = net_write(sn->csd, &buf[pos], r-pos); 216 wr = net_write(sn->csd, &buf[pos], r-pos);
159 if(wr <= 0) { 217 if(wr <= 0) {
160 result = REQ_ABORTED; 218 handler->result = REQ_ABORTED;
161 break; 219 break;
162 } 220 }
163 } 221 }
164 } 222 }
165 } else { 223 } else {
166 response_length += r; 224 parser->response_length += r;
167 wr = net_write(sn->csd, buf, r); 225 wr = net_write(sn->csd, buf, r);
168 if(wr <= 0) { 226 if(wr <= 0) {
169 result = REQ_ABORTED; 227 handler->result = REQ_ABORTED;
170 break; 228 break;
171 } 229 }
172 } 230 }
231 }
232 if(r < 0 && errno == EWOULDBLOCK) {
233 event->events = EVENT_POLLIN;
234 return 1;
173 } 235 }
174 236
175 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); 237 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs);
176 if(ctlen_header) { 238 if(ctlen_header) {
177 int64_t ctlenhdr; 239 int64_t ctlenhdr;
178 if(util_strtoint(ctlen_header, &ctlenhdr)) { 240 if(util_strtoint(ctlen_header, &ctlenhdr)) {
179 if(ctlenhdr != response_length) { 241 if(ctlenhdr != parser->response_length) {
180 log_ereport( 242 log_ereport(
181 LOG_FAILURE, 243 LOG_FAILURE,
182 "cgi-send: script: %s: content length mismatch", 244 "cgi-send: script: %s: content length mismatch",
183 path); 245 handler->path);
184 rq->rq_attr.keep_alive = 0; 246 rq->rq_attr.keep_alive = 0;
185 result = REQ_ABORTED; 247 handler->result = REQ_ABORTED;
186 } 248 }
187 } 249 }
188 } 250 }
189 251
190 if(result == REQ_ABORTED) { 252 return 0;
191 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", path); 253 }
192 kill(cgip.pid, SIGKILL); 254
193 } 255 int cgi_stderr_readevent(EventHandler *ev, Event *event) {
194 256 CGIHandler *handler = event->cookie;
195 int exit_code = cgi_close(&cgip); 257
258 char buf[4096];
259 ssize_t r = read(handler->process.err[0], buf, 4096);
260 log_ereport(LOG_INFORM, "cgi pid %d %s stderr: %.*s", (int)handler->process.pid, handler->path, (int)r, buf);
261
262 return 0;
263 }
264
265 int cgi_event_finish(EventHandler *ev, Event *event) {
266 CGIHandler *handler = event->cookie;
267 CGIResponseParser *parser = handler->parser;
268 Session *sn = parser->sn;
269 Request *rq = parser->rq;
270
271 if(handler->result == REQ_ABORTED) {
272 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", handler->path);
273 kill(handler->process.pid, SIGKILL);
274 }
275
276 int exit_code = cgi_close(&handler->process);
196 if(exit_code != 0) { 277 if(exit_code != 0) {
197 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", path, exit_code); 278 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code);
198 ret = REQ_ABORTED; 279 handler->result = REQ_ABORTED;
199 } 280 }
200 281
201 cgi_parser_free(parser); 282 cgi_parser_free(parser);
202 return result; 283 // return to nsapi loop
284 nsapi_function_return(sn, rq, handler->result);
285 return 0;
203 } 286 }
204 287
205 int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]) { 288 int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]) {
206 if(pipe(p->in) || pipe(p->out)) { 289 if(pipe(p->in) || pipe(p->out) || pipe(p->err)) {
207 log_ereport( 290 log_ereport(
208 LOG_FAILURE, 291 LOG_FAILURE,
209 "send-cgi: cannot create pipe: %s", 292 "send-cgi: cannot create pipe: %s",
210 strerror(errno)); 293 strerror(errno));
211 return REQ_ABORTED; 294 return REQ_ABORTED;
239 } 322 }
240 if(dup2(p->out[1], STDOUT_FILENO) == -1) { 323 if(dup2(p->out[1], STDOUT_FILENO) == -1) {
241 perror("cgi_start: dup2"); 324 perror("cgi_start: dup2");
242 exit(EXIT_FAILURE); 325 exit(EXIT_FAILURE);
243 } 326 }
327 if(dup2(p->err[1], STDERR_FILENO) == -1) {
328 perror("cgi_start: dup2");
329 exit(EXIT_FAILURE);
330 }
244 331
245 // we need to close this unused pipe 332 // we need to close this unused pipe
246 // otherwise stdin cannot reach EOF 333 // otherwise stdin cannot reach EOF
247 system_close(p->in[1]); 334 system_close(p->in[1]);
248 335
249 // execute program 336 // execute program
250 exit(execve(script.ptr, argv, envp)); 337 exit(execve(script.ptr, argv, envp));
251 } else { 338 } else {
252 // parent 339 // parent
253 system_close(p->out[1]); 340 system_close(p->out[1]);
341 system_close(p->err[1]);
254 p->out[1] = -1; 342 p->out[1] = -1;
343 p->err[1] = -1;
255 } 344 }
256 345
257 return REQ_PROCEED; 346 return REQ_PROCEED;
258 } 347 }
259 348
270 if(p->out[0] != -1) { 359 if(p->out[0] != -1) {
271 system_close(p->out[0]); 360 system_close(p->out[0]);
272 } 361 }
273 if(p->out[1] != -1) { 362 if(p->out[1] != -1) {
274 system_close(p->out[1]); 363 system_close(p->out[1]);
364 }
365 if(p->err[0] != -1) {
366 system_close(p->err[0]);
367 }
368 if(p->err[1] != -1) {
369 system_close(p->err[1]);
275 } 370 }
276 371
277 return status; 372 return status;
278 } 373 }
279 374
281 CGIResponseParser* parser = pool_malloc(sn->pool, sizeof(CGIResponseParser)); 376 CGIResponseParser* parser = pool_malloc(sn->pool, sizeof(CGIResponseParser));
282 parser->sn = sn; 377 parser->sn = sn;
283 parser->rq = rq; 378 parser->rq = rq;
284 parser->status = 0; 379 parser->status = 0;
285 parser->msg = NULL; 380 parser->msg = NULL;
381 parser->response_length = 0;
382 parser->cgiheader = TRUE;
286 cxBufferInit(&parser->tmp, NULL, 64, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); 383 cxBufferInit(&parser->tmp, NULL, 64, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
287 return parser; 384 return parser;
288 } 385 }
289 386
290 void cgi_parser_free(CGIResponseParser *parser) { 387 void cgi_parser_free(CGIResponseParser *parser) {

mercurial