Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Pass signals and argv from C to embedded Perl

by bliako (Monsignor)
on Jan 30, 2019 at 15:17 UTC ( [id://1229172]=perlquestion: print w/replies, xml ) Need Help??

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

Hello Wise Monks,

I am running a Perl script via a Perl interpreter embedded in a C program.

I can not figure out how to:

  • a) pass C's argv to Perl script as ARGV
  • b) pass any signal the C program gets, for example Ctrl-C/SIGINT, to the Perl script.

Below, there is a simplified example slightly modified from perlembed which runs a string of code via the perl -e .. thingy, as I understand it.

The first problem is with ARGV, it is not passed on. But then again the perl -e .. thingy does not pass ARGV to -e script or does it? So, my first question is there a way to pass arguments to eval_sv(). Right now I am going a round-about way of globally loading ARGV data (using an eval_pv() again). Bear in mind that the Perl code I want to run in the script is a fully-fledged Perl script, with parsing of ARGV. I want to avoid converting it into a sub.

The second problem is with the C program catching a ctrl-c and passing it on to the Perl code currently being run. If I install a sig-handler (in Perl script) which dies on receiving SIGINT, it works and prints out the message (re: commented code). But when the "ignoring-you" sig-handler is installed, I don't even get the message to print out. But the C-application is terminated.

thanks, bliako

/* harness for embedding Perl into C modified by Bliako from https://perldoc.perl.org/perlembed.html Compile: $(perl -MConfig -e 'print $Config{cc}') embedex.c $(perl -MExtUti +ls::Embed -e ccopts -e ldopts) -o embedex 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); void signal_handler(int signum){ printf("got signal %d\n", signum); } int main(int argc, char **argv, char **env){ 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; perl_parse(my_perl, NULL, 2, (char *[]){"", "-e", "1"}, (char **)N +ULL); const char perlcode[] = "#$SIG{INT} = sub { print \"Caught your ctrl-c and exiting!\"; exit(0) +; };\n" "$SIG{INT} = sub { print \"Caught your ctrl-c but ignoring it!\" };\n" "print \"$0: ARGV:\"; print ' '.$_ foreach(@ARGV); print \"\\n\";\n" "print \"my pid $$\\n\";\n" "print \"now sleeping, and you ctrl-c me\\n\";\n" "sleep(10000);\n" ; 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(); }

Replies are listed 'Best First'.
Re: Pass signals and argv from C to embedded Perl
by ikegami (Patriarch) on Jan 30, 2019 at 16:18 UTC

    I can not figure out how to pass C's argv to Perl script as ARGV

    You don't want to pass argv to the Perl interpreter; you want to pass arguments to it. That's done via 3rd and 4th argument of perl_parse.

    char** perlargv = malloc( (argc+3) * sizeof(char*) ); perlargv[0] = "perl"; perlargv[1] = "-e"; perlargv[2] = "1"; perlargv[3] = "--"; int i = argc-1; while (i--) perlargv[i+4] = argv[i+1]; perl_parse(my_perl, NULL, argc+3, perlargv, NULL);

    Don't forget to free the memory you allocated for the arguments. This should be done after freeing the Perl interpreter.


    I can not figure out how to pass any signal the C program gets, for example Ctrl-C/SIGINT, to the Perl script.

    You don't want to "pass signals"; you simply want the Perl program to handle them. That's done by setting $SIG{INT}. The system calls whatever handler was set to handle the signal, whether this was done by C code or by Perl code. You've already proved that this works.

    As for not getting the message, could you have been suffering from buffering because you didn't end your messages with a line feed?

      Cool! the ARGV method you suggested works just fine. I would not be able to figure that out from perlembed. Thanks.

      And you were right about buffering. Ending the message with a line feed fixed that. So now I get the sighandler message BUT still the Perl code terminates even if Perl's sighandler has no die() and is meant to just ignore it/print message.

      Update: just a warning that an infinite loop ensues if argc==0

      Update2: of course argc is always > 0.

      Update3: it turns out one can send parameters to perl -e ... using perl -e 'print join(",",@ARGV)."\n"' -- -a -b -c 12, ikegami adds -- before any user-specified arguments to perl.

      Update4: I have changed perlargv[i+3] to perlargv[i+4] so that -- is not overwritten.

      Update5: kschwab found out why ctrl-c kills the program (because of the long sleep, sleep(1000)),

      Update6: based on all the suggestions by ikegami and kschwab, this works for me:

      /* 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_argv_signal.c $ +(perl -MExtUtils::Embed -e ccopts -e ldopts) -o perl_embed_argv_signa +l 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** perlargv = malloc( (argc+3) * sizeof(char*) ); perlargv[0] = "perl"; perlargv[1] = "-e"; perlargv[2] = "1"; perlargv[3] = "--"; int i = argc-1; while (i--) perlargv[i+4] = argv[i+1]; perl_parse(my_perl, NULL, argc+3, perlargv, NULL); const char perlcode[] = "#$SIG{INT} = sub { print \"Caught your ctrl-c and exiting!\\n\"; exit +(0); };\n" "$SIG{INT} = sub { print \"Caught your ctrl-c but ignoring it!\\n\" }; +\n" "print \"$0: ARGV:\"; print ' '.$_ foreach(@ARGV); print \"\\n\";\n" "print \"my pid $$\\n\";\n" "print \"now sleeping, and you ctrl-c me\\n\";\n" "for(1..100){sleep(1);print\"$_\\n\";}\n" ; 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(); }

        kschwab found out why ctrl-c kills the program (because of the long sleep, sleep(1000)),

        While it should interrupt the sleep, it shouldn't cause it to exit; the message from the SIGINT handle should be printed. In your question, you said the message wasn't being printed.

        If you want an uninterruptible sleep (i.e. one that resumes after handling a signal),

        sub uninterruptible_sleep { my $sleep_until = time() + $_[0]; while (1) { my $time_left = $sleep_until - time(); return if $time_left <= 0; sleep($time_left); } }

        I have changed perlargv[i+3] to perlargv[i+4] so that -- is not overwritten.

        Fixed. I originally had -e1 as one argument (as I wold write it on the command line), but forgot to adjust the offset when I split them into two arguments.

        I've had problems linking, as there is a discrepancy between what the bash command returns for the path for an EXTERN.h and what exists on this machine.

        We also have some shell evaluations. The first expression evaluates and works just fine:

        $ (perl -MConfig -e 'print $Config{cc}') x86_64-linux-gnu-gcc$

        The second one seems to work; the problem is that I don't have an EXTERN.h exactly there:

        $ (perl -MExtUtils::Embed -e ccopts -e ldopts) -Wl,-E -fstack-protector-strong -L/usr/local/lib -L/usr/lib/x86_64-l +inux-gnu/perl/5.26/CORE -lperl -ldl -lm -lpthread -lc -lcrypt -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pip +e -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I +/usr/lib/x86_64-linux-gnu/perl/5.26/CORE $

        One thing I figured out is that it has a newline in it. The bigger problem is that I do not have a perl.h nor an EXTERN.h in this location.

        Where do I have one?

        $ locate EXTERN.h /usr/lib/x86_64-linux-gnu/perl/5.26.1/CORE/EXTERN.h $

        5.26 is just a link:

        $ pwd /usr/lib/x86_64-linux-gnu/perl $ ls -l 5.26* lrwxrwxrwx 1 root root 6 Jul 18 2018 5.26 -> 5.26.1

        I read up a bit with ExtUtils::Embed, gave this a look-see:

        perl -MExtUtils::Embed -e perl_inc  -I/usr/lib/x86_64-linux-gnu/perl/5.26/CORE

        Let's try re-ordering:

        x86_64-linux-gnu-gcc perl_embed_argv_signal.c -Wl,-E  -fstack-protector-strong  -I/usr/lib/x86_64-linux-gnu/perl/5.26.1/CORE -L/usr/local/lib  -L/usr/lib/x86_64-linux-gnu/perl/5.26.1/CORE -lperl -ldl -lm -lpthread -lc -lcrypt -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64  -o embedex

        A different linking problem:

        /usr/bin/ld: cannot find -lperl collect2: error: ld returned 1 exit status

        Then from all those years in comp.lang.c, I remember that the thing you do is s/l/lib/, so:

        Update the package index: # sudo apt-get update Install libperl-dev deb package: # sudo apt-get install libperl-dev

        Same command with necessary library:

        $ x86_64-linux-gnu-gcc perl_embed_argv_signal.c -Wl,-E -fstack-protec +tor-strong -I/usr/lib/x86_64-linux-gnu/perl/5.26.1/CORE -L/usr/local +/lib -L/usr/lib/x86_64-linux-gnu/perl/5.26.1/CORE -lperl -ldl -lm -l +pthread -lc -lcrypt -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno- +strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FIL +E_OFFSET_BITS=64 -o embedex $ ls 1.84.txt 2.analyse_text.pl embedex 1.analyse_text.pl 2.bliako.pl hs_ref_GRCh38.p12_chr20.fa 1.bliako.pl 2.bliako.txt hs_ref_GRCh38.p12_chr20.fa.3. +state 1.bliako.txt 2.create.bash hs_ref_GRCh38.p12_chr20.fa.gz 1.c_calls_perl.c 3.bliako.txt Markov 1.em_perl.c 84-0.txt perl_embed_argv_signal.c 1.manifest 84.state 'Untitled Document 1' 1.predict.pl bliako1.pm 'Untitled Document 2' $ ./embedex fruit flying dutchman ./embedex : perl_alloc() ./embedex : perl_construct() ./embedex : executing : #$SIG{INT} = sub { print "Caught your ctrl-c and exiting!\n"; exit(0); + }; $SIG{INT} = sub { print "Caught your ctrl-c but ignoring it!\n" }; print "$0: ARGV:"; print ' '.$_ foreach(@ARGV); print "\n"; print "my pid $$\n"; print "now sleeping, and you ctrl-c me\n"; for(1..100){sleep(1);print"$_\n";} -e: ARGV: fruit flying dutchman my pid 21007 now sleeping, and you ctrl-c me 1 2 3 4 5 ^CCaught your ctrl-c but ignoring it! 6 7 8 9 ^CCaught your ctrl-c but ignoring it! 10 11 12 13 ... 99 100 ./embedex : done. $

        And now with ctrl-c commented out the other way:

        $ x86_64-linux-gnu-gcc 2.perl_embed_argv_signal.c -Wl,-E -fstack-prot +ector-strong -I/usr/lib/x86_64-linux-gnu/perl/5.26.1/CORE -L/usr/loc +al/lib -L/usr/lib/x86_64-linux-gnu/perl/5.26.1/CORE -lperl -ldl -lm +-lpthread -lc -lcrypt -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fn +o-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_F +ILE_OFFSET_BITS=64 -o embedex2 $ ./embedex2 slithy toves wirr and wimbel ./embedex2 : perl_alloc() ./embedex2 : perl_construct() ./embedex2 : executing : $SIG{INT} = sub { print "Caught your ctrl-c and exiting!\n"; exit(0); +}; #$SIG{INT} = sub { print "Caught your ctrl-c but ignoring it!\n" }; print "$0: ARGV:"; print ' '.$_ foreach(@ARGV); print "\n"; print "my pid $$\n"; print "now sleeping, and you ctrl-c me\n"; for(1..15){sleep(1);print"$_\n";} -e: ARGV: slithy toves wirr and wimbel my pid 21094 now sleeping, and you ctrl-c me 1 2 3 4 5 ^CCaught your ctrl-c and exiting! $

        I'm glad to finally see output....

Re: Pass signals and argv from C to embedded Perl
by kschwab (Vicar) on Jan 31, 2019 at 13:45 UTC
    Replace:
    "sleep(10000);\n"
    
    With:
    "for(1..100){sleep(1);print\"$_\\n\";}\n"
    
    And, make sure there's a newline in the print from the SIGINT handler. It should work now. The ctrl-c interrupts the sleep(10000), so that's why the perl exits. With the change above, you'll see the for loop resume after the ctrl-c.

      That works thank you

        Can you post the version that incorporates ikegami's source?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1229172]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (5)
As of 2024-04-26 09:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found