Oninit Logo
The Down System Specialists
+1-913-674-0360
+44-2081-337529
Partnerships Contact

Preventing Orphaned backend processes

When the front end applications dies or is killed, then it is possible for the backend process not to realise that the connection has been lost and it fails to close. It is more common to see this with SE engines, and in the following discussion a SE is assumed, but it still applies to other engines.

The front-end application fork off and exec a sqlexec process resulting in the classic parent-child process interaction. The parent and child communicate via pipes.

When a child process dies or is killed then the operating system sends a signal (SIGCHLD) to the parent process, which the parent should trap and handle accordingly.

It is not as straightforward if the parent process dies, however, as no signal will be sent to the child. So the child must have alternative methods for detecting the death of its parent.

In the frontend/backend scenario there are two ways of notifiying the child. Firstly by getting a -1 or 0 return from the blocked read on the pipe which tells the child that the parent process is no longer there. The second is by getting a SIGPIPE signal when it tries to write to the pipe. Both of these methods work without major problems as long at the backend process is still trying to communicate with the frontend. If it doesn't try to communicate then it has not way of knowing that the parent had died and the backend process is now orphaned.

The backend process is designed to trap signals, and the application needs to use this fact to interact with the backend. The backend process will trap the SIGTERM signal, setting a flag which is periodically checked. When the flag is detected the backend will wait for further instructions from the application before continuing.

There are two library functions that can be used to set the flag in the backend proceess.

sqlbreak() will send a signal (SIGTERM) to the backend forcing it to listen to the application. The backend might not react to this function immediately.

sqlexit() will cause the backend to terminate. By closing the communication pipes at the application end the backend wil terminate if it is blocking on the pipe read.

This means if the frontend issues both library calls the backend will terminate correctly, it will be forced to listen to the frontend and then find it's pipe closed and therefore close cleanly. What is required is the frontend code to close the backend process before it dies. This can almost always be done by proper signal handling. These are the signals defined in Sun Unix: (the file is /usr/include/sys/signal.h)

SIGHUP 1 hangup
SIGINT 2 interrupt
SIGQUIT 3 quit
SIGILL 4 illegal instruction
SIGTRAP 5 trace trap
SIGABRT 6 abort
SIGEMT 7 emulator trap
SIGFPE 8 arithmetic exception
SIGKILL 9 kill
SIGBUS 10 bus error
SIGSEGV 11 segmentation violation
SIGSYS 12 bad argument to system call
SIGPIPE 13 write on a pipe or other socket with nothing to read it
SIGALRM 14 alarm clock
SIGTERM 15 software termination signal
SIGURG 16 urgent condition present on socket
SIGSTOP 17 stop
SIGTSTP 18 stop signal generated from keyboard
SIGCONT 19 continue after stop
SIGCHLD 20 child status has changed
SIGTTIN 21 background read attempted from control terminal
SIGTTOU 22 background write attempted to control terminal
SIGIO 23 I/O is possible on a descriptor
SIGXCPU 24 cpu time limit exceeded
SIGXFSZ 25 file size limit exceeded
SIGVTALRM 26 virtual time alarm
SIGPROF 27 profiling timer alarm
SIGWINCH 28 window changed
SIGLOST 29 resource lost
SIGUSR1 30 user-defined signal 1
SIGUSR2 31 user-defined signal 2


The highlighted signals will cause a core image if controlled.

The most important signal is SIGKILL, this can not be trapped or ignored. The use of 'kill -9' is the most common cause for orphaned backend processes.


Below is an example of ESQL code that can be used for signal handling.

#include <stdio.h>
#include <signal.h>

EXEC SQL include sqlca.h;  


#define ERROR sqlca.sqlcode
#define CISAM sqlca.sqlerrd[1]

int sighand(void); 

int main(int argc, char *argv[])
{
	EXEC SQL BEGIN DECLARE SECTION;
		char dbname[18];
	EXEC SQL END DECLARE SECTION;

	signal(SIGHUP,  sighand);
	signal(SIGINT,  sighand);
	signal(SIGQUIT, sighand);
	signal(SIGFPE,  sighand); 
	signal(SIGBUS,  sighand);
	signal(SIGSEGV, sighand);
	signal(SIGTERM, sighand);
	
	/* The above could be replaced by 
	int i;
	for(i=1; i<NSIG; i++)
		signal(i, sighand);
	*/

	strcpy(dbname, argv[1]);
	EXEC SQL DATABASE :dbname;

	/* 
	The main code lives from here
	*/

	EXEC SQL CLOSE DATABASE;
	exit(0);
}

/* signal handling function */
int sighand() 
{

	sqlbreak(); 
	sqlexit();
	exit(1); 
}

To discuss how Oninit ® can assist please call on +1-913-674-0360 or alternatively just send an email specifying your requirements.