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