Search code examples
linuxsignalsinterrupt-handlingsigaction

SIGACTION handler to repeat loading after interruption


Hi stackoverflow family,

I'm doing a uni task to make a linux program to read a password and secure it and "re-read" the password again if the user interrupts it during.

Here's my code for catching handler.

void catch_suspend(int sig_num)
{
    printf("\nSuspending execution...\n");
    fflush(stdout);

    echo_on(YES);           // re-enable echo mode

    raise(SIGSTOP); // stop self

    // we'll get back here when the process is resumed
    printf("Resuming execution...\n");

    echo_on(NO);            // disable echo mode again

    printf("Password: ");       // reproduce the prompt
    fflush(stdout);
}

and here is the main program

int main(int argc, char *argv[])
{
#define MAX_SIZE 30
    char user[MAX_SIZE];    // user name supplied by the user
    char passwd[MAX_SIZE];  // password supplied by the user

    sigset_t sigs;
    struct sigaction sa_new;

    memset(&sa_new, 0, sizeof(sa_new)); // initialization to zeros
    sa_new.sa_handler = catch_suspend;      // set handler
    sigemptyset(&sa_new.sa_mask);       // mask: empty set
    sigaddset(&sa_new.sa_mask, SIGINT); // mask: add SIGINT
    sigaddset(&sa_new.sa_mask, SIGQUIT);    // mask: add SIGQUIT
    sa_new.sa_flags = 0;            // no flags

    printf("Username: ");       // prompt the user for a user name
    fflush(stdout);

    fgets(user, MAX_SIZE, stdin);   // wait for input

    sigaction(SIGTSTP, &sa_new, &sa_old);   // set the handler for SIGTSTP and get old handler setting

    printf("Password: ");       // prompt the user for a password
    fflush(stdout);

    echo_on(NO);            // set input to no-echo mode
    fgets(passwd, MAX_SIZE, stdin); // get the user input
    echo_on(YES);           // re-enable echo on input

    printf("\n");           // the Enter pressed by the user was not echoed
    fflush(stdout);

    // verify the password (\n is stored, don't compare it)
    etc...

    return 0;

I should also block all signals except SIGINT and SIGQUIT, not sure if I've done it right with the mask. The problem I've encountered now is this: after interrupting during the password reading, process is stopped. That's ok. But after I use "fg" command to continue, my handler only writes the output and the program ends, but I need it to run fgets(passwd, MAX_SIZE, stdin) (where it was stopped) again.

I guess that I'm probably not setting the old_action correctly, but the manuals I've read didn't really make it more clear to me. Anyone who can lend a helping hand?


Solution

  • Okay, I've finally figured it out and I'm gonna leave it here for anyone having the same issues.

    I didn't set the sigaction flag as followed:

    sa_new.sa_flags = SA_RESTART;
    

    This flag controls what happens when a signal is delivered during certain primitives (such as open, read or write), and the signal handler returns normally. There are two alternatives: the library function can resume, or it can return failure with error code EINTR.

    The choice is controlled by the SA_RESTART flag for the particular kind of signal that was delivered. If the flag is set, returning from a handler resumes the library function. If the flag is clear, returning from a handler makes the function fail