Re: sqlexit funny behavior
Posted in 1997
Handling fork/exec in programs connected to a database is fiddly. If it is available, your child process should use sqldetach() to break its copy of the connection to the database. It is designed to detach the child process from a database connection. The child cannot reliably use the connection, anyway. If sqldetach() is not available, then you have to work around the problems. When file descriptors are in used by Informix, they should have the close-on-exec flag set; they don't, but they should. One way to fix this would be to work out which file descriptors are being used by Informix, and to set the flag on them yourself. The code below (from Stefan) shows one way in which you can find the plausible descriptors. Another way would be to loop through the file descriptors from number 3 upwards before starting the database, noting which ones are in use, and then scanning again after the database connection is in place to find which ones are now in use (you might base that on the code in fd.c attached below). You might well find three file descriptors in use by Informix, two for communications with the engine and one for an error message file. Using POSIX compliant headers, etc, the code required to set the close-on-exec flag on a file descriptor is: #include <fcntl.h> int set_close_on_exec(int fd) { return(fcntl(fp, F_SETFD, FD_CLOEXEC)); } Note that the FD_CLOEXEC method only works if your child process does in fact exec a new program. If it doesn't, then the child process should simply close all the file descriptors that you neither inherited nor opened. If you are using olipcshm connections, this won't work either -- you don't have a file descriptor for you connection. On the other hand, if you're using olipcshm, you also have sqldetach(), so it isn't actually a problem. Incidentally, under Solaris 2.5.1, my shell has a large number of inherited open file descriptors. There's a trivial program which will tell you which file descriptors are open. For me, it gives the output: ooo-o-o--oo----------------------------------------------------- That means that stdin, stdout, stderr are open, plus descriptors 4, 6, 9 and 10. For the code, see below... Yours, Jonathan Leffler (johnl@informix.com) #include <disclaimer.h> >From: Stefan Weideneder <stefan@sweidi.weideneder.de> >Date: Fri, 07 Mar 1997 19:26:31 +0100 >X-Informix-List-Id: <news.34914> > >andy lennard wrote: >> We had a similar problem, which was caused by opening a pipe after having >> attached to a database. >> >> Normally sqlturbo is blocked on a read from the pipe it has >> established with the client app. sqlexit closes this pipe at the >> client end, sqlturbo gets an error and closes down. >> >> However, after our popen the fds have been duplicated in the forked >> process (including the database connection) and the sqlturbo sits >> waiting on this instead. No error is generated and so sqlexit hangs. > >----------------------------------------------------------------------- > >int pid_i; >int fd_vi[4]; > >pipe( fd_vi ); // do your error handling >pipe( fd_vi + 2 ); >close(fd_vi[ 0 ]); // The following is defined for UNIX >close(fd_vi[ 1 ]); // An open() will always return the >close(fd_vi[ 2 ]); // first unused channel. Now we now >close(fd_vi[ 3 ]); // the channels. Informix will open > // two pipes, one for reading, another > // one for writing. > >EXEC SQL DATABASE ... ; > >if ( ( pid_i = fork() ) == 0 ) >{ > // that's just another way to close the connection > // to your sqlturbo process. I'm not sure, but I > // think it will work; > close( fd_vi[3] ); > close( fd_vi[2] ); > close( fd_vi[1] ); > close( fd_vi[0] ); > > // The sqlturbo will never get an EOF, unless all > // open writing file descriptors are closed. > // That's what we did above. There were two reading > // file descriptors, but I don't want to look, how > // Informix is using it's pipes. > > ... do whatever you want. > > // There are special functions in ESQL/C like > // sqlquit(), which do exactly the same. But if > // you don't trust in this functions, use the code > // above. >} >else if ( pid _i > 0 ) >{ > // The parent process will still have it's open > // connection to the sqlturbo. > >} > >> The fix was to either to pclose before sqlexit, or re-do popen to close >> unnecessary fds. >> >> Hope that helps. >> >> Andy. >> >> > We have several server-type applications when started, open a database, >> > read tables for initialization, close the database via E/SQL and then >> > remain idle for hours or days until called upon. This situation seemed >> > perfect for sqlexit to free the database resources used during >> > initialization. When the servers' initialization are finished, sqlexit is >> > called after the CLOSE DATABASE statement. >> > >> > The problem appears when the server wakes up, does its work, closes >> > the database and then calls sqlexit for the second time. From the >> > debugger, the server goes into an infinite loop inside sqlexit. The >> > stack track shows an infinite loop in the following routines: >> > >> > sqlexit >> > killbackend >> > wait >> > _wait_sys >> > $cerror >> > >> > The ESQL product is INFORMIX-ESQL Version 5.03.UC1 >> > The On-Line server is RSAM Version 5.03.UC1 >> > The hardware and OS are HP-UX 09.01 A 9000/755 and >> > HP-UX 10.20 A 9000/755. >> > >> > The sqlexit routine would be a very nice thing to have working >> > properly in these server applications. What I am doing wrong? >> > >> > Any help and/or advice would be appreciated on this. /* @(#)File: fd.c @(#)Version: 1.2 @(#)Last changed: 97/02/05 @(#)Purpose: Print pattern of open file descriptors @(#)Author: J Leffler @(#)Copyright: (C) JLSS 1989,1997 */ /*TABSTOP=4*/ #include <limits.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #ifndef lint static const char sccs[] = "@(#)fd.c 1.2 97/02/05"; #endif static int max_open(void) { #if defined(OPEN_MAX) return(OPEN_MAX); #elif defined(_SC_OPEN_MAX) return(sysconf(_SC_OPEN_MAX)); #elif defined(_POSIX_OPEN_MAX) return(_POSIX_OPEN_MAX); #else return(20); #endif } int main(void) { int fd; int max_fd = max_open(); struct stat buff; for (fd = 0; fd < max_fd; fd++) { if (fstat(fd, &buff) < 0) putchar('-'); else putchar('o'); } putchar('\\n'); return(0); }