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
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.