0

I have an interactive C program that reads a phrase from the terminal, and computes a series of anagrams. I also have a Perl program that reads and buffers lines from STDIN until receiving a reserved "end of input" token, sorts the lines, organizes them into columns and sends them to STDOUT. The C program uses the BSD/MacOS popen() to start the Perl program with a single bi-directional pipe, and fprintf()'s the anagrams on the pipe until it's generated all the anagrams; sends the "end of input" token, and then issues getline()'s on the pipe to read back and print the formatted results.

It works fine normally. But I added a SIGINT handler to the C program so I could interrupt a long-running anagram, print whatever results it had thus far, and loop around for more terminal input. The handler sets a flag that the main program checks after writing to the pipe. If it's set, it breaks out of the anagramming loop and proceeds as if the loop had ended naturally: it sends the end-of-input string, and reads back the formatted results.

It breaks out okay; the fprintf end-of-input gets a normal return code; but the first getline returns a null pointer indicating EOF, so I don't get any partial results. The C program continues normally, as intended.

I don't find anything in the docs to indicate that a handled SIGINT affects any open pipes, but I can't think what else would be preventing me from receiving anything from the Perl program.

0

1 Answer 1

1

The SIGINT signal goes to every process in the foreground process group; the foreground process group will typically include whatever the popen runs,

/* popen.c */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void poke(int unused) { fprintf(stderr, "sigint - C\n"); }
int main(void) {
    FILE *fh;
    signal(SIGINT, poke);
    fh = popen(
      "exec perl -e '$SIG{INT}=sub {warn q{ouch}};sleep 1 while 1'",
      "r+");
    if (!fh) abort();
    while (1) sleep(1);
    return 42;
}

as can be verified with something like:

$ make popen
$ ./popen
^Csigint - C
ouch at -e line 1.
^Csigint - C
ouch at -e line 1.
^\Quit

Therefore, all the programs must handle the signal, or the signal can be turned off in the terminal. Details will vary by language, but to ignore a signal in Perl one can simply ignore it:

$SIG{INT} = "IGNORE";

Another option is to block the signal in a parent process, though subsequent programs might install their own signal handlers.

Yet another option is to disable the ISIG flag in the terminal. In this case control+c will not cause a signal to be sent to the foreground process group; instead, a character will be available for some program to read. See the termios manual page for details (the man page section will vary by operating system). An interface such as curses might be typical here, though one could instead make a suitable tcsetattr(3) call to disable ISIG.

#!/usr/bin/env perl
# example script showing how control+c can be read as a key
use strict;
use warnings;
use Curses;
initscr;
noecho;
raw;    # disables ISIG, among other things
while (1) {
    my $ch = getchar() // die "getchar failed??";
    last if $ch eq 'q';
    move 0, 0;
    clrtoeol;
    addstring sprintf "key: %vx", $ch;
}
endwin;

This should display key: 3 when control+c is pressed; a signal will not be sent to the foreground process group. Type q to exit.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .