Reputation: 629
I'm working on a test and i'm asked how to make a "read" sleep or a "write" stop a process"
For the latter I don't understand why my sigpipe is, indeed raised, but isn't stopping the process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2
void signal_handler(int signal){
printf("sigpipe received\n");
}
int main(void)
{
int tube[DESCRIPTOR_COUNT];
pipe(tube);
// signal(SIGPIPE, signal_handler);
close(tube[READING]);
if(write(tube[WRITING], "message", 8)<0)
if(errno==EPIPE)
printf("EPIPE returned\n");
printf("123");
return EXIT_SUCCESS;
}
Without signal() (quoted)
With signal() (unquoted)
SIGPIPE is indeed received, but in the case I don't handle it the process should stop, but since I can write "123" this means the process wasn't stopped. Why?
Also I'm on Fedora 28, i'm using codeblocks 17.12.
Was SIGPIPE ignored...? Reason?
SOLUTION ?
struct sigaction action;
action.sa_handler = SIG_DFL;
sigaction(SIGPIPE, &action, 0);
Replacing signal()
with this will have the default behaviour as expected!
EDIT I've now changed the title from "SIGPIPE doesn't stop process" to "Why was default SIGPIPE handler changed?"
======================================================
Answer
After talking with the guys from codeblocks, codeblocks uses wxWidgets, and on linux (fedora 28 here) wxWidgets uses gtk library, as explained by Mark Plotnick in the comments, gtk changes the signal handler for SIGPIPE, since codeblocks runs codes using a fork or an exec, the code run through codeblocks is influenced by gtk library.
Upvotes: 2
Views: 1701
Reputation: 755104
The behaviour you are reporting is consistent with the Code::Blocks IDE setting, implicitly or explicitly, the SIGPIPE behaviour to SIG_IGN. This is readily inherited. It isn't what I'd expect — I'd expect your program to be launched with SIGPIPE (and, indeed, all other signals) set to SIG_DFL, the default signal behaviour. If this turns out to be the problem, you have the basis for a bug report to the developers of Code::Blocks. If it turns out not to be the problem, then we've got some hard thinking ahead to work out what actually is happening*
.
You could demonstrate whether this is what's happening with Code::Blocks by paying attention to the return value from signal()
, or by using sigaction()
to interrogate the signal handling mode without modifying it.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2
static void signal_handler(int signum)
{
// Lazy; normally, I'd format the signal number into the string, carefully
(void)signum;
write(STDOUT_FILENO, "sigpipe received\n", sizeof("sigpipe received\n")-1);
}
int main(void)
{
void (*handler)(int) = signal(SIGPIPE, signal_handler);
if (handler == SIG_DFL)
printf("old handler was SIG_DFL\n");
else if (handler == SIG_IGN)
{
printf("old handler was SIG_IGN\n");
(void)signal(SIGPIPE, SIG_IGN);
}
else
{
// Standard C does not allow a cast from function pointer to object pointer
//printf("there was a non-standard handler installed (%p)\n", (void *)handler);
printf("there was a non-standard handler installed\n");
}
int tube[DESCRIPTOR_COUNT];
pipe(tube);
close(tube[READING]);
if (write(tube[WRITING], "message", 8) < 0)
{
if (errno == EPIPE)
printf("EPIPE returned\n");
else
printf("errno = %d\n", errno);
}
printf("123\n");
return EXIT_SUCCESS;
}
Note that the standard idiom for setting a signal handler using signal()
in a program was:
if (signal(signum, SIG_IGN) != SIG_IGN)
signal(signum, signal_handler);
This means that if a program was protected from signals, it stays protected. If it was handling signals (by default, or perhaps explicitly by a prior call to signal()
), then you install your own signal handler. Equivalent code using sigaction()
would be:
struct sigaction sa;
if (sigaction(signum, 0, &sa) == 0 && sa.sa_handler != SIG_IGN)
{
sa.sa_handler = signal_handler;
sa.sa_flag &= ~SA_SIGINFO;
sigaction(signum, sa, 0);
}
(One advantage of this: the structure is initialized by the call to sigaction()
, so there's no need to fiddle with the masks. The tweak to the flags ensures that the basic handler, not the extended handler, is used.)
When I compile the source code (pipe13.c
) into program pipe13
and run it, I get:
$ make pipe13
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes pipe13.c -o pipe13
$ pipe13
old handler was SIG_DFL
sigpipe received
EPIPE returned
123
$ (trap '' 13; pipe13)
old handler was SIG_IGN
EPIPE returned
123
$
This variant uses sigaction()
to interrogate the signal handling:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2
int main(void)
{
struct sigaction sa;
if (sigaction(SIGPIPE, 0, &sa) != 0)
fprintf(stderr, "siagaction() failed\n");
else if (sa.sa_handler == SIG_DFL)
printf("old handler was SIG_DFL\n");
else if (sa.sa_handler == SIG_IGN)
printf("old handler was SIG_IGN\n");
else
printf("there was a non-standard handler installed\n");
int tube[DESCRIPTOR_COUNT];
pipe(tube);
close(tube[READING]);
if (write(tube[WRITING], "message", 8) < 0)
{
if (errno == EPIPE)
printf("EPIPE returned\n");
else
printf("errno = %d\n", errno);
}
printf("123\n");
return EXIT_SUCCESS;
}
When run (program pipe83
), I get:
$ pipe83
old handler was SIG_DFL
$ echo $?
141
$ (trap '' 13; pipe83)
old handler was SIG_IGN
EPIPE returned
123
$
Note that with the default signal handling, the program terminates before printing 123
. POSIX shells encode 'child died from signal N' by reporting the exit status as 128 + N
; SIGPIPE is 13
, so 141
indicates that the shell died from a SIGPIPE signal. (Yes, modern youngsters would probably write (trap '' PIPE; pipe83)
and it works — such niceties weren't available when I learned shell programming.)
It would not be all that hard to generalize the code to test whether Code::Blocks sets any other signals to other than the default handling. It can be a little fiddly, though, if you want to adapt to which signals are available on a machine.
*
In chat, we established that the program is run in a VMware image running Fedora 28, hosted on a Windows 10 machine. Because of this, there are enough possible places for there to be trouble that it is not clear that the problem is necessarily in Code::Blocks — it is simply not clear where the problem originates. However, the problem does indeed seem to be that the test program is started with SIGPIPE handling set to SIG_IGN instead of SIG_DFL when it is run from Code::Blocks.
Upvotes: 4
Reputation: 1
Code::Blocks is not a compiler, just an IDE. It (probably) runs the GCC compiler. And the GCC compiler don't matter much for signal handling. Read signal(7) and signal-safety(7) for more (calling printf
inside a signal handler is forbidden because printf
is not async-signal-safe so your printf("sigpipe received\n");
inside a signal handler is undefined behavior). Signal handling is done mostly by the Linux kernel (see its source code on kernel.org) with a small bit of it handled by your C standard library, probably GNU glibc.
It seems codeblocks changes the default signal handler for SIGPIPE
This is very unlikely (and almost certainly false). You could use strace(1) on your program to understand what system calls it is doing.
printf("123");
You forgot a \n
. Remember that stdout
is usually line-buffered. Or you should call fflush(3).
When you run a program from inside Code::Blocks it could run without a terminal. I strongly recommend running your program inside a terminal emulator (see tty(4) and pty(7)). The C standard library is permitted to use something like isatty(3) to behave differently when stdout
is or is not a tty (in particular, buffering is done differently, see setvbuf(3)). Read the tty demystified and termios(3).
You could also try to run your program by redirecting its stdin, stdout, stderr to a file or a pipe (perhaps using |& cat
or |& less
with bash
or zsh
), or anything which is not a tty.
BTW, Code::Blocks is free software. You can study its source code to understand what it is doing.
Consider also using stdbuf(1) to run your program with different buffering operations.
Upvotes: 0