1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 
11 
12 #include <dx/dx.h>
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdarg.h>
17 
18 #if defined(HAVE_UNISTD_H)
19 #include <unistd.h>
20 #endif
21 #if defined(HAVE_WAIT_H)
22 #include <wait.h>
23 #endif
24 #if defined(HAVE_CTYPE_H)
25 #include <ctype.h>
26 #endif
27 #if defined(HAVE_ERRNO_H)
28 #include <errno.h>
29 #endif
30 #if defined(HAVE_STRING_H)
31 #include <string.h>
32 #endif
33 #if defined(HAVE_FCNTL_H)
34 #include <fcntl.h>
35 #endif
36 #if defined(HAVE_SYS_TIMEB_H)
37 #include <sys/timeb.h>
38 #endif
39 #if defined(HAVE_TIME_H)
40 #include <time.h>
41 #endif
42 #if defined(HAVE_SYS_TIMES_H)
43 #include <sys/time.h>
44 #endif
45 #if defined(HAVE_SYS_PARAM_H)
46 #include <sys/param.h>
47 #endif
48 #if defined(HAVE_SYS_TYPES_H)
49 #include <sys/types.h>
50 #endif
51 #if defined(HAVE_SYS_FILIO_H)
52 #include <sys/filio.h>
53 #endif
54 #if defined(HAVE_IO_H)
55 #include <io.h>
56 #endif
57 #if defined(HAVE_WINIOCTL_H)
58 #include <winioctl.h>
59 #endif
60 #if defined(HAVE_SYS_IOCTL_H)
61 #include <sys/ioctl.h>
62 #endif
63 #if defined(HAVE_NETINET_IN_H)
64 #include <netinet/in.h>
65 #endif
66 #if defined(HAVE_SYS_UN_H)
67 #include <sys/un.h>
68 #endif
69 #if defined(HAVE_SYS_STAT_H)
70 #include <sys/stat.h>
71 #endif
72 #if defined(HAVE_NETDB_H)
73 #include <netdb.h>
74 #endif
75 #if defined(HAVE_PWD_H)
76 #include <pwd.h>
77 #endif
78 #if defined(HAVE_SYS_SELECT_H)
79 #include <sys/select.h>
80 #endif
81 
82 #include "remote.h"
83 #include "pmodflags.h"
84 #include "obmodule.h"
85 #include "config.h"
86 #include "utils.h"
87 #include "parse.h"
88 #include "distp.h"
89 #include "context.h"
90 #include "_macro.h"
91 #include "_variable.h"
92 #include "ccm.h"
93 #include "graph.h"
94 
95 extern Object _dxfExportBin_FP(Object o, int fd); /* from libdx/rwobject.c */
96 extern Object _dxfImportBin_FP(int fd); /* from libdx/rwobject.c */
97 extern int _dxfHostIsLocal(char *host); /* from libdx/ */
98 
99 #define MAX_STARTUP_ARGS 100
100 
101 #define	BUFLEN	4096
102 
ExHostToFQDN(const char host[],char fqdn[MAXHOSTNAMELEN])103 Error ExHostToFQDN( const char host[], char fqdn[MAXHOSTNAMELEN] )
104 {
105     long addr;
106     struct hostent *hp, *hp2;
107 
108     hp = gethostbyname(host);
109     if ( hp == NULL || hp->h_addr_list[0] == NULL ) {
110        DXUIMessage("WARNING", "gethostbyname returned error");
111        DXUIMessage("WARNING", "it appears %s doesn't have a host entry?", host);
112        DXUIMessage("WARNING", "trying localhost.");
113        addr = 0x7f000001; /* 127.0.0.1 */
114     } else
115     	addr = *(long *)hp->h_addr_list[0];
116 
117     hp2 = gethostbyaddr((const char *)&addr, sizeof(struct in_addr),
118                         AF_INET );
119     if ( hp2 == NULL || hp2->h_name == NULL ) {
120         if(_dxfHostIsLocal((char *)host)) {
121             strncpy( fqdn, "localhost", MAXHOSTNAMELEN );
122             DXMessage("Warning: Cannot get FQDN; no DNS entry. Assuming localhost.");
123             return OK;
124         }
125         DXUIMessage("ERROR", "gethostbyaddr returned error");
126         DXSetError(ERROR_UNEXPECTED, "gethostbyaddr error--is it possible this machine doesn't have a DNS/host entry?");
127         return ERROR;
128     }
129     strncpy( fqdn, hp2->h_name, MAXHOSTNAMELEN );
130     return OK;
131 }
132 
133 
134 /* This routine returns the pid of the forked child to communicate with (or
135  * 0 if no waiting is required),
136  * or, if failure, -1.  It (for now) also does a perror.  It also returns
137  * remin, remout, and remerr, stdin,out,err for the created task.
138  */
139 int
ExConnectTo(char * host,char * user,char * cwd,int ac,char * av[],char * ep[],char * spath,int * remin,int * remout,int * remerr)140 ExConnectTo(char *host, char *user, char *cwd, int ac, char *av[], char *ep[],
141     char *spath, int *remin, int *remout, int *remerr)
142 {
143  #if defined(HAVE_FORK)
144    char s[BUFSIZ];
145     char script_name[500],cmd[1000];
146     char localhost[MAXHOSTNAMELEN];
147     FILE *fp = NULL;
148     int i, k;
149     char **rep;
150     char *fargv[MAX_STARTUP_ARGS];
151     struct hostent *he;
152     int findx;
153     char *pathEnv;
154     int  j;
155     int ret;
156     int found;
157     int rsh_noenv;
158     char *local_rsh_cmd;
159 #if ! defined(DXD_HAS_LIBIOP)
160     int in[2], out[2], err[2];
161     int child;
162 #endif
163 
164 
165 #if HAVE_GETDTABLESIZE
166     int  width = getdtablesize();
167 #else
168 #ifdef DXD_HAS_WINSOCKETS
169     int  width = FD_SETSIZE;
170 #else
171     int  width = MAXFUPLIM;
172 #endif
173 #endif
174     char *dnum;
175 
176     local_rsh_cmd = getenv( "DXRSH" );
177     if ( !local_rsh_cmd )
178       local_rsh_cmd = RSH;
179 
180     /*
181      * Initialize return values (to default negative results).
182      */
183     if (remin)
184 	*remin  = -1;
185     if (remout)
186 	*remout = -1;
187     if (remerr)
188 	*remerr = -1;
189 
190     /*
191      * Check to see if "host" is a valid hostname.
192      */
193     if (strcmp("unix", host) != 0)
194     {
195 	he = gethostbyname (host);
196 	if (he == NULL)
197 	{
198 	    /* herror (host); */
199 	    return (-1);
200 	}
201     }
202 
203     for (pathEnv = NULL, i = 0; ep[i] && pathEnv == NULL; ++i)
204 	if (    strncmp (ep[i], "PATH=", strlen("PATH=")) == 0 ||
205 		strncmp (ep[i], "PATH =", strlen("PATH =")) == 0)
206 	    pathEnv = ep[i];
207 
208     if (_dxfHostIsLocal(host)) {
209 	char *path;
210 	struct stat sbuffer;
211 
212 	if (user != NULL)
213 	    fprintf (stdout, "Different user on local machine ignored\n");
214 
215         if(ac > MAX_STARTUP_ARGS) {
216             DXUIMessage("ERROR", "number of arguments exceeds max");
217             goto error_return;
218         }
219 	for (i = 0; i < ac; ++i)
220 	    fargv[i] = av[i];
221 	fargv[i] = 0;
222         findx = ac+1;
223 	rep = ep;
224         /* First search directories in spath for specified command. Then
225 	 * scan through ep looking for "PATH".  If "PATH" is found, then
226 	 * look through path for the specified command, and if it's
227 	 * found and executable, replace it in fargv with the expanded
228 	 * path name.
229 	 */
230 #if DXD_HAS_LIBIOP
231 	if (*fargv[0] != '.' && *fargv[0] != '/' &&
232             strncmp(fargv[0], "os ", 3) != 0 ) {
233 #else
234 	if (*fargv[0] != '.' && *fargv[0] != '/') {
235 #endif
236             found = FALSE;
237             for(k = 0; k < 2; k++) {
238                 if(k == 0)
239                     path = spath;
240                 else {
241 	            path = strchr (pathEnv, '=');
242                     if(path == NULL)
243                         path = ".";
244                     else path++;
245                 }
246 	        while (*path) {
247 		    for (i = 0; *path != '\0' && *path != ':'; ++i, ++path)
248 		        s[i] = *path;
249 	 	    if (*path == ':')
250 		        ++path;
251 		    s[i] = '\0';
252 		    strcat (s, "/");
253 		    strcat (s, av[0]);
254 		    if (stat (s, &sbuffer) < 0) {
255 		        /* if the file doesn't exist, go on */
256 		        if (errno == ENOENT)
257 			    /* We have no more paths to search */
258 			    if (*path == '\0' && k == 1)  {
259                                 DXUIMessage("ERROR", "can't find %s", av[0]);
260 			        goto error_return;
261                             }
262 		        /* Ignore bad stats, we just can't find it */
263 		        continue;
264 		    }
265 		    /* If it's executable, and a regular file, we're done. */
266 		    if ((sbuffer.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) != 0 &&
267 		        (sbuffer.st_mode & S_IFREG) != 0) {
268                         found = TRUE;
269 		        break;	/* out of while(*path) */
270                     }
271                 }
272                 if(found)
273                     break;
274 	    }
275 	    fargv[0] = s;
276 	}
277     }
278     else {		/* Host is NOT local */
279 #if DXD_POPEN_OK
280 	/* The general thread here is to first create a script on the
281 	 * remote host using popen().  The script contains shell code to
282 	 *   1) set up a similar environment to the current one (PATH,...)
283 	 *   2) exec 'dx -...'
284 	 *   3) remove the script from the remote file system.
285 	 * Then we set up fargv to contain the following:
286 	 *    rsh host [ -l user ] /bin/sh /tmp/dx-$host:$pid
287 	 */
288 
289         if(gethostname(localhost, sizeof(localhost)) < 0) {
290             DXUIMessage("ERROR", "gethostname returned error");
291             goto error_return;
292         }
293        if ( ExHostToFQDN( localhost, localhost ) == ERROR )
294             goto error_return;
295 
296 	sprintf(script_name,"/tmp/dx-%s:%d",localhost,getpid());
297 	findx=0;
298 	fargv[findx++] = local_rsh_cmd;
299 	fargv[findx++] = host;
300 
301 	if (user != NULL) {
302 	    fargv[findx++] = "-l";
303 	    fargv[findx++] = user;
304 	    sprintf(cmd,"%s %s -l %s 'cat > %s'", local_rsh_cmd,
305 		 host, user, script_name);
306 	} else
307 	    sprintf(cmd,"%s %s 'cat > %s'", local_rsh_cmd,
308 		host,script_name);
309 
310 	fargv[findx++] = "/bin/sh";
311 	fargv[findx++] = script_name;
312         fargv[findx++] = ">";
313         fargv[findx++] = "/dev/null";
314 	fargv[findx++] = NULL ;
315 
316 	fp = popen(cmd,"w");
317 	if (!fp) {
318 		perror("popen");
319 		goto error_return;
320 	}
321 	setbuf(fp,NULL); /* Unbuffered so file is 'immediately' updated */
322 
323         rsh_noenv = getenv( "DXRSH_NOENV" ) != NULL; /*  Set $DISPLAY only?  */
324 
325 	for (i = 0; ep[i]; ++i) {
326 	    /* Environment variables which are NOT passed to remote process */
327 	    static char *eignore[] = {"HOST=", "HOSTNAME=", "TZ=",
328                                       "SHELL=", "DXHOST=", "ARCH=", 0};
329 	    int ignore;
330 
331 	    /* Look for and skip certain environment variables */
332 	    /* We could do this more quickly elsewhere, but ... */
333 	    for (ignore=j=0 ; !ignore && eignore[j] ; j++)
334 		if (!strncmp(ep[i], eignore[j], strlen(eignore[j])))
335 		   ignore = 1;
336 	    if (ignore)
337 		continue;
338 
339 	    /*
340 	     * Write the environment variable to the remote script file
341 	     */
342 	    if ((strncmp(ep[i],"DISPLAY=unix:",strlen("DISPLAY=unix:"))==0 ||
343 		 strncmp(ep[i],"DISPLAY=:",    strlen("DISPLAY=:")) == 0) &&
344 		(dnum = strchr(ep[i], ':')) != NULL)
345 	    {
346 		fprintf(fp,"DISPLAY='%s%s';export DISPLAY\n",
347 							localhost,dnum);
348 	    } else if ( !rsh_noenv ) {
349 		int k;
350 		char evar[256], c;
351 		char eval[1024];
352 
353 		for (j=0 ; ep[i][j] && (ep[i][j] != '=') ; j++)
354 			evar[j] = ep[i][j];
355 		evar[j] = '\0';
356 		k = j + 1;	/* Skip the '=' sign */
357 		for (j=0 ; (c=ep[i][k]); k++, j++) {
358 			if (c == '\'') {		/* ' -> '\'' */
359 				/* Value contains a double quote */
360 				eval[j++] = '\'';
361 				eval[j++] = '\\';
362 				eval[j++] = '\'';
363 			}
364 			eval[j] = c;
365 		}
366 		eval[j] = '\0';
367                 if(strcmp(evar, "PATH"))
368 	            fprintf(fp,"%s='%s'; export %s\n", evar, eval, evar);
369                 else
370 	            fprintf(fp,"%s=$PATH:'%s'; export %s\n", evar, eval, evar);
371 	    }
372 	}
373         /* replace PATH with new search path for outboard module */
374         if(spath)
375 	    fprintf(fp,"PATH='%s':$PATH; export PATH\n", spath);
376 
377         if(cwd != NULL) {
378             fprintf(fp, "\nif [ -d %s ]; then\n", cwd);
379             fprintf(fp, " cd %s\n", cwd);
380             fprintf(fp, "else\n echo cannot change directory to %s\n", cwd);
381             fprintf(fp, "fi \n");
382         }
383 
384 	fprintf(fp,"\n(sleep 15; rm -f %s) &\nexec ", script_name);
385 
386 	for (i = 0; i < ac; ++i)
387 	    fprintf(fp, "%s ",av[i]);
388 	fprintf(fp,"\n");
389 	ret = pclose(fp);
390 	if (ret == -1) {
391 	    perror("popen/pclose");
392 	    goto error_return;
393 	}
394         else if(ret != 0)
395             goto error_return;
396 
397 	rep = ep;
398 #else
399       DXUIMessage("ERROR", "popen is unavailable on this platform");
400       goto error_return;
401 #endif
402     }
403 
404 #if DXD_HAS_LIBIOP
405     {
406     char cmdpvs[1000];
407     strcpy(cmdpvs, fargv[0]);
408     for(i = 1; i < findx-1; i++) {
409         strcat(cmdpvs, " ");
410         strcat(cmdpvs, fargv[i]);
411     }
412     strcat(cmdpvs, "&");
413     ExDebug("*2", "cmd to run %s", cmdpvs);
414     system(cmdpvs);
415     return(1);
416     }
417 #elif sgi
418 #ifdef OLDSTUFF
419     if(pcreateve(fargv[0], fargv, rep) < 0) {
420         perror("pcreateve");
421         exit(1);
422     }
423 #else
424     {
425 #if DXD_HAS_VFORK
426         pid_t pid = vfork();
427 #else
428         pid_t pid = fork();
429 #endif
430        if ( pid < 0 ) {
431            perror("fork()");
432            exit(1);
433        }
434        else if ( pid == 0 ) {
435            execve( fargv[0], fargv, rep );
436            perror("execve()");
437            exit(1);
438        }
439     }
440 #endif
441     return(1);
442 
443 #else
444     /* Set up three pipes */
445     if (remin && pipe(in) < 0) {
446 	perror("pipe(in)");
447 	goto error_return;
448     }
449     if (remout && pipe(out) < 0) {
450 	perror("pipe(out)");
451 	goto error_return;
452     }
453     if (remerr && pipe(err) < 0) {
454 	perror("pipe(err)");
455 	goto error_return;
456     }
457 
458 #if DXD_HAS_VFORK
459     child = vfork();
460 #else
461     child = fork();
462 #endif
463     if (child == 0) {
464 	if (remin)
465 	    close (in[1]);
466 	if (remout)
467 	    close (out[0]);
468 	if (remerr)
469 	    close (err[0]);
470 	if (remin && dup2(in[0], 0) < 0) {
471 	    perror("dup2");
472 	    exit(1);
473 	}
474 	if (remout && dup2(out[1], 1) < 0) {
475 	    perror("dup2");
476 	    exit(1);
477 	}
478 	if (remerr && dup2(err[1], 2) < 0) {
479 	    perror("dup2");
480 	    exit(1);
481 	}
482 	/* Close all other file descriptors */
483 	for (i = 3; i < width; ++i)
484 	    close(i);
485 
486 	if (cwd != NULL && chdir (cwd) < 0) {
487 	    perror ("chdir");
488 	    exit(1);
489 	}
490 	if (execve(fargv[0], fargv, rep) < 0) {
491 	    perror("execve");
492 	    exit(1);
493 	}
494     }
495     else if (child > 0) {
496 	if (remin) {
497 	    close (in[0]);
498 	    *remin = in[1];
499 	}
500 	if (remout) {
501 	    close (out[1]);
502 	    *remout = out[0];
503 	}
504 	if (remerr) {
505 	    close (err[1]);
506 	    *remerr = err[0];
507 	}
508     }
509     else {
510 	perror("fork");
511 	goto error_return;
512     }
513 
514     return (child);
515 #endif
516 
517 error_return:
518 #endif
519     return (-1);
520 }
521 
522 Error ChildOutput(int fd, Pointer in)
523 {
524     char buffer[512];
525     int nbytes;
526 
527     nbytes = read(fd, buffer, 512);
528     ExDebug("*1", "ChildOutput %d %d", fd, nbytes);
529     if(nbytes <= 0) {
530         /* EOF or terminate */
531         DXRegisterInputHandler (NULL, fd, NULL);
532         close(fd);
533         return(ERROR);
534     }
535     else {
536         buffer[nbytes] = '\0';
537         DXMessage("%s: %s", (char *)in, buffer);
538     }
539     return(OK);
540 }
541 
542 int _dxfExRemoteExec(int dconnect, char *host, char *ruser, int r_argc,
543                      char **r_argv, int outboard)
544 {
545 #ifdef DXD_WIN    /*   AJ   */
546     return 0;
547 #else
548     char                myhost[MAXHOSTNAMELEN];
549     char		cwd[MAXPATHLEN];
550     char		*spath	= NULL;
551     int                 dxport, dxsock;
552     int                 dxfd    = -1;
553     struct sockaddr_in  dxserver;
554 #if DXD_SOCKET_UNIXDOMAIN_OK
555     struct sockaddr_un  dxuserver;
556     int			dxusock;
557 #endif
558     int                 i;
559     extern char         **environ;
560     int                 child = 0;
561     int                 in = -1, out = -1, error = -1;
562     char                **nargv = NULL;
563     int                 nargc;
564 
565 
566     printf("In _dxfExRemoteExec. Host is %s\n", host);
567     if (gethostname (myhost, sizeof(myhost)) < 0)
568         goto error;
569 
570     if ( ExHostToFQDN( myhost, myhost ) == ERROR)
571         goto error;
572 
573     if (getcwd (cwd, sizeof (cwd)) == NULL)
574 	goto error;
575     if (spath == NULL)
576     {
577 	spath = getenv ("DXMODULES");
578 	if (spath == NULL)
579 	    spath = ".";
580     }
581 
582     /* create the socket that will remain open between dx and module */
583 #if DXD_SOCKET_UNIXDOMAIN_OK
584     dxport = _dxfSetupServer(OBPORT, &dxsock, &dxserver, &dxusock, &dxuserver);
585 #else
586     dxport = _dxfSetupServer(OBPORT, &dxsock, &dxserver);
587 #endif
588     if (dxport < 0)
589         goto error;
590 
591     /* add hostname, port number and trailing NULL to arguments */
592     nargc = r_argc + 2;
593 
594     nargv = (char **)DXAllocateLocalZero((nargc + 1) * sizeof(char *));
595     if(nargv == NULL) {
596         DXUIMessage("ERROR", "Could not allocate memory for argument list");
597         goto error;
598     }
599     for(i = 0; i < r_argc; i++)
600         nargv[i] = r_argv[i];
601 
602     if(outboard) {
603         nargv[r_argc] = (char *)DXAllocateLocal(
604                                      (strlen(myhost)+1)*sizeof(char));
605         if(nargv[r_argc] == NULL) {
606             DXUIMessage("ERROR",
607                         "Could not allocate memory for argument list");
608             goto error;
609         }
610         strcpy(nargv[r_argc], myhost);
611         nargv[r_argc+1] = (char *)DXAllocateLocal(5*sizeof(char));
612         if(nargv[r_argc+1] == NULL) {
613             DXUIMessage("ERROR",
614                         "Could not allocate memory for argument list");
615             goto error;
616         }
617         sprintf(nargv[r_argc+1], "%4d", dxport);
618         nargv[r_argc+2] = NULL;
619     }
620     else {
621         nargv[r_argc] = (char *)DXAllocateLocal(
622                                      (strlen("-connect")+1)*sizeof(char));
623         if(nargv[r_argc] == NULL) {
624             DXUIMessage("ERROR",
625                         "Could not allocate memory for argument list");
626             goto error;
627         }
628         strcpy(nargv[r_argc], "-connect");
629         nargv[r_argc+1] = (char *)DXAllocateLocal(
630                                   (strlen(myhost) + 6)*sizeof(char));
631         if(nargv[r_argc+1] == NULL) {
632             DXUIMessage("ERROR",
633                         "Could not allocate memory for argument list");
634             goto error;
635         }
636         strcpy(nargv[r_argc+1], myhost);
637         sprintf(nargv[r_argc+1]+strlen(myhost), ":%4d", dxport);
638         nargv[r_argc+2] = NULL;
639     }
640 
641     if(dconnect) {
642         fprintf(stderr, "start on %s: ", host);
643         for(i = 0; i < nargc; i++)
644             fprintf(stderr, "%s ", nargv[i]);
645         fprintf(stderr, "\n");
646         fflush(stderr);
647     }
648     else {
649         if(outboard) {
650             _dxfPrintConnectTimeOut(nargv[0], host);
651             child = ExConnectTo(host, ruser, cwd, nargc, nargv,
652                         environ, spath, &in, NULL, NULL);
653         }
654         else {
655             _dxfPrintConnectTimeOut("Distributed DX", host);
656             child = ExConnectTo(host, ruser, cwd, nargc, nargv,
657                         environ, NULL, &in, &out, &error);
658         }
659 
660         /* we don't need child's stdin */
661         if(in >= 0)
662             close(in);
663         if(out >= 0) {
664             ExDebug("*1", "register %d output for %s", out, host);
665 	    DXRegisterInputHandler (ChildOutput, out, (Pointer)host);
666         }
667         if(error >= 0) {
668             ExDebug("*1", "register %d stderr for %s", error, host);
669 	    DXRegisterInputHandler (ChildOutput, error, (Pointer)host);
670         }
671     }
672 
673     if(child > 0 || dconnect) {
674 #if DXD_SOCKET_UNIXDOMAIN_OK
675         /* if we are debugging do not set a timeout on connection */
676         dxfd = _dxfCompleteServer(dxsock, dxserver,
677                                   dxusock, dxuserver,
678                                   !dconnect);
679 #else
680         /* if we are debugging do not set a timeout on connection */
681         dxfd = _dxfCompleteServer(dxsock, dxserver,
682                                   !dconnect);
683 #endif
684         if (dxfd < 0)
685             goto error;
686     }
687     if(child == -1)
688         goto error;
689 
690 done:
691     DXFree(nargv[r_argc]);
692     DXFree(nargv[r_argc+1]);
693     DXFree(nargv);
694     return (dxfd);
695 
696 error:
697     if (dxfd >= 0)
698 	close (dxfd);
699     goto done;
700 
701 #endif
702 }
703 
704 Error OBDelete(Pointer in)
705 {
706     ObInfo *ob_info = (ObInfo *)in;
707 
708     close(ob_info->fd);
709 #ifndef DXD_HAS_LIBIOP
710     if (!_dxd_exDebugConnect)
711         wait(0);
712 #endif
713     DXRegisterInputHandler (NULL, ob_info->fd, NULL);
714 
715     DXFree((Pointer)ob_info->modid);
716 
717     DXFree((Pointer)ob_info);
718     return(OK);
719 }
720 
721 Error OBAsync(int fd, Pointer in)
722 {
723     int msb_signature = 0xEF33AB88;
724     int lsb_signature = 0x88AB33EF;
725     ObInfo *ob_info = (ObInfo *)in;
726     int i;
727 
728     /* make sure this is from an outboard ReadyToRun routine,
729      * and handle errors.
730      */
731     if (read(fd, &i, sizeof(i)) <= 0) {
732 	/* put an ioctl here to be sure?  this should be ok */
733 	DXRegisterInputHandler (NULL, ob_info->fd, NULL);
734 	ob_info->deleted = 1;
735 	return ERROR;
736     }
737 
738     if (i == msb_signature || i == lsb_signature)
739 	DXReadyToRun(ob_info->modid);
740 
741     return OK;
742 }
743 
744 #define HIDDEN_INPUTS 5    /* exec name, host name, flags, #inputs, #outputs */
745 #define PREDEFINED_EXP_GRP_MEMBERS 2  /* sent to outboard: #in, inlist, #out */
746 #define PREDEFINED_IMP_GRP_MEMBERS 3  /* returned: rc, rc_string, #out */
747 
748 #define OUTERRMSG  "connection to outboard module failed"
749 
750 /* all outboards call this as their module entry point.
751  */
752 Error DXOutboard (Object *in, Object *out)
753 {
754     return _dxfExRemote(in, out);
755 }
756 
757 Error
758 _dxfExRemote (Object *in, Object *out)
759 {
760     Error	ret	= ERROR;
761     char	*cmd;
762     char	*hostp;
763     char	*userp;
764     int		*iptr;
765     int		i;
766     int		nin;
767     int		nout;
768     int		flags;
769     int		cnt;
770     int		memcount;
771     int		persistent = 0;
772     int		async = 0;
773     char	*asyvarname;
774     Array	parmlist = NULL;
775     Group	g	 = NULL;
776     char	buff[BUFLEN];
777     char	host[MAXHOSTNAMELEN];
778     int		fd = -1;
779     int		instance = 0;
780     Object	obj;
781     ErrorCode	rc;
782     char	*rs	= NULL;
783     ObInfo      *ob_info = NULL;
784     char        *av[2];
785     Object	attr;
786     int		isMsg;
787     Object      cachelist[2];
788     char	cachetag[32];
789     int 	one = 1;
790     int 	zero = 0;
791 
792 
793     /*
794      * extract info from hidden parms.
795      */
796     if (! DXExtractString (in[0], &cmd))
797 	goto cleanup;
798     if (! DXExtractString (in[1], &hostp))
799 	goto cleanup;
800     if (! DXExtractInteger (in[2], &flags))
801 	goto cleanup;
802     if (! DXExtractInteger (in[3], &nin))
803 	goto cleanup;
804     if (! DXExtractInteger (in[4], &nout))
805 	goto cleanup;
806 
807     ExDebug("*2","DXOutboard hidden inputs: %s, %s, %d, %d, %d",
808 	    cmd, hostp, flags, nin, nout);
809 
810 
811     strcpy (buff, hostp);
812     hostp = buff;
813     userp = strchr (buff, ',');
814     if (userp)
815 	*userp++ = '\000';
816     if ( ExHostToFQDN( hostp, host ) == ERROR )
817 	goto cleanup;
818     hostp = host;
819 
820     persistent = flags & MODULE_PERSISTENT;
821     async = flags & (MODULE_ASYNC | MODULE_ASYNCLOCAL);
822 
823     instance = _dxf_ExGetCurrentInstance();
824     if (async)
825 	DXExtractString(in[nin-2], &asyvarname);
826 
827     nin -= HIDDEN_INPUTS;
828 
829     g = DXNewGroup ();
830     if (g == NULL)
831 	goto cleanup;
832     memcount = 0;
833 
834     /*
835      * package remaining parms into a single group object for transmission
836      * to remote process.  members are: input parm count, input object list
837      * (so remote process knows which are null and which are being sent),
838      * the maximum number of outputs the module will produce, and then all
839      * inputs which aren't null.
840      */
841     obj = (Object)_dxfExNewInteger (nin);
842 
843     if (! DXSetEnumeratedMember (g, memcount++, obj))
844 	goto cleanup;
845 
846     parmlist = DXNewArray(TYPE_INT, CATEGORY_REAL, 0);
847     if (!parmlist)
848 	goto cleanup;
849 
850     for (i = 0; i < nin; i++) {
851 	if (! DXAddArrayData(parmlist, i, 1, in[HIDDEN_INPUTS+i] ?
852 			     (Pointer)&one : (Pointer)&zero))
853 	    goto cleanup;
854     }
855 
856     if (! DXSetEnumeratedMember(g, memcount++, (Object)parmlist))
857 	goto cleanup;
858 
859     if (! DXSetEnumeratedMember (g, memcount++, in[4]))
860 	goto cleanup;
861 
862     for (i = HIDDEN_INPUTS; i < nin+HIDDEN_INPUTS; i++)
863     {
864 	if (! in[i])
865 	    continue;
866 
867 	if (! DXSetEnumeratedMember (g, memcount++, in[i]))
868 	    goto cleanup;
869     }
870 
871     cachelist[0] = in[0];
872     cachelist[1] = in[1];
873     sprintf(cachetag, "RemoteConnect%d", instance);
874 
875     /*
876      * if this is a persistent module and you have already opened the
877      * socket connection, get the open file descriptor from the cache.
878      * make sure the socket is still alive.
879      */
880     if (persistent &&
881 	((obj = DXGetCacheEntryV(cachetag, 0, 2, cachelist)) != NULL))
882     {
883         ob_info = (ObInfo *) DXGetPrivateData ((Private) obj);
884         fd = ob_info->fd;
885         DXDelete ((Object) obj);  /* get rid of our extra reference here */
886 	if (ob_info->deleted) {
887 	    DXSetError (ERROR_DATA_INVALID, OUTERRMSG);
888 	    DXSetCacheEntryV(NULL, 0, cachetag, 0, 2, cachelist);
889 	    goto cleanup;
890 	}
891     }
892     else
893     {
894 	av[0] = cmd;
895 	av[1] = NULL;
896 
897 	fd = _dxfExRemoteExec (_dxd_exDebugConnect, hostp, userp, 1, av, 1);
898 
899         if (fd < 0)
900         {
901 	    DXSetError (ERROR_BAD_PARAMETER,
902 			"connection to host %s failed", hostp);
903 	    goto cleanup;
904         }
905         ExDebug("*2", "Connected to Outboard module");
906 
907 	if (persistent)
908 	{
909 	    ob_info = (ObInfo *) DXAllocateZero (sizeof(ObInfo));
910 	    if (!ob_info)
911 	    {
912 		close (fd);
913 		goto cleanup;
914 	    }
915 	    ob_info->fd = fd;
916 	    if (async)
917 		ob_info->modid = (char *)DXGetModuleId();
918 
919 	    obj = (Object) DXNewPrivate ((Pointer) ob_info, OBDelete);
920 	    if (!obj)
921 	    {
922 		close (fd);
923 		DXFree (ob_info->modid);
924 		DXFree (ob_info);
925 		goto cleanup;
926 	    }
927 
928 	    DXSetCacheEntryV((Object)obj, CACHE_PERMANENT,
929 			     cachetag, 0, 2, cachelist);
930 	}
931     }
932 
933     /*
934      * Send the data to the remote module and get rid of the packaging.
935      */
936     if (_dxfExportBin_FP ((Object) g, fd) == ERROR) {
937 	DXSetError(ERROR_DATA_INVALID, "Failed to send data to outboard module");
938 	goto cleanup;
939     }
940     ExDebug("*2", "Sending Input Objects to Outboard Module");
941 
942     DXDelete ((Object) g);
943     g = NULL;
944 
945 
946     /*
947      * Import objects until you get one that's not a message or
948      * an async request to run.
949      *
950      */
951     do {
952         int b;
953 	isMsg = 0;
954 	if ((obj = _dxfImportBin_FP (fd)) == NULL) {
955             fd_set fdset;
956             struct timeval tv;
957             int nsel;
958 	    DXSetError(ERROR_DATA_INVALID, "Failed to receive data from outboard module");
959             FD_ZERO(&fdset);
960             FD_SET(fd, &fdset);
961             tv.tv_sec = 0;
962             tv.tv_usec = 0;
963             nsel = select(fd + 1, (SELECT_ARG_TYPE *) &fdset, NULL, NULL, &tv);
964             if(nsel > 0) {
965                 if((IOCTL(fd, FIONREAD, (char *)&b) < 0) || b <= 0)  {
966                     DXSetError(ERROR_DATA_INVALID,
967                                "Connection to outboard module has been broken");
968                     if(persistent)
969                         DXSetCacheEntryV(NULL, 0, cachetag, 0, 2, cachelist);
970                 }
971             }
972 	    goto cleanup;
973 	}
974         ExDebug("*2", "Received Object from Outboard Module");
975 	attr = DXGetAttribute(obj, "message");
976 	if (attr != NULL && DXExtractInteger(attr, &isMsg) != NULL && isMsg)
977 	{
978 	    /* message */
979 	    if (isMsg == 1)
980 	    {
981 		Object whoObj = NULL;
982 		Object msgObj = NULL;
983 		char *who, *msg;
984 		va_list nolist;
985 
986 		whoObj = DXGetEnumeratedMember((Group)obj, 0, NULL);
987 		if (!whoObj || !DXExtractString(whoObj, &who))
988 		    goto message_cleanup;
989 
990 		msgObj = DXGetEnumeratedMember((Group)obj, 1, NULL);
991 		if (!msgObj || !DXExtractString(msgObj, &msg))
992 		    goto message_cleanup;
993 
994 		DXqmessage (who, msg, nolist);
995 	    }
996 	    /* async request */
997 	    if (isMsg == 2)
998 	    {
999 		if (async)
1000 		    DXReadyToRun((Pointer)asyvarname);
1001 	    }
1002  message_cleanup: ;
1003 	}
1004 	if (isMsg)
1005 	    DXDelete(obj);
1006     } while(isMsg);
1007     g = (Group)obj;
1008 
1009     /* turn on input handler here */
1010     if (persistent && async) {
1011 	if (ob_info->async_handler == 0) {
1012 	    DXRegisterInputHandler (OBAsync, ob_info->fd, ob_info);
1013 	    ob_info->async_handler = 1;
1014 	}
1015     }
1016 
1017     /* return from outboard.  values are packaged into a single group
1018      * for transmission.  members are: return code, return message string,
1019      * output object list (so this routine knows how many members aren't
1020      * null) and the actual output objects.
1021      */
1022 
1023     /*
1024      * Check the error code.  If it is not a good one then get the
1025      * error message and set the error status and return.
1026      */
1027 
1028     obj = DXGetEnumeratedMember (g, 0, NULL);
1029     if (obj == NULL)
1030 	goto cleanup;
1031     if (! DXExtractInteger (obj, (int *) &rc))
1032 	goto cleanup;
1033     if (rc != ERROR_NONE)
1034     {
1035 	obj = DXGetEnumeratedMember (g, 1, NULL);
1036 	if (obj == NULL)
1037 	    goto cleanup;
1038 	if (! DXExtractString (obj, &rs))
1039 	    goto cleanup;
1040         ExDebug("*2", "Outboard Module returning with error %s", rs);
1041 	DXSetError (rc, rs);
1042 	goto cleanup;
1043     }
1044 
1045     /*
1046      * Find out how many outputs were generated and extract them.
1047      * Here we reference the returned objects so that they don't get
1048      * deleted when we get rid of the packaging.  BUT, before we
1049      * return we need to unreference them so that we are handing
1050      * out objects with a reference count of zero.
1051      */
1052 
1053     obj = DXGetEnumeratedMember (g, 2, NULL);
1054     if (obj == NULL)
1055 	goto cleanup;
1056 
1057     iptr = (int *) DXGetArrayData ((Array) obj);
1058     if (iptr == NULL)
1059 	goto cleanup;
1060 
1061     for (i = 0, cnt = 3; i < nout; i++)
1062     {
1063 	if (*iptr++)
1064 	{
1065 	    obj = DXGetEnumeratedMember (g, cnt++, NULL);
1066 	    if (obj == NULL)
1067 		goto cleanup;
1068 	    out[i] = DXReference (obj);
1069             ExDebug("*2", "Setting output %d for Outboard Module", i);
1070 	}
1071     }
1072 
1073     DXDelete ((Object) g);
1074     g = NULL;
1075     for (i = 0; i < nout; i++)
1076 	DXUnreference (out[i]);
1077 
1078     ret = OK;
1079     /* fall thru */
1080 
1081   cleanup:
1082     DXDelete ((Object) g);
1083 
1084     /* fall thru */
1085 
1086     if (!persistent && fd >= 0)
1087     {
1088 	close (fd);
1089 	/* If we have to, wait for the child. */
1090 #ifndef DXD_HAS_LIBIOP
1091 	if (!_dxd_exDebugConnect)
1092 	    wait(0);
1093 #endif
1094     }
1095 
1096     return (ret);
1097 }
1098 
1099 Error
1100 _dxf_ExDeleteRemote (char *name, int instance)
1101 {
1102     node	*module;
1103     node	*id;
1104     int		flags;
1105     int		both;
1106     Object	in[2];
1107     char	cachetag[32];
1108 
1109 
1110     module = (node*)_dxf_ExMacroSearch(name);
1111 
1112     if (module == NULL)
1113     {
1114 	DXSetError(ERROR_BAD_PARAMETER, "#8370", name);
1115 	return ERROR;
1116     }
1117 
1118     flags = module->v.function.flags;
1119     both = MODULE_OUTBOARD | MODULE_PERSISTENT;
1120     if ((flags & both) != both)
1121     {
1122 	return OK;
1123     }
1124 
1125     id = module->v.module.in;
1126     in[0] = ((Object)id->v.id.dflt->v.constant.obj);
1127     id = id->next;
1128     in[1] = ((Object)id->v.id.dflt->v.constant.obj);
1129     sprintf(cachetag, "RemoteConnect%d", instance);
1130 
1131     DXSetCacheEntryV(NULL, 0, cachetag, 0, 2, in);
1132 
1133     return (OK);
1134 }
1135 
1136 Error
1137 _dxf_ExDeleteReallyRemote (char *procgroup, char *name, int instance)
1138 {
1139     node	*module;
1140     int		flags;
1141     int		both;
1142     int		fd;
1143     DelRemote   d;
1144 
1145     module = (node *)_dxf_ExMacroSearch(name);
1146 
1147     if (module == NULL)
1148     {
1149 	DXSetError(ERROR_BAD_PARAMETER, "#8370", name);
1150 	return ERROR;
1151     }
1152 
1153     flags = module->v.function.flags;
1154     both = MODULE_OUTBOARD | MODULE_PERSISTENT;
1155     if ((flags & both) != both)
1156     {
1157 	return OK;
1158     }
1159 
1160     d.del_namelen = strlen(name) + 1;
1161     d.del_name = name;
1162     d.del_instance = instance;
1163 
1164     /* this has to happen on the current host for the process group */
1165     fd = _dxf_ExGetSocketId(procgroup);
1166     if (fd < 0) {
1167 	DXSetError(ERROR_DATA_INVALID,
1168 		   "can't find connection to host for process group '%s'",
1169 		   procgroup);
1170 	return ERROR;
1171     }
1172 
1173     _dxf_ExDistMsgfd(DM_PERSISTDELETE, (Pointer)&d, fd);
1174 
1175     return (OK);
1176 }
1177 
1178 int
1179 _dxf_ExInitRemote (void)
1180 {
1181     return (OK);
1182 }
1183 
1184 static void ExIncrementGlobalVariable (char *name, int cause_execution)
1185 {
1186     int val;
1187 
1188     if(!DXGetIntVariable(name, &val))
1189         val = 0;
1190 
1191     DXSetIntVariable(name, val+1, 1, cause_execution);
1192 }
1193 
1194 /* the point of making the module id routines is to hide
1195  * exactly what a module id is.  right now it's the
1196  * fully qualified pathname down to the module, plus an
1197  * instance number, all in string form; but at some time
1198  * we might want to convert module ids over to simple numbers.
1199  * hopefully having these cover routines will mean that
1200  * people's code won't break if we do this.
1201  */
1202 
1203 /* allocate space for the cpath of the current module
1204  *  and return it.
1205  */
1206 Pointer DXGetModuleId()
1207 {
1208     Pointer id;
1209     int len;
1210 
1211     len = DXGetModuleCacheStrLen() + 1;
1212 
1213     id = DXAllocate(len);
1214     if (!id)
1215 	return NULL;
1216 
1217     DXGetModuleCacheStr(id);
1218     return id;
1219 }
1220 
1221 /* copy a module id
1222  */
1223 Pointer DXCopyModuleId(Pointer id)
1224 {
1225     Pointer nid;
1226 
1227     if (id == NULL || (char *)id == '\0')
1228 	return NULL;
1229 
1230     nid = DXAllocate(strlen((char *)id) + 1);
1231     if (!nid)
1232 	return NULL;
1233 
1234     strcpy(nid, id);
1235     return nid;
1236 }
1237 
1238 /* compare two module ids
1239  */
1240 Error DXCompareModuleId(Pointer id1, Pointer id2)
1241 {
1242     if (id1 == NULL || id2 == NULL)
1243 	return ERROR;
1244 
1245     return ((strcmp(id1, id2) == 0) ? OK : ERROR);
1246 }
1247 
1248 /* get the space back from a module id string
1249  */
1250 Error DXFreeModuleId(Pointer id)
1251 {
1252     if (!id)
1253 	return OK;
1254 
1255     DXFree(id);
1256     return OK;
1257 }
1258 
1259 Error DXReadyToRun(Pointer id)
1260 {
1261     if (!id)
1262 	return ERROR;
1263 
1264     ExIncrementGlobalVariable (id, 1);
1265     return OK;
1266 }
1267 
1268 Error DXReadyToRunNoExecute(Pointer id)
1269 {
1270     if (!id)
1271 	return ERROR;
1272 
1273     ExIncrementGlobalVariable (id, 0);
1274     return OK;
1275 }
1276 
1277