sigpipe c server/client - where does the program restart? - c

I've a client/server program, now I want to handle signals. When the client closes the connection (if for example I close the terminal), the server has to handle a SIGPIPE, am I right? I'd like to implement something like this. Is it possible?
server.c:
void function(){
printf("...");
read(socket,buff,size);
//IF THE CLIENT CLOSES, THE SERVER RECEIVES A SIGPIPE
...the resting part of the scheduled code should be ignored if sigpipe is received, and the program should begin from where I wrote on the handler of the sigpipe...
printf("not working"); //this should be ignored, but it's printed 2 times immediatly, and when I've finished the actions indicated in the function by the handler, it prints it another time, because the program counter restarts from here...
}
void sigpipehandler(){
close(socket);
main(); //I'd like that the program restarts from the main when I've received a SIGPIPE. It restarts from the main, but only after having printed "not working" two times...
}
int main(){
sigPipe.sa_sigaction = &sigpipehandler;
sigPipe.sa_flags = SA_SIGINFO;
sigaction(SIGPIPE, &sigpipehandler, NULL);
...code...
}

Converting comments into an answer.
Note that you only get SIGPIPE when you write to a pipe where there is no process with the read end of the pipe open. You get EOF (zero bytes read) when you read from a pipe that has no process with the write end of the pipe open.
So, if I change the read() with a write() in the example. How can I handle the SIGPIPE?
Simplest is to ignore SIGPIPE (signal(SIGPIPE, SIG_IGN)) and then monitor the return value from write(). If it comes back with -1 and errno set to EINTR, you can assume you got interrupted by some signal, and most probably a SIGPIPE, especially if you don't have any other signal handling set. Of course, you should be looking at the return value from write() — and read() — anyway.
Alternatively, if you want an explicit SIGPIPE handler, then you definitely do not want to recursively call main() from your signal handler. You can write a loop in main(), and have the signal handler set a flag which you test in the loop. Per Standard C, about the only thing you can do in a signal handler is modify a variable or exit.
static volatile sigatomic_t sig_recvd = 0;
static int sock_fd = -1;
void sigpipehandler(int signum)
{
close(sock_fd);
sock_fd = -1;
sig_recvd = signum;
}
int main(void)
{
sigPipe.sa_sigaction = &sigpipehandler;
sigPipe.sa_flags = SA_SIGINFO;
sigemptyset(&sigPipe.sa_mask);
sigaction(SIGPIPE, &sigpipehandler, NULL);
int done = 0;
while (!done)
{
if (sock_fd == -1)
{
if (sig_recvd != 0)
{
...report signal received...
sig_recvd = 0;
}
...(re)open socket on sock_fd...
}
...code as before - sets done = 1 when loop should terminate...
}
return 0;
}
Note that naming a variable the same as a system call (socket in your code) is treading on thin ice; hence, I renamed it sock_fd. A global variable called socket would be a really bad idea.

Related

Why write() terminate my thread?

I created a thread with pthread_create() for the execution of a function, inside the function there is a write() function to send data via socket descriptor. If the write() function fails to send data (because the socket connection is lost), my thread is terminated.
Can I keep a thread when write() fails?
This is my code:
void broadcastMsg(char *msg) {
int i=0;
while(1) {
...
...
// My thread terminated from here
if(write(client_database.sock_desc[i], msg, strlen(msg)) < 0) {
client_database.sock_desc[i] = -3;
i++;
continue;
}
i++;
}
}
/* Start thread from this function */
void *cliListener(void *argvp) {
int read_desc;
char buf[MAX_TRANSFER_BUF];
int cli_sock_desc_id = atoi(argvp);
while(1) {
memset(buf, 0, MAX_TRANSFER_BUF);
read_desc = read(client_database.sock_desc[cli_sock_desc_id], buf, MAX_TRANSFER_BUF);
...
...
broadcastMsg(buf);
}
}
int main(void) {
...
...
pthread_t tid_1;
pthread_create(&tid_1, NULL, cliListener, cli_listener_arg);
...
...
}
If you try to write to a pipe or socket descriptor where the other end of the pipe or socket has been closed (so noone will ever read it), the system will send a SIGPIPE signal to your process, which by default will kill the process1. You can avoid this fate by ignoring SIGPIPE, generally early in the main function:
signal(SIGPIPE, SIG_IGN);
once you do this, the write will no longer kill your thread or process; instead it will return an error (-1) with errno set to EPIPE. You need to make sure you are ALWAYS checking the return value of your write calls, and doing the appropriate thing when there is an error, otherwise your program might run on in background after you think it has stopped...
1Depending on how you've set things up it might kill just the thread and not the entire process, but for general safety and hygiene its supposed to kill the entire process

Parent process in server stuck in accept call. How do I terminate the parent cleanly from child

Hello from a beginner C programmer.
I have a simple server client setup. I only want one client to connect to the server, but I want other clients to be able to try and get a message that the server is occupied.
I am able to connect to the server with one client, and let other clients trying to connect know there is no room. My problem occurs when the client tells the server to shut down. The child process is able to break out of the loops and terminate. The parent, however, is not able to receive the message from the child using pipe, because it is stuck on accept.
I could use kill(2) to end the parent, but will I get a clean termination with closing of sockets and files then?
I have also tried to let the parent not stop at accept using fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK); but this opens up new problems.
I want to somehow make the child tell the parent to skip the accept line and continue so that it gets the pipe message and exits the loop.
If this is a bad way to terminate servers I would appreciate to learn about that.
Simplified server code:
void termination_handler (int signum)
{
if(signum == SIGTERM){
//Is this where the accept call is changed?
}
}
void main(){
struct sigaction sa = {0}; //2b) Initialise the struct sigaction variable to all 0s beforehand.
sa.handler = termination_handler; //2a) Set the member for handler (to the signal-handler function)
sigaction(SIGTERM, &sa, NULL);
pid_t pid;
int loop = 1;
while(loop){
int sock = accept(net_sock, NULL, NULL); //After connection
//parent is stuck here
if(kill(pid,0) == -1){
pid = fork();
}
else{
//Tell second client there is no room and close socket
}
//Child
if(pid == 0){
while(loop){
//Read signal in from client to end child loop and terminate child
//Write with pipe to parent to end parent loop and terminate parent
kill(getppid(), SIGTERM) // Is this how I do it?
}
}
//Parent
else{
close(sock);
//Read with pipe from child to end loop
//After first connection, the parent won't get this message
}
}
The OS will close filedescriptors for you. Unless you have other cleanup work to do (such as writing into files or removing some files), a kill with an unhandled terminating signal (e.g., SIGTERM or SIGINT) should be sufficient.
If you do have other cleanup work to do, have the child signal the parent with a signal for which the parent has a signal handler established (you need to establish the handler with sigaction). That will break accept with return code -1 and errno == EINTR, allowing you to do whatever you need to do.
volatile sig_atomic_t usr1 = 0;
void usr1_handler(int Sig) { usr1 = 1; }
//...
int main() { //...
sigaction(SIGUSR1, &(struct sigaction){.sa_handler=usr1_handler},0);
//...
usr1 = 0;
sock = accept( /*... */ );
if ( -1 == sock && EINTR == errno && usr1 ) //was interrupted by USR1
/* cleanup and exit */;
Let the child signal it's parent before ending. If done correctly accept() returns on signal reception, returning -1 and setting errno to EINTR.
From accept()'s documentation:
RETURN VALUE
Upon successful completion, accept() shall return the non-negative file descriptor of the accepted socket. Otherwise, -1 shall be returned, errno shall be set to indicate the error, [...]
[...]
ERRORS
The accept() function shall fail if:
[...]
[EINTR]
The accept() function was interrupted by a signal that was caught before a valid connection arrived.

Why my sig_int() function can't prevent my function from exit in c?

The codes is as below, and is the same as the one in book apue3e:
#include "apue.h"
#include "sys/wait.h"
static void sig_int(int);
int
main(int argc, char *argv[]) {
pid_t pid;
char buf[MAXLINE];
int status;
if (signal(SIGINT, sig_int) == SIG_ERR) {
err_sys("signal error");
}
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf)-1] == '\n') {
buf[strlen(buf)-1] = '\0';
}
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) {
execlp(buf, buf, (char *)NULL);
err_ret("couldn't execlvp: %s\n", buf);
exit(127);
}
if ((pid = waitpid(pid, &status, 0)) < 0) {
err_sys("waitpid_error");
}
printf("%% ");
}
exit(0);
}
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
So, my question is why this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
[W]hy this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
Given
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
and
signal(SIGINT, sig_int)
Your process doesn't exit when you press CTRL-C for the simple reason your signal handler doesn't cause the process to exit.
You replaced the default SIGINT handler with your own, so the default action of exiting the process no longer happens.
Since you're running on Linux, I'll refer to the GNU glibc documentation on termination signals:
24.2.2 Termination Signals
These signals are all used to tell a process to terminate, in one way
or another. They have different names because they’re used for
slightly different purposes, and programs might want to handle them
differently.
The reason for handling these signals is usually so your program can
tidy up as appropriate before actually terminating. For example, you
might want to save state information, delete temporary files, or
restore the previous terminal modes. Such a handler should end by
specifying the default action for the signal that happened and then
reraising it; this will cause the program to terminate with that
signal, as if it had not had a handler. (See Termination in
Handler.)
The (obvious) default action for all of these signals is to cause the
process to terminate.
...
Macro: int SIGINT
The SIGINT (“program interrupt”) signal is sent when the user types
the INTR character (normally C-c).
The Termination in Handler glibc documentation states:
24.4.2 Handlers That Terminate the Process
Handler functions that terminate the program are typically used to
cause orderly cleanup or recovery from program error signals and
interactive interrupts.
The cleanest way for a handler to terminate the process is to raise
the same signal that ran the handler in the first place. Here is how
to do this:
volatile sig_atomic_t fatal_error_in_progress = 0;
void
fatal_error_signal (int sig)
{
/* Since this handler is established for more than one kind of signal,
it might still get invoked recursively by delivery of some other kind
of signal. Use a static variable to keep track of that. */
if (fatal_error_in_progress)
raise (sig);
fatal_error_in_progress = 1;
/* Now do the clean up actions:
- reset terminal modes
- kill child processes
- remove lock files */
…
/* Now reraise the signal. We reactivate the signal’s
default handling, which is to terminate the process.
We could just call exit or abort,
but reraising the signal sets the return status
from the process correctly. */
signal (sig, SIG_DFL);
raise (sig);
}
Also, note that there can be significant differences between signal() and sigaction(). See What is the difference between sigaction and signal?
Finally, calling printf() from with a signal handler is undefined behavior. Only async-signal-safe functions can be safely called from within a signal handler. See POSIX 2.4 Signal Concepts for the gory details.

How do I interrupt a background thread that's blocking on IO?

I have a C program with two threads: the main thread continously reads data from the network and prints it out to the screen, while the secondary thread listens for and handles keypresses from standard input.
Currently my program catches SIGINT, SIGTERM, and SIGPIPE in order to terminate the program cleanly. My problem is that at the end of the main thread (once the main loop has terminated from the signal handler), it attempts to revert the terminal settings using tcsetattr, however this blocks until the current fgetc call on the other thread returns.
How can I interrupt the background thread so that the fgetc call returns and the main thread can restore the terminal settings and exit cleanly?
I have tried using pthread_kill(thread, SIGINT) but that just causes my existing signal handler to be called again.
Relevant code:
// If the program should still be running.
static sig_atomic_t running = 1;
// Background thread that reads keypresses.
pthread_t thread;
static void *get_keypresses();
static void receive_signal(int signal) {
(void)signal;
running = 0;
}
int main(int argc, char *argv[]) {
// Set up signal handling.
if(signal(SIGINT, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGINT.\n");
}
if(signal(SIGTERM, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGTERM.\n");
}
if(signal(SIGPIPE, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGPIPE.\n");
}
// Set up thread attributes.
pthread_attr_t thread_attrs;
if(pthread_attr_init(&thread_attrs) != 0) {
perror("Unable to create thread attributes");
exit(2);
}
if(pthread_attr_setdetachstate(&thread_attrs, PTHREAD_CREATE_DETACHED) != 0) {
perror("Unable to set thread attributes");
exit(2);
}
// Set up terminal for reading keypresses.
struct termios orig_term_attr;
struct termios new_term_attr;
tcgetattr(fileno(stdin), &orig_term_attr);
memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
new_term_attr.c_lflag &= ~(ECHO|ICANON);
tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
// Start background thread to read keypresses.
if((pthread_create(&thread, &thread_attrs, &get_keypresses, NULL)) != 0) {
perror("Unable to create thread");
exit(2);
}
// Main loop.
while(running) {
// Read data from network and output to screen.
}
// Restore original terminal attributes. ***IT BLOCKS HERE***
tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);
return 0;
}
// Get input from the keyboard.
static void *get_keypresses() {
int c;
while(running) {
// Get keypress. ***NEEDS TO BE INTERRUPTED HERE***
if((c = fgetc(stdin)) != - 1) {
// Handle keypress.
}
}
return NULL;
}
There are two ways to go:
you change your code not to block (using O_NONBLOCK, poll(), select(), etc.),
you force your blocked code to get kicked out of the blocking system call.
Forcing the end of a system call can basically be done in two ways: either you make the system call impossible to finish (for example, you close the file descriptor the blocking call waits on), or you send some signal to that thread, and make sure that the signal handling is properly set up. The proper signal handling means that the signal is not ignored, not masked, and that the signal flags are set up in such a way that the system call does not get restarted after the signal handling (see sigaction(), SA_RESTART flag).
Replace
if((c = fgetc(stdin)) != -1)
with
if (0 < read(fileno(stdin), &c, 1))
read() would be interupted if the thread received a signal.
I managed to find a solution that works well for me: I made reading from standard input non-blocking.
fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
This also requires some form of sleep (or usleep or nanosleep) in the background thread.
Thanks for HapKoM for getting me thinking in the right direction.
One way that I see is to read stdin by poll() or select() with some small timeout. And when it returns on timeout you can check "running" flag and perform clear termination if it is set to "false" value.
May be it is not the best solution, but it should work.

Wake up thread blocked on accept() call

Sockets on Linux question
I have a worker thread that is blocked on an accept() call. It simply waits for an incoming network connection, handles it, and then returns to listening for the next connection.
When it is time for the program to exit, how do I signal this network worker thread (from the main thread) to return from the accept() call while still being able to gracefully exit its loop and handle its cleanup code.
Some things I tried:
pthread_kill to send a signal. Feels kludgy to do this, plus it doesn't reliably allow the thread to do it's shutdown logic. Also makes the program terminate as well. I'd like to avoid signals if at all possible.
pthread_cancel. Same as above. It's a harsh kill on the thread. That, and the thread may be doing something else.
Closing the listen socket from the main thread in order to make accept() abort. This doesn't reliably work.
Some constraints:
If the solution involves making the listen socket non-blocking, that is fine. But I don't want to accept a solution that involves the thread waking up via a select call every few seconds to check the exit condition.
The thread condition to exit may not be tied to the process exiting.
Essentially, the logic I am going for looks like this.
void* WorkerThread(void* args)
{
DoSomeImportantInitialization(); // initialize listen socket and some thread specific stuff
while (HasExitConditionBeenSet()==false)
{
listensize = sizeof(listenaddr);
int sock = accept(listensocket, &listenaddr, &listensize);
// check if exit condition has been set using thread safe semantics
if (HasExitConditionBeenSet())
{
break;
}
if (sock < 0)
{
printf("accept returned %d (errno==%d)\n", sock, errno);
}
else
{
HandleNewNetworkCondition(sock, &listenaddr);
}
}
DoSomeImportantCleanup(); // close listen socket, close connections, cleanup etc..
return NULL;
}
void SignalHandler(int sig)
{
printf("Caught CTRL-C\n");
}
void NotifyWorkerThreadToExit(pthread_t thread_handle)
{
// signal thread to exit
}
int main()
{
void* ptr_ret= NULL;
pthread_t workerthread_handle = 0;
pthread_create(&workerthread, NULL, WorkerThread, NULL);
signal(SIGINT, SignalHandler);
sleep((unsigned int)-1); // sleep until the user hits ctrl-c
printf("Returned from sleep call...\n");
SetThreadExitCondition(); // sets global variable with barrier that worker thread checks on
// this is the function I'm stalled on writing
NotifyWorkerThreadToExit(workerthread_handle);
// wait for thread to exit cleanly
pthread_join(workerthread_handle, &ptr_ret);
DoProcessCleanupStuff();
}
Close the socket using the shutdown() call. This will wake up any threads blocked on it, while keeping the file descriptor valid.
close() on a descriptor another thread B is using is inherently hazardous: another thread C may open a new file descriptor which thread B will then use instead of the closed one. dup2() a /dev/null onto it avoids that problem, but does not wake up blocked threads reliably.
Note that shutdown() only works on sockets -- for other kinds of descriptors you likely need the select+pipe-to-self or cancellation approaches.
You can use a pipe to notify the thread that you want it to exit. Then you can have a select() call which selects on both the pipe and the listening socket.
For example (compiles but not fully tested):
// NotifyPipe.h
#ifndef NOTIFYPIPE_H_INCLUDED
#define NOTIFYPIPE_H_INCLUDED
class NotifyPipe
{
int m_receiveFd;
int m_sendFd;
public:
NotifyPipe();
virtual ~NotifyPipe();
int receiverFd();
void notify();
};
#endif // NOTIFYPIPE_H_INCLUDED
// NotifyPipe.cpp
#include "NotifyPipe.h"
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
NotifyPipe::NotifyPipe()
{
int pipefd[2];
int ret = pipe(pipefd);
assert(ret == 0); // For real usage put proper check here
m_receiveFd = pipefd[0];
m_sendFd = pipefd[1];
fcntl(m_sendFd,F_SETFL,O_NONBLOCK);
}
NotifyPipe::~NotifyPipe()
{
close(m_sendFd);
close(m_receiveFd);
}
int NotifyPipe::receiverFd()
{
return m_receiveFd;
}
void NotifyPipe::notify()
{
write(m_sendFd,"1",1);
}
Then select with receiverFd(), and notify for termination using notify().
Close the listening socket and accept will return an error.
What doesn't reliably work with this? Describe the problems you're facing.
pthread_cancel to cancel a thread blocked in accept() is risky if the pthread implementation does not implement cancellation properly, that is if the thread created a socket, just before returning to your code, a pthread_cancel() is called for it, the thread is canceled, and the newly created socket is leaked. Although FreeBSD 9.0 and later does not have such a race condition problem, but you should check your OS first.

Resources