add config.*
[catagits/fcgi2.git] / perl / FCGI.XL
1 use Config;
2
3 open OUT, ">FCGI.xs";
4
5 print "Generating FCGI.xs for Perl version $]\n";
6 #unless (exists $Config{apiversion} && $Config{apiversion} >= 5.005) 
7 unless ($] >= 5.005) {
8     for (qw(sv_undef diehook warnhook in_eval)) {
9         print OUT "#define PL_$_ $_\n" 
10     }
11 }
12 print OUT while <DATA>;
13 close OUT;
14 __END__
15 /* $Id: FCGI.XL,v 1.6 2001/09/20 12:00:24 skimo Exp $ */
16
17 #include "EXTERN.h"
18 #include "perl.h"
19 #include "XSUB.h"
20
21 #include "fcgi_config.h"
22 #include "fcgiapp.h"
23 #include "fastcgi.h"
24
25 #ifndef FALSE
26 #define FALSE (0)
27 #endif
28
29 #ifndef TRUE
30 #define TRUE  (1)
31 #endif
32
33 #ifndef dTHX
34 #define dTHX
35 #endif
36
37 #ifdef USE_SFIO
38 typedef struct
39 {
40     Sfdisc_t    disc;
41     FCGX_Stream *stream;
42 } FCGI_Disc;
43
44 static ssize_t
45 sffcgiread(f, buf, n, disc)
46 Sfio_t*         f;      /* stream involved */
47 Void_t*         buf;    /* buffer to read into */
48 size_t          n;      /* number of bytes to read */
49 Sfdisc_t*       disc;   /* discipline */
50 {
51     return FCGX_GetStr(buf, n, ((FCGI_Disc *)disc)->stream);
52 }
53
54 static ssize_t
55 sffcgiwrite(f, buf, n, disc)
56 Sfio_t*         f;      /* stream involved */
57 const Void_t*   buf;    /* buffer to read into */
58 size_t          n;      /* number of bytes to read */
59 Sfdisc_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
66 Sfdisc_t *
67 sfdcnewfcgi(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
83 Sfdisc_t *
84 sfdcdelfcgi(disc)
85     Sfdisc_t*   disc;
86 {
87     Safefree(disc);
88     return 0;
89 }
90 #endif
91
92 #if defined(USE_LOCKING) && defined(USE_THREADS)
93 static perl_mutex   accept_mutex;
94 #endif
95
96 typedef struct FCGP_Request {
97     int             accepted;
98     int             bound;
99     SV*             svin;
100     SV*             svout;
101     SV*             sverr;
102     GV*             gv[3];
103     HV*             hvEnv;
104     FCGX_Request*   requestPtr;
105 #ifdef USE_SFIO
106     int             sfcreated[3];
107     IO*             io[3];
108 #endif
109 } FCGP_Request;
110
111 static void FCGI_Finish(FCGP_Request* request);
112
113 static void 
114 FCGI_Flush(FCGP_Request* request)
115 {
116     dTHX;
117
118     if(!request->bound) {
119         return;
120         }
121 #ifdef USE_SFIO
122     sfsync(IoOFP(GvIOp(request->gv[1])));
123     sfsync(IoOFP(GvIOp(request->gv[2])));
124 #else
125     FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->svout))));
126     FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->sverr))));
127 #endif
128 }
129
130 static void
131 FCGI_UndoBinding(FCGP_Request* request)
132 {
133     dTHX;
134
135 #ifdef USE_SFIO
136     sfdcdelfcgi(sfdisc(IoIFP(request->io[0]), SF_POPDISC));
137     sfdcdelfcgi(sfdisc(IoOFP(request->io[1]), SF_POPDISC));
138     sfdcdelfcgi(sfdisc(IoOFP(request->io[2]), SF_POPDISC));
139 #else
140     sv_unmagic((SV *)request->gv[0], 'q');
141     sv_unmagic((SV *)request->gv[1], 'q');
142     sv_unmagic((SV *)request->gv[2], 'q');
143 #endif
144     request->bound = FALSE;
145 }
146
147 static void
148 FCGI_Bind(FCGP_Request* request)
149 {
150     dTHX;
151
152 #ifdef USE_SFIO
153     sfdisc(IoIFP(request->io[0]), sfdcnewfcgi(request->requestPtr->in));
154     sfdisc(IoOFP(request->io[1]), sfdcnewfcgi(request->requestPtr->out));
155     sfdisc(IoOFP(request->io[2]), sfdcnewfcgi(request->requestPtr->err));
156 #else
157     sv_magic((SV *)request->gv[1], request->svout, 'q', Nullch, 0);
158     sv_magic((SV *)request->gv[2], request->sverr, 'q', Nullch, 0);
159     sv_magic((SV *)request->gv[0], request->svin, 'q', Nullch, 0);
160 #endif
161     request->bound = TRUE;
162 }
163
164 static void
165 populate_env(envp, hv)
166 char **envp;
167 HV *hv;
168 {
169     int i;
170     char *p, *p1;
171     SV   *sv;
172     dTHX;
173
174     hv_clear(hv);
175     for(i = 0; ; i++) {
176         if((p = envp[i]) == NULL) {
177             break;
178         }
179         p1 = strchr(p, '=');
180         assert(p1 != NULL);
181         sv = newSVpv(p1 + 1, 0);
182         /* call magic for this value ourselves */
183         hv_store(hv, p, p1 - p, sv, 0);
184         SvSETMAGIC(sv);
185     }
186 }
187
188 static int
189 FCGI_IsFastCGI(FCGP_Request* request)
190 {
191     static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: CGI */
192
193     if (request->requestPtr->listen_sock == FCGI_LISTENSOCK_FILENO) {
194         if (isCGI == -1)
195             isCGI = FCGX_IsCGI();
196         return !isCGI;
197     }
198
199     /* A explicit socket is being used -> assume FastCGI */
200     return 1;
201 }
202
203 static int 
204 FCGI_Accept(FCGP_Request* request)
205 {
206     dTHX;
207
208     if (!FCGI_IsFastCGI(request)) {
209         static int been_here = 0;
210
211         /*
212          * Not first call to FCGI_Accept and running as CGI means
213          * application is done.
214          */
215         if (been_here)
216             return EOF;
217
218         been_here = 1;
219     } else {
220 #ifdef USE_SFIO
221         int i;
222 #endif
223         FCGX_Request *fcgx_req = request->requestPtr;
224         int acceptResult;
225
226         FCGI_Finish(request);
227 #if defined(USE_LOCKING) && defined(USE_THREADS)
228         MUTEX_LOCK(&accept_mutex);
229 #endif
230         acceptResult = FCGX_Accept_r(fcgx_req);
231 #if defined(USE_LOCKING) && defined(USE_THREADS)
232         MUTEX_UNLOCK(&accept_mutex);
233 #endif
234         if(acceptResult < 0) {
235             return acceptResult;
236         }
237
238         populate_env(fcgx_req->envp, request->hvEnv);
239
240 #ifdef USE_SFIO
241         for (i = 0; i < 3; ++i) {
242             request->io[i] = GvIOn(request->gv[i]);
243             if (!(i == 0 ? IoIFP(request->io[i]) 
244                          : IoOFP(request->io[i]))) {
245                 IoIFP(request->io[i]) = sftmp(0);
246                 /*IoIFP(request->io[i]) = sfnew(NULL, NULL, SF_UNBOUND, 0, 
247                                      SF_STRING | (i ? SF_WRITE : SF_READ));*/
248                 if (i != 0) 
249                     IoOFP(request->io[i]) = IoIFP(request->io[i]);
250                 request->sfcreated[i] = TRUE;
251             }
252         }
253 #else
254         if (!request->svout) {
255             newSVrv(request->svout = newSV(0), "FCGI::Stream");
256             newSVrv(request->sverr = newSV(0), "FCGI::Stream");
257             newSVrv(request->svin = newSV(0), "FCGI::Stream");
258         }
259         sv_setiv(SvRV(request->svout), INT2PTR(IV, fcgx_req->out));
260         sv_setiv(SvRV(request->sverr), INT2PTR(IV, fcgx_req->err));
261         sv_setiv(SvRV(request->svin), INT2PTR(IV, fcgx_req->in));
262 #endif
263         FCGI_Bind(request);
264         request->accepted = TRUE;
265     }
266     return 0;
267 }
268
269 static void 
270 FCGI_Finish(FCGP_Request* request)
271 {
272 #ifdef USE_SFIO
273     int i;
274 #endif
275     int was_bound;
276     dTHX;
277
278     if(!request->accepted) {
279         return;
280     }
281
282     if (was_bound = request->bound) {
283         FCGI_UndoBinding(request);
284     }
285 #ifdef USE_SFIO
286     for (i = 0; i < 3; ++i) {
287         if (request->sfcreated[i]) {
288             sfclose(IoIFP(request->io[i]));
289             IoIFP(request->io[i]) = IoOFP(request->io[i]) = Nullfp;
290             request->sfcreated[i] = FALSE;
291         }
292     }
293 #endif
294     if (was_bound)
295         FCGX_Finish_r(request->requestPtr);
296     else
297         FCGX_Free(request->requestPtr, 1);
298     request->accepted = FALSE;
299 }
300
301 static int 
302 FCGI_StartFilterData(FCGP_Request* request)
303 {
304     return request->requestPtr->in ? 
305             FCGX_StartFilterData(request->requestPtr->in) : -1;
306 }
307
308 static FCGP_Request *
309 FCGI_Request(in, out, err, env, socket, flags)
310     GV*     in;
311     GV*     out;
312     GV*     err;
313     HV*     env;
314     int     socket;
315     int     flags;
316 {
317     FCGX_Request* fcgx_req;
318     FCGP_Request* req;
319
320     Newz(551, fcgx_req, 1, FCGX_Request);
321     FCGX_InitRequest(fcgx_req, socket, flags);
322     Newz(551, req, 1, FCGP_Request);
323     req->requestPtr = fcgx_req;
324     SvREFCNT_inc(in);
325     req->gv[0] = in;
326     SvREFCNT_inc(out);
327     req->gv[1] = out;
328     SvREFCNT_inc(err);
329     req->gv[2] = err;
330     SvREFCNT_inc(env);
331     req->hvEnv = env;
332
333     return req;
334 }
335
336 static void
337 FCGI_Release_Request(FCGP_Request *req)
338 {
339     SvREFCNT_dec(req->gv[0]);
340     SvREFCNT_dec(req->gv[1]);
341     SvREFCNT_dec(req->gv[2]);
342     SvREFCNT_dec(req->hvEnv);
343     FCGI_Finish(req);
344     Safefree(req->requestPtr);
345     Safefree(req);
346 }
347
348 static void
349 FCGI_Init()
350 {
351 #if defined(USE_LOCKING) && defined(USE_THREADS)
352     dTHX;
353
354     MUTEX_INIT(&accept_mutex);
355 #endif
356
357     FCGX_Init();
358 }
359
360 typedef FCGX_Stream *   FCGI__Stream;
361 typedef FCGP_Request *  FCGI;
362 typedef GV*             GLOBREF;
363 typedef HV*             HASHREF;
364
365 MODULE = FCGI           PACKAGE = FCGI      PREFIX = FCGI_
366
367 BOOT:
368     FCGI_Init();
369
370 SV *
371 RequestX(in, out, err, env, socket, flags)
372     GLOBREF in;
373     GLOBREF out;
374     GLOBREF err;
375     HASHREF env;
376     int     socket;
377     int     flags;
378
379     PROTOTYPE: ***$$$
380     CODE:
381     RETVAL = sv_setref_pv(newSV(0), "FCGI", 
382                 FCGI_Request(in, out, err, env, socket, flags));
383
384     OUTPUT:
385     RETVAL
386
387 int
388 OpenSocket(path, backlog)
389     char* path;
390     int backlog;
391
392     PROTOTYPE: $$
393     CODE:
394     RETVAL = FCGX_OpenSocket(path, backlog);
395     OUTPUT:
396     RETVAL
397
398 void
399 CloseSocket(socket)
400     int socket;
401
402     PROTOTYPE: $
403     CODE:
404     close(socket);
405
406 int
407 FCGI_Accept(request)
408
409     FCGI    request;
410
411     PROTOTYPE: $
412
413 void
414 FCGI_Finish(request)
415     FCGI    request;
416
417     PROTOTYPE: $
418
419 void
420 FCGI_Flush(request)
421     FCGI    request;
422
423     PROTOTYPE: $
424
425 HV *
426 GetEnvironment(request)
427     FCGI    request;
428
429     PROTOTYPE: $
430
431     CODE:
432     RETVAL = request->hvEnv;
433
434     OUTPUT: 
435     RETVAL
436
437 void
438 GetHandles(request)
439     FCGI    request;
440
441     PROTOTYPE: $
442
443     PREINIT:
444     int     i;
445
446     PPCODE:
447     EXTEND(sp,3);
448     for (i = 0; i < 3; ++i)
449         PUSHs(sv_2mortal(newRV((SV *) request->gv[i])));
450
451 int
452 FCGI_IsFastCGI(request)
453     FCGI    request;
454
455     PROTOTYPE: $
456
457 void
458 Detach(request)
459     FCGI    request;
460
461     PROTOTYPE: $
462
463     CODE:
464     if (request->accepted && request->bound)
465         FCGI_UndoBinding(request);
466
467 void
468 Attach(request)
469     FCGI    request;
470
471     PROTOTYPE: $
472
473     CODE:
474     if (request->accepted && !request->bound)
475         FCGI_Bind(request);
476
477 void
478 LastCall(request)
479     FCGI    request;
480
481     PROTOTYPE: $
482
483     CODE:
484     FCGX_ShutdownPending();
485
486 int
487 FCGI_StartFilterData(request)
488
489     FCGI    request;
490
491     PROTOTYPE: $
492
493 void
494 DESTROY(request)
495     FCGI    request;
496
497     CODE:
498     FCGI_Release_Request(request);
499
500
501
502 MODULE = FCGI           PACKAGE = FCGI::Stream
503
504 #ifndef USE_SFIO
505
506 void
507 PRINT(stream, ...)
508         FCGI::Stream    stream;
509
510         PREINIT:
511         int     n;
512
513         CODE:
514         for (n = 1; n < items; ++n) {
515             STRLEN len;
516             register char *tmps = (char *)SvPV(ST(n),len);
517             FCGX_PutStr(tmps, len, stream);
518         }
519         if (SvTRUEx(perl_get_sv("|", FALSE))) 
520             FCGX_FFlush(stream);
521
522 int
523 WRITE(stream, bufsv, len, ...)
524         FCGI::Stream    stream;
525         SV *    bufsv;
526         int     len;
527
528         PREINIT:
529         int     offset;
530         char *  buf;
531         STRLEN  blen;
532         int     n;
533
534         CODE:
535         offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
536         buf = SvPV(bufsv, blen);
537         if (offset < 0) offset += blen;
538         if (len > blen - offset)
539             len = blen - offset;
540         if (offset < 0 || offset >= blen ||
541                 (n = FCGX_PutStr(buf+offset, len, stream)) < 0) 
542             ST(0) = &PL_sv_undef;
543         else {
544             ST(0) = sv_newmortal();
545             sv_setpvf(ST(0), "%c", n);
546         }
547
548 int
549 READ(stream, bufsv, len, ...)
550         FCGI::Stream    stream;
551         SV *    bufsv;
552         int     len;
553
554         PREINIT:
555         int     offset;
556         char *  buf;
557
558         CODE:
559         offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
560         if (! SvOK(bufsv))
561             sv_setpvn(bufsv, "", 0);
562         buf = SvGROW(bufsv, len+offset+1);
563         len = FCGX_GetStr(buf+offset, len, stream);
564         SvCUR_set(bufsv, len+offset);
565         *SvEND(bufsv) = '\0';
566         (void)SvPOK_only(bufsv);
567         SvSETMAGIC(bufsv);
568         RETVAL = len;
569
570         OUTPUT:
571         RETVAL
572
573 SV *
574 GETC(stream)
575         FCGI::Stream    stream;
576
577         PREINIT:
578         int     retval;
579
580         CODE:
581         if ((retval = FCGX_GetChar(stream)) != -1) {
582             ST(0) = sv_newmortal();
583             sv_setpvf(ST(0), "%c", retval);
584         } else ST(0) = &PL_sv_undef;
585
586 bool
587 CLOSE(stream)
588         FCGI::Stream    stream;
589
590 #       ALIAS:
591 #       DESTROY = 1
592
593         CODE:
594         RETVAL = FCGX_FClose(stream) != -1;
595
596         OUTPUT:
597         RETVAL
598
599 #endif