diff options
Diffstat (limited to 'util/mainlib.c')
-rw-r--r-- | util/mainlib.c | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/util/mainlib.c b/util/mainlib.c new file mode 100644 index 0000000..fd3da89 --- /dev/null +++ b/util/mainlib.c @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2004 Smithsonian Astrophysical Observatory + */ + +/* + * mainlib.c -- support for main routines called as library subroutines + * + */ + +#include <mainlib.h> + +extern char *optarg; +extern int optind; + +/* + * + * private routines + * + */ + +/* + *---------------------------------------------------------------------------- + * + * Routine: Pipe2Buf + * + * Purpose: read pipe til EOF and fill buffer with results + * + * Results: number of bytes read (and buffer in passed argument) + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +Pipe2Buf(int fd, char **buf) +#else +static int Pipe2Buf(fd, buf) + int fd; + char **buf; +#endif +{ + int got; + int clen=0; + int blen=0; + + /* initial buffer size */ + blen = SZ_LINE*10; + *buf = xmalloc(blen); + + /* read til EOF and fill buffer */ + while( 1 ){ + /* reallocate as needed */ + if( (clen + SZ_LINE) >= blen ){ + blen += SZ_LINE*10; + *buf = xrealloc(*buf, blen); + } + /* read */ + got=read(fd, *buf+clen, SZ_LINE); + /* got data */ + if( got > 0 ){ + clen += got; + continue; + } + /* EOF */ + else if( got == 0 ){ + /* reallocate to actual size, but null terminate as a courtesy */ + blen = clen+1; + *buf = xrealloc(*buf, blen); + *(*buf+clen) = '\0'; + break; + } + /* error */ + else{ + xfree(*buf); + blen = -1; + break; + } + } + /* return the news */ + return blen; +} + +/* + *--------------------------------------------------------------------------- + * + * Routine: MainLibListAdd + * + * Purpose: add a member of an mainlib list + * + * Results: 1 on success, 0 for failure + * + *--------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +MainLibListAdd (MainLib ml, MainLibEntry mle) +#else +static void MainLibListAdd(ml, mle) + MainLib ml; + MainLibEntry mle; +#endif +{ + MainLibEntry cur; + + if( ml->head == NULL ){ + ml->head = mle; + } + else{ + for(cur=ml->head; cur->next!=NULL; cur=cur->next) + ; + cur->next = mle; + } +} + +/* + *--------------------------------------------------------------------------- + * + * Routine: MainLibListDel + * + * Purpose: remove a member of an mainlib list + * + * Results: 1 on success, 0 for failure + * + *--------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +MainLibListDel (MainLib ml, MainLibEntry mle) +#else +static void MainLibListDel(ml, mle) + MainLib ml; + MainLibEntry mle; +#endif +{ + MainLibEntry cur; + + /* remove from list of mainlibs */ + if( ml->head ){ + if( ml->head == mle ){ + ml->head = mle->next; + } + else{ + for(cur=ml->head; cur!=NULL; cur=cur->next){ + if( cur->next == mle ){ + cur->next = mle->next; + break; + } + } + } + } +} + +/* + * + * semi-public routines + * + */ + +/* + *--------------------------------------------------------------------------- + * + * Routine: MainLibLookup + * + * Purpose: lookup a mainlib procedure + * + * Results: MainLibEntry record on success, NULL on failure + * + *--------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +MainLibEntry +MainLibLookup (MainLib ml, char *xclass, char *name) +#else +MainLibEntry MainLibLookup(ml, xclass, name) + MainLib ml; + char *xclass; + char *name; +#endif +{ + MainLibEntry cur; + + if( !ml || (!xclass && !name) ) return NULL; + + for(cur=ml->head; cur!=NULL; cur=cur->next){ + if( (!xclass || !strcmp(xclass, cur->xclass)) && + (!name || !strcmp(name, cur->name)) ){ + return cur; + } + } + return NULL; +} + +/* + * + * public routines + * + */ + +/* + *---------------------------------------------------------------------------- + * + * Routine: MainLibNew + * + * Purpose: create mainlib struct + * + * Returns: mainlib list handle or NULL + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +MainLib +MainLibNew (void) +#else +MainLib MainLibNew() +#endif +{ + MainLib ml; + + /* allocate struct */ + if( !(ml = (MainLib)xcalloc(1, sizeof(MainLibRec))) ) + return NULL; + return ml; +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: MainLibAdd + * + * Purpose: add a mainlib name to allow external processes to be called + * as subroutines + * + * Returns: mainlib handle associated with this name or NULL + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +MainLibEntry +MainLibAdd (MainLib ml, char *xclass, char *name, MainLibProc proc, int type) +#else +MainLibEntry MainLibAdd(ml, xclass, name, proc, type) + MainLib ml; + char *xclass; + char *name; + MainLibProc proc; + int type; +#endif +{ + MainLibEntry mle; + + /* sanity check */ + if( !ml ) return NULL; + + /* allocate struct */ + if( !(mle = (MainLibEntry)xcalloc(1, sizeof(MainLibEntryRec))) ) return NULL; + /* fill in the blanks */ + mle->xclass = xstrdup(xclass); + mle->name = xstrdup(name); + mle->proc = proc; + mle->type = type; + /* add to list */ + MainLibListAdd(ml, mle); + return mle; +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: MainLibProcess + * + * Purpose: call external processes as subroutines + * + * Results: number of bytes in return buffer (buffer in passed argument) + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int MainLibProcess(MainLib ml, char *cmd, char **buf, char *mode) +#else +int MainLibProcess(ml, cmd, buf, mode) + MainLib ml; + char *cmd; + char **buf; + char *mode; +#endif +{ + int i, j; + int got; + int ncmd=0; + int ip=0; + int ifd=-1; + int ofd=-1; + int efd=-1; + int fillbuf=1; + int debug=0; + int use_ifd=0; + int use_ofd=0; + int use_efd=0; +#if HAVE_TCL + int use_tcl=0; +#endif + int nargs[MAINLIB_CMDS+1]; + int pipes[MAINLIB_CMDS+1][2]; + char *c, *s, *t; + char *undef=NULL; + char tbuf[SZ_LINE]; + char ibuf[SZ_LINE]; + char ibuf2[SZ_LINE]; + char *args[(MAINLIB_CMDS+1)*MAINLIB_ARGS]; + char *cmds[MAINLIB_CMDS+1]; +#if HAVE_TCL + void *tcl=NULL; +#endif + MainLibEntry mles[MAINLIB_CMDS+1]; + static int mainlibinit=0; + static int mainlib_debug=0; + static char *mainlib_path=NULL; + + /* sanity check */ + if( !ml ){ + gerror(stderr, "invalid or missing command list for mainlib\n"); + got = -1; + goto done; + } + + /* initialization */ + if( !mainlibinit ){ + if( (s = (char *)getenv("MAINLIB_DEBUG")) ) + mainlib_debug = istrue(s); + mainlib_path = (char *)getenv("PATH"); + mainlibinit = 1; + } + + /* clear arrays */ + memset(cmds, 0, sizeof(char *)*(MAINLIB_CMDS+1)); + memset(mles, 0, sizeof(MainLibEntry)*(MAINLIB_CMDS+1)); + memset(args, 0, sizeof(char *)*(MAINLIB_CMDS+1)*MAINLIB_ARGS); + + /* separate commands */ + c = xstrdup(cmd); + for(s=c; (t=strchr(s, '|'))!=NULL; s=t+1){ + if( ncmd >= MAINLIB_CMDS ){ + gerror(stderr, "max cmds exceeded (%d) for mainlib\n", ncmd); + got = -1; + goto done; + } + *t = '\0'; + cmds[++ncmd] = xstrdup(s); + } + /* get last command */ + cmds[++ncmd] = xstrdup(s); + if( c ) xfree(c); + + /* seed the debug flag (override with switch) */ + debug = mainlib_debug; + + /* check mode string */ + if( (t = xstrdup(mode)) ){ + if( keyword(t, "stdin", tbuf, SZ_LINE) ) ifd = atoi(tbuf); + if( keyword(t, "stdout", tbuf, SZ_LINE) ) ofd = atoi(tbuf); + if( keyword(t, "stderr", tbuf, SZ_LINE) ) efd = atoi(tbuf); + if( keyword(t, "undef", tbuf, SZ_LINE) ) undef = xstrdup(tbuf); + if( keyword(t, "fillbuf", tbuf, SZ_LINE) ) fillbuf = istrue(tbuf); + if( keyword(t, "debug", tbuf, SZ_LINE) ) debug = istrue(tbuf); +#if HAVE_TCL + if( keyword(t, "tcl", tbuf, SZ_LINE) ){ + sscanf(tbuf, "%p", &tcl); + use_tcl=1; + } +#endif + xfree(t); + } + /* make sure unef is defined */ + if( !undef ){ + undef = xstrdup("extn"); + } + + /* pipe for top level */ + pipe(pipes[0]); + + /* package up arguments for each command */ + for(i=1; i<=ncmd; i++){ + if( pipe(pipes[i]) < 0 ){ + gerror(stderr, "can't set up pipes for mainlib\n"); + got = -1; + goto done; + } + ip = 0; + /* make sure we have a command */ + if( !word(cmds[i], ibuf, &ip) ){ + gerror(stderr, "invalid or missing command for mainlib\n"); + got = -1; + goto done; + } + /* make sure its a valid command */ + nowhite(ibuf, ibuf2); + if( !(mles[i] = MainLibLookup(ml, NULL, ibuf2)) ){ + if( undef ){ + ip = 0; + while( word(undef, tbuf, &ip) ){ + if( !strcasecmp(tbuf, "error") ){ + break; + } +#if HAVE_TCL + else if( use_tcl && ml->tcllookup && ml->tcllookup(tcl, ibuf2) ){ + mles[i] = MainLibAdd(ml, cmds[i], ibuf2, NULL, MAINLIB_TCL); + break; + } +#endif + else if( (s=Find(ibuf2, "x", NULL, mainlib_path)) != NULL ){ + mles[i] = MainLibAdd(ml, s, ibuf2, NULL, MAINLIB_EXTN); + xfree(s); + break; + } + } + if( !mles[i] ){ + gerror(stderr, "can't locate procedure '%s' for mainlib\n", ibuf); + got = -1; + goto done; + } + } +#ifdef OLD + switch(mainlibundef){ + case MAINLIB_ERROR: + gerror(stderr, "can't locate procedure '%s' for mainlib\n", ibuf); + got = -1; + goto done; + case MAINLIB_EXTN: + if( (s=Find(ibuf2, "x", NULL, mainlib_path)) != NULL ){ + mles[i] = MainLibAdd(ml, s, ibuf2, NULL, MAINLIB_EXTN); + xfree(s); + } + else + gerror(stderr, "can't locate procedure '%s' for mainlib\n", ibuf); + break; + default: + gerror(stderr, "can't locate procedure '%s' for mainlib\n", ibuf); + got = -1; + goto done; + } +#endif + + } + + /* package up the arguments */ + ip = 0; + nargs[i] = 0; + while( word(cmds[i], ibuf, &ip) ){ + nowhite(ibuf, ibuf2); + if( *ibuf2 ){ + if( nargs[i] >= (MAINLIB_ARGS-1) ){ + gerror(stderr, "max args exceeded (%d) for mainlib\n", nargs[i]); + got = -1; + goto done; + } + args[i*MAINLIB_ARGS+nargs[i]] = xstrdup(ibuf2); + nargs[i]++; + args[i*MAINLIB_ARGS+nargs[i]] = NULL; + } + } + } + + /* execute all commands */ + for(i=1; i<=ncmd; i++){ + /* fork new process */ + if( (ml->pids[i] = fork()) == -1 ){ + gerror(stderr, "can't fork()\n"); + got = -1; + goto done; + } + /* child will call mainlib routine */ + if( ml->pids[i] == 0 ){ + /* reset optind in case its being used by child */ + optind = 1; + /* stdin for this new process is */ + if( i == 1 ){ + /* first process either reads stdin from specified ifd, + or else we leave it alone */ + if( ifd >= 0 ){ + if( ifd != 0 ){ + close(0); dup(ifd); + } + else{ + use_ifd = 1; + } + } + } + /* all other processes read stdin from previous process stdout */ + else{ + close(0); dup(pipes[i][0]); + } + /* set up stdout for this new process */ + if( i < ncmd ){ + close(1); dup(pipes[i+1][1]); + } + else{ + /* last process either writes stdout or to specified ofd */ + if( ofd >= 0 ){ + if( ofd != 1 ){ + close(1); dup(ofd); + } + else{ + use_ofd = 1; + } + } + /* or sends stdout to main process */ + else{ + close(1); dup(pipes[0][1]); + } + } + /* all processes redirect stderr, if required */ + if( efd >= 0 ){ + if( efd != 2 ){ + close(2); dup(efd); + } + else{ + use_efd = 1; + } + } + /* close all pipes */ + for(j=0; j<=ncmd; j++){ + close(pipes[j][0]); close(pipes[j][1]); + } + /* including user-specified pipes */ + if( (ifd >=0) && !use_ifd ) close(ifd); + if( (ofd >=0) && !use_ofd ) close(ofd); + if( (efd >=0) && !use_efd ) close(efd); + /* call specified subroutine */ + switch(mles[i]->type){ + case MAINLIB_ARGV: + if( debug ) + fprintf(stderr, "Executing MainLib: %s\n", args[i*MAINLIB_ARGS]); + mles[i]->proc(nargs[i], &args[i*MAINLIB_ARGS]); + break; + case MAINLIB_EXTN: + if( debug ) + fprintf(stderr, "Executing external program: %s\n", mles[i]->xclass); + if( mles[i]->xclass ) + execvp(mles[i]->xclass, (void *)&args[i*MAINLIB_ARGS]); + else + execvp(mles[i]->name, (void *)&args[i*MAINLIB_ARGS]); + break; +#if HAVE_TCL + case MAINLIB_TCL: + if( debug ) + fprintf(stderr, "Executing Tcl proc: %s\n", mles[i]->xclass); + if( use_tcl && ml->tcleval){ + ml->tcleval(tcl, mles[i]->xclass); + } + else{ + gerror(stderr, "Tcl support missing for mainlib\n"); + } + break; +#endif + default: + gerror(stderr, "unknown program type for mainlib\n"); + break; + } + _exit(0); + } + } + + /* close all pipes except the final input pipe */ + close(pipes[0][1]); + for(i=1; i<=ncmd; i++){ + close(pipes[i][0]); + close(pipes[i][1]); + } + + /* if not filling buffer, just return the pipe (user calls cleanup) */ + if( !fillbuf ){ + got = pipes[0][0]; + ml->npid = ncmd; + goto done2; + } + + /* wait for output from last process */ + got = Pipe2Buf(pipes[0][0], buf); + + /* and now close final input pipe */ + close(pipes[0][0]); + +done: + /* done with processing, wait for children to finish */ + MainLibProcessCleanup(ml); + +done2: + /* free up space */ + for(i=1; i<=ncmd; i++){ + for(j=0; j<nargs[i]; j++){ + if( args[i*MAINLIB_ARGS+j] ) xfree(args[i*MAINLIB_ARGS+j]); + } + if( cmds[i] ) xfree(cmds[i]); + } + if( undef ) xfree(undef); + + /* return: error (-1), number of bytes in buffer, or pipe fd */ + return got; +} + +/* + *--------------------------------------------------------------------------- + * + * Routine: MainLibProcessCleanup + * + * Purpose: cleanup after mainlib processing + * + * Results: 1 on success, 0 for failure + * + *--------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +MainLibProcessCleanup (MainLib ml) +#else +int MainLibProcessCleanup(ml) + MainLib ml; +#endif +{ + int i; + int status; + + for(i=1; i<=ml->npid; i++){ + if( ml->pids[i] ){ +#if HAVE_MINGW32==0 + while( waitpid(ml->pids[i], &status, 0) < 0 ){ + if( errno != EINTR ) break; + } +#endif + ml->pids[i] = 0; + } + } + return 1; +} + +/* + *--------------------------------------------------------------------------- + * + * Routine: MainLibDel + * + * Purpose: delete a MainLibEntry record structure + * + * Results: 0 on success, -1 for failure + * + *--------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +MainLibDel (MainLib ml, MainLibEntry mle) +#else +int MainLibDel(ml, mle) + MainLib ml; + MainLibEntry mle; +#endif +{ + + /* make sure we have something to do */ + if( !ml || !mle ) return -1; + + /* free up space */ + if( mle->xclass ) xfree(mle->xclass); + if( mle->name ) xfree(mle->name); + + /* remove from list of mainlibs */ + MainLibListDel(ml, mle); + + /* free up record struct */ + xfree((char *)mle); + + return 0; +} + +/* + *--------------------------------------------------------------------------- + * + * Routine: MainLibFree + * + * Purpose: free all members of this mainlib list + * + * Results: 1 on success, 0 for failure + * + *--------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +MainLibFree (MainLib ml) +#else +int MainLibFree(ml) + MainLib ml; +#endif +{ + MainLibEntry mle, tmle; + + /* sanity check */ + if( !ml ) return 0; + + /* free all commands */ + for(mle=ml->head; mle!=NULL; ){ + tmle = mle->next; + MainLibDel(ml, mle); + mle = tmle; + } + +#if HAVE_DLFCN_H + /* release dynamic library */ + if( ml->dl ) dlclose(ml->dl); +#endif + + /* free global struct */ + xfree(ml); + return 1; +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: MainLibLoad + * + * Purpose: load shared library and execute init function + * + * Results: 0=>OK, -1=> no shared obj, -2=>no init function + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int MainLibLoad(char *name, char *shlib, void **ml, char **ermsg) +#else +int MainLibLoad(name, shlib, ml, ermsg) + char *name; + char *shlib; + void **ml; + char **ermsg; +#endif +{ +#if HAVE_DLFCN_H + char tbuf[SZ_LINE]; + MainLibInitCall irtn=NULL; + MainLib tml=NULL; + void *tdl=NULL; + + /* sanity checks */ + if( !name ){ + if( ermsg ) *ermsg = "no package name specified"; + return -3; + } + if( !ml ){ + if( ermsg ) *ermsg = "no return ml struct specified"; + return -3; + } + + /* get name of init call */ + snprintf(tbuf, SZ_LINE-1, "%sMainLibInit", name); + + /* load shared library */ + if( !(tdl=dlopen(shlib, RTLD_LAZY)) ){ + if( ermsg ) *ermsg = (char *)dlerror(); + return -1; + } + + /* look up init funtion */ + if( !(irtn=(MainLibInitCall)dlsym(tdl, tbuf)) ){ + if( ermsg ) *ermsg = (char *)dlerror(); + return -2; + } + + /* execute init function */ + tml = (*irtn)(); + + /* store values */ + tml->dl = tdl; + + /* look up and return MainLibProcess funtion, if it exists */ + tml->mainlibprocess=(MainLibProcessCall)dlsym(tml->dl, "MainLibProcess"); + + /* store MainLib record */ + *ml = tml; + + /* if we got here, its OK */ + return 0; +#else + /* avoid -W unused parameter warning */ + if( 0 ){ + name = name; + shlib = shlib; + ml = ml; + } + /* didn't find dlfcn.h */ + if( ermsg ) *ermsg = "dynamic load supported not found"; + return -1; +#endif +} |