daveola has asked for the wisdom of the Perl Monks concerning the following question:

I am using the perlembed examples for embedding perl into C code.

The perl code uses 'exit' in places in order to exit the perl code, but the *perl* exit in eval_pv evidently does an 'exit' in the C program and it kills my program entirely.

How can I have embedded perl that can exit (and hence do things like flush filehandles, call END blocks, etc..) but not exit the C main program?

I found the ability to catch/redefine *CORE::GLOBAL::exit, but that just means I don't exit the perl eval anymore.

Is this possible? i.e., keep eval_pv from exiting my C program but still "exit" the perl code?

To clarify, I've edited the test program from @bliako. We are simply embedding perl code that does:

sub doubler {
 return $_[0] * 2 if $#_ == 0;
 print STDERR "doubler() Error, wrong number of args\n";
 exit(-1);
}
print "Start perl code\n";
print "Doubler of 42: ".doubler(42)."\n";
print "Doubler of <missing arg>: ".doubler()."\n";    ## oops!  called doubler(<num>) wrong!
print "Done\n";
The error created in the second call to doubler causes an exit.

But this exit in the perl code is causing the C code to exit, which I do not want.
I can't replace the exit with a 'return' because I want the perl code to exit, just not the C code.
I can't catch exit to keep it from exiting, because then the perl code keeps running.
And finally, what if I wanted to interpret a perl script? Then I don't have control over whether it wants to exit the perl code

So how do I get exit() to exit perl but not C?

Here's the full test code you can run:

#include <stdio.h>
#include <signal.h>
#include <EXTERN.h> /* from the Perl distribution     */
#include <perl.h> /* from the Perl distribution     */

static PerlInterpreter *my_perl;  /***    The Perl interpreter    ***/

void cleanup(void);
//let perl handle it
void signal_handler(int signum){ printf("got signal %d\n", signum); }

int main(int argc, char **argv, char **env){
  signal(SIGINT, signal_handler);
  PERL_SYS_INIT3(&argc,&argv,&env);
  my_perl = perl_alloc();
  perl_construct(my_perl);
  PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
  char *embedding[] = { "", "-e", "0" };
  perl_parse(my_perl, NULL, 3, embedding, NULL);

  printf("\n----- RUNNING\n");

  const char perlcode[] =
"sub doubler {"
" return $_[0] * 2 if $#_ == 0;"
" print STDERR \"doubler() Error, wrong number of args\\n\";"
" exit(-1);"
"}"
"print \"Start perl code\\n\";"
"print \"Doubler of 42: \".doubler(42).\"\\n\";"
"print \"Doubler of <missing arg>: \".doubler().\"\\n\";"
"print \"Done\\n\";"
;

  SV *ret = eval_pv(perlcode, FALSE);
  printf("\n----- DONE RUNNING\n");
  if( SvTRUE(ERRSV) ){ fprintf(stderr, "%s : eval_sv() has failed with:\n%s\nfor the code:\n%s\n", argv[0], SvPVx_nolen(ERRSV), perlcode); cleanup(); exit(1); }

  printf("%s : done.\n", argv[0]);
  exit(EXIT_SUCCESS);
}
void cleanup(void){
  perl_destruct(my_perl);
  perl_free(my_perl);
  PERL_SYS_TERM();
}
  • Comment on Embedded perl: allowing 'exit' in eval_pv without exiting C program

Replies are listed 'Best First'.
Re: Embedded perl: allowing 'exit' in eval_pv without exiting C program
by bliako (Prior) on Mar 25, 2021 at 11:50 UTC

    Use return(0); instead of exit(0); for "exiting" Perl's main.

    The problem remaining is to be able to exit the Perl script from a Perl sub whose exit() calls you do not control (as opposed from Perl main). Perhaps with a signal trap? or redefining exit() if at all possible?

    Here is a simple test perl-embed program to harness:

    /* harness for embedding Perl into C modified by Bliako from https://perldoc.perl.org/perlembed.html Compile: $(perl -MConfig -e 'print $Config{cc}') perl_embed_simple.c $(perl + -MExtUtils::Embed -e ccopts -e ldopts) -o perl_embed_simple 30/01/2019 */ #include <stdio.h> #include <signal.h> #include <EXTERN.h> /* from the Perl distribution */ #include <perl.h> /* from the Perl distribution */ static PerlInterpreter *my_perl; /*** The Perl interpreter ***/ void cleanup(void); //let perl handle it void signal_handler(int signum){ printf("got signal %d\n", signum); } int main(int argc, char **argv, char **env){ signal(SIGINT, signal_handler); PERL_SYS_INIT3(&argc,&argv,&env); my_perl = perl_alloc(); printf("%s : perl_alloc()\n", argv[0]); perl_construct(my_perl); printf("%s : perl_construct()\n", argv[0]); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; char *embedding[] = { "", "-e", "0" }; perl_parse(my_perl, NULL, 3, embedding, NULL); printf("%s : perl_parse()\n", argv[0]); const char perlcode[] = "print \"hello world\\n\";" "return(0);" "print \"still here\";" ; printf("%s : executing :\n%s\n", argv[0], perlcode); SV *ret = eval_pv(perlcode, FALSE); if( SvTRUE(ERRSV) ){ fprintf(stderr, "%s : eval_sv() has failed wi +th:\n%s\nfor the code:\n%s\n", argv[0], SvPVx_nolen(ERRSV), perlcode) +; cleanup(); exit(1); } printf("%s : done.\n", argv[0]); exit(EXIT_SUCCESS); } void cleanup(void){ perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); }

    bw, bliako

Re: Embedded perl: allowing 'exit' in eval_pv without exiting C program
by syphilis (Bishop) on Mar 25, 2021 at 01:31 UTC
    Is this possible? i.e., keep eval_pv from exiting my C program but still "exit" the perl code?

    Interesting question.
    Could you provide a minimal script that demonstrates the issue; something that those interested can actually run ?

    Cheers,
    Rob
      I've updated my question to include an example.
Re: Embedded perl: allowing 'exit' in eval_pv without exiting C program
by ikegami (Pope) on Mar 26, 2021 at 19:42 UTC

    die should be used in that situation.

    But let's say you can't edit the Perl code. Instead of evaluating the code directly, call myeval (defined below) with the the string to evaluate as an argument.

    our $override_exit = 0; BEGIN { *CORE::GLOBAL::exit = sub(;$) { no warnings qw( exiting ); last EXIT_OVERRIDE if $override_exit; CORE::exit($_[0] // 0); }; } sub myeval { my $exit_was_called = 1; EXIT_OVERRIDE: { local $override_exit = 1; eval($_[0]); die $@ if $@; $exit_was_called = 0; } die("Exit called\n") if $exit_was_called; }

    Seeking work! You can reach me at ikegami@adaelis.com

      But then what happens if they call exit(0)? Then we die() and get a failure from the eval.

      I suppose I can just catch that and not use the die()??

      I hate the idea of another layer of eval, but I guess that's what I need at this point?

        die("Exit called\n") is a placeholder for however you want to handle it, though it does allow the XS code to handle the situation (since XS can catch exceptions).

        Seeking work! You can reach me at ikegami@adaelis.com

Re: Embedded perl: allowing 'exit' in eval_pv without exiting C program
by afoken (Canon) on Mar 25, 2021 at 11:39 UTC
    [...] calls 'exit' in the C program [...]

    Perl's exit or C's exit?

    Preventing the latter from terminating perl will be quite hard ...

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      We're doing an exit() in perl, but it's causing exit to be called on the C code. That's the whole problem.
Re: Embedded perl: allowing 'exit' in eval_pv without exiting C program
by Anonymous Monk on Mar 25, 2021 at 11:50 UTC
      I'm not sure what you're suggesting here - this is C code, no? We don't have a my_exit in perl, it looks like this lets us call exit on perl from C, which isn't what I want, but maybe I'm reading it wrong?