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?
printf
from a signal handler is an undefined behavior. \$\endgroup\$write(STDOUT_FILENO, ...)
? Can I use printf after longjmp? \$\endgroup\$write
translates directly to a system call, it it kind of safe. In doubt, consultman 7 signal-safety
. \$\endgroup\$