Use pthread_mutexes for locking the accept() and data.
[catagits/fcgi2.git] / perl / FCGI.PL
CommitLineData
1b64d24d 1use Config;
2
3open OUT, ">FCGI.xs";
4
5print "Generating FCGI.xs for Perl version $]\n";
6#unless (exists $Config{apiversion} && $Config{apiversion} >= 5.005)
7unless ($] >= 5.005) {
8 for (qw(sv_undef diehook warnhook in_eval)) {
9 print OUT "#define PL_$_ $_\n"
10 }
11}
12print OUT while <DATA>;
13close OUT;
14__END__
cebfd7c4 15/* $Id: FCGI.PL,v 1.8 1999/07/30 08:22:31 skimo Exp $ */
1b64d24d 16
17#include "EXTERN.h"
18#include "perl.h"
19#include "XSUB.h"
20
21#include "fcgiapp.h"
22
23#ifndef FALSE
24#define FALSE (0)
25#endif
26
27#ifndef TRUE
28#define TRUE (1)
29#endif
30
0074e702 31#ifdef WIN32
32#define environ _environ
33#endif
34
1b64d24d 35extern char **environ;
1b64d24d 36
37#ifdef USE_SFIO
38typedef struct
39{
40 Sfdisc_t disc;
41 FCGX_Stream *stream;
42} FCGI_Disc;
43
44static ssize_t
45sffcgiread(f, buf, n, disc)
46Sfio_t* f; /* stream involved */
47Void_t* buf; /* buffer to read into */
48size_t n; /* number of bytes to read */
49Sfdisc_t* disc; /* discipline */
50{
51 return FCGX_GetStr(buf, n, ((FCGI_Disc *)disc)->stream);
52}
53
54static ssize_t
55sffcgiwrite(f, buf, n, disc)
56Sfio_t* f; /* stream involved */
57const Void_t* buf; /* buffer to read into */
58size_t n; /* number of bytes to read */
59Sfdisc_t* disc; /* discipline */
60{
61 n = FCGX_PutStr(buf, n, ((FCGI_Disc *)disc)->stream);
62 FCGX_FFlush(((FCGI_Disc *)disc)->stream);
63 return n;
64}
65
66Sfdisc_t *
67sfdcnewfcgi(stream)
68 FCGX_Stream *stream;
69{
70 FCGI_Disc* disc;
71
72 New(1000,disc,1,FCGI_Disc);
73 if (!disc) return (Sfdisc_t *)disc;
74
75 disc->disc.exceptf = (Sfexcept_f)NULL;
76 disc->disc.seekf = (Sfseek_f)NULL;
77 disc->disc.readf = sffcgiread;
78 disc->disc.writef = sffcgiwrite;
79 disc->stream = stream;
80 return (Sfdisc_t *)disc;
81}
82
83Sfdisc_t *
84sfdcdelfcgi(disc)
85 Sfdisc_t* disc;
86{
87 Safefree(disc);
88 return 0;
89}
90#endif
91
90a18d65 92static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: FCGI */
93
94static FCGX_Request global_fcgx_request;
95
96typedef struct FCGP_Request {
97 int compat;
98 int acceptCalled;
99 int finishCalled;
100 SV* svin;
101 SV* svout;
102 SV* sverr;
eede4b76 103 GV* gv[3];
cebfd7c4 104 GV* gvNew[3];
34bfd355 105 HV* hvEnv;
90a18d65 106 FCGX_Stream* in;
eede4b76 107 FCGX_ParamArray envp;
90a18d65 108 FCGX_Request* requestPtr;
2c38ecd7 109#ifdef USE_SFIO
110 int sfcreated[3];
111#endif
90a18d65 112} FCGP_Request;
113
114static FCGP_Request global_request;
115static SV* global_sverr;
1b64d24d 116
117static int
90a18d65 118FCGI_Flush(FCGP_Request* request)
1b64d24d 119{
eede4b76 120 if(!request->acceptCalled || isCGI) {
1b64d24d 121 return;
122 }
123#ifdef USE_SFIO
2c38ecd7 124 sfsync(IoOFP(GvIOp(request->gv[1])));
125 sfsync(IoOFP(GvIOp(request->gv[2])));
1b64d24d 126#else
90a18d65 127 FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->svout)));
128 FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->sverr)));
1b64d24d 129#endif
130}
131
34bfd355 132static void
133FCGI_UndoBinding(FCGP_Request* request)
134{
135#ifdef USE_SFIO
2c38ecd7 136 IO *io[3];
137 int i;
138#endif
139
140#ifdef USE_SFIO
141 sfdcdelfcgi(sfdisc(IoIFP(io[0] = GvIOp(request->gv[0])), SF_POPDISC));
142 sfdcdelfcgi(sfdisc(IoOFP(io[1] = GvIOp(request->gv[1])), SF_POPDISC));
143 sfdcdelfcgi(sfdisc(IoOFP(io[2] = GvIOp(request->gv[2])), SF_POPDISC));
144 for (i = 0; i < 3; ++i) {
145 if (request->sfcreated[i]) {
146 sfclose(IoIFP(io[i]));
147 IoIFP(io[i]) = IoOFP(io[i]) = Nullfp;
148 request->sfcreated[i] = FALSE;
149 }
150 }
34bfd355 151#else
152 FCGI_Flush(request);
153 sv_unmagic((SV *)request->gv[0], 'q');
154 sv_unmagic((SV *)request->gv[1], 'q');
155 sv_unmagic((SV *)request->gv[2], 'q');
156#endif
157}
158
1b64d24d 159static int
cebfd7c4 160FCGI_Accept(FCGP_Request* request)
1b64d24d 161{
90a18d65 162 if(isCGI == -1) {
1b64d24d 163 /*
164 * First call to FCGI_Accept. Is application running
165 * as FastCGI or as CGI?
166 */
167 isCGI = FCGX_IsCGI();
168 } else if(isCGI) {
169 /*
170 * Not first call to FCGI_Accept and running as CGI means
171 * application is done.
172 */
173 return(EOF);
eede4b76 174 }
175 if(request->acceptCalled && !request->finishCalled) {
34bfd355 176 FCGI_UndoBinding(request);
1b64d24d 177 }
178 if(!isCGI) {
2c38ecd7 179#ifdef USE_SFIO
180 IO *io[3];
181 int i;
182#endif
1b64d24d 183 FCGX_Stream *out, *error;
eede4b76 184 int acceptResult = FCGX_Accept_r(&request->in, &out, &error,
185 &request->envp,
90a18d65 186 request->requestPtr);
1b64d24d 187 if(acceptResult < 0) {
188 return acceptResult;
189 }
190#ifdef USE_SFIO
2c38ecd7 191 for (i = 0; i < 3; ++i) {
cebfd7c4 192 io[i] = GvIOn(request->gv[i] = request->gvNew[i]);
2c38ecd7 193 if (!(i == 0 ? IoIFP(io[i]) : IoOFP(io[i]))) {
194 IoIFP(io[i]) = sftmp(0);
195 /*IoIFP(io[i]) = sfnew(NULL, NULL, SF_UNBOUND, 0,
196 SF_STRING | (i ? SF_WRITE : SF_READ));*/
197 if (i != 0) IoOFP(io[i]) = IoIFP(io[i]);
198 request->sfcreated[i] = TRUE;
199 }
200 }
201 sfdisc(IoIFP(io[0]), sfdcnewfcgi(request->in));
202 sfdisc(IoOFP(io[1]), sfdcnewfcgi(out));
203 sfdisc(IoOFP(io[2]), sfdcnewfcgi(error));
1b64d24d 204#else
90a18d65 205 if (!request->svout) {
eede4b76 206 newSVrv(request->svout = newSV(0), "FCGI::Stream");
eede4b76 207 newSVrv(request->sverr = newSV(0), "FCGI::Stream");
eede4b76 208 newSVrv(request->svin = newSV(0), "FCGI::Stream");
1b64d24d 209 }
cebfd7c4 210 sv_magic((SV *)request->gv[1] = request->gvNew[1],
211 request->svout, 'q', Nullch, 0);
212 sv_magic((SV *)request->gv[2] = request->gvNew[2],
213 request->sverr, 'q', Nullch, 0);
214 sv_magic((SV *)request->gv[0] = request->gvNew[0],
215 request->svin, 'q', Nullch, 0);
90a18d65 216 sv_setiv(SvRV(request->svout), (IV) out);
217 sv_setiv(SvRV(request->sverr), (IV) error);
218 sv_setiv(SvRV(request->svin), (IV) request->in);
219
220 if (request->compat) {
221 global_sverr = request->sverr;
6ef7789b 222 if (PL_warnhook) SvREFCNT_dec(PL_warnhook);
223 PL_warnhook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN")));
6ef7789b 224 if (PL_diehook) SvREFCNT_dec(PL_diehook);
225 PL_diehook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE")));
226 }
1b64d24d 227#endif
90a18d65 228 request->finishCalled = FALSE;
1b64d24d 229 }
90a18d65 230 request->acceptCalled = TRUE;
1b64d24d 231 return 0;
232}
233
234static void
90a18d65 235FCGI_Finish(FCGP_Request* request)
1b64d24d 236{
90a18d65 237 if(!request->acceptCalled || isCGI) {
1b64d24d 238 return;
239 }
34bfd355 240 FCGI_UndoBinding(request);
90a18d65 241 request->in = NULL;
242 FCGX_Finish_r(request->requestPtr);
243 request->finishCalled = TRUE;
244#ifndef USE_SFIO
245 if (request->compat) {
246 if (PL_warnhook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN"))) {
247 SvREFCNT_dec(PL_warnhook);
248 PL_warnhook = Nullsv;
249 }
250 if (PL_diehook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE"))) {
251 SvREFCNT_dec(PL_diehook);
252 PL_diehook = Nullsv;
253 }
1b64d24d 254 }
255#endif
256}
257
258static int
90a18d65 259FCGI_StartFilterData(FCGP_Request* request)
260{
261 return request->in ? FCGX_StartFilterData(request->in) : -1;
262}
263
264static void
265FCGI_SetExitStatus(FCGP_Request* request, int status)
266{
267 if (request->in) FCGX_SetExitStatus(status, request->in);
268}
269
270static FCGP_Request *
271FCGI_Request()
1b64d24d 272{
90a18d65 273 FCGX_Request* fcgx_req;
274 FCGP_Request* req;
275
276 Newz(551, fcgx_req, 1, FCGX_Request);
277 Newz(551, req, 1, FCGP_Request);
278 req->requestPtr = fcgx_req;
279
280 return req;
1b64d24d 281}
282
283static void
90a18d65 284FCGI_Release_Request(FCGP_Request *req)
1b64d24d 285{
90a18d65 286 Safefree(req->requestPtr);
287 Safefree(req);
1b64d24d 288}
289
34bfd355 290static void
291populate_env(envp, hv)
292char **envp;
293HV *hv;
294{
295 int i;
296 char *p, *p1;
297 SV *sv;
298
299 for(i = 0; ; i++) {
300 if((p = envp[i]) == NULL) {
301 break;
302 }
303 p1 = strchr(p, '=');
304 assert(p1 != NULL);
305 sv = newSVpv(p1 + 1, 0);
306 /* call magic for this value ourselves */
307 hv_store(hv, p, p1 - p, sv, 0);
308 SvSETMAGIC(sv);
309 }
310}
311
1b64d24d 312/*
313 * For each variable in the array envp, either set or unset it
314 * in the global hash %ENV.
315 */
316static void
317DoPerlEnv(envp, set)
318char **envp;
319int set;
320{
1b64d24d 321 if (!set)
322 perl_eval_pv("%ENV = %FCGI::ENV", 0);
323 else {
324 perl_eval_pv("%FCGI::ENV = %ENV", 0);
34bfd355 325 populate_env(envp, perl_get_hv("ENV", TRUE));
1b64d24d 326 }
327}
328
eede4b76 329#define REQUEST_ARG(arg,request) \
330 if (sv_isa(ST(arg), "FCGI")) { \
331 request = (FCGP_Request*) SvIV((SV*)SvRV(ST(arg))); \
332 } else \
333 croak("request is not of type FCGI")
1b64d24d 334
eede4b76 335typedef FCGX_Stream * FCGI__Stream;
336typedef FCGP_Request * FCGI;
1b64d24d 337
338MODULE = FCGI PACKAGE = FCGI
339
90a18d65 340BOOT:
341 FCGX_Init();
342 FCGX_InitRequest(&global_fcgx_request);
343 memset(&global_request, 0, sizeof(global_request));
344 global_request.compat = 1;
345 global_request.requestPtr = &global_fcgx_request;
cebfd7c4 346 global_request.gvNew[0] = gv_fetchpv("STDIN",TRUE, SVt_PVIO);
347 global_request.gvNew[1] = gv_fetchpv("STDOUT",TRUE, SVt_PVIO);
348 global_request.gvNew[2] = gv_fetchpv("STDERR",TRUE, SVt_PVIO);
eede4b76 349
350
351SV *
352request()
353
354 PROTOTYPE:
355 CODE:
356 RETVAL = Perl_sv_setref_pv(Perl_newSV(0), "FCGI", FCGI_Request());
357
358 OUTPUT:
359 RETVAL
360
361
362int
363accept(...)
364
365 PROTOTYPE: ;$***$
366
367 PREINIT:
368 FCGP_Request* request = &global_request;
369
370 CODE:
371 if (items != 0 && items != 5)
372 croak("Usage: FCGI::accept() or "
373 "FCGI::accept(request, IN, OUT, ERR, env)");
374 if (items) {
375 static const char* names[] = {"", "IN", "OUT", "ERR"};
376 int i;
377
378 REQUEST_ARG(0,request);
379 for(i = 1; i <= 3; ++i) {
380 if (SvROK(ST(i)) && isGV(SvRV(ST(i)))) {
cebfd7c4 381 request->gvNew[i-1] = (GV*)SvRV(ST(i));
eede4b76 382 } else
383 croak("%s is not a GLOB reference", names[i]);
384 }
34bfd355 385 if (SvROK(ST(4)) && SvTYPE(SvRV(ST(4))) == SVt_PVHV) {
386 request->hvEnv = (HV*)SvRV(ST(4));
387 } else
388 croak("env is not a reference to a hash");
eede4b76 389 }
390 {
eede4b76 391 int acceptStatus;
392 /*
393 * Unmake Perl variable settings for the request just completed.
394 */
395 if(request->envp != NULL) {
396 DoPerlEnv(request->envp, FALSE);
397 request->envp = NULL;
398 }
399 /*
400 * Call FCGI_Accept but preserve environ.
401 */
cebfd7c4 402 acceptStatus = FCGI_Accept(request);
eede4b76 403 /*
404 * Make Perl variable settings for the new request.
405 */
34bfd355 406 if(acceptStatus >= 0 && !isCGI) {
407 if (request->compat)
408 DoPerlEnv(request->envp, TRUE);
409 else {
410 populate_env(request->envp, request->hvEnv);
411 request->envp = NULL;
412 }
eede4b76 413 } else {
414 request->envp = NULL;
415 }
416 RETVAL = acceptStatus;
417 }
418 OUTPUT:
419 RETVAL
420
421
422void
423finish(...)
424
425 PROTOTYPE: ;$
426
427 PREINIT:
428 FCGP_Request* request = &global_request;
429
430 CODE:
431 if (items != 0 && items != 1)
432 croak("Usage: FCGI::finish() or "
433 "FCGI::finish(request)");
434 if (items) {
435 REQUEST_ARG(0,request);
436 }
437 {
438 /*
439 * Unmake Perl variable settings for the completed request.
440 */
441 if(request->envp != NULL) {
442 DoPerlEnv(request->envp, FALSE);
443 request->envp = NULL;
444 }
445 /*
446 * Finish the request.
447 */
448 FCGI_Finish(request);
449 }
450
451
452void
453flush(...)
454
455 PROTOTYPE: ;$
456
457 PREINIT:
458 FCGP_Request* request = &global_request;
459
460 CODE:
461 if (items != 0 && items != 1)
462 croak("Usage: FCGI::flush([request])");
463 if (items) {
464 REQUEST_ARG(0,request);
465 }
466 FCGI_Flush(request);
467
468void
cebfd7c4 469set_exit_status(...)
eede4b76 470
471 PROTOTYPE: $;$
472
473 PREINIT:
474 FCGP_Request* request = &global_request;
cebfd7c4 475 int status;
eede4b76 476
477 CODE:
478 if (items != 1 && items != 2)
479 croak("Usage: FCGI::set_exit_status(status[,request])");
480 if (items == 2) {
cebfd7c4 481 REQUEST_ARG(0,request);
eede4b76 482 }
cebfd7c4 483 status = (int)SvIV(ST(items-1));
eede4b76 484 FCGI_SetExitStatus(request, status);
485
486int
487start_filter_data(...)
488
489 PROTOTYPE: ;$
490
491 PREINIT:
492 FCGP_Request* request = &global_request;
493
494 CODE:
495 if (items != 0 && items != 1)
496 croak("Usage: FCGI::flush([request])");
497 if (items) {
498 REQUEST_ARG(0,request);
499 }
500 RETVAL = FCGI_StartFilterData(request);
501
502 OUTPUT:
503 RETVAL
504
505
506void
507DESTROY(request)
508 FCGI request;
509
510 CODE:
511 FCGI_Release_Request(request);
512
513
514
515MODULE = FCGI PACKAGE = FCGI::Stream
90a18d65 516
1b64d24d 517#ifndef USE_SFIO
518void
519DIE(msg)
520 char * msg;
521
522 CODE:
cebfd7c4 523 if (!PL_in_eval) /* ? maybe !(PL_in_eval & 1) */
90a18d65 524 FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
1b64d24d 525
526void
527WARN(msg)
528 char * msg;
529
530 CODE:
90a18d65 531 FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
1b64d24d 532
533void
534PRINT(stream, ...)
eede4b76 535 FCGI::Stream stream;
1b64d24d 536
537 PREINIT:
538 int n;
539
540 CODE:
541 for (n = 1; n < items; ++n) {
542 STRLEN len;
543 register char *tmps = (char *)SvPV(ST(n),len);
544 FCGX_PutStr(tmps, len, stream);
545 }
546 if (SvTRUEx(perl_get_sv("|", FALSE)))
547 FCGX_FFlush(stream);
548
549int
550WRITE(stream, bufsv, len, ...)
eede4b76 551 FCGI::Stream stream;
1b64d24d 552 SV * bufsv;
553 int len;
554
555 PREINIT:
556 int offset;
557 char * buf;
558 STRLEN blen;
559 int n;
560
561 CODE:
562 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
563 buf = SvPV(bufsv, blen);
564 if (offset < 0) offset += blen;
565 if (len > blen - offset)
566 len = blen - offset;
567 if (offset < 0 || offset >= blen ||
568 (n = FCGX_PutStr(buf+offset, len, stream)) < 0)
569 ST(0) = &PL_sv_undef;
570 else {
571 ST(0) = sv_newmortal();
572 sv_setpvf(ST(0), "%c", n);
573 }
574
575int
576READ(stream, bufsv, len, ...)
eede4b76 577 FCGI::Stream stream;
1b64d24d 578 SV * bufsv;
579 int len;
580
581 PREINIT:
582 int offset;
583 char * buf;
584
585 CODE:
586 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
587 if (! SvOK(bufsv))
588 sv_setpvn(bufsv, "", 0);
589 buf = SvGROW(bufsv, len+offset+1);
590 len = FCGX_GetStr(buf+offset, len, stream);
591 SvCUR_set(bufsv, len+offset);
592 *SvEND(bufsv) = '\0';
593 (void)SvPOK_only(bufsv);
594 SvSETMAGIC(bufsv);
595 RETVAL = len;
596
597 OUTPUT:
598 RETVAL
599
600SV *
601GETC(stream)
eede4b76 602 FCGI::Stream stream;
1b64d24d 603
604 PREINIT:
605 int retval;
606
607 CODE:
608 if ((retval = FCGX_GetChar(stream)) != -1) {
609 ST(0) = sv_newmortal();
610 sv_setpvf(ST(0), "%c", retval);
611 } else ST(0) = &PL_sv_undef;
612
613bool
614CLOSE(stream)
eede4b76 615 FCGI::Stream stream;
1b64d24d 616
617 ALIAS:
618 DESTROY = 1
619
620 CODE:
621 RETVAL = FCGX_FClose(stream) != -1;
622
623 OUTPUT:
624 RETVAL
625
626#endif