start.c

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

Generated on Sun Apr 6 17:31:38 2008 for GRASS by  doxygen 1.5.5