0

I am trying to run Emacs as a foreground process in the terminal window using a Perl system call. I would like to terminate both Emacs and the Perl script when I press CTRL-C in the terminal window. However, only the Emacs command terminates. Here is an example script test.pl:

#! /usr/bin/env perl
use strict;
use warnings;

my $cmd = shift;

for ( my $i = 1; $i <= 3; $i ++) {
    my $res = system ($cmd);
    if ( $res == -1 ) {
        die "Failed to execute '$cmd': $!\n";
    }
    my $signal = ($? & 127);
    my $exit_code = ($? >> 8);
    print "exit_code = $exit_code, signal = $signal\n";
    if ( $signal ) {
        die "Command '$cmd' died with signal $signal\n";
    }
}

The output of

$ test.pl 'emacs file.txt'

is (after pressing CTRL-C three times):

^Cexit_code = 0, signal = 0
^Cexit_code = 0, signal = 0
^Cexit_code = 0, signal = 0

So Emacs is terminated, but the Perl script keeps running.. However, if I run

$ test.pl 'sleep 10'

I get output:

^Cexit_code = 0, signal = 2
Command 'sleep 10' died with signal 2

So if I run sleep 10 instead of emacs file.txt it works fine.

Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174

2 Answers2

1

Your code is wrongly assuming that the command you will execute will capture the INT signal and then set its exit code using a convention of (128 * "exit status") + signum.

That is true for sleep, but false for many other programs.

But you can build a wrapper in such a way that is true for any command you want. This would be a emacs wrapper for dealing only with Control-C:

#!/bin/bash -e
trap "exit 130" INT
emacs "$@"

Just put it in your path and execute it inside your perl code, instead of emacs. Extending it for other signals is trivial. While it works for me, it seems like this version would work for you:

#!/bin/bash -e
emacs "$@"

Another solution would be using perl facilities for dealing with subprocesses

juanleon
  • 9,220
  • 30
  • 41
  • Thanks for the reply @juanleon. But it is not working yet, when I run `test.pl 'wrapper.sh file.txt'` I get `^Cexit_code = 130, signal = 0` so it seems it does not send a SIGINT to the parent Perl script.. – Håkon Hægland Mar 10 '15 at 09:40
  • Weird. It works fine for me. What values has `$?` for you? 130 is ok, but since you do `my $signal = ($? & 127);` and `my $exit_code = ($? >> 8);`, $signal should be 2 and $exit_code should be 0. Did you modified that code? Notice that nobody will send SIGINT to parent perl script; the perl script relays SIGINT to the command in execution (when you hit Control-C), and the perl script regains control when command stops for whatever reason. – juanleon Mar 10 '15 at 10:00
  • Ok, I see.. no I did not modify the code, I get `$? = 33280` – Håkon Hægland Mar 10 '15 at 10:04
  • Maybe it is related to Perl version? I am using Perl v5.18.2.. What version of Perl are you running? – Håkon Hægland Mar 10 '15 at 10:13
  • v5.14.2. I would guess that in your case perl is setting the value of $? in a different way than mine (locally I get the same exit code I am setting in my commands). My days of perl hacking are gone and mostly forgotten, so I don't know why. Anyways, it seems like you are not getting good consistency (sleep vs emacs vs wrapper). – juanleon Mar 10 '15 at 10:58
  • So, the solution is trivial: just remove the line `trap...` from the wrapper. The shell script will behave as sleep for perl. – juanleon Mar 11 '15 at 05:24
  • Hmm..If I remove the line with `trap` it just gives signal 0 when I press `CTRL-C` so it works as though I did not use the wrapper at all.. – Håkon Hægland Mar 11 '15 at 05:31
0

You can use IPC::Open2 to run Emacs as a child process. Then use waitpid from the Perl script to wait for the child. If the Perl script (the parent) gets a SIGINT signal the child will also receive a SIGINT signal. So the following seems to work:

use strict;
use warnings;
use IPC::Open2;

my $cmd = shift;

$SIG{INT} = sub { die "Parent got SIGINT\n" };
for ( my $i = 1; $i <= 3; $i ++) {
    print "i = $i\n";
    my ($chld_out, $chld_in);
    my $pid = open2($chld_out, $chld_in, $cmd);
    waitpid($pid, 0);
}

References:

Community
  • 1
  • 1
Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174