1 /*
2  * Copyright (c) 2000 Charles Ying. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the same terms as sendmail itself.
6  *
7  */
8 
9 #include "EXTERN.h"
10 #include "perl.h"
11 #include "XSUB.h"
12 #include <pthread.h>
13 
14 #include "intpools.h"
15 
16 #include "libmilter/mfapi.h"
17 
18 /* Keys for each callback for the register callback hash */
19 
20 #define KEY_CONNECT	newSVpv("connect", 0)
21 #define KEY_HELO	newSVpv("helo", 0)
22 #define KEY_ENVFROM	newSVpv("envfrom", 0)
23 #define KEY_ENVRCPT	newSVpv("envrcpt", 0)
24 #define KEY_HEADER	newSVpv("header", 0)
25 #define KEY_EOH		newSVpv("eoh", 0)
26 #define KEY_BODY	newSVpv("body", 0)
27 #define KEY_EOM		newSVpv("eom", 0)
28 #define KEY_ABORT	newSVpv("abort", 0)
29 #define KEY_CLOSE	newSVpv("close", 0)
30 
31 /* Macro for pushing the SMFICTX * argument */
32 
33 #define XPUSHs_Sendmail_Milter_Context	\
34 	(XPUSHs(sv_2mortal(sv_setref_iv(NEWSV(25, 0), \
35 		"Sendmail::Milter::Context", (IV) ctx))))
36 
37 /* Global callback variable names */
38 
39 #define GLOBAL_CONNECT		"Sendmail::Milter::Callbacks::_xxfi_connect"
40 #define GLOBAL_HELO		"Sendmail::Milter::Callbacks::_xxfi_helo"
41 #define GLOBAL_ENVFROM		"Sendmail::Milter::Callbacks::_xxfi_envfrom"
42 #define GLOBAL_ENVRCPT		"Sendmail::Milter::Callbacks::_xxfi_envrcpt"
43 #define GLOBAL_HEADER		"Sendmail::Milter::Callbacks::_xxfi_header"
44 #define GLOBAL_EOH		"Sendmail::Milter::Callbacks::_xxfi_eoh"
45 #define GLOBAL_BODY		"Sendmail::Milter::Callbacks::_xxfi_body"
46 #define GLOBAL_EOM		"Sendmail::Milter::Callbacks::_xxfi_eom"
47 #define GLOBAL_ABORT		"Sendmail::Milter::Callbacks::_xxfi_abort"
48 #define GLOBAL_CLOSE		"Sendmail::Milter::Callbacks::_xxfi_close"
49 
50 
51 /* Callback prototypes for first-level callback wrappers. */
52 
53 sfsistat hook_connect(SMFICTX *, char *, _SOCK_ADDR *);
54 sfsistat hook_helo(SMFICTX *, char *);
55 sfsistat hook_envfrom(SMFICTX *, char **);
56 sfsistat hook_envrcpt(SMFICTX *, char **);
57 sfsistat hook_header(SMFICTX *, char *, char *);
58 sfsistat hook_eoh(SMFICTX *);
59 sfsistat hook_body(SMFICTX *, u_char *, size_t);
60 sfsistat hook_eom(SMFICTX *);
61 sfsistat hook_abort(SMFICTX *);
62 sfsistat hook_close(SMFICTX *);
63 
64 
65 /* A structure for housing callbacks and their mutexes. */
66 
67 struct callback_cache_t
68 {
69 	SV *xxfi_connect;
70 	SV *xxfi_helo;
71 	SV *xxfi_envfrom;
72 	SV *xxfi_envrcpt;
73 	SV *xxfi_header;
74 	SV *xxfi_eoh;
75 	SV *xxfi_body;
76 	SV *xxfi_eom;
77 	SV *xxfi_abort;
78 	SV *xxfi_close;
79 };
80 
81 typedef struct callback_cache_t callback_cache_t;
82 
83 
84 /* The Milter perl interpreter pool */
85 
86 static intpool_t I_pool;
87 
88 
89 /* Routines for managing callback caches */
90 
91 void
init_callback_cache(pTHX_ interp_t * interp)92 init_callback_cache(pTHX_ interp_t *interp)
93 {
94 	callback_cache_t *cache_ptr;
95 
96 	if (interp->cache != NULL)
97 		return;
98 
99 	alloc_interpreter_cache(interp, sizeof(callback_cache_t));
100 
101 	cache_ptr = (callback_cache_t *)interp->cache;
102 
103 	cache_ptr->xxfi_connect =	get_sv(GLOBAL_CONNECT,	FALSE);
104 	cache_ptr->xxfi_helo =		get_sv(GLOBAL_HELO,	FALSE);
105 	cache_ptr->xxfi_envfrom =	get_sv(GLOBAL_ENVFROM,	FALSE);
106 	cache_ptr->xxfi_envrcpt =	get_sv(GLOBAL_ENVRCPT,	FALSE);
107 	cache_ptr->xxfi_header =	get_sv(GLOBAL_HEADER,	FALSE);
108 	cache_ptr->xxfi_eoh =		get_sv(GLOBAL_EOH,	FALSE);
109 	cache_ptr->xxfi_body =		get_sv(GLOBAL_BODY,	FALSE);
110 	cache_ptr->xxfi_eom =		get_sv(GLOBAL_EOM,	FALSE);
111 	cache_ptr->xxfi_abort =		get_sv(GLOBAL_ABORT,	FALSE);
112 	cache_ptr->xxfi_close =		get_sv(GLOBAL_CLOSE,	FALSE);
113 }
114 
115 
116 /* Set global variables in the parent interpreter. */
117 
118 void
init_callback(char * var_name,SV * parent_callback)119 init_callback(char *var_name, SV *parent_callback)
120 {
121 	SV *new_sv;
122 
123 	new_sv = get_sv(var_name, TRUE);
124 	sv_setsv(new_sv, parent_callback);
125 }
126 
127 
128 /* Main interfaces. */
129 
130 void
init_callbacks(max_interpreters,max_requests)131 init_callbacks(max_interpreters, max_requests)
132 	int max_interpreters;
133 	int max_requests;
134 {
135 	init_interpreters(&I_pool, max_interpreters, max_requests);
136 }
137 
138 
139 SV *
get_callback(perl_desc,key)140 get_callback(perl_desc, key)
141 	HV *perl_desc;
142 	SV *key;
143 {
144 	HE *entry;
145 
146 	entry = hv_fetch_ent(perl_desc, key, 0, 0);
147 
148 	if (entry == NULL)
149 		croak("couldn't fetch callback symbol from descriptor.");
150 
151 	return newSVsv(HeVAL(entry));
152 }
153 
154 
155 void
register_callbacks(desc,name,my_callback_table,flags)156 register_callbacks(desc, name, my_callback_table, flags)
157 	struct smfiDesc		*desc;
158 	char			*name;
159 	HV			*my_callback_table;
160 	int			flags;
161 {
162 	memset(desc, '\0', sizeof(struct smfiDesc));
163 
164 	desc->xxfi_name = strdup(name);
165 	desc->xxfi_version = SMFI_VERSION;
166 	desc->xxfi_flags = flags;
167 
168 	if (hv_exists_ent(my_callback_table, KEY_CONNECT, 0))
169 	{
170 		init_callback(GLOBAL_CONNECT,
171 			get_callback(my_callback_table, KEY_CONNECT));
172 
173 		desc->xxfi_connect =	hook_connect;
174 	}
175 
176 	if (hv_exists_ent(my_callback_table, KEY_HELO, 0))
177 	{
178 		init_callback(GLOBAL_HELO,
179 			get_callback(my_callback_table, KEY_HELO));
180 
181 		desc->xxfi_helo	=	hook_helo;
182 	}
183 
184 	if (hv_exists_ent(my_callback_table, KEY_ENVFROM, 0))
185 	{
186 		init_callback(GLOBAL_ENVFROM,
187 			get_callback(my_callback_table, KEY_ENVFROM));
188 
189 		desc->xxfi_envfrom =	hook_envfrom;
190 	}
191 
192 	if (hv_exists_ent(my_callback_table, KEY_ENVRCPT, 0))
193 	{
194 		init_callback(GLOBAL_ENVRCPT,
195 			get_callback(my_callback_table, KEY_ENVRCPT));
196 
197 		desc->xxfi_envrcpt =	hook_envrcpt;
198 	}
199 
200 	if (hv_exists_ent(my_callback_table, KEY_HEADER, 0))
201 	{
202 		init_callback(GLOBAL_HEADER,
203 			get_callback(my_callback_table, KEY_HEADER));
204 
205 		desc->xxfi_header =	hook_header;
206 	}
207 
208 	if (hv_exists_ent(my_callback_table, KEY_EOH, 0))
209 	{
210 		init_callback(GLOBAL_EOH,
211 			get_callback(my_callback_table, KEY_EOH));
212 
213 		desc->xxfi_eoh =	hook_eoh;
214 	}
215 
216 	if (hv_exists_ent(my_callback_table, KEY_BODY, 0))
217 	{
218 		init_callback(GLOBAL_BODY,
219 			get_callback(my_callback_table, KEY_BODY));
220 
221 		desc->xxfi_body =	hook_body;
222 	}
223 
224 	if (hv_exists_ent(my_callback_table, KEY_EOM, 0))
225 	{
226 		init_callback(GLOBAL_EOM,
227 			get_callback(my_callback_table, KEY_EOM));
228 
229 		desc->xxfi_eom =	hook_eom;
230 	}
231 
232 	if (hv_exists_ent(my_callback_table, KEY_ABORT, 0))
233 	{
234 		init_callback(GLOBAL_ABORT,
235 			get_callback(my_callback_table, KEY_ABORT));
236 
237 		desc->xxfi_abort =	hook_abort;
238 	}
239 
240 	if (hv_exists_ent(my_callback_table, KEY_CLOSE, 0))
241 	{
242 		init_callback(GLOBAL_CLOSE,
243 			get_callback(my_callback_table, KEY_CLOSE));
244 
245 		desc->xxfi_close =	hook_close;
246 	}
247 }
248 
249 
250 /* Second-layer callbacks. These do the actual work. */
251 
252 sfsistat
callback_noargs(pTHX_ SV * callback,SMFICTX * ctx)253 callback_noargs(pTHX_ SV *callback, SMFICTX *ctx)
254 {
255 	int n;
256 	sfsistat retval;
257 	dSP;
258 	ENTER;
259 	SAVETMPS;
260 	PUSHMARK(SP);
261 
262 	XPUSHs_Sendmail_Milter_Context;
263 
264 	PUTBACK;
265 
266 	n = call_sv(callback, G_EVAL | G_SCALAR);
267 
268 	SPAGAIN;
269 
270 	/* Check the eval first. */
271 	if (SvTRUE(ERRSV))
272 	{
273 		POPs;
274 		retval = SMFIS_TEMPFAIL;
275 	}
276 	else if (n == 1)
277 	{
278 		retval = (sfsistat) POPi;
279 	}
280 	else
281 	{
282 		retval = SMFIS_CONTINUE;
283 	}
284 
285 	PUTBACK;
286 	FREETMPS;
287 	LEAVE;
288 
289 	return retval;
290 }
291 
292 sfsistat
callback_s(pTHX_ SV * callback,SMFICTX * ctx,char * arg1)293 callback_s(pTHX_ SV *callback, SMFICTX *ctx, char *arg1)
294 {
295 	int n;
296 	sfsistat retval;
297 	dSP;
298 	ENTER;
299 	SAVETMPS;
300 	PUSHMARK(SP);
301 
302 	XPUSHs_Sendmail_Milter_Context;
303 	XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
304 
305 	PUTBACK;
306 
307 	n = call_sv(callback, G_EVAL | G_SCALAR);
308 
309 	SPAGAIN;
310 
311 	/* Check the eval first. */
312 	if (SvTRUE(ERRSV))
313 	{
314 		POPs;
315 		retval = SMFIS_TEMPFAIL;
316 	}
317 	else if (n == 1)
318 	{
319 		retval = (sfsistat) POPi;
320 	}
321 	else
322 	{
323 		retval = SMFIS_CONTINUE;
324 	}
325 
326 	PUTBACK;
327 	FREETMPS;
328 	LEAVE;
329 
330 	return retval;
331 }
332 
333 sfsistat
callback_body(pTHX_ SV * callback,SMFICTX * ctx,u_char * arg1,size_t arg2)334 callback_body(pTHX_ SV *callback, SMFICTX *ctx,
335 	            u_char *arg1, size_t arg2)
336 {
337 	int n;
338 	sfsistat retval;
339 	dSP;
340 	ENTER;
341 	SAVETMPS;
342 	PUSHMARK(SP);
343 
344 	XPUSHs_Sendmail_Milter_Context;
345 	XPUSHs(sv_2mortal(newSVpvn(arg1, arg2)));
346 	XPUSHs(sv_2mortal(newSViv((IV) arg2)));
347 
348 	PUTBACK;
349 
350 	n = call_sv(callback, G_EVAL | G_SCALAR);
351 
352 	SPAGAIN;
353 
354 	/* Check the eval first. */
355 	if (SvTRUE(ERRSV))
356 	{
357 		POPs;
358 		retval = SMFIS_TEMPFAIL;
359 	}
360 	else if (n == 1)
361 	{
362 		retval = (sfsistat) POPi;
363 	}
364 	else
365 	{
366 		retval = SMFIS_CONTINUE;
367 	}
368 
369 	PUTBACK;
370 	FREETMPS;
371 	LEAVE;
372 
373 	return retval;
374 }
375 
376 sfsistat
callback_argv(pTHX_ SV * callback,SMFICTX * ctx,char ** arg1)377 callback_argv(pTHX_ SV *callback, SMFICTX *ctx, char **arg1)
378 {
379 	int n;
380 	sfsistat retval;
381 	char **iter = arg1;
382 	dSP;
383 	ENTER;
384 	SAVETMPS;
385 	PUSHMARK(SP);
386 
387 	XPUSHs_Sendmail_Milter_Context;
388 
389 	while(iter != NULL)
390 	{
391 		if (*iter == NULL)
392 			break;
393 
394 		XPUSHs(sv_2mortal(newSVpv(*iter, 0)));
395 		iter++;
396 	}
397 
398 	PUTBACK;
399 
400 	n = call_sv(callback, G_EVAL | G_SCALAR);
401 
402 	SPAGAIN;
403 
404 	/* Check the eval first. */
405 	if (SvTRUE(ERRSV))
406 	{
407 		POPs;
408 		retval = SMFIS_TEMPFAIL;
409 	}
410 	else if (n == 1)
411 	{
412 		retval = (sfsistat) POPi;
413 	}
414 	else
415 	{
416 		retval = SMFIS_CONTINUE;
417 	}
418 
419 	PUTBACK;
420 	FREETMPS;
421 	LEAVE;
422 
423 	return retval;
424 }
425 
426 sfsistat
callback_ss(pTHX_ SV * callback,SMFICTX * ctx,char * arg1,char * arg2)427 callback_ss(pTHX_ SV *callback, SMFICTX *ctx, char *arg1, char *arg2)
428 {
429 	int n;
430 	sfsistat retval;
431 	dSP;
432 	ENTER;
433 	SAVETMPS;
434 	PUSHMARK(SP);
435 
436 	XPUSHs_Sendmail_Milter_Context;
437 	XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
438 	XPUSHs(sv_2mortal(newSVpv(arg2, 0)));
439 
440 	PUTBACK;
441 
442 	n = call_sv(callback, G_EVAL | G_SCALAR);
443 
444 	SPAGAIN;
445 
446 	/* Check the eval first. */
447 	if (SvTRUE(ERRSV))
448 	{
449 		POPs;
450 		retval = SMFIS_TEMPFAIL;
451 	}
452 	else if (n == 1)
453 	{
454 		retval = (sfsistat) POPi;
455 	}
456 	else
457 	{
458 		retval = SMFIS_CONTINUE;
459 	}
460 
461 	PUTBACK;
462 	FREETMPS;
463 	LEAVE;
464 
465 	return retval;
466 }
467 
468 sfsistat
callback_ssockaddr(pTHX_ SV * callback,SMFICTX * ctx,char * arg1,_SOCK_ADDR * arg_sa)469 callback_ssockaddr(pTHX_ SV *callback, SMFICTX *ctx, char *arg1,
470 		   _SOCK_ADDR *arg_sa)
471 {
472 	int n;
473 	sfsistat retval;
474 	dSP;
475 	ENTER;
476 	SAVETMPS;
477 	PUSHMARK(SP);
478 
479 	XPUSHs_Sendmail_Milter_Context;
480 
481 	XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
482 
483 	/* A Perl sockaddr_in is all we handle right now. */
484 	if (arg_sa == NULL)
485 	{
486 		XPUSHs(sv_2mortal(newSVsv(&PL_sv_undef)));
487 	}
488 	else if (arg_sa->sa_family == AF_INET)
489 	{
490 		XPUSHs(sv_2mortal(newSVpvn((char *)arg_sa,
491 					   sizeof(_SOCK_ADDR))));
492 	}
493 	else if (arg_sa->sa_family == AF_INET6)
494 	{
495 		XPUSHs(sv_2mortal(newSVpvn((char *)arg_sa,
496 				   sizeof(struct sockaddr_in6))));
497 	}
498 	else
499 	{
500 		XPUSHs(sv_2mortal(newSVsv(&PL_sv_undef)));
501 	}
502 
503 	PUTBACK;
504 
505 	n = call_sv(callback, G_EVAL | G_SCALAR);
506 
507 	SPAGAIN;
508 
509 	/* Check the eval first. */
510 	if (SvTRUE(ERRSV))
511 	{
512 		POPs;
513 		retval = SMFIS_TEMPFAIL;
514 	}
515 	else if (n == 1)
516 	{
517 		retval = (sfsistat) POPi;
518 	}
519 	else
520 	{
521 		retval = SMFIS_CONTINUE;
522 	}
523 
524 	PUTBACK;
525 	FREETMPS;
526 	LEAVE;
527 
528 	return retval;
529 }
530 
531 
532 /* First-layer callbacks */
533 
534 sfsistat
hook_connect(ctx,hostname,hostaddr)535 hook_connect(ctx, hostname, hostaddr)
536 	SMFICTX		*ctx;
537 	char		*hostname;
538 	_SOCK_ADDR	*hostaddr;
539 {
540 	interp_t *interp;
541 	sfsistat retval;
542 	SV *callback;
543 
544 	if ((interp = lock_interpreter(&I_pool)) == NULL)
545 		croak("could not lock a new perl interpreter.");
546 
547 	PERL_SET_CONTEXT(interp->perl);
548 
549 	init_callback_cache(aTHX_ interp);
550 	callback = ((callback_cache_t *)(interp->cache))->xxfi_connect;
551 
552 	retval = callback_ssockaddr(aTHX_ callback, ctx,
553 					  hostname, hostaddr);
554 
555 	unlock_interpreter(&I_pool, interp);
556 
557 	return retval;
558 }
559 
560 sfsistat
hook_helo(ctx,helohost)561 hook_helo(ctx, helohost)
562 	SMFICTX		*ctx;
563 	char		*helohost;
564 {
565 	interp_t *interp;
566 	sfsistat retval;
567 	SV *callback;
568 
569 	if ((interp = lock_interpreter(&I_pool)) == NULL)
570 		croak("could not lock a new perl interpreter.");
571 
572 	PERL_SET_CONTEXT(interp->perl);
573 
574 	init_callback_cache(aTHX_ interp);
575 	callback = ((callback_cache_t *)(interp->cache))->xxfi_helo;
576 
577 	retval = callback_s(aTHX_ callback, ctx, helohost);
578 
579 	unlock_interpreter(&I_pool, interp);
580 
581 	return retval;
582 }
583 
584 sfsistat
hook_envfrom(ctx,argv)585 hook_envfrom(ctx, argv)
586 	SMFICTX *ctx;
587 	char **argv;
588 {
589 	interp_t *interp;
590 	sfsistat retval;
591 	SV *callback;
592 
593 	if ((interp = lock_interpreter(&I_pool)) == NULL)
594 		croak("could not lock a new perl interpreter.");
595 
596 	PERL_SET_CONTEXT(interp->perl);
597 
598 	init_callback_cache(aTHX_ interp);
599 	callback = ((callback_cache_t *)(interp->cache))->xxfi_envfrom;
600 
601 	retval = callback_argv(aTHX_ callback, ctx, argv);
602 
603 	unlock_interpreter(&I_pool, interp);
604 
605 	return retval;
606 }
607 
608 sfsistat
hook_envrcpt(ctx,argv)609 hook_envrcpt(ctx, argv)
610 	SMFICTX *ctx;
611 	char **argv;
612 {
613 	interp_t *interp;
614 	sfsistat retval;
615 	SV *callback;
616 
617 	if ((interp = lock_interpreter(&I_pool)) == NULL)
618 		croak("could not lock a new perl interpreter.");
619 
620 	PERL_SET_CONTEXT(interp->perl);
621 
622 	init_callback_cache(aTHX_ interp);
623 	callback = ((callback_cache_t *)(interp->cache))->xxfi_envrcpt;
624 
625 	retval = callback_argv(aTHX_ callback, ctx, argv);
626 
627 	unlock_interpreter(&I_pool, interp);
628 
629 	return retval;
630 }
631 
632 sfsistat
hook_header(ctx,headerf,headerv)633 hook_header(ctx, headerf, headerv)
634 	SMFICTX *ctx;
635 	char *headerf;
636 	char *headerv;
637 {
638 	interp_t *interp;
639 	sfsistat retval;
640 	SV *callback;
641 
642 	if ((interp = lock_interpreter(&I_pool)) == NULL)
643 		croak("could not lock a new perl interpreter.");
644 
645 	PERL_SET_CONTEXT(interp->perl);
646 
647 	init_callback_cache(aTHX_ interp);
648 	callback = ((callback_cache_t *)(interp->cache))->xxfi_header;
649 
650 	retval = callback_ss(aTHX_ callback, ctx, headerf, headerv);
651 
652 	unlock_interpreter(&I_pool, interp);
653 
654 	return retval;
655 }
656 
657 sfsistat
hook_eoh(ctx)658 hook_eoh(ctx)
659 	SMFICTX *ctx;
660 {
661 	interp_t *interp;
662 	sfsistat retval;
663 	SV *callback;
664 
665 	if ((interp = lock_interpreter(&I_pool)) == NULL)
666 		croak("could not lock a new perl interpreter.");
667 
668 	PERL_SET_CONTEXT(interp->perl);
669 
670 	init_callback_cache(aTHX_ interp);
671 	callback = ((callback_cache_t *)(interp->cache))->xxfi_eoh;
672 
673 	retval = callback_noargs(aTHX_ callback, ctx);
674 
675 	unlock_interpreter(&I_pool, interp);
676 
677 	return retval;
678 }
679 
680 sfsistat
hook_body(ctx,bodyp,bodylen)681 hook_body(ctx, bodyp, bodylen)
682 	SMFICTX *ctx;
683 	u_char *bodyp;
684 	size_t bodylen;
685 {
686 	interp_t *interp;
687 	sfsistat retval;
688 	SV *callback;
689 
690 	if ((interp = lock_interpreter(&I_pool)) == NULL)
691 		croak("could not lock a new perl interpreter.");
692 
693 	PERL_SET_CONTEXT(interp->perl);
694 
695 	init_callback_cache(aTHX_ interp);
696 	callback = ((callback_cache_t *)(interp->cache))->xxfi_body;
697 
698 	retval = callback_body(aTHX_ callback, ctx, bodyp, bodylen);
699 
700 	unlock_interpreter(&I_pool, interp);
701 
702 	return retval;
703 }
704 
705 sfsistat
hook_eom(ctx)706 hook_eom(ctx)
707 	SMFICTX *ctx;
708 {
709 	interp_t *interp;
710 	sfsistat retval;
711 	SV *callback;
712 
713 	if ((interp = lock_interpreter(&I_pool)) == NULL)
714 		croak("could not lock a new perl interpreter.");
715 
716 	PERL_SET_CONTEXT(interp->perl);
717 
718 	init_callback_cache(aTHX_ interp);
719 	callback = ((callback_cache_t *)(interp->cache))->xxfi_eom;
720 
721 	retval = callback_noargs(aTHX_ callback, ctx);
722 
723 	unlock_interpreter(&I_pool, interp);
724 
725 	return retval;
726 }
727 
728 sfsistat
hook_abort(ctx)729 hook_abort(ctx)
730 	SMFICTX *ctx;
731 {
732 	interp_t *interp;
733 	sfsistat retval;
734 	SV *callback;
735 
736 	if ((interp = lock_interpreter(&I_pool)) == NULL)
737 		croak("could not lock a new perl interpreter.");
738 
739 	PERL_SET_CONTEXT(interp->perl);
740 
741 	init_callback_cache(aTHX_ interp);
742 	callback = ((callback_cache_t *)(interp->cache))->xxfi_abort;
743 
744 	retval = callback_noargs(aTHX_ callback, ctx);
745 
746 	unlock_interpreter(&I_pool, interp);
747 
748 	return retval;
749 }
750 
751 sfsistat
hook_close(ctx)752 hook_close(ctx)
753 	SMFICTX *ctx;
754 {
755 	interp_t *interp;
756 	sfsistat retval;
757 	SV *callback;
758 
759 	if ((interp = lock_interpreter(&I_pool)) == NULL)
760 		croak("could not lock a new perl interpreter.");
761 
762 	PERL_SET_CONTEXT(interp->perl);
763 
764 	init_callback_cache(aTHX_ interp);
765 	callback = ((callback_cache_t *)(interp->cache))->xxfi_close;
766 
767 	retval = callback_noargs(aTHX_ callback, ctx);
768 
769 	unlock_interpreter(&I_pool, interp);
770 
771 	return retval;
772 }
773 
774