3
\$\begingroup\$

I'm writing a C++ application for POSIX environment with terminal-based User Interface. I'd like to make my application's UI be consistent between Ctrl-Z and fg(i.e: SIGSTOP and SIGCONT).

Here is a minimal example (without using longjmp. this code uses C and POSIX headers only, but entire application will be C++):

#include <cstdio>
#include <cstdlib>
#include <csignal>
#include <unistd.h>

void PrintMessage() {
    printf("Press q to quit");
}

sig_atomic_t signalCheck;

void SigCONTHandler(int sig) {
    if(sig != SIGCONT) {
        return;
    }   
    printf("From Signal Handler\n");
    fflush(stdout);

    if(signalCheck != 0) {
        PrintMessage();
    }   
}

int main() {
    setbuf(stdout, NULL);
    signal(SIGCONT, SigCONTHandler);

    int ch = 0;
    while(true) {
        PrintMessage();
        signalCheck = 1;
        ch = getchar();
        signalCheck = 0;
        if(ch == 'q') {
            break;
        }   
        if(ch == EOF) {
            printf("I asked you to process q.\n");
            return EXIT_FAILURE;
        }   
    }   
    return EXIT_SUCCESS;
}

Usage(Ubuntu 16.04, g++ 5.4, TERM=linux):

$ ./app
Press q to quit
Press q to quit
Press q to quit
Press q to quit
Press q to quit^Z
[2]+  Stopped                 ./app
$ fg
./app
From Signal Handler
Press q to quit
Press q to quitq

This code works as intended, but I feel this code is Bad. signalCheck variable keep own state to check whether the application need to print PrintMessage() or not. However, if a application become complex, I don't think I can print whole thing to stdout in SigContHandler. Moreover, it won't work without setbuf(stdout, NULL), so I'm trying to make it with longjmp to restore state.

#include <cstdio>
#include <cstdlib>
#include <csignal>
#include <csetjmp>
#include <unistd.h>

sigjmp_buf jumpBuffer;

void PrintMessage() {
    printf("Press q to quit");
}

void SigCONTHandler(int sig) {
    if(sig != SIGCONT) {
        return;
    }   
    printf("From Signal Handler\n");
    siglongjmp(jumpBuffer, 1); 
}

int main() {
    // setbuf(stdout, NULL);
    signal(SIGCONT, SigCONTHandler);

    int ch = 0;
    while(true) {
        if(sigsetjmp(jumpBuffer, 1) == 0) {
            PrintMessage();
            ch = getchar();
            if(ch == 'q') {
                break;
            }   
            if(ch == EOF) {
                printf("I asked you to process q.\n");
                return EXIT_FAILURE;
            }   
        }   
    }   
    return EXIT_SUCCESS;
}

This code works, and I don't need to turn off stdout buffer, but this code also looks bad.

If I need to choose one of them, what I need to choose it? Is it portable from latest Linux to old Unix box?

\$\endgroup\$
  • 1
    \$\begingroup\$ I am not sure I understand what you are trying to achieve. In any case, calling printf from a signal handler is an undefined behavior. \$\endgroup\$ – vnp Apr 13 '17 at 4:44
  • \$\begingroup\$ @vnp I didn't know that. It is okay to change it to write(STDOUT_FILENO, ...)? Can I use printf after longjmp? \$\endgroup\$ – Byoungchan Lee Apr 13 '17 at 4:58
  • \$\begingroup\$ Since write translates directly to a system call, it it kind of safe. In doubt, consult man 7 signal-safety. \$\endgroup\$ – vnp Apr 13 '17 at 6:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.