7

I write my own shell (source code is listed below) and set user's default shell to it.

I login with this user and type ctrl-C, and this shell is killed even though this signal is catched. However, I run this shell directly from bash, it works as I expect. What makes the difference.

Result

Login with user whose default shell is set to my own shell:

BMC login:
BMC login: naroot
Password:
BMC > signal = 2
BMC login:

Directly run it under bash:

~# /tmp/systemshell
BMC > signal = 2
BMC > signal = 2
BMC > signal = 2
BMC >

source code

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <readline/readline.h>
#include <readline/history.h>

#include <signal.h> // sigaction(), sigsuspend(), sig*()

void signalHandler(int signum) {
    signal(SIGINT, signalHandler);
    signal(SIGTSTP, signalHandler);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    printf("signal = %d\n", signum);
}

int main()
{
    signal(SIGINT, signalHandler);
    signal(SIGTSTP, signalHandler);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    char *input;

    while (1) {
        input = readline("BMC > ");
        if (input) {
            printf("%s\n", input);
            free(input);
        }
    }
    return 0;
}
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Yu-Ting Chen
  • 157
  • 1
  • 11
  • 2
    Related, see [Ctrl + C interrupt event handling in Linux](https://stackoverflow.com/q/17766550/608639), [How to avoid using printf in a signal handler?](https://stackoverflow.com/q/16891019/608639) and [Print int from signal handler using write or async-safe functions](https://stackoverflow.com/q/14573000/608639). – jww Mar 15 '19 at 09:41
  • 3
    Not the answer to your question but you ca't use `printf` in a signal handler because it is not re-entrant. If a signal is handled while you are in the middle of printing the input, you'll corrupt `printf`s internal buffers. – JeremyP Mar 15 '19 at 09:48
  • 1
    This might be the answer, but it's a guess, hence writing as a comment: When a signal is delivered, it can cause blocked system calls (e.g. to read a character from the keyboard) to be interrupted. It's possible that, if the shell is directly connected to a tty, `readline` is fooled into thinking it is at `EOF`. Try checking `errno` when `readline` returns `NULL`. If it's `EAGAIN` that's probably the issue. – JeremyP Mar 15 '19 at 09:53
  • I tried to reproduce your issue but failed, could you provide any more details? – a.l. Mar 16 '19 at 03:37
  • Why is this tagged `bash`? It's purely a question about C. – Charles Duffy Mar 18 '19 at 00:48

1 Answers1

0

I wonder why ash or other bash could work, so dig into ash's source code. I try this snippets.

It works.

    int ofd;
    ofd = fd = open(_PATH_TTY, O_RDWR);
    if (fd < 0) {
/* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
 * That sometimes helps to acquire controlling tty.
 * Obviously, a workaround for bugs when someone
 * failed to provide a controlling tty to bash! :) */
        fd = 2;
        while (!isatty(fd))
            if (--fd < 0)
                goto out;
    }
    /* fd is a tty at this point */
    fd = fcntl(fd, F_DUPFD, 10);
    if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, dont */
        close(ofd);
    if (fd < 0)
        goto out; /* F_DUPFD failed */
    close_on_exec_on(fd);
    while (1) { /* while we are in the background */
        pgrp = tcgetpgrp(fd);
        if (pgrp < 0) {
out:
            ash_msg("can't access tty; job control turned off");
            mflag = on = 0;
            goto close;
        }
        if (pgrp == getpgrp())
            break;
        killpg(0, SIGTTIN);
    }
    initialpgrp = pgrp;

    setsignal(SIGTSTP);
    setsignal(SIGTTOU);
    setsignal(SIGTTIN);
    pgrp = rootpid;
    setpgid(0, pgrp);
    xtcsetpgrp(fd, pgrp);
Yu-Ting Chen
  • 157
  • 1
  • 11