start.c

Go to the documentation of this file.
00001 
00015 #include <string.h>
00016 #include <stdlib.h>
00017 #include <unistd.h>
00018 
00019 #ifdef __MINGW32__
00020 #include <windows.h>
00021 #include <process.h>
00022 #include <fcntl.h>
00023 #endif
00024 
00025 #include <grass/dbmi.h>
00026 
00027 #define READ  0
00028 #define WRITE 1
00029 
00030 
00042 dbDriver *db_start_driver(const char *name)
00043 {
00044     dbDriver *driver;
00045     dbDbmscap *list, *cur;
00046     const char *startup;
00047     int p1[2], p2[2];
00048     int pid;
00049     int stat;
00050     dbConnection connection;
00051     char ebuf[5];
00052 
00053 #ifdef __MINGW32__
00054     int stdin_orig, stdout_orig;
00055     int have_stdin, have_stdout;
00056     int stdin_fd, stdout_fd;
00057 #endif
00058 
00059     /* Set some environment variables which are later read by driver.
00060      * This is necessary when application is running without GISRC file and all
00061      * gis variables are set by application. 
00062      * Even if GISRC is set, application may change some variables during runtime,
00063      * if for example reads data from different gdatabase, location or mapset*/
00064 
00065     /* setenv() is not portable, putenv() is POSIX, putenv() in glibc 2.0-2.1.1 doesn't conform to SUSv2,
00066      * G_putenv() as well, but that is what we want, makes a copy of string */
00067     if (G_get_gisrc_mode() == G_GISRC_MODE_MEMORY) {
00068         G_debug(3, "G_GISRC_MODE_MEMORY\n");
00069         sprintf(ebuf, "%d", G_GISRC_MODE_MEMORY);
00070         G_putenv("GRASS_DB_DRIVER_GISRC_MODE", ebuf);   /* to tell driver that it must read variables */
00071 
00072         if (G__getenv("DEBUG")) {
00073             G_putenv("DEBUG", G__getenv("DEBUG"));
00074         }
00075         else {
00076             G_putenv("DEBUG", "0");
00077         }
00078 
00079         G_putenv("GISDBASE", G__getenv("GISDBASE"));
00080         G_putenv("LOCATION_NAME", G__getenv("LOCATION_NAME"));
00081         G_putenv("MAPSET", G__getenv("MAPSET"));
00082     }
00083     else {
00084         /* Warning: GISRC_MODE_MEMORY _must_ be set to G_GISRC_MODE_FILE, because the module can be 
00085          *          run from an application which previously set environment variable to G_GISRC_MODE_MEMORY */
00086         sprintf(ebuf, "%d", G_GISRC_MODE_FILE);
00087         G_putenv("GRASS_DB_DRIVER_GISRC_MODE", ebuf);
00088     }
00089 
00090     /* read the dbmscap file */
00091     if (NULL == (list = db_read_dbmscap()))
00092         return (dbDriver *) NULL;
00093 
00094     /* if name is empty use connection.driverName, added by RB 4/2000 */
00095     if (name == '\0') {
00096         db_get_connection(&connection);
00097         if (NULL == (name = connection.driverName))
00098             return (dbDriver *) NULL;
00099     }
00100 
00101     /* find this system name */
00102     for (cur = list; cur; cur = cur->next)
00103         if (strcmp(cur->driverName, name) == 0)
00104             break;
00105     if (cur == NULL) {
00106         char msg[256];
00107 
00108         db_free_dbmscap(list);
00109         sprintf(msg, "%s: no such driver available", name);
00110         db_error(msg);
00111         return (dbDriver *) NULL;
00112     }
00113 
00114     /* allocate a driver structure */
00115     driver = (dbDriver *) db_malloc(sizeof(dbDriver));
00116     if (driver == NULL) {
00117         db_free_dbmscap(list);
00118         return (dbDriver *) NULL;
00119     }
00120 
00121     /* copy the relevant info from the dbmscap entry into the driver structure */
00122     db_copy_dbmscap_entry(&driver->dbmscap, cur);
00123     startup = driver->dbmscap.startup;
00124 
00125     /* free the dbmscap list */
00126     db_free_dbmscap(list);
00127 
00128     /* run the driver as a child process and create pipes to its stdin, stdout */
00129 
00130 #ifdef __MINGW32__
00131     /* create pipes (0 in array for reading, 1 for writing) */
00132     /* p1 : module -> driver, p2 driver -> module */
00133 
00134     /* I have seen problems with pipes on NT 5.1 probably related
00135      * to buffer size (psize, originaly 512 bytes). 
00136      * But I am not sure, some problems were fixed by bigger 
00137      * buffer but others remain. 
00138      * Simple test which failed on NT 5.1 worked on NT 5.2 
00139      * But there are probably other factors. 
00140      */
00141     /* More info about pipes from MSDN:
00142        - Anonymous pipes are implemented using a named pipe 
00143        with a unique name.
00144        - CreatePipe() - nSize :
00145        ... The size is only a suggestion; the system uses 
00146        the value to calculate an appropriate buffering 
00147        mechanism. ...
00148        => that that the size specified is not significant 
00149        - If the pipe buffer is full before all bytes are written, 
00150        WriteFile does not return until another process or thread 
00151        uses ReadFile to make more buffer space available.
00152        (Which does not seem to be true on NT 5.1)
00153      */
00154     if (_pipe(p1, 250000, _O_BINARY) < 0 || _pipe(p2, 250000, _O_BINARY) < 0) {
00155         db_syserror("can't open any pipes");
00156         return (dbDriver *) NULL;
00157     }
00158 
00159     /* convert pipes to FILE* */
00160     driver->send = fdopen(p1[WRITE], "wb");
00161     driver->recv = fdopen(p2[READ], "rb");
00162 
00163     fflush(stdout);
00164     fflush(stderr);
00165 
00166     /* Set pipes for stdin/stdout driver */
00167 
00168     have_stdin = have_stdout = 1;
00169 
00170     if (_fileno(stdin) < 0) {
00171         have_stdin = 0;
00172         stdin_fd = 0;
00173     }
00174     else {
00175         stdin_fd = _fileno(stdin);
00176 
00177         if ((stdin_orig = _dup(_fileno(stdin))) < 0) {
00178             db_syserror("can't duplicate stdin");
00179             return (dbDriver *) NULL;
00180         }
00181 
00182     }
00183 
00184     if (_dup2(p1[0], stdin_fd) != 0) {
00185         db_syserror("can't duplicate pipe");
00186         return (dbDriver *) NULL;
00187     }
00188 
00189     if (_fileno(stdout) < 0) {
00190         have_stdout = 0;
00191         stdout_fd = 1;
00192     }
00193     else {
00194         stdout_fd = _fileno(stdout);
00195 
00196         if ((stdout_orig = _dup(_fileno(stdout))) < 0) {
00197             db_syserror("can't duplicate stdout");
00198             return (dbDriver *) NULL;
00199         }
00200 
00201     }
00202 
00203     if (_dup2(p2[1], stdout_fd) != 0) {
00204         db_syserror("can't duplicate pipe");
00205         return (dbDriver *) NULL;
00206     }
00207 
00208     /* Warning: the driver on Windows must have extension .exe
00209      *          otherwise _spawnl fails. The name used as _spawnl 
00210      *          parameter can be without .exe 
00211      */
00212     /* spawnl() works but the process inherits all handlers, 
00213      * that means, for example p1[WRITE] remains open and if 
00214      * module exits the pipe is not close and driver remains running. 
00215      * Using CreateProcess() + SetHandleInformation() does not help.
00216      * => currently the only know solution is to close all file 
00217      *    descriptors in driver (which is another problem)
00218      */
00219 
00220     G_debug(2, "dbmi_client/start startup: [%s]", startup);
00221     pid = _spawnl(_P_NOWAIT, startup, startup, NULL);
00222 
00223     /* This does not help. It runs but pipe remains open when close() is 
00224      * called in model but caling close() on that descriptor in driver gives 
00225      * error. */
00226     /* 
00227        {
00228        STARTUPINFO    si;
00229        PROCESS_INFORMATION  pi;
00230 
00231        GetStartupInfo(&si);
00232 
00233        SetHandleInformation ( stdin, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
00234        SetHandleInformation ( stdout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
00235        SetHandleInformation ( driver->send, HANDLE_FLAG_INHERIT, 0);
00236        SetHandleInformation ( driver->recv, HANDLE_FLAG_INHERIT, 0);
00237 
00238        CreateProcess(NULL, startup, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
00239        }
00240      */
00241 
00242     /* Reset stdin/stdout for module and close duplicates */
00243     if (have_stdin) {
00244         if (_dup2(stdin_orig, _fileno(stdin)) != 0) {
00245             db_syserror("can't reset stdin");
00246             return (dbDriver *) NULL;
00247         }
00248         close(stdin_orig);
00249     }
00250 
00251 
00252     if (have_stdout) {
00253         if (_dup2(stdout_orig, _fileno(stdout)) != 0) {
00254             db_syserror("can't reset stdout");
00255             return (dbDriver *) NULL;
00256         }
00257         close(stdout_orig);
00258     }
00259 
00260     if (pid == -1) {
00261         db_syserror("can't _spawnl");
00262         return (dbDriver *) NULL;
00263     }
00264 
00265     /* record driver process id in driver struct */
00266     driver->pid = pid;
00267 
00268     /* most systems will have to use unbuffered io to get the 
00269      *  send/recv to work */
00270 #ifndef USE_BUFFERED_IO
00271     setbuf(driver->send, NULL);
00272     setbuf(driver->recv, NULL);
00273 #endif
00274 
00275     db__set_protocol_fds(driver->send, driver->recv);
00276     if (db__recv_return_code(&stat) != DB_OK || stat != DB_OK)
00277         driver = NULL;
00278 
00279     return driver;
00280 
00281 #else /* __MINGW32__ */
00282 
00283     /* open the pipes */
00284     if ((pipe(p1) < 0) || (pipe(p2) < 0)) {
00285         db_syserror("can't open any pipes");
00286         return (dbDriver *) NULL;
00287     }
00288 
00289     /* create a child */
00290     if ((pid = fork()) < 0) {
00291         db_syserror("can't create fork");
00292         return (dbDriver *) NULL;
00293     }
00294 
00295     if (pid > 0) {              /* parent */
00296         close(p1[READ]);
00297         close(p2[WRITE]);
00298 
00299         /* record driver process id in driver struct */
00300         driver->pid = pid;
00301 
00302         /* convert pipes to FILE* */
00303         driver->send = fdopen(p1[WRITE], "wb");
00304         driver->recv = fdopen(p2[READ], "rb");
00305 
00306         /* most systems will have to use unbuffered io to get the send/recv to work */
00307 #ifndef USE_BUFFERED_IO
00308         setbuf(driver->send, NULL);
00309         setbuf(driver->recv, NULL);
00310 #endif
00311 
00312         db__set_protocol_fds(driver->send, driver->recv);
00313         if (db__recv_return_code(&stat) != DB_OK || stat != DB_OK)
00314             driver = NULL;
00315 
00316         return driver;
00317     }
00318     else {                      /* child process */
00319 
00320         close(p1[WRITE]);
00321         close(p2[READ]);
00322 
00323         close(0);
00324         close(1);
00325 
00326         if (dup(p1[READ]) != 0) {
00327             db_syserror("dup r");
00328             _exit(EXIT_FAILURE);
00329         }
00330 
00331         if (dup(p2[WRITE]) != 1) {
00332             db_syserror("dup w");
00333             _exit(EXIT_FAILURE);
00334         }
00335 
00336         execl(startup, startup, NULL);
00337 
00338         db_syserror("execl");
00339         return NULL;            /* to keep lint, et. al. happy */
00340     }
00341 
00342 #endif /* __MINGW32__ */
00343 }