/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #include #include #include #include #include #include #ifdef DXD_WIN #include #include #else #include #endif #if DXD_HAS_UNIX_SYS_INCLUDES #include #endif #include #if HAVE_SYS_FILIO_H #include #endif #ifdef DXD_HAS_WINSOCKETS #include #else #include #endif #ifdef DXD_WIN #include #include #else #include #endif #ifndef DXD_HAS_WINSOCKETS #include #endif #if DXD_SOCKET_UNIXDOMAIN_OK #include #endif #include #ifndef DXD_HAS_UNIX_SYS_INCLUDES #define S_IXUSR S_IEXEC #define S_IXGRP S_IEXEC #define S_IXOTH S_IEXEC #endif #ifndef DXD_HAS_WINSOCKETS #include #endif #ifndef DXD_LACKS_UNIX_UID #include #endif #include "pmodflags.h" #ifdef DXD_WIN #define popen _popen #define pclose _pclose #endif #include "obmodule.h" #include "config.h" #include "utils.h" #include "parse.h" #include "distp.h" #include "context.h" #if DXD_NEEDS_SYS_SELECT_H #include #endif extern Object _dxfExportBin_FP(Object o, int fd); extern Object _dxfImportBin_FP(int fd); extern int _dxfExRemoteExec(int dconnect, char *host, char *ruser, int r_argc, char **r_argv, int outboard); extern Error _dxfExRemote(Object *in, Object *out); extern void _dxfPrintConnectTimeOut(char *execname, char *hostname); #if DXD_POPEN_OK && DXD_HAS_LIBIOP #define popen popen_host #define pclose pclose_host #endif #define MAX_STARTUP_ARGS 100 #define BUFLEN 4096 /* This routine returns the pid of the forked child to communicate with (or * 0 if no waiting is required), * or, if failure, -1. It (for now) also does a perror. It also returns * remin, remout, and remerr, stdin,out,err for the created task. */ int ExConnectTo(char *host, char *user, char *cwd, int ac, char *av[], char *ep[], char *spath, int *remin, int *remout, int *remerr) { char s[BUFSIZ]; char wd[BUFSIZ],script_name[500],cmd[1000]; char cmdpvs[1000]; char localhost[BUFSIZ]; FILE *fp = NULL; int i, k; int in[2], out[2], err[2]; char **rep; char *fargv[MAX_STARTUP_ARGS]; int child; struct hostent *he; char *home; int findx; char *pathEnv; char *is; char *os; int j; int ret; int found; #if DXD_HAS_GETDTABLESIZE int width = getdtablesize(); #else #ifdef DXD_HAS_WINSOCKETS int width = FD_SETSIZE; #else int width = MAXFUPLIM; #endif #endif char *dnum; char dstring[256]; #ifdef DXD_WIN /* AJ */ goto error_return; #endif /* * Initialize return values (to default negative results). */ if (remin) *remin = -1; if (remout) *remout = -1; if (remerr) *remerr = -1; /* * Check to see if "host" is a valid hostname. */ if (strcmp("unix", host) != 0) { he = gethostbyname (host); if (he == NULL) { herror (host); return (-1); } } for (pathEnv = NULL, i = 0; ep[i] && pathEnv == NULL; ++i) if ( strncmp (ep[i], "PATH=", strlen("PATH=")) == 0 || strncmp (ep[i], "PATH =", strlen("PATH =")) == 0) pathEnv = ep[i]; if (_dxfHostIsLocal(host)) { char *path; char *opath; struct stat sbuffer; if (user != NULL) fprintf (stdout, "Different user on local machine ignored\n"); if(ac > MAX_STARTUP_ARGS) { DXUIMessage("ERROR", "number of arguments exceeds max"); goto error_return; } for (i = 0; i < ac; ++i) fargv[i] = av[i]; fargv[i] = 0; findx = ac+1; rep = ep; /* First search directories in spath for specified command. Then * scan through ep looking for "PATH". If "PATH" is found, then * look through path for the specified command, and if it's * found and executable, replace it in fargv with the expanded * path name. */ #if DXD_HAS_LIBIOP if (*fargv[0] != '.' && *fargv[0] != '/' && strncmp(fargv[0], "os ", 3) != 0 ) { #else if (*fargv[0] != '.' && *fargv[0] != '/') { #endif found = FALSE; for(k = 0; k < 2; k++) { if(k == 0) path = spath; else { path = strchr (pathEnv, '='); if(path == NULL) path = "."; else path++; } while (*path) { for (i = 0; *path != '\0' && *path != ':'; ++i, ++path) s[i] = *path; if (*path == ':') ++path; s[i] = '\0'; strcat (s, "/"); strcat (s, av[0]); if (stat (s, &sbuffer) < 0) { /* if the file doesn't exist, go on */ if (errno == ENOENT) /* We have no more paths to search */ if (*path == '\0' && k == 1) { DXUIMessage("ERROR", "can't find %s", av[0]); goto error_return; } /* Ignore bad stats, we just can't find it */ continue; } /* If it's executable, and a regular file, we're done. */ if ((sbuffer.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) != 0 && (sbuffer.st_mode & S_IFREG) != 0) { found = TRUE; break; /* out of while(*path) */ } } if(found) break; } fargv[0] = s; } } else { /* Host is NOT local */ #if DXD_POPEN_OK /* The general thread here is to first create a script on the * remote host using popen(). The script contains shell code to * 1) set up a similar environment to the current one (PATH,...) * 2) exec 'dx -...' * 3) remove the script from the remote file system. * Then we set up fargv to contain the following: * rsh host [ -l user ] /bin/sh /tmp/dx-$host:$pid */ if(gethostname(localhost, BUFSIZ) < 0) { DXUIMessage("ERROR", "gethostname returned error"); goto error_return; } sprintf(script_name,"/tmp/dx-%s:%d",localhost,getpid()); findx=0; fargv[findx++] = RSH; fargv[findx++] = host; if (user != NULL) { fargv[findx++] = "-l"; fargv[findx++] = user; sprintf(cmd,"%s %s -l %s 'cat > %s'",RSH, host, user, script_name); } else sprintf(cmd,"%s %s 'cat > %s'",RSH,host,script_name); fargv[findx++] = "/bin/sh"; fargv[findx++] = script_name; fargv[findx++] = ">"; fargv[findx++] = "/dev/null"; fargv[findx++] = NULL ; fp = popen(cmd,"w"); if (!fp) { perror("popen"); goto error_return; } setbuf(fp,NULL); /* Unbuffered so file is 'immediately' updated */ for (i = 0; ep[i]; ++i) { /* Environment variables which are NOT passed to remote process */ static char *eignore[] = {"HOST=", "HOSTNAME=", "TZ=", "SHELL=", "DXHOST=", "ARCH=", 0}; int ignore; /* Look for and skip certain environment variables */ /* We could do this more quickly elsewhere, but ... */ for (ignore=j=0 ; !ignore && eignore[j] ; j++) if (!strncmp(ep[i], eignore[j], strlen(eignore[j]))) ignore = 1; if (ignore) continue; /* * Write the environment variable to the remote script file */ if ((strncmp(ep[i],"DISPLAY=unix:",strlen("DISPLAY=unix:"))==0 || strncmp(ep[i],"DISPLAY=:", strlen("DISPLAY=:")) == 0) && (dnum = strchr(ep[i], ':')) != NULL) { fprintf(fp,"DISPLAY='%s%s';export DISPLAY\n", localhost,dnum); } else { int k; char evar[256], c; char eval[1024]; for (j=0 ; ep[i][j] && (ep[i][j] != '=') ; j++) evar[j] = ep[i][j]; evar[j] = '\0'; k = j + 1; /* Skip the '=' sign */ for (j=0 ; c = ep[i][k] ; k++, j++) { if (c == '\'') { /* ' -> '\'' */ /* Value contains a double quote */ eval[j++] = '\''; eval[j++] = '\\'; eval[j++] = '\''; } eval[j] = c; } eval[j] = '\0'; if(strcmp(evar, "PATH")) fprintf(fp,"%s='%s'; export %s\n", evar, eval, evar); else fprintf(fp,"%s=$PATH:'%s'; export %s\n", evar, eval, evar); } } /* replace PATH with new search path for outboard module */ if(spath) fprintf(fp,"PATH='%s':$PATH; export PATH\n", spath); if(cwd != NULL) { fprintf(fp, "\nif [ -d %s ]; then\n", cwd); fprintf(fp, "cd %s\n", cwd); fprintf(fp, "else\n echo cannot change directory to %s\n", cwd); fprintf(fp, "fi \n"); } fprintf(fp,"\n(sleep 15; rm -f %s) &\nexec ", script_name); for (i = 0; i < ac; ++i) fprintf(fp, "%s ",av[i]); fprintf(fp,"\n"); ret = pclose(fp); if (ret == -1) { perror("popen/pclose"); goto error_return; } else if(ret != 0) goto error_return; rep = ep; #else DXUIMessage("ERROR", "popen is unavailable on this platform"); goto error_return; #endif } #if DXD_HAS_LIBIOP strcpy(cmdpvs, fargv[0]); for(i = 1; i < findx-1; i++) { strcat(cmdpvs, " "); strcat(cmdpvs, fargv[i]); } strcat(cmdpvs, "&"); ExDebug("*2", "cmd to run %s", cmdpvs); system(cmdpvs); return(1); #elif sgi if(pcreateve(fargv[0], fargv, rep) < 0) { perror("pcreateve"); exit(1); } return(1); #else /* Set up three pipes */ if (remin && pipe(in) < 0) { perror("pipe(in)"); goto error_return; } if (remout && pipe(out) < 0) { perror("pipe(out)"); goto error_return; } if (remerr && pipe(err) < 0) { perror("pipe(err)"); goto error_return; } #if DXD_HAS_VFORK child = vfork(); #else child = fork(); #endif if (child == 0) { if (remin) close (in[1]); if (remout) close (out[0]); if (remerr) close (err[0]); if (remin && dup2(in[0], 0) < 0) { perror("dup2"); exit(1); } if (remout && dup2(out[1], 1) < 0) { perror("dup2"); exit(1); } if (remerr && dup2(err[1], 2) < 0) { perror("dup2"); exit(1); } /* Close all other file descriptors */ for (i = 3; i < width; ++i) close(i); if (cwd != NULL && chdir (cwd) < 0) { perror ("chdir"); exit(1); } if (execve(fargv[0], fargv, rep) < 0) { perror("execve"); exit(1); } } else if (child > 0) { if (remin) { close (in[0]); *remin = in[1]; } if (remout) { close (out[1]); *remout = out[0]; } if (remerr) { close (err[1]); *remerr = err[0]; } } else { perror("fork"); goto error_return; } return (child); #endif error_return: return (-1); } Error ChildOutput(int fd, Pointer in) { char buffer[512]; int nbytes; nbytes = read(fd, buffer, 512); ExDebug("*1", "ChildOutput %d %d", fd, nbytes); if(nbytes <= 0) { /* EOF or terminate */ DXRegisterInputHandler (NULL, fd, NULL); close(fd); return(ERROR); } else { buffer[nbytes] = '\0'; DXMessage("%s: %s", (char *)in, buffer); } return(OK); } int _dxfExRemoteExec(int dconnect, char *host, char *ruser, int r_argc, char **r_argv, int outboard) { #ifdef DXD_WIN /* AJ */ return 0; #else char myhost[MAXHOSTNAMELEN + 2]; char cwd[MAXPATHLEN]; char *spath = NULL; int dxport, dxsock; int dxfd = -1; struct sockaddr_in dxserver; #if DXD_SOCKET_UNIXDOMAIN_OK struct sockaddr_un dxuserver; int dxusock; #endif int i, len; extern char **environ; int child = 0; int in = -1, out = -1, error = -1; char **nargv = NULL; int nargc; if (gethostname (myhost, sizeof(myhost)) < 0) goto error; if ((unsigned int)(getcwd (cwd, sizeof (cwd))) == (unsigned int)NULL) goto error; if (spath == NULL) { spath = getenv ("DXMODULES"); if (spath == NULL) spath = "."; } /* create the socket that will remain open between dx and module */ #if DXD_SOCKET_UNIXDOMAIN_OK dxport = _dxfSetupServer(OBPORT, &dxsock, &dxserver, &dxusock, &dxuserver); #else dxport = _dxfSetupServer(OBPORT, &dxsock, &dxserver); #endif if (dxport < 0) goto error; /* add hostname, port number and trailing NULL to arguments */ nargc = r_argc + 2; nargv = (char **)DXAllocateLocalZero((nargc + 1) * sizeof(char *)); if(nargv == NULL) { DXUIMessage("ERROR", "Could not allocate memory for argument list"); goto error; } for(i = 0; i < r_argc; i++) nargv[i] = r_argv[i]; if(outboard) { nargv[r_argc] = (char *)DXAllocateLocal( (strlen(myhost)+1)*sizeof(char)); if(nargv[r_argc] == NULL) { DXUIMessage("ERROR", "Could not allocate memory for argument list"); goto error; } strcpy(nargv[r_argc], myhost); nargv[r_argc+1] = (char *)DXAllocateLocal(5*sizeof(char)); if(nargv[r_argc+1] == NULL) { DXUIMessage("ERROR", "Could not allocate memory for argument list"); goto error; } sprintf(nargv[r_argc+1], "%4d", dxport); nargv[r_argc+2] = NULL; } else { nargv[r_argc] = (char *)DXAllocateLocal( (strlen("-connect")+1)*sizeof(char)); if(nargv[r_argc] == NULL) { DXUIMessage("ERROR", "Could not allocate memory for argument list"); goto error; } strcpy(nargv[r_argc], "-connect"); nargv[r_argc+1] = (char *)DXAllocateLocal( (strlen(myhost) + 6)*sizeof(char)); if(nargv[r_argc+1] == NULL) { DXUIMessage("ERROR", "Could not allocate memory for argument list"); goto error; } strcpy(nargv[r_argc+1], myhost); sprintf(nargv[r_argc+1]+strlen(myhost), ":%4d", dxport); nargv[r_argc+2] = NULL; } if(dconnect) { fprintf(stderr, "start on %s: ", host); for(i = 0; i < nargc; i++) fprintf(stderr, "%s ", nargv[i]); fprintf(stderr, "\n"); fflush(stderr); } else { if(outboard) { _dxfPrintConnectTimeOut(nargv[0], host); child = ExConnectTo(host, ruser, cwd, nargc, nargv, environ, spath, &in, NULL, NULL); } else { _dxfPrintConnectTimeOut("Distributed DX", host); child = ExConnectTo(host, ruser, cwd, nargc, nargv, environ, NULL, &in, &out, &error); } /* we don't need child's stdin */ if(in >= 0) close(in); if(out >= 0) { ExDebug("*1", "register %d output for %s", out, host); DXRegisterInputHandler (ChildOutput, out, (Pointer)host); } if(error >= 0) { ExDebug("*1", "register %d stderr for %s", error, host); DXRegisterInputHandler (ChildOutput, error, (Pointer)host); } } if(child > 0 || dconnect) { #if DXD_SOCKET_UNIXDOMAIN_OK /* if we are debugging do not set a timeout on connection */ dxfd = _dxfCompleteServer(dxsock, dxserver, dxusock, dxuserver, !dconnect); #else /* if we are debugging do not set a timeout on connection */ dxfd = _dxfCompleteServer(dxsock, dxserver, !dconnect); #endif if (dxfd < 0) goto error; } if(child == -1) goto error; done: DXFree(nargv[r_argc]); DXFree(nargv[r_argc+1]); DXFree(nargv); return (dxfd); error: if (dxfd >= 0) close (dxfd); goto done; #endif } Error OBDelete(Pointer in) { ObInfo *ob_info = (ObInfo *)in; close(ob_info->fd); #ifndef DXD_HAS_LIBIOP if (!_dxd_exDebugConnect) wait(0); #endif DXRegisterInputHandler (NULL, ob_info->fd, NULL); DXFree((Pointer)ob_info->modid); DXFree((Pointer)ob_info); return(OK); } Error OBAsync(int fd, Pointer in) { int msb_signature = 0xEF33AB88; int lsb_signature = 0x88AB33EF; ObInfo *ob_info = (ObInfo *)in; int i; /* make sure this is from an outboard ReadyToRun routine, * and handle errors. */ if (read(fd, &i, sizeof(i)) <= 0) { /* put an ioctl here to be sure? this should be ok */ DXRegisterInputHandler (NULL, ob_info->fd, NULL); ob_info->deleted = 1; return ERROR; } if (i == msb_signature || i == lsb_signature) DXReadyToRun(ob_info->modid); return OK; } #define HIDDEN_INPUTS 5 /* exec name, host name, flags, #inputs, #outputs */ #define PREDEFINED_EXP_GRP_MEMBERS 2 /* sent to outboard: #in, inlist, #out */ #define PREDEFINED_IMP_GRP_MEMBERS 3 /* returned: rc, rc_string, #out */ #define OUTERRMSG "connection to outboard module failed" /* all outboards call this as their module entry point. */ Error DXOutboard (Object *in, Object *out) { return _dxfExRemote(in, out); } Error _dxfExRemote (Object *in, Object *out) { extern int _dxf_ExGetCurrentInstance(); Error ret = ERROR; char *cmd; char *hostp; char *userp; int *iptr; int i; int nin; int nout; int flags; int cnt; int memcount; int persistent = 0; int async = 0; char *asyvarname; Array parmlist = NULL; Group g = NULL; char buff[BUFLEN]; int fd = -1; int c; int instance = 0; Object obj; ErrorCode rc; char *rs = NULL; ObInfo *ob_info = NULL; char *av[2]; Object attr; int isMsg; char *msg; Object cachelist[2]; char cachetag[32]; int one = 1; int zero = 0; /* * extract info from hidden parms. */ if (! DXExtractString (in[0], &cmd)) goto cleanup; if (! DXExtractString (in[1], &hostp)) goto cleanup; if (! DXExtractInteger (in[2], &flags)) goto cleanup; if (! DXExtractInteger (in[3], &nin)) goto cleanup; if (! DXExtractInteger (in[4], &nout)) goto cleanup; ExDebug("*2","DXOutboard hidden inputs: %s, %s, %d, %d, %d", cmd, hostp, flags, nin, nout); strcpy (buff, hostp); hostp = buff; userp = strchr (buff, ','); if (userp) *userp++ = '\000'; persistent = flags & MODULE_PERSISTENT; async = flags & (MODULE_ASYNC | MODULE_ASYNCLOCAL); instance = _dxf_ExGetCurrentInstance(); if (async) DXExtractString(in[nin-2], &asyvarname); nin -= HIDDEN_INPUTS; g = DXNewGroup (); if (g == NULL) goto cleanup; memcount = 0; /* * package remaining parms into a single group object for transmission * to remote process. members are: input parm count, input object list * (so remote process knows which are null and which are being sent), * the maximum number of outputs the module will produce, and then all * inputs which aren't null. */ obj = (Object)_dxfExNewInteger (nin); if (! DXSetEnumeratedMember (g, memcount++, obj)) goto cleanup; parmlist = DXNewArray(TYPE_INT, CATEGORY_REAL, 0); if (!parmlist) goto cleanup; for (i = 0; i < nin; i++) { if (! DXAddArrayData(parmlist, i, 1, in[HIDDEN_INPUTS+i] ? (Pointer)&one : (Pointer)&zero)) goto cleanup; } if (! DXSetEnumeratedMember(g, memcount++, (Object)parmlist)) goto cleanup; if (! DXSetEnumeratedMember (g, memcount++, in[4])) goto cleanup; for (i = HIDDEN_INPUTS; i < nin+HIDDEN_INPUTS; i++) { if (! in[i]) continue; if (! DXSetEnumeratedMember (g, memcount++, in[i])) goto cleanup; } cachelist[0] = in[0]; cachelist[1] = in[1]; sprintf(cachetag, "RemoteConnect%d", instance); /* * if this is a persistent module and you have already opened the * socket connection, get the open file descriptor from the cache. * make sure the socket is still alive. */ if (persistent && ((obj = DXGetCacheEntryV(cachetag, 0, 2, cachelist)) != NULL)) { ob_info = (ObInfo *) DXGetPrivateData ((Private) obj); fd = ob_info->fd; DXDelete ((Object) obj); /* get rid of our extra reference here */ if (ob_info->deleted) { DXSetError (ERROR_INVALID_DATA, OUTERRMSG); DXSetCacheEntryV(NULL, 0, cachetag, 0, 2, cachelist); goto cleanup; } } else { av[0] = cmd; av[1] = NULL; fd = _dxfExRemoteExec (_dxd_exDebugConnect, hostp, userp, 1, av, 1); if (fd < 0) { DXSetError (ERROR_BAD_PARAMETER, "connection to host %s failed", hostp); goto cleanup; } ExDebug("*2", "Connected to Outboard module"); if (persistent) { ob_info = (ObInfo *) DXAllocateZero (sizeof(ObInfo)); if (!ob_info) { close (fd); goto cleanup; } ob_info->fd = fd; if (async) ob_info->modid = (char *)DXGetModuleId(); obj = (Object) DXNewPrivate ((Pointer) ob_info, OBDelete); if (!obj) { close (fd); DXFree (ob_info->modid); DXFree (ob_info); goto cleanup; } DXSetCacheEntryV((Object)obj, CACHE_PERMANENT, cachetag, 0, 2, cachelist); } } /* * Send the data to the remote module and get rid of the packaging. */ if (_dxfExportBin_FP ((Object) g, fd) == ERROR) { DXSetError(ERROR_INVALID_DATA, "Failed to send data to outboard module"); goto cleanup; } ExDebug("*2", "Sending Input Objects to Outboard Module"); DXDelete ((Object) g); g = NULL; /* * Import objects until you get one that's not a message or * an async request to run. * */ do { int b; isMsg = 0; if ((obj = _dxfImportBin_FP (fd)) == NULL) { fd_set fdset; struct timeval tv; int nsel; DXSetError(ERROR_INVALID_DATA, "Failed to receive data from outboard module"); FD_ZERO(&fdset); FD_SET(fd, &fdset); tv.tv_sec = 0; tv.tv_usec = 0; nsel = select(fd + 1, (SelectPtr) &fdset, NULL, NULL, &tv); if(nsel > 0) { if((IOCTL(fd, FIONREAD, (char *)&b) < 0) || b <= 0) { DXSetError(ERROR_INVALID_DATA, "Connection to outboard module has been broken"); if(persistent) DXSetCacheEntryV(NULL, 0, cachetag, 0, 2, cachelist); } } goto cleanup; } ExDebug("*2", "Received Object from Outboard Module"); attr = DXGetAttribute(obj, "message"); if (attr != NULL && DXExtractInteger(attr, &isMsg) != NULL && isMsg) { /* message */ if (isMsg == 1) { Object whoObj = NULL; Object msgObj = NULL; char *who, *msg; va_list nolist = { NULL }; whoObj = DXGetEnumeratedMember((Group)obj, 0, NULL); if (!whoObj || !DXExtractString(whoObj, &who)) goto message_cleanup; msgObj = DXGetEnumeratedMember((Group)obj, 1, NULL); if (!msgObj || !DXExtractString(msgObj, &msg)) goto message_cleanup; DXqmessage (who, msg, nolist); } /* async request */ if (isMsg == 2) { if (async) DXReadyToRun((Pointer)asyvarname); } message_cleanup: ; } if (isMsg) DXDelete(obj); } while(isMsg); g = (Group)obj; /* turn on input handler here */ if (persistent && async) { if (ob_info->async_handler == 0) { DXRegisterInputHandler (OBAsync, ob_info->fd, ob_info); ob_info->async_handler = 1; } } /* return from outboard. values are packaged into a single group * for transmission. members are: return code, return message string, * output object list (so this routine knows how many members aren't * null) and the actual output objects. */ /* * Check the error code. If it is not a good one then get the * error message and set the error status and return. */ obj = DXGetEnumeratedMember (g, 0, NULL); if (obj == NULL) goto cleanup; if (! DXExtractInteger (obj, (int *) &rc)) goto cleanup; if (rc != ERROR_NONE) { obj = DXGetEnumeratedMember (g, 1, NULL); if (obj == NULL) goto cleanup; if (! DXExtractString (obj, &rs)) goto cleanup; ExDebug("*2", "Outboard Module returning with error %s", rs); DXSetError (rc, rs); goto cleanup; } /* * Find out how many outputs were generated and extract them. * Here we reference the returned objects so that they don't get * deleted when we get rid of the packaging. BUT, before we * return we need to unreference them so that we are handing * out objects with a reference count of zero. */ obj = DXGetEnumeratedMember (g, 2, NULL); if (obj == NULL) goto cleanup; iptr = (int *) DXGetArrayData ((Array) obj); if (iptr == NULL) goto cleanup; for (i = 0, cnt = 3; i < nout; i++) { if (*iptr++) { obj = DXGetEnumeratedMember (g, cnt++, NULL); if (obj == NULL) goto cleanup; out[i] = DXReference (obj); ExDebug("*2", "Setting output %d for Outboard Module", i); } } DXDelete ((Object) g); g = NULL; for (i = 0; i < nout; i++) DXUnreference (out[i]); ret = OK; /* fall thru */ cleanup: DXDelete ((Object) g); /* fall thru */ close: if (!persistent && fd >= 0) { close (fd); /* If we have to, wait for the child. */ #ifndef DXD_HAS_LIBIOP if (!_dxd_exDebugConnect) wait(0); #endif } return (ret); } Error _dxf_ExDeleteRemote (char *name, int instance) { node *module; node *id; int flags; int both; Object in[2]; char cachetag[32]; module = (node*)_dxf_ExMacroSearch(name); if (module == NULL) { DXSetError(ERROR_BAD_PARAMETER, "#8370", name); return ERROR; } flags = module->v.function.flags; both = MODULE_OUTBOARD | MODULE_PERSISTENT; if ((flags & both) != both) { return OK; } id = module->v.module.in; in[0] = ((Object)id->v.id.dflt->v.constant.obj); id = id->next; in[1] = ((Object)id->v.id.dflt->v.constant.obj); sprintf(cachetag, "RemoteConnect%d", instance); DXSetCacheEntryV(NULL, 0, cachetag, 0, 2, in); return (OK); } Error _dxf_ExDeleteReallyRemote (char *procgroup, char *name, int instance) { node *module; node *id; int flags; int both; int fd; DelRemote d; module = (node *)_dxf_ExMacroSearch(name); if (module == NULL) { DXSetError(ERROR_BAD_PARAMETER, "#8370", name); return ERROR; } flags = module->v.function.flags; both = MODULE_OUTBOARD | MODULE_PERSISTENT; if ((flags & both) != both) { return OK; } d.del_namelen = strlen(name) + 1; d.del_name = name; d.del_instance = instance; /* this has to happen on the current host for the process group */ fd = _dxf_ExGetSocketId(procgroup); if (fd < 0) { DXSetError(ERROR_INVALID_DATA, "can't find connection to host for process group '%s'", procgroup); return ERROR; } _dxf_ExDistMsgfd(DM_PERSISTDELETE, (Pointer)&d, fd); return (OK); } int _dxf_ExInitRemote (void) { return (OK); } static void ExIncrementGlobalVariable (char *name, int cause_execution) { int val; if(!DXGetIntVariable(name, &val)) val = 0; DXSetIntVariable(name, val+1, 1, cause_execution); } /* the point of making the module id routines is to hide * exactly what a module id is. right now it's the * fully qualified pathname down to the module, plus an * instance number, all in string form; but at some time * we might want to convert module ids over to simple numbers. * hopefully having these cover routines will mean that * people's code won't break if we do this. */ /* allocate space for the cpath of the current module * and return it. */ Pointer DXGetModuleId() { Pointer id; int len; char *cpath; len = DXGetModulePathLen() + 1; id = DXAllocate(len); if (!id) return NULL; DXGetModulePath(id); return id; } /* copy a module id */ Pointer DXCopyModuleId(Pointer id) { Pointer nid; int len; if (id == NULL || (char *)id == '\0') return NULL; nid = DXAllocate(strlen((char *)id) + 1); if (!nid) return NULL; strcpy(nid, id); return nid; } /* compare two module ids */ Error DXCompareModuleId(Pointer id1, Pointer id2) { if (id1 == NULL || id2 == NULL) return ERROR; return ((strcmp(id1, id2) == 0) ? OK : ERROR); } /* get the space back from a module id string */ Error DXFreeModuleId(Pointer id) { if (!id) return OK; DXFree(id); return OK; } Error DXReadyToRun(Pointer id) { if (!id) return ERROR; ExIncrementGlobalVariable (id, 1); return OK; } Error DXReadyToRunNoExecute(Pointer id) { if (!id) return ERROR; ExIncrementGlobalVariable (id, 0); return OK; }