in reply to How to hook up C's socketpair() with Perl's?

Hi, others (notably jesuashok) have already pointed out the pipe solution in case of colocated processes (which should be the case given the fork/exec approach). I'd only suggest to use dup or dup2 in the child process on the pipe handles, just before calling exec, to redirect the two pipes on STDIN and STDOUT. This would allow you to write an "agnostic" filter, i.e. a filter that reads from STDIN and writes on STDOUT - something that you could use in a canned sequence of pipes on the command line :)

Just to make me clear, here's the C part:

/* gcc callfilter.c -o callfilter */ #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <string.h> int main (int argc, char *argv[]) { int writer[2]; /* From the parent's point of view */ int reader[2]; /* From the parent's point of view */ int pid; char *msg = "hullo"; char buf[10]; int nread; /* Get pipes on */ if (pipe(reader)) { perror("reader pipe(): "); return 1; } if (pipe(writer)) { perror("writer pipe(): "); return 1; } pid = fork(); if (pid < 0) { perror("fork(): "); return 1; } else if (pid == 0) { /* In the child */ /* close unneeded endpoints */ close(reader[0]); close(writer[1]); /* Duplicate handles on STDIN and STDOUT for child */ dup2(writer[0], 0); dup2(reader[1], 1); /* call filter */ execl("./filter.pl", "./filter.pl", NULL); /* patched, thanks to + djp */ perror("execl(): "); return 1; } else { /* In the parent */ /* close unneeded endpoints */ close(reader[1]); close(writer[0]); /* Send something to the filter */ write(writer[1], msg, strlen(msg)); close(writer[1]); /* Read response from the filter */ printf("filter says:\n"); while ((nread = read(reader[0], buf, 9)) > 0) { buf[nread] = '\x0'; printf("%s", buf); } if (nread < 0) { perror("read(): "); return 1; } printf("\n"); close(reader[0]); } return 0; }
and here's the Perl filter.pl (to be located in the same directory with the correct permissions, see execl above):
#!/usr/bin/perl use strict; use warnings; local $/; my $in = <STDIN>; print {*STDOUT} scalar reverse $in;
The result:
poletti@PolettiX:~/sviluppo/perl/dup$ ./callfilter filter says: olluh

Update: patched C example, thanks to djp.

Flavio
perl -ple'$_=reverse' <<<ti.xittelop@oivalf

Don't fool yourself.

Replies are listed 'Best First'.
Re^2: How to hook up C's socketpair() with Perl's?
by djp (Hermit) on Jul 17, 2006 at 03:14 UTC
    execl("./filter.pl", NULL);
    should be
    execl("./filter.pl", "./filter.pl", NULL);
Re^2: How to hook up C's socketpair() with Perl's?
by mhutch7714 (Acolyte) on Jul 19, 2006 at 10:25 UTC
    Frodo72,

    You have nearly what I need! Perhaps I've made the problem more difficult than it needs to be.

    When I substitute my "filter.pl", it nearly works as needed.

    Can I trouble you for advice on how to trigger the pipe to "swallow" additional strings without closing writer1 (on your original line 48). I'll need to read a series of address strings from a database and attempt substitution on each.

    I test my filter.pl by firing from the comandline, after which I'm able to enter text, <cr>, and the substituted string appears. I enter <cr>, and it's ready for the next substitution. Ctl-D terminates it.

    When I invoke it from callfilter, the msg line substitutes, but later lines do not. Is this behavior due to closure of the file/pipe?

    Thanks in advance,

    mhutch

    (p.s. -- Any recommendations on where to RTFM about agnostic filters in perl?)
      The example was clearly only a "proof of concept", you can modify it to suit your needs. In particular you can close the pipes' ends when you really need it. In my example:
      else { /* In the parent */ /* close unneeded endpoints */ close(reader[1]); close(writer[0]); /* Send something to the filter */ write(writer[1], msg, strlen(msg)); close(writer[1]); /* HERE I CLOSE PARENT->FILTER */ /* Read response from the filter */ printf("filter says:\n"); while ((nread = read(reader[0], buf, 9)) > 0) { buf[nread] = '\x0'; printf("%s", buf); } if (nread < 0) { perror("read(): "); return 1; } printf("\n"); close(reader[0]); /* HERE I CLOSE FILTER->PARENT */ }
      I closed the file descriptors immediately when they were not needed any more. I wanted to send only one string, then I closed the writer[1]. But you can keep it open until you're done (note: the following code is "high-level"):
      else { /* In the parent */ /* close unneeded endpoints */ close(reader[1]); close(writer[0]); while (msg = next_string_to_filter()) { /* Send something to the filter */ write(writer[1], msg, strlen(msg)); /* Read response from the filter */ read_and_consume_answer(reader[0]); } /* Close communication with filter */ close(writer[1]); close(reader[0]); }
      A little note is due here about read_and_consume: be very, very careful about synchronisation between the two processes. You probably want to use the filter in a line-oriented fashion (from what I see from your post): be careful that read() blocks! Otherwise, you could prepare all the input (i.e. all the lines that you have to filter), send them all to the filter at once and read the answers. But I'll cut it here before someone reminds me that we're in PerlMonks, not CMonks ;)
      Any recommendations on where to RTFM about agnostic filters in perl?
      With agnostic I tried to express the fact that the filter does not have to know anything more than "read from STDIN, write to STDOUT", which is what any program normally does. I fear that you aren't going to find much about "agnostic filters" in any language :)

      More than this, be careful of buffering, use strict; and use warnings; ;)

      Flavio
      perl -ple'$_=reverse' <<<ti.xittelop@oivalf

      Don't fool yourself.