How to implement SIGKILL and SIGTERM and print a message?

Hello,

I am running a webserver that uses sockets, forks, and children. The parent process listens for connections and the child processes the information.

I am trying to figure out why the code I have below SIGTERM, and SIGKILL never fire. I was messing around with the printfs and doesnt seem to be entering the switch for sigterm and sigkill and sighup.

I do have sigint and sighup working.

Can someone help me fix this code so that the server gracefully terminates when a sigkill and sigterm are detected. I was trying to get it so when the signal is recieved to terminate, the printf would write out terminating or something(basically so i know its working).


//int main code
 //ignore sighup signal, you can log out and server still runs.
    signal(SIGHUP,catcher);

 //stop user from using control c.
    signal(SIGINT, catcher);


    //not working
  //  signal(SIGTERM, catcher);
  //  signal(SIGKILL, catcher);

FUNCTIONS:---------------------------------------------------------------
//gracefully quit on sigterm and sigkill.
void catcher(int sig)
{

  printf ("Caught signal %d\n", sig);
  fflush(stdout);
  switch (sig)
        {
        case SIGINT:

          signal(SIGINT, catcher);
          printf("Control-C not Allowed\n");
          fflush(stdout);
          break;

        case SIGHUP:

          signal(SIGHUP,catcher); /* dont die on sighup */
          printf("SIGHUP Ignored\n");
          fflush(stdout);
          break;


          case SIGTERM:

          signal(SIGTERM, catcher);
          printf("exiting sigterm");
          exit(1);
          fflush(stdout);
          break;

        case SIGKILL:
          printf("sigkilla");
          fflush(stdout);
          signal(SIGKILL, catcher);
          printf("sigkilla");
          exit(1);
          fflush(stdout);
          break;
  }




  1. You can't catch SIGKILL by design.

  2. You should be able to catch SIGTERM -

    Have you unblocked it?
    You are calling exit in your signal handler
    Have you considered using atexit()

In general two things leap out...

  1. you should do the absolute bare minimum in a signal handler, set a flag or a few things, not fflush() or anything the allocates/frees from the heap.

  2. use the posix signal handling APIs, sigaction, sigprocmask etc.

Hmmm, I was playing around with a linux firewall and when I closed it down I noticed while the firewall was shutting down, it said, "sending sigkill" "sending sigterm". Thats basically where I got the idea.

Ill have to look in to the unblocking, and the posix. I didnt realize signals where blocked. I thought when i declare in int main to not use default action that would mean the signals have to go through my code.

anyone else feel free to offer opinions.

thanks,
norelco55

Porter is right about keeping things simple in a signal handler. But there is more - do not call most routines in the standard C library - as Porter points out fflush() is one of them.

If you absolutely must do I/O use write(). The idea is that you call something that cannot be interrupted by another signal - this is called an atomic operation.

write() is "partially" guaranteed to be atomic, but is as close as you'll get. POSIX says write() must guarantee a write the size of PIPE_BUF bytes or smaller to be atomic. There are other I/O system calls that are like this.

There are basically two types of signals...

  1. synchronous signals, eg SIGSEGV, SIGILL etc

  2. asynchronous signals, eg SIGCHLD, SIGQUIT, SIGINT etc

The first type mean something has gone wrong with execution and either you die or have to take some immediate remedy.

The second type are just notifying you that an external event has occured.

I normally structure my async handlers such that the main program is reading input using select or poll and has a pipe dedicated to asynchronous events which I read from.

When a signal occurs I simply write the number of the signal to the write end of the pipe, then let the main loop pickup the notification by reading the pipe and doing the appropriate response during normal processing.

Here is something I had before, which was to ignore control c. How do i unblock a signal? And how is this code? I changed the handler to work with my catcher function now.


//declare act to deal with action on signal set.
static struct sigaction act;

//setting to my function catcher

 act.sa_handler = catcher;       
 act.sa_flags    = 0;
 
 sigfillset(&(act.sa_mask));      /* create full set of signals */




    //ignore control c and dont hangup when loggedout.
    signal(SIGINT, SIG_IGN);
    signal(SIGHUP, SIG_IGN);


     //Terminate gracefully
     sigaction( SIGTERM, &act, NULL );
     sigaction( SIGKILL  , &act, NULL );


----------------------------------------------------------------
void catcher(int sig)
{

    switch (sig)
        {
          case SIGTERM:

                            signal(SIGTERM, catcher);
                            atexit();
           break;

        case SIGKILL:

                         signal(SIGKILL, catcher);
                         atexit();
          break;
       }
  }

thanks,
Norelco

  1. Both sigaction and signal install signal handlers,

(a) don't use both in the same program,
(b) sigaction is the posix one, use that.
(c) With sigaction you don't need to reinstall the signal handler.

  1. atexit installs another callback function, so when the program exits politely, through a call to exit you code will get called so you can tidy up.

  2. only set flags in the signal handlers, don't do 'work'.

  3. you have done sigfillset but done nothing with the sigset_t, you need to call sigprocmask to block or unblock those signals.

  4. to ignore a signal, you can

(a) either use SIG_IGN as the callback
(b) or simply block it so it is never delivered to your program.
(c) or get it to call an empty signal handler function

  1. compile with maximum warnings, if you are using gcc use "-Wall -Werror", that would have caught your "atexit".

  2. And as mentioned, you can't catch SIGKILL, the kernel will just evaporate your process.

Are there any list of function calls that should not be called in a signal handler ?
I have been thinking write () is uninterrupted until it is done - this is something new from the statement that it is ' partially ' atomic.

Is there any way I could test this behavior by some kind of simulation ?

Here is one more point / question ,
with newer signal semantics - using sigaction

kernel guarantees the binary that when executing the handler registered for a possible signal, within that block another signal of the same kind would not be delivered until it returns - in such case why shouldn't a fflush operation be used ?

Different operating systems have different implementations of functions, so it is better to err on the side of safety. It would be simpler to have a list of functions that can be called.

Also depends if you are using non-blocking IO.

Is there any way I could test this behavior by some kind of simulation ?

Here is one more point / question ,
with newer signal semantics - using sigaction

Good point, except alot of memory management his handled inside libc, not the kernel, so a signal can occur in the middle of, say, malloc or free, while the routine is updating a list of free memory pointers, if you interrupt and call in the middle another memory allocation routine you could upset the apple cart. Basically, those routines not being reentrant.

If you move onto threaded code, you don't use signal handlers at all, you use sigwait() to return you a signal once it occurs.

Thanks for all the help. I have everything working except KIll -9, But I am learning in stages thanks to you all :).

questions:

  1. I am still confused about using atexit(). Isn't wastefull to resources to call another function that just exits? All I need to do really is call exit(); I havent implemented logging or anything and all filehandles and kids are closed.

  2. I still need to work on the printfs..I just got back from AC at 2am. Banged this out when I got back--haha.

  3. "you have done sigfillset but done nothing with the sigset_t, you need to call sigprocmask to block or unblock those signals."

I am still working on this one!!

  1. There has to be a way to at least know sigkill is coming so I can print before it happens. I say this because some linux distros show the sigkill or are they faking it with sigterm? I did notice sigterm and sigkill are always together.

Here is what I Have now: Feel free to critique(so I don't learn bad Habits) :).

So, is flush in or out? I am thinking, I dont need it because when I exit, that should clean up right?

static struct sigaction act; 


sigemptyset (&act.sa_mask);
  act.sa_flags = 0;

  /* Register the handler for SIGINT To Ignore. */
  act.sa_handler = SIG_IGN;
  sigaction (SIGINT, &act, NULL);

  /* Register the handler for SIGHUP To Ignore */
  act.sa_handler = SIG_IGN;
  sigaction (SIGHUP, &act, NULL);


       //calls function catcher.

  /* Register the handler for SIGTERM. */
  act.sa_handler =  catcher;
  sigaction (SIGTERM, &act, 0);


  //SIGKILL CANNOT BE CAUGHT!! KILL -9
  /* Register the handler for SIGKILL. */
  act.sa_handler =  catcher;
  sigaction (SIGKILL, &act, 0);




=======================================================
//gracefully quit on sigterm and sigkill.
void catcher(int sig)
{

  printf ("Caught signal %d\n", sig);
  fflush(stdout);
  switch (sig)

   case SIGTERM:

          printf("exiting sigterm");
          exit(1);
          fflush(stdout);
          break;

        case SIGKILL:

          printf("sigkilla");
          exit(1);
          fflush(stdout);
          break;
  }
}   // end function catcher

  1. "atexit" does not call exit, it installs a callback function that will be called when the process does exit.

  2. exit(int) is a function that does not return, there is no point in having code following it, eg fflush().

3.remember, the code in a signal handler:

(a) just be setting variables/flags
(b) using minimal IO, write for example may be used, but nothing that uses the heap
(c) siglongjmp may be used, give that a go.....

thanks for all the help.