Question

I wrote an embedded application (a vt52 terminal emulator) running on a linux rom based system with no built in ptys; and Unix98 broken. But Since I must have ptys for a terminal to work ... I manually compiled the old style BSD pty's to be a kernel module. But for some strange reason I am unable to send SIGINT (ctrl-c) successfully using BSD ptys...

The VT52 program is a daemon, and not the parent of the program I want to send a signal to -- but that shouldn't matter, should it? I mean, there is a pty slave involved....

VT52 communicates by creating a BSD pty pair ( /dev/terminal with major=3, minor=0 permissions=crwxrwxrwx, and also /dev/ptyp0 as major 2, minor 0 permissions=crwxrwxrwx) and opens them for reading and writing. The terminal sends keystrokes, including the occasional ctrl-c \003, to ptyp0, and it also reads returning data from ptyp0.

Programs connect to the vt52 dameon via /dev/terminal after being started as a session leader, and gaining the pty as a controlling terminal. Typically I run the busybox sh (?ash?) program using the following code as a session leader:

// snippet from session.c
child=fork();
...
session=setsid();
...
// Child alone gets here.
close(0); close(1); close(2); // Close all IO to allow for a new CTTY.
open( argv[1], O_RDONLY ); // stdin
open( argv[1], O_WRONLY ); // stdout 
open( argv[1], O_WRONLY ); // stderr
ioctl( 0, TIOCSCTTY, 0 ); // Set the controlling TTY based on stdin
return execvp( cmd, args ); // run whatever program the user requested.

And I know it worked, because I ran "ps a" over the vt52, and got:

PID TTY        STAT   TIME COMMAND
476 ttymxc0    Ss+    0:00 /sbin/getty -L 115200 ttymxc0 vt102
631 ?          Ds+   34:13 ./vt52 /dev/ttyGS0
706 terminal   Ss     0:00 /bin/sh -i
711 terminal   R+     0:00 ps a      

So, session.c worked, and the CTTY is correct... and therefore, when I send ctrl-c; the shell should terminate process groups/subprocesses -- but it doesn't. For example, if I run "sleep 20", and hit ctrl-c, nothing happens for 20 seconds...

I checked to make sure vt52 was actually writing \x03 to /dev/ptyp0, and it did...

So, Then I checked the pseudo terminals settings via "stty -a":

speed 38400 baud; rows 84; columns 75;
intr - ^C; quit = "^\; erase = ^?; kill = ^U; eof = ^D; 
eol = <undef>; eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; inext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff iuclc -ixany -imaxbel
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

And everything looks good... So -- I don't understand why SIGINT is not being sent to the shell's children. What am I missing?

If the shell is at a command prompt, and I press ctrl-c, it goes to the next line ... although I don't know if it does that because it catches a signal, or receives a character code -- and I don't know how to figure it out... but regardless, the shell is not doing job control based on SIGINT / control-C.

help... :) how do I turn it on?

Was it helpful?

Solution

The clue is that pressing ctrl-c generates a new prompt under the shell. I wrote a program to parse stdin, and print out each character in hexadecimal. The code for control C is not passed through the PTY -- hence the pty was generating the signal after all, the shell was detecting it; but the subprocesses weren't terminating.

The problem turned out to be that since shells inherit the signal action (sigaction) of their parent process, one must check or reset the signals before launching a new shell; and on the embedded device (sony PRS900) -- the default action for SIGINT and many other signals had been changed to SA_IGN (ignore).

So, one simply needs to add some code to the session.c program to initialize all signals to their default action, and then enable all of them. That will solve the problem, and the interrupts will function properly.

A complete copy of the session program follows; it will create a new session, set all signals to default values and enable them, and it will then exec a program of your choice...

// A program to create a new Linux session connecting to a specified terminal,
// and then execing a program.
// Written by Andrew Robinson of Scappoose Oregon.
// Released under (C) 2014, GPL v3
// https://www.gnu.org/copyleft/gpl.html


#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>

int main(int narg, char * const argv[] ) {
    pid_t session;
    char *const cmd  = argv[2];
    char *const *args = argv+2;

    if (narg<3) {
        fprintf(stderr,
            "Usage: %s new_tty program [params]\n", argv[0] );
        return 1;
    }

    session=fork();

    if (session<0) return session;
    if (session) {
        int status;
        printf("Session pid %d exited\n", waitpid( session, &status, 0 ) );
        return status;
    }

    // Child falls through to here... print the sessionID.

{  // Signal handling is all to be set to defaults.
   // INT should then terminate subprocesses of the shell.
    sigset_t mask;
    struct sigaction act;
    int i;

    // set all signals to the default action.
    sigemptyset( &act.sa_mask );
    act.sa_handler=SIG_DFL;
    act.sa_flags=0;
    for(i=1; i< _NSIG-1; ++i ) // Iterate over all signals.
        sigaction( i, &act, NULL ); // Set action to default.

    // enable all signals.
    sigprocmask( SIG_SETMASK, &mask, NULL );
    sigprocmask( 0,NULL,&mask );

    if (!sigismember(&mask,SIGINT)) printf("SIGINT enabled\n");
}

    session=setsid(); // make a session leader.
    if (session<0) {
        perror("bad-session:");
    }
    printf("%d\n", (int)session );
    fflush(stdout);

    close(0); close(1); close(2); // Close all IO to allow for a new CTTY.

    open( argv[1], O_RDONLY ); // stdin
    open( argv[1], O_WRONLY ); // stdout 
    open( argv[1], O_WRONLY ); // stderr

    ioctl( 0, TIOCSCTTY, 0 ); // Set the controlling TTY based on stdin
    return execvp( cmd, args );
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top