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     pid = _spawnl(_P_NOWAIT, startup, startup, NULL);
00221 
00222     /* This does not help. It runs but pipe remains open when close() is 
00223      * called in model but caling close() on that descriptor in driver gives 
00224      * error. */
00225     /* 
00226        {
00227        STARTUPINFO    si;
00228        PROCESS_INFORMATION  pi;
00229 
00230        GetStartupInfo(&si);
00231 
00232        SetHandleInformation ( stdin, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
00233        SetHandleInformation ( stdout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
00234        SetHandleInformation ( driver->send, HANDLE_FLAG_INHERIT, 0);
00235        SetHandleInformation ( driver->recv, HANDLE_FLAG_INHERIT, 0);
00236 
00237        CreateProcess(NULL, startup, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
00238        }
00239      */
00240 
00241     /* Reset stdin/stdout for module and close duplicates */
00242     if (have_stdin) {
00243         if (_dup2(stdin_orig, _fileno(stdin)) != 0) {
00244             db_syserror("can't reset stdin");
00245             return (dbDriver *) NULL;
00246         }
00247         close(stdin_orig);
00248     }
00249 
00250 
00251     if (have_stdout) {
00252         if (_dup2(stdout_orig, _fileno(stdout)) != 0) {
00253             db_syserror("can't reset stdout");
00254             return (dbDriver *) NULL;
00255         }
00256         close(stdout_orig);
00257     }
00258 
00259     if (pid == -1) {
00260         db_syserror("can't _spawnl");
00261         return (dbDriver *) NULL;
00262     }
00263 
00264     /* record driver process id in driver struct */
00265     driver->pid = pid;
00266 
00267     /* most systems will have to use unbuffered io to get the 
00268      *  send/recv to work */
00269 #ifndef USE_BUFFERED_IO
00270     setbuf(driver->send, NULL);
00271     setbuf(driver->recv, NULL);
00272 #endif
00273 
00274     db__set_protocol_fds(driver->send, driver->recv);
00275     if (db__recv_return_code(&stat) != DB_OK || stat != DB_OK)
00276         driver = NULL;
00277 
00278     return driver;
00279 
00280 #else /* __MINGW32__ */
00281 
00282     /* open the pipes */
00283     if ((pipe(p1) < 0) || (pipe(p2) < 0)) {
00284         db_syserror("can't open any pipes");
00285         return (dbDriver *) NULL;
00286     }
00287 
00288     /* create a child */
00289     if ((pid = fork()) < 0) {
00290         db_syserror("can't create fork");
00291         return (dbDriver *) NULL;
00292     }
00293 
00294     if (pid > 0) {              /* parent */
00295         close(p1[READ]);
00296         close(p2[WRITE]);
00297 
00298         /* record driver process id in driver struct */
00299         driver->pid = pid;
00300 
00301         /* convert pipes to FILE* */
00302         driver->send = fdopen(p1[WRITE], "wb");
00303         driver->recv = fdopen(p2[READ], "rb");
00304 
00305         /* most systems will have to use unbuffered io to get the send/recv to work */
00306 #ifndef USE_BUFFERED_IO
00307         setbuf(driver->send, NULL);
00308         setbuf(driver->recv, NULL);
00309 #endif
00310 
00311         db__set_protocol_fds(driver->send, driver->recv);
00312         if (db__recv_return_code(&stat) != DB_OK || stat != DB_OK)
00313             driver = NULL;
00314 
00315         return driver;
00316     }
00317     else {                      /* child process */
00318 
00319         close(p1[WRITE]);
00320         close(p2[READ]);
00321 
00322         close(0);
00323         close(1);
00324 
00325         if (dup(p1[READ]) != 0) {
00326             db_syserror("dup r");
00327             _exit(EXIT_FAILURE);
00328         }
00329 
00330         if (dup(p2[WRITE]) != 1) {
00331             db_syserror("dup w");
00332             _exit(EXIT_FAILURE);
00333         }
00334 
00335         execl("/bin/sh", "sh", "-c", startup, NULL);
00336 
00337         db_syserror("execl");
00338         return NULL;            /* to keep lint, et. al. happy */
00339     }
00340 
00341 #endif /* __MINGW32__ */
00342 }

Generated on Thu Jul 16 13:20:15 2009 for GRASS Programmer's Manual by  doxygen 1.5.6