1 #include <uwsgi.h>
2
3 extern struct uwsgi_server uwsgi;
4
5 #define kill_on_error if (!uc.do_not_kill_on_error) { if (kill(cgi_pid, SIGKILL)) uwsgi_error("kill()");}
6
7 struct uwsgi_cgi {
8 struct uwsgi_dyn_dict *mountpoint;
9 struct uwsgi_dyn_dict *helpers;
10 size_t buffer_size;
11 int timeout;
12 struct uwsgi_string_list *index;
13 struct uwsgi_string_list *allowed_ext;
14 struct uwsgi_string_list *unset;
15 struct uwsgi_string_list *loadlib;
16 struct uwsgi_string_list *cgi_safe;
17 int optimize;
18 int from_docroot;
19 int has_mountpoints;
20 struct uwsgi_dyn_dict *default_cgi;
21 int path_info;
22 int do_not_kill_on_error;
23 int async_max_attempts;
24 int close_stdin_on_eof;
25 } uc ;
26
uwsgi_opt_add_cgi(char * opt,char * value,void * foobar)27 static void uwsgi_opt_add_cgi(char *opt, char *value, void *foobar) {
28
29 char *val = strchr(value, '=');
30 if (!val) {
31 uwsgi_dyn_dict_new(&uc.mountpoint, value, strlen(value), NULL, 0);
32 }
33 else {
34 uwsgi_dyn_dict_new(&uc.mountpoint, value, val-value, val+1, strlen(val+1));
35 }
36
37 }
38
uwsgi_opt_add_cgi_maphelper(char * opt,char * value,void * foobar)39 static void uwsgi_opt_add_cgi_maphelper(char *opt, char *value, void *foobar) {
40 char *val = strchr(value, '=');
41 if (!val) {
42 uwsgi_log("invalid CGI helper syntax, must be ext=command\n");
43 exit(1);
44 }
45 uwsgi_dyn_dict_new(&uc.helpers, value, val-value, val+1, strlen(val+1));
46 }
47
48 struct uwsgi_option uwsgi_cgi_options[] = {
49
50 {"cgi", required_argument, 0, "add a cgi mountpoint/directory/script", uwsgi_opt_add_cgi, NULL, 0},
51
52 {"cgi-map-helper", required_argument, 0, "add a cgi map-helper", uwsgi_opt_add_cgi_maphelper, NULL, 0},
53 {"cgi-helper", required_argument, 0, "add a cgi map-helper", uwsgi_opt_add_cgi_maphelper, NULL, 0},
54
55 {"cgi-from-docroot", no_argument, 0, "blindly enable cgi in DOCUMENT_ROOT", uwsgi_opt_true, &uc.from_docroot, 0},
56
57 {"cgi-buffer-size", required_argument, 0, "set cgi buffer size", uwsgi_opt_set_64bit, &uc.buffer_size, 0},
58 {"cgi-timeout", required_argument, 0, "set cgi script timeout", uwsgi_opt_set_int, &uc.timeout, 0},
59
60 {"cgi-index", required_argument, 0, "add a cgi index file", uwsgi_opt_add_string_list, &uc.index, 0},
61 {"cgi-allowed-ext", required_argument, 0, "cgi allowed extension", uwsgi_opt_add_string_list, &uc.allowed_ext, 0},
62
63 {"cgi-unset", required_argument, 0, "unset specified environment variables", uwsgi_opt_add_string_list, &uc.unset, 0},
64
65 {"cgi-loadlib", required_argument, 0, "load a cgi shared library/optimizer", uwsgi_opt_add_string_list, &uc.loadlib, 0},
66 {"cgi-optimize", no_argument, 0, "enable cgi realpath() optimizer", uwsgi_opt_true, &uc.optimize, 0},
67 {"cgi-optimized", no_argument, 0, "enable cgi realpath() optimizer", uwsgi_opt_true, &uc.optimize, 0},
68
69 {"cgi-path-info", no_argument, 0, "disable PATH_INFO management in cgi scripts", uwsgi_opt_true, &uc.path_info, 0},
70
71 {"cgi-do-not-kill-on-error", no_argument, 0, "do not send SIGKILL to cgi script on errors", uwsgi_opt_true, &uc.do_not_kill_on_error, 0},
72 {"cgi-async-max-attempts", no_argument, 0, "max waitpid() attempts in cgi async mode (default 10)", uwsgi_opt_set_int, &uc.async_max_attempts, 0},
73
74 {"cgi-close-stdin-on-eof", no_argument, 0, "close STDIN on input EOF", uwsgi_opt_true, &uc.close_stdin_on_eof, 0},
75
76 {"cgi-safe", required_argument, 0, "skip security checks if the cgi file is under the specified path", uwsgi_opt_add_string_list, &uc.cgi_safe, 0},
77
78 {0, 0, 0, 0, 0, 0, 0},
79
80 };
81
uwsgi_cgi_apps()82 static void uwsgi_cgi_apps() {
83
84 struct uwsgi_dyn_dict *udd = uc.mountpoint;
85 struct stat st;
86
87 while(udd) {
88 if (udd->vallen) {
89 if (uc.optimize) {
90 udd->value = realpath(udd->value, NULL);
91 if (!udd->value) {
92 uwsgi_log("unable to find CGI path %.*s\n", udd->vallen, udd->value);
93 exit(1);
94 }
95 udd->vallen = strlen(udd->value);
96 udd->status = 1;
97 if (stat(udd->value, &st)) {
98 uwsgi_error("stat()");
99 uwsgi_log("something horrible happened during CGI initialization\n");
100 exit(1);
101 }
102
103 if (!S_ISDIR(st.st_mode)) {
104 udd->status = 2;
105 }
106 }
107 uc.has_mountpoints = 1;
108 uwsgi_log("initialized CGI mountpoint: %.*s = %.*s\n", udd->keylen, udd->key, udd->vallen, udd->value);
109 }
110 else {
111 if (uc.optimize) {
112 udd->key = realpath(udd->key, NULL);
113 if (!udd->key) {
114 uwsgi_log("unable to find CGI path %.*s\n", udd->keylen, udd->key);
115 exit(1);
116 }
117 udd->keylen = strlen(udd->key);
118 udd->status = 1;
119
120 if (stat(udd->key, &st)) {
121 uwsgi_error("stat()");
122 uwsgi_log("something horrible happened during CGI initialization\n");
123 exit(1);
124 }
125
126 if (!S_ISDIR(st.st_mode)) {
127 udd->status = 2;
128 }
129
130 }
131 uwsgi_log("initialized CGI path: %.*s\n", udd->keylen, udd->key);
132 uc.default_cgi = udd;
133 }
134 udd = udd->next;
135 }
136
137 }
138
uwsgi_cgi_init()139 static int uwsgi_cgi_init(){
140
141 void (*cgi_sym)(void);
142
143 if (!uc.buffer_size) uc.buffer_size = 65536;
144 if (!uc.timeout) uc.timeout = 60;
145
146 struct uwsgi_string_list *ll = uc.loadlib;
147 while(ll) {
148 char *colon = strchr(ll->value, ':');
149 if (!colon) {
150 uwsgi_log("invalid cgi-loadlib syntax, must be in the form lib:func\n");
151 exit(1);
152 }
153 *colon = 0;
154 void *cgi_lib = dlopen(ll->value, RTLD_NOW | RTLD_GLOBAL);
155 if (!cgi_lib) {
156 uwsgi_log( "cgi-loadlib: %s\n", dlerror());
157 exit(1);
158 }
159
160 cgi_sym = dlsym(cgi_lib, colon+1);
161 if (!cgi_sym) {
162 uwsgi_log("unknown symbol %s in lib %s\n", colon+1, ll->value);
163 exit(1);
164 }
165
166 cgi_sym();
167 uwsgi_log("[cgi-loadlib] loaded symbol %s from %s\n", colon+1, ll->value);
168
169 *colon = ':';
170 ll = ll->next;
171 }
172
173 return 0;
174
175 }
176
uwsgi_cgi_get_helper(char * filename)177 static char *uwsgi_cgi_get_helper(char *filename) {
178
179 struct uwsgi_dyn_dict *helpers = uc.helpers;
180 size_t len = strlen(filename);
181
182 while(helpers) {
183 if (len >= (size_t) helpers->keylen) {
184 if (!uwsgi_strncmp((filename+len)-helpers->keylen, helpers->keylen, helpers->key, helpers->keylen)) {
185 return helpers->value;
186 }
187 }
188 helpers = helpers->next;
189 }
190
191 return NULL;
192
193 }
194
195 /*
196 start reading each line until Status or Location are found
197 -1 error
198 0 not found
199 1 found
200 */
uwsgi_cgi_check_status(struct wsgi_request * wsgi_req,char * buf,size_t len)201 static int uwsgi_cgi_check_status(struct wsgi_request *wsgi_req, char *buf, size_t len) {
202 char *key = buf, *value = NULL;
203 size_t header_size = 0;
204 size_t i;
205
206 for(i=0;i<len;i++) {
207 // end of a line
208 if (buf[i] == '\n') {
209 // end of headers
210 if (key == NULL) {
211 // Default status
212 #ifdef UWSGI_DEBUG
213 uwsgi_log("setting default Status header\n");
214 #endif
215 if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1;
216 return 1;
217 }
218 // invalid header
219 else if (value == NULL) return -1;
220 header_size = (buf+i) - key;
221 // security check
222 if (buf+i > buf) {
223 // remove \r
224 if ((buf[i-1]) == '\r') {
225 header_size--;
226 }
227 }
228
229 // enough space for Status ?
230 if (header_size >= 11) {
231 // "Status: NNN"
232 if (!strncasecmp("Status: ", key, 8)) {
233 #ifdef UWSGI_DEBUG
234 uwsgi_log("found Status header: %.*s\n", header_size, key);
235 #endif
236 if (uwsgi_response_prepare_headers(wsgi_req, key+8, header_size - 8)) return -1;
237 return 1;
238 }
239 // Location: X
240 if (!strncasecmp("Location: ", key, 10)) {
241 #ifdef UWSGI_DEBUG
242 uwsgi_log("found Location header: %.*s\n", header_size, key);
243 #endif
244 if (uwsgi_response_prepare_headers(wsgi_req, "302 Found", 9)) return -1;
245 return 1;
246 }
247 }
248
249 key = NULL;
250 value = NULL;
251 }
252 else if (buf[i] == ':') {
253 value = buf+i;
254 }
255 else if (buf[i] != '\r') {
256 if (key == NULL) key = buf + i;
257 }
258
259 }
260
261 // no Status/Location found
262 return 0;
263
264 }
265
uwsgi_cgi_parse(struct wsgi_request * wsgi_req,int fd,char * buf,size_t blen)266 static int uwsgi_cgi_parse(struct wsgi_request *wsgi_req, int fd, char *buf, size_t blen) {
267
268 size_t i;
269 size_t header_size = 0;
270 int status_sent = 0;
271 size_t remains = blen;
272 char *ptr = buf;
273 size_t len = 0;
274
275 while(remains > 0) {
276 ssize_t rlen = uwsgi_read_true_nb(fd, ptr, remains, uc.timeout);
277 if (rlen < 0) {
278 if (!errno) return 1;
279 return -1;
280 }
281 // timed out
282 if (rlen == 0) return -1;
283 remains -= rlen;
284 len += rlen;
285 ptr += rlen;
286
287 // Search for Status/Location headers
288 if (!status_sent) {
289 status_sent = uwsgi_cgi_check_status(wsgi_req, buf, len);
290 if (status_sent < 0) return -1;
291 // need more data ?
292 if (status_sent == 0) continue;
293 }
294
295 // send headers
296 char *key = buf;
297 char *value = NULL;
298
299 for(i=0;i<len;i++) {
300 // end of a line
301 if (buf[i] == '\n') {
302 // end of headers
303 if (key == NULL) {
304 i++;
305 goto send_body;
306 }
307 // invalid header
308 else if (value == NULL) {
309 return -1;
310 }
311 header_size = (buf+i) - key;
312 // security check
313 if (buf+i > buf) {
314 if ((buf[i-1]) == '\r') {
315 header_size--;
316 }
317 }
318
319 #ifdef UWSGI_DEBUG
320 uwsgi_log("found CGI header: %.*s\n", header_size, key);
321 #endif
322
323 // Ignore "Status: NNN" header
324 if (header_size >= 11) {
325 if (!strncasecmp("Status: ", key, 8)) {
326 key = NULL;
327 value = NULL;
328 continue;
329 }
330 }
331
332 uwsgi_response_add_header(wsgi_req, NULL, 0, key, header_size);
333 key = NULL;
334 value = NULL;
335 }
336 else if (buf[i] == ':') {
337 value = buf+i;
338 }
339 else if (buf[i] != '\r') {
340 if (key == NULL) {
341 key = buf + i;
342 }
343 }
344 }
345 }
346
347 return -1;
348
349 send_body:
350
351 if (len-i > 0) {
352 uwsgi_response_write_body_do(wsgi_req, buf+i, len-i);
353 }
354
355 return 0;
356 }
357
uwsgi_cgi_get_docroot(char * path_info,uint16_t path_info_len,int * need_free,int * is_a_file,int * discard_base,char ** script_name)358 static char *uwsgi_cgi_get_docroot(char *path_info, uint16_t path_info_len, int *need_free, int *is_a_file, int *discard_base, char **script_name) {
359
360 struct uwsgi_dyn_dict *udd = uc.mountpoint, *choosen_udd = NULL;
361 int best_found = 0;
362 struct stat st;
363 char *path = NULL;
364
365 if (uc.has_mountpoints) {
366 while(udd) {
367 if (udd->vallen) {
368 if (!uwsgi_starts_with(path_info, path_info_len, udd->key, udd->keylen) && udd->keylen > best_found) {
369 best_found = udd->keylen ;
370 choosen_udd = udd;
371 path = udd->value;
372 *script_name = udd->key;
373 *discard_base = udd->keylen;
374 if (udd->key[udd->keylen-1] == '/') {
375 *discard_base = *discard_base-1;
376 }
377 }
378 }
379 udd = udd->next;
380 }
381 }
382
383 if (choosen_udd == NULL) {
384 choosen_udd = uc.default_cgi;
385 if (!choosen_udd) return NULL;
386 path = choosen_udd->key;
387 }
388
389 if (choosen_udd->status == 0) {
390 char *tmp_udd = uwsgi_malloc(PATH_MAX+1);
391 if (!realpath(path, tmp_udd)) {
392 free(tmp_udd);
393 return NULL;
394 }
395
396 if (stat(tmp_udd, &st)) {
397 uwsgi_error("stat()");
398 free(tmp_udd);
399 return NULL;
400 }
401
402 if (!S_ISDIR(st.st_mode)) {
403 *is_a_file = 1;
404 }
405
406 *need_free = 1;
407 return tmp_udd;
408 }
409
410 if (choosen_udd->status == 2)
411 *is_a_file = 1;
412 return path;
413 }
414
uwsgi_cgi_walk(struct wsgi_request * wsgi_req,char * full_path,char * docroot,size_t docroot_len,int discard_base,char ** path_info)415 static int uwsgi_cgi_walk(struct wsgi_request *wsgi_req, char *full_path, char *docroot, size_t docroot_len, int discard_base, char **path_info) {
416
417 // and now start walking...
418 uint16_t i;
419 char *ptr = wsgi_req->path_info+discard_base;
420 char *dst = full_path+docroot_len;
421 char *part = ptr;
422 int part_size = 0;
423 struct stat st;
424
425 if (wsgi_req->path_info_len == 0) return 0;
426
427 if (ptr[0] == '/') part_size++;
428
429 for(i=0;i<wsgi_req->path_info_len-discard_base;i++) {
430 if (ptr[i] == '/') {
431 memcpy(dst, part, part_size-1);
432 *(dst+part_size-1) = 0;
433
434 if (stat(full_path, &st)) {
435 uwsgi_404(wsgi_req);
436 return -1;
437 }
438
439
440 // not a directory, stop walking
441 if (!S_ISDIR(st.st_mode)) {
442 if (i < (wsgi_req->path_info_len-discard_base)-1) {
443 *path_info = ptr + i;
444 }
445
446 return 0;
447 }
448
449
450 // check for buffer overflow !!!
451 *(dst+part_size-1) = '/';
452 *(dst+part_size) = 0;
453
454 dst += part_size ;
455 part_size = 0;
456 part = ptr + i + 1;
457 }
458
459 part_size++;
460 }
461
462 if (part < wsgi_req->path_info+wsgi_req->path_info_len) {
463 memcpy(dst, part, part_size-1);
464 *(dst+part_size-1) = 0;
465 }
466
467 return 0;
468
469
470 }
471
472 static int uwsgi_cgi_run(struct wsgi_request *, char *, size_t, char *, char *, char *, char *, int, int);
473
uwsgi_cgi_request(struct wsgi_request * wsgi_req)474 static int uwsgi_cgi_request(struct wsgi_request *wsgi_req) {
475
476 char full_path[PATH_MAX];
477 char tmp_path[PATH_MAX];
478 struct stat cgi_stat;
479 int need_free = 0;
480 int is_a_file = 0;
481 int discard_base = 0;
482 size_t docroot_len = 0;
483 size_t full_path_len = 0;
484 char *helper = NULL;
485 char *path_info = NULL;
486 char *script_name = NULL;
487
488 /* Standard CGI request */
489 if (!wsgi_req->uh->pktsize) {
490 uwsgi_log("Empty CGI request. skip.\n");
491 return -1;
492 }
493
494
495 if (uwsgi_parse_vars(wsgi_req)) {
496 return -1;
497 }
498
499 char *docroot = NULL;
500
501 // check for file availability (and 'runnability')
502 if (uc.from_docroot) {
503 docroot = wsgi_req->document_root;
504 docroot_len = wsgi_req->document_root_len;
505 }
506 else {
507 docroot = uwsgi_cgi_get_docroot(wsgi_req->path_info, wsgi_req->path_info_len, &need_free, &is_a_file, &discard_base, &script_name);
508 if (docroot)
509 docroot_len = strlen(docroot);
510 }
511
512 if (docroot == NULL || docroot_len == 0) {
513 uwsgi_404(wsgi_req);
514 return UWSGI_OK;
515 }
516
517 memcpy(full_path, docroot, docroot_len);
518
519 if (!is_a_file) {
520
521 *(full_path+docroot_len) = '/';
522 *(full_path+docroot_len+1) = 0;
523
524 if (uwsgi_cgi_walk(wsgi_req, full_path, docroot, docroot_len, discard_base, &path_info)) {
525 if (need_free)
526 free(docroot);
527 return UWSGI_OK;
528 }
529
530 if (realpath(full_path, tmp_path) == NULL) {
531 if (need_free)
532 free(docroot);
533 uwsgi_404(wsgi_req);
534 return UWSGI_OK;
535 }
536
537 full_path_len = strlen(tmp_path);
538 // add +1 to copy the null byte
539 memcpy(full_path, tmp_path, full_path_len+1);
540
541 struct uwsgi_string_list *safe = uc.cgi_safe;
542 while(safe) {
543 if (!uwsgi_starts_with(full_path, full_path_len, safe->value, safe->len))
544 break;
545 safe = safe->next;
546 }
547
548 if (!safe) {
549 if (uwsgi_starts_with(full_path, full_path_len, docroot, docroot_len)) {
550 uwsgi_log("CGI security error: %s is not under %s\n", full_path, docroot);
551 if (need_free)
552 free(docroot);
553 return -1;
554 }
555 }
556
557 }
558 else {
559 *(full_path+docroot_len) = 0;
560 path_info = wsgi_req->path_info+discard_base;
561 }
562
563 if (stat(full_path, &cgi_stat)) {
564 uwsgi_404(wsgi_req);
565 if (need_free)
566 free(docroot);
567 return UWSGI_OK;
568 }
569
570 if (S_ISDIR(cgi_stat.st_mode)) {
571
572 // add / to directories
573 if (wsgi_req->path_info_len == 0 || (wsgi_req->path_info_len > 0 && wsgi_req->path_info[wsgi_req->path_info_len-1] != '/')) {
574 uwsgi_redirect_to_slash(wsgi_req);
575 if (need_free)
576 free(docroot);
577 return UWSGI_OK;
578 }
579 struct uwsgi_string_list *ci = uc.index;
580 full_path[full_path_len] = '/';
581 full_path_len++;
582 int found = 0;
583 while(ci) {
584 if (full_path_len + ci->len + 1 < PATH_MAX) {
585 // add + 1 to ensure null byte
586 memcpy(full_path+full_path_len, ci->value, ci->len + 1);
587 if (!access(full_path, R_OK)) {
588
589 found = 1;
590 break;
591 }
592 }
593 ci = ci->next;
594 }
595
596 if (!found) {
597 uwsgi_404(wsgi_req);
598 if (need_free)
599 free(docroot);
600 return UWSGI_OK;
601 }
602
603 }
604
605 full_path_len = strlen(full_path);
606
607 int cgi_allowed = 1;
608 struct uwsgi_string_list *allowed = uc.allowed_ext;
609 while(allowed) {
610 cgi_allowed = 0;
611 if (full_path_len >= allowed->len) {
612 if (!uwsgi_strncmp(full_path+(full_path_len-allowed->len), allowed->len, allowed->value, allowed->len)) {
613 cgi_allowed = 1;
614 break;
615 }
616 }
617 allowed = allowed->next;
618 }
619
620 if (!cgi_allowed) {
621 uwsgi_403(wsgi_req);
622 if (need_free)
623 free(docroot);
624 return UWSGI_OK;
625 }
626
627 // get the helper
628 if (!is_a_file) {
629 helper = uwsgi_cgi_get_helper(full_path);
630
631 if (helper == NULL) {
632 if (access(full_path, X_OK)) {
633 uwsgi_error("access()");
634 uwsgi_403(wsgi_req);
635 if (need_free)
636 free(docroot);
637 return UWSGI_OK;
638 }
639 }
640 }
641
642 int ret = uwsgi_cgi_run(wsgi_req, docroot, docroot_len, full_path, helper, path_info, script_name, is_a_file, discard_base);
643 if (need_free) free(docroot);
644 return ret;
645 }
646
uwsgi_cgi_run(struct wsgi_request * wsgi_req,char * docroot,size_t docroot_len,char * full_path,char * helper,char * path_info,char * script_name,int is_a_file,int discard_base)647 static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t docroot_len, char *full_path, char *helper, char *path_info, char *script_name, int is_a_file, int discard_base) {
648
649 int cgi_pipe[2];
650 int post_pipe[2];
651 int nargs = 0;
652 int waitpid_status;
653 int i;
654 char **argv;
655
656 char *command = full_path;
657 int stdin_closed = 0;
658
659 if (is_a_file) {
660 command = docroot;
661 }
662
663 if (pipe(cgi_pipe)) {
664 uwsgi_error("uwsgi_cgi_run()/pipe()");
665 return UWSGI_OK;
666 }
667
668 if (pipe(post_pipe)) {
669 close(cgi_pipe[0]);
670 close(cgi_pipe[1]);
671 uwsgi_error("uwsgi_cgi_run()/pipe()");
672 return UWSGI_OK;
673 }
674
675 pid_t cgi_pid = fork();
676
677 if (cgi_pid < 0) {
678 uwsgi_error("uwsgi_cgi_run()/fork()");
679 close(cgi_pipe[0]);
680 close(cgi_pipe[1]);
681 close(post_pipe[0]);
682 close(post_pipe[1]);
683 return UWSGI_OK;
684 }
685
686 if (cgi_pid > 0) {
687
688 close(cgi_pipe[1]);
689 close(post_pipe[0]);
690
691 uwsgi_socket_nb(cgi_pipe[0]);
692 uwsgi_socket_nb(post_pipe[1]);
693
694 // ok start sending post data...
695 size_t remains = wsgi_req->post_cl;
696 while(remains > 0) {
697 ssize_t rlen = 0;
698 char *buf = uwsgi_request_body_read(wsgi_req, 8192, &rlen);
699 if (!buf) {
700 goto clear2;
701 }
702 if (buf == uwsgi.empty) break;
703 // write data to the node
704 if (uwsgi_write_true_nb(post_pipe[1], buf, rlen, uc.timeout)) {
705 goto clear2;
706 }
707 remains -= rlen;
708 }
709
710 if (uc.close_stdin_on_eof) {
711 close(post_pipe[1]);
712 stdin_closed = 1;
713 }
714
715 // wait for data
716 char *buf = uwsgi_malloc(uc.buffer_size);
717
718 int completed = uwsgi_cgi_parse(wsgi_req, cgi_pipe[0], buf, uc.buffer_size);
719 if (completed < 0) {
720 uwsgi_log("invalid CGI response !!!\n");
721 kill_on_error
722 goto clear;
723 }
724
725 while (!completed) {
726 ssize_t rlen = uwsgi_read_true_nb(cgi_pipe[0], buf, uc.buffer_size, uc.timeout);
727 if (rlen > 0) {
728 if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) {
729 kill_on_error
730 goto clear;
731 }
732 }
733 else if (rlen == 0) {
734 uwsgi_log("CGI timeout !!!\n");
735 kill_on_error
736 goto clear;
737 }
738 else {
739 if (errno) {
740 uwsgi_req_error("error reading CGI response\n");
741 kill_on_error
742 }
743 goto clear;
744 }
745 }
746
747 clear:
748 free(buf);
749 clear2:
750 close(cgi_pipe[0]);
751 if (!stdin_closed)
752 close(post_pipe[1]);
753
754 // now wait for process exit/death
755 // in async mode we need a trick...
756 if (uwsgi.async > 1) {
757 pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG);
758 if (diedpid < 0) {
759 uwsgi_error("waitpid()");
760 }
761 else if (diedpid == 0) {
762 // pass the pid of the cgi to async_plagued (the after request hook will clear the process)
763 wsgi_req->async_plagued = (int) cgi_pid;
764 }
765 }
766 else {
767 if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
768 uwsgi_error("waitpid()");
769 }
770 }
771
772 return UWSGI_OK;
773 }
774
775 // now map wsgi_req->poll.fd (or async_post) to 0 & cgi_pipe[1] to 1
776 dup2(post_pipe[0], 0);
777 close(post_pipe[0]);
778
779 dup2(cgi_pipe[1],1);
780 close(cgi_pipe[1]);
781
782 // close all the fd > 2
783 DIR *dirp = opendir("/proc/self/fd");
784 if (dirp == NULL)
785 dirp = opendir("/dev/fd");
786 if (dirp != NULL) {
787 struct dirent *dent;
788 while ((dent = readdir(dirp)) != NULL) {
789 int fd = atoi(dent->d_name);
790 if ((fd > 2) && fd != dirfd(dirp))
791 close(fd);
792 }
793 closedir(dirp);
794 } else {
795 for(i=3;i<(int)uwsgi.max_fd;i++) {
796 close(i);
797 }
798 }
799
800 // fill cgi env
801 for(i=0;i<wsgi_req->var_cnt;i+=2) {
802 // no need to free the putenv() memory
803 if (putenv(uwsgi_concat3n(wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, "=", 1, wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len))) {
804 uwsgi_error("putenv()");
805 }
806 }
807
808
809 if (setenv("GATEWAY_INTERFACE", "CGI/1.1", 0)) {
810 uwsgi_error("setenv()");
811 }
812
813 if (setenv("SERVER_SOFTWARE", uwsgi_concat2("uWSGI/", UWSGI_VERSION), 0)) {
814 uwsgi_error("setenv()");
815 }
816
817 // for newer php
818 if (setenv("REDIRECT_STATUS", "200", 0)) {
819 uwsgi_error("setenv()");
820 }
821
822
823
824 if (path_info) {
825
826 size_t pi_len = wsgi_req->path_info_len - (path_info - wsgi_req->path_info);
827
828 if (setenv("PATH_INFO", uwsgi_concat2n(path_info, pi_len, "", 0), 1)) {
829 uwsgi_error("setenv()");
830 }
831
832 if (wsgi_req->document_root_len > 0) {
833 if (setenv("PATH_TRANSLATED", uwsgi_concat3n(wsgi_req->document_root, wsgi_req->document_root_len, path_info, pi_len, "", 0) , 1)) {
834 uwsgi_error("setenv()");
835 }
836 }
837 else {
838 if (setenv("PATH_TRANSLATED", uwsgi_concat3n(docroot, docroot_len, path_info, pi_len, "", 0) , 1)) {
839 uwsgi_error("setenv()");
840 }
841 }
842
843 }
844 else {
845 unsetenv("PATH_INFO");
846 unsetenv("PATH_TRANSLATED");
847 }
848
849 if (is_a_file) {
850 if (setenv("DOCUMENT_ROOT", uwsgi.cwd, 0)) {
851 uwsgi_error("setenv()");
852 }
853
854 if (setenv("SCRIPT_FILENAME", docroot, 0)) {
855 uwsgi_error("setenv()");
856 }
857
858 if (script_name && discard_base > 1) {
859 if (setenv("SCRIPT_NAME", uwsgi_concat2n(script_name, discard_base, "", 0), 1)) {
860 uwsgi_error("setenv()");
861 }
862 }
863 }
864 else {
865 if (setenv("DOCUMENT_ROOT", docroot, 0)) {
866 uwsgi_error("setenv()");
867 }
868
869 if (setenv("SCRIPT_FILENAME", full_path, 0)) {
870 uwsgi_error("setenv()");
871 }
872
873 if (setenv("SCRIPT_NAME", uwsgi_concat2n(wsgi_req->path_info, discard_base, full_path+docroot_len, strlen(full_path+docroot_len)), 1)) {
874 uwsgi_error("setenv()");
875 }
876
877 char *base = uwsgi_get_last_char(full_path, '/');
878 if (base) {
879 // a little trick :P
880 *base = 0;
881 if (chdir(full_path)) {
882 uwsgi_error("chdir()");
883 }
884 *base = '/';
885 }
886 else if (docroot_len > 0) {
887 if (chdir(docroot)) {
888 uwsgi_error("chdir()");
889 }
890 }
891 }
892
893 struct uwsgi_string_list *drop_env = uc.unset;
894 while(drop_env) {
895 unsetenv(drop_env->value);
896 drop_env = drop_env->next;
897 }
898
899 argv = uwsgi_malloc(sizeof(char *) * 3);
900
901 // check if we need to parse indexed QUERY_STRING
902 if (wsgi_req->query_string_len > 0) {
903 if (!memchr(wsgi_req->query_string, '=', wsgi_req->query_string_len)) {
904 nargs = 1;
905 for(i=0;i<wsgi_req->query_string_len;i++) {
906 if (wsgi_req->query_string[i] == '+')
907 nargs++;
908 }
909
910
911 // reallocate argv and qs
912 argv = uwsgi_malloc(sizeof(char *) * (3+nargs));
913 char *qs = uwsgi_concat2n(wsgi_req->query_string, wsgi_req->query_string_len, "", 0);
914 // set the start position of args in argv
915 i = 1;
916 if (helper) i = 2;
917 char *p, *ctx = NULL;
918 uwsgi_foreach_token(qs, "+", p, ctx) {
919 // create a copy for the url_decoded string
920 char *arg_copy = uwsgi_str(p);
921 uint16_t arg_copy_len = strlen(p);
922 http_url_decode(p, &arg_copy_len, arg_copy);
923 // and a final copy for shell escaped arg
924 argv[i] = uwsgi_malloc( (arg_copy_len * 2) +1);
925 escape_shell_arg(arg_copy, arg_copy_len, argv[i]);
926 i++;
927 }
928 free(qs);
929 }
930 }
931
932 if (helper) {
933 if (!uwsgi_starts_with(helper, strlen(helper), "sym://", 6)) {
934 void (*cgi_func)(char *) = dlsym(RTLD_DEFAULT, helper+6);
935 if (cgi_func) {
936 cgi_func(command);
937 }
938 else {
939 uwsgi_log("unable to find symbol %s\n", helper+6);
940 }
941 exit(0);
942 }
943 argv[0] = helper;
944 argv[1] = command;
945 argv[nargs+2] = NULL;
946 }
947 else {
948 argv[0] = command;
949 argv[nargs+1] = NULL;
950 }
951
952 if (execvp(argv[0], argv)) {
953 uwsgi_error("uwsgi_cgi_run()/execvp()");
954 }
955
956 // never here
957 exit(1);
958 }
959
960
uwsgi_cgi_after_request(struct wsgi_request * wsgi_req)961 static void uwsgi_cgi_after_request(struct wsgi_request *wsgi_req) {
962 if (wsgi_req->async_plagued > 0) {
963 int waitpid_status;
964 pid_t cgi_pid = (pid_t) wsgi_req->async_plagued;
965 int max_attempts = uc.async_max_attempts;
966 if (!max_attempts) max_attempts = 10;
967 while(max_attempts) {
968 pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG);
969 if (diedpid < 0) {
970 uwsgi_error("waitpid()");
971 break;
972 }
973 else if (diedpid == 0) {
974 int ret = uwsgi.wait_milliseconds_hook(1000);
975 if (ret < 0) {
976 kill_on_error
977 if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
978 uwsgi_error("waitpid()");
979 }
980 }
981 }
982 else {
983 break;
984 }
985 max_attempts--;
986 }
987 if (max_attempts == 0) {
988 kill_on_error
989 if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
990 uwsgi_error("waitpid()");
991 }
992 }
993 }
994
995 log_request(wsgi_req);
996 }
997
998 #ifdef UWSGI_ROUTING
uwsgi_routing_func_cgi(struct wsgi_request * wsgi_req,struct uwsgi_route * ur)999 static int uwsgi_routing_func_cgi(struct wsgi_request *wsgi_req, struct uwsgi_route *ur){
1000
1001 char **subject = (char **) (((char *)(wsgi_req))+ur->subject);
1002 uint16_t *subject_len = (uint16_t *) (((char *)(wsgi_req))+ur->subject_len);
1003
1004 struct uwsgi_buffer *ub_command = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, ur->data, ur->data_len);
1005 if (!ub_command) return UWSGI_ROUTE_BREAK;
1006 struct uwsgi_buffer *ub_helper = NULL;
1007 if (ur->data2_len) {
1008 ub_helper = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, ur->data2, ur->data2_len);
1009 if (!ub_helper) {
1010 uwsgi_buffer_destroy(ub_command);
1011 return UWSGI_ROUTE_BREAK;
1012 }
1013 }
1014 else {
1015 if (!uwsgi_is_file(ub_command->buf)) {
1016 uwsgi_404(wsgi_req);
1017 uwsgi_buffer_destroy(ub_command);
1018 return UWSGI_ROUTE_BREAK;
1019 }
1020
1021 if (access(ub_command->buf, X_OK)) {
1022 uwsgi_403(wsgi_req);
1023 uwsgi_buffer_destroy(ub_command);
1024 return UWSGI_ROUTE_BREAK;
1025 }
1026 }
1027 // we need a NULL suffix-ed copy of the docroot
1028 char *docroot = uwsgi_concat2n(wsgi_req->document_root, wsgi_req->document_root_len, "", 0);
1029 uwsgi_cgi_run(wsgi_req, wsgi_req->document_root, wsgi_req->document_root_len, ub_command->buf, ub_helper ? ub_helper->buf : NULL, NULL, NULL, 0, 0 );
1030 free(docroot);
1031 uwsgi_buffer_destroy(ub_command);
1032 if (ub_helper) uwsgi_buffer_destroy(ub_helper);
1033 return UWSGI_ROUTE_BREAK;
1034 }
1035
uwsgi_router_cgi_helper(struct uwsgi_route * ur,char * args)1036 static int uwsgi_router_cgi_helper(struct uwsgi_route *ur, char *args) {
1037 ur->func = uwsgi_routing_func_cgi;
1038 char *space = strchr(args, ' ');
1039 if (!space) {
1040 uwsgi_log("invalid cgihelper syntax, must be \"cgihelper:helper command\"\n");
1041 return -1;
1042 }
1043 *space = 0;
1044 ur->data = space+1;
1045 ur->data_len = strlen(space+1);
1046 ur->data2 = args;
1047 ur->data2_len = strlen(args);
1048 return 0;
1049 }
uwsgi_router_cgi(struct uwsgi_route * ur,char * args)1050 static int uwsgi_router_cgi(struct uwsgi_route *ur, char *args) {
1051 ur->func = uwsgi_routing_func_cgi;
1052 ur->data = args;
1053 ur->data_len = strlen(args);
1054 return 0;
1055 }
uwsgi_cgi_register_router()1056 static void uwsgi_cgi_register_router() {
1057 uwsgi_register_router("cgi", uwsgi_router_cgi);
1058 uwsgi_register_router("cgihelper", uwsgi_router_cgi_helper);
1059 }
1060 #endif
1061
1062 struct uwsgi_plugin cgi_plugin = {
1063
1064 .name = "cgi",
1065 .modifier1 = 9,
1066 .init = uwsgi_cgi_init,
1067 .init_apps = uwsgi_cgi_apps,
1068 .options = uwsgi_cgi_options,
1069 .request = uwsgi_cgi_request,
1070 .after_request = uwsgi_cgi_after_request,
1071 #ifdef UWSGI_ROUTING
1072 .on_load = uwsgi_cgi_register_router,
1073 #endif
1074
1075 };
1076