Properly ending a daemon written in C

From Try-AS/400
Jump to navigation Jump to search

Properly ending a daemon, or background task can be done in multiple ways. Since we're using C as programming language, using the UNIX way of handling this affair proved to be most easy.

OS/400 incorporates quite a bit of UNIX APIs, including signals. If you use option 4 on wrkactjob, or end the subsystem the job in question is running within, SIGTERM is being sent to tell the application program to cleanly end.

Using the signal API

In short, declare the necessary infrastructure for a signal, and make it do something.

#include <string.h>
#include <signal.h>
#include <stdlib.h>

/* Using this globally eases handling. */
int exit_flag = 0;

void set_exit_flag(int signum) {
  /* We ignore signum, because we only watch for SIGTERM. */
  exit_flag = 1;
}

int main() {
  struct sigaction termaction;

  /* Create signal handler for SIGTERM. */
  memset(&termaction, 0, sizeof(struct sigaction));
  termaction.sa_handler = set_exit_flag;
  sigaction(SIGTERM, &termaction, NULL);

  /* Main loop. */
  while( exit_flag == 0 ) {
    ...
  }

  /* Add cleanup routines here. */

  exit(0);
}

In our case, we set exit_flag to 1, so the program exits from its main loop at the next iteration.

Blocking Calls

If the application program is "stuck" in kind of a wait condition, it depends on the wait condition if the blocking call is cancelled, allowing the application code to take control again.

This code snippet shows an example how to restart an interrupted recv() from a socket by restarting the while loop.

/* Main loop. */
while( exit_flag == 0 ) {

  /* Handle incoming network data. */
  if ( (n = recv(sockfd, rcvBuf, BUFSIZE, 0)) == -1) {
    if ( errno == EINTR ) {
      continue;
    } else {
      /* Some other error happened. Handle next request. */
      Qp0zLprintf("recv(): %s\n", strerror(errno));
      break;
    }
  }

/* Do things with the received data. */
}

Note: Some OS' automatically restart the last operation if it became interrupted by a signal. OS/400 V4R5 does not, so we restart in a loop, making this example a generic restart function, if EINTR is given as error reason.