Changes to remove compiler warnings
[dyninst.git] / pdutil / src / rpcUtil.C
1 /*
2  * $Log: rpcUtil.C,v $
3  * Revision 1.31  1994/09/22 03:19:52  markc
4  * Changes to remove compiler warnings
5  *
6  * Revision 1.30  1994/09/20  18:23:58  hollings
7  * added option to use rexec as well as fork and rsh to start processes.
8  *
9  * Revision 1.29  1994/08/18  19:55:04  markc
10  * Added ifdef for solaris.
11  *
12  * Revision 1.28  1994/08/17  18:25:25  markc
13  * Added RPCgetArg
14  * Change RPC_make_arg_list to avoid leaving a hole at the head of the arg list
15  * Changed RPCProcessCreate to use the new version of arg list
16  * Changed the execl to execlp
17  *
18  * Revision 1.27  1994/07/28  22:22:04  krisna
19  * changed definitions of ReadFunc and WriteFunc to conform to prototypes
20  *
21  * Revision 1.26  1994/07/19  18:30:27  markc
22  * Made machineName default to zero as last parameter to RPC_make_arg_list.
23  * Added typecast to malloc call in RPC_make_arg_list.
24  *
25  * Added typecast to malloc call in RPC_make_arg_list.
26  *
27  * Revision 1.25  1994/07/18  19:08:25  hollings
28  * added extra arg to RPC_make_arg_list.
29  *
30  * Revision 1.24  1994/06/22  00:37:13  markc
31  * Fixed code to remove warnings.
32  *
33  * Revision 1.23  1994/06/02  23:36:58  markc
34  * Added support for igen error checking.
35  *
36  * Revision 1.22  1994/05/17  00:14:45  hollings
37  * added rcs log entry.
38  *
39  * Revision 1.21  1994/05/16  04:27:47  hollings
40  * Added inlcude of vfork.h on SUNS to prevent problem with optimizer.
41  *
42  * Revision 1.20  1994/05/12  18:47:51  jcargill
43  * Changed make args function to leave room for program name in arg_list[0],
44  * and added code to RPCprocessCreate to poke it in there before execv'ing.
45  *
46  * Revision 1.19  1994/04/21  23:23:49  hollings
47  * removed paradynd name from make args function.
48  *
49  * Revision 1.18  1994/04/06  22:46:12  markc
50  * Fixed bug in XDRrpc constructor that clobbered the fd value.  Added feature
51  * to RPC_readReady to do blocking select.
52  *
53  * Revision 1.17  1994/04/01  20:05:27  hollings
54  * Removed kill of rsh process (not needed and it causes a race condition).
55  *
56  * Revision 1.16  1994/04/01  04:59:13  markc
57  * Put in support to encode NULL ptrs to strings in xdr_String.
58  *
59  * Revision 1.15  1994/03/31  22:59:08  hollings
60  * added well known port as a paramter to xdrRPC constructor.
61  *
62  * Revision 1.14  1994/03/31  22:45:04  markc
63  * Added Log for rcs.
64  *
65 */
66
67 //
68 // This file defines a set of utility routines for RPC services.
69 //
70 //
71
72 // overcome malloc redefinition due to /usr/include/rpc/types.h declaring 
73 // malloc 
74
75 #include <signal.h>
76 #include <sys/wait.h>
77
78 /* prevents malloc from being redefined */
79 #ifdef MIPS
80 #define MALLOC_DEFINED_AS_VOID
81 #endif
82
83 #include "util/h/rpcUtil.h"
84 #include "util/h/tunableConst.h"
85
86 #include <stdio.h>
87 #include <string.h>
88 #include <stdlib.h>
89 #include <errno.h>
90 #include <assert.h>
91 #include <fcntl.h>
92 #include <memory.h>
93 #include <netinet/in.h>
94 #include <netdb.h>
95 #include <sys/types.h>
96 #include <sys/time.h>
97 #include <unistd.h>
98 #include <sys/file.h>
99
100 extern "C" {
101 #include <rpc/types.h>
102 #include <rpc/xdr.h>
103 }
104
105 // functions that g++-fixincludes missed
106 #ifdef MIPS
107 extern "C" {
108 void bzero (char*, int);
109 int select (int, fd_set*, fd_set*, fd_set*, struct timeval*);
110 char *strdup (char*);
111 int gethostname(char*, int);
112 int socket(int, int, int);
113 int bind(int s, struct sockaddr *, int);
114 int getsockname(int, struct sockaddr*, int *);
115 int listen(int, int);
116 int connect(int s, struct sockaddr*, int);
117 int socketpair(int, int, int, int sv[2]);
118 int vfork();
119 int accept(int, struct sockaddr *addr, int *); 
120 }
121 #elif SPARC
122 #include <sys/socket.h>
123 #include <vfork.h>
124 extern "C" {
125 void bzero (char*, int);
126 int select (int, fd_set*, fd_set*, fd_set*, struct timeval*);
127 int socket(int, int, int);
128 int gethostname(char*, int);
129 int bind(int s, struct sockaddr *, int);
130 int getsockname(int, struct sockaddr*, int *);
131 int listen(int, int);
132 int connect(int s, struct sockaddr*, int);
133 int socketpair(int, int, int, int sv[2]);
134 int accept(int, struct sockaddr *addr, int *); 
135 }
136 #elif SOLARIS
137 #endif
138
139
140 #define RSH_COMMAND     "rsh"
141
142 tunableBooleanConstant useRexec(FALSE, NULL, userConstant, "useRexec",
143     "Use rsedc instead of rsh to establish connection to daemon");
144
145 int RPCdefaultXDRRead(const void* handle, char *buf, u_int len)
146 {
147     int fd = (int) handle;
148     int ret;
149
150     do {
151         ret = read(fd, buf, len);
152     } while (ret < 0 && errno == EINTR);
153
154     if (ret <= 0) return(-1);
155     return (ret);
156 }
157
158 int RPCdefaultXDRWrite(const void* handle, char *buf, u_int len)
159 {
160     int fd = (int) handle;
161     int ret;
162
163     do {
164         ret = write(fd, buf, len);
165     } while (ret < 0 && errno == EINTR);
166
167     if (ret != len) 
168         return(-1);
169     else
170         return (ret);
171 }
172
173 XDRrpc::~XDRrpc()
174 {
175   if (fd >= 0)
176     {
177       fcntl (fd, F_SETFL, FNDELAY);
178       close(fd);
179     }
180   if (__xdrs__) 
181     {
182       xdr_destroy (__xdrs__);
183       delete (__xdrs__);
184     }
185 }
186
187 //
188 // prepare for RPC's to be done/received on the passed fd.
189 //
190 XDRrpc::XDRrpc(int f, xdrIOFunc readRoutine, xdrIOFunc writeRoutine, int nblock)
191 {
192     fd = f;
193     __xdrs__ = new XDR;
194     if (!readRoutine) readRoutine = (xdrIOFunc) RPCdefaultXDRRead;
195     if (!writeRoutine) writeRoutine = (xdrIOFunc) RPCdefaultXDRWrite;
196     (void) xdrrec_create(__xdrs__, 0, 0, (char *) fd, readRoutine,writeRoutine);
197     if (nblock)
198       fcntl (fd, F_SETFL, FNDELAY);
199 }
200
201 //
202 // prepare for RPC's to be done/received on the passed fd.
203 //
204 XDRrpc::XDRrpc(char *machine,
205                char *user,
206                char *program,
207                xdrIOFunc readRoutine, 
208                xdrIOFunc writeRoutine,
209                char **arg_list,
210                int nblock,
211                int wellKnownPortFd)
212 {
213     fd = RPCprocessCreate(pid, machine, user, program, arg_list, 
214         wellKnownPortFd);
215     if (fd >= 0) {
216         __xdrs__ = new XDR;
217         if (!readRoutine) readRoutine = (xdrIOFunc) RPCdefaultXDRRead;
218         if (!writeRoutine) writeRoutine = (xdrIOFunc) RPCdefaultXDRWrite;
219         (void) xdrrec_create(__xdrs__, 0, 0, (char *) fd, 
220                 readRoutine, writeRoutine);
221         if (nblock)
222           fcntl (fd, F_SETFL, FNDELAY);
223     } else {
224         __xdrs__ = NULL;
225         fd = -1;
226     }
227 }
228
229 int
230 RPC_readReady (int fd, int timeout)
231 {
232   fd_set readfds;
233   struct timeval tvptr, *the_tv;
234
235   tvptr.tv_sec = timeout; tvptr.tv_usec = 0;
236   if (fd < 0) return -1;
237   FD_ZERO(&readfds);
238   FD_SET (fd, &readfds);
239
240   // -1 timeout = blocking select
241   if (timeout == -1)
242      the_tv = 0;
243   else
244      the_tv = &tvptr;
245  
246   if (select (fd+1, &readfds, NULL, NULL, the_tv) == -1)
247     {
248       // if (errno == EBADF)
249         return -1;
250     }
251   return (FD_ISSET (fd, &readfds));
252 }
253
254 int 
255 RPC_undo_arg_list (int argc, char **arg_list, char **machine, int &family,
256                    int &type, int &well_known_socket, int &flag)
257 {
258   int loop;
259   char *ptr;
260   int sum = 0;
261
262   for (loop=0; loop < argc; ++loop)
263     {
264       if (!strncmp(arg_list[loop], "-p", 2))
265         {
266           well_known_socket = (int) strtol (arg_list[loop] + 2, &ptr, 10);
267           if (ptr == (arg_list[loop] + 2))
268             return(-1);
269           sum |= 1;
270         }
271       else if (!strncmp(arg_list[loop], "-f", 2))
272         {
273           family = (int) strtol (arg_list[loop] + 2, &ptr, 10);
274           if (ptr == (arg_list[loop] + 2))
275             return(-1);
276           sum |= 2;
277         }
278       else if (!strncmp(arg_list[loop], "-t", 2))
279         {
280           type = (int) strtol (arg_list[loop] + 2, &ptr, 10);
281           if (ptr == (arg_list[loop] + 2))
282             return(-1);
283           sum |= 4;
284         }
285       else if (!strncmp(arg_list[loop], "-m", 2))
286         {
287           *machine = strdup (arg_list[loop] + 2);
288           if (!(*machine)) return -1;
289           sum |= 8;
290         }
291       else if (!strncmp(arg_list[loop], "-l", 2))
292         {
293           flag = (int) strtol (arg_list[loop] + 2, &ptr, 10);
294           if (ptr == (arg_list[loop] + 2))
295             return(-1);
296           sum |= 16;
297         }
298     }
299   if (sum == (16 + 8 + 4 + 2 + 1))
300         return 0;
301   else
302         return -1;
303 }
304
305 /*
306  * Build an argument list starting at position 0.
307  * Note, this arg list will be used in an exec system call
308  * AND, the command name will have to be inserted at the head of the list
309  * But, a NULL space will NOT be left at the head of the list
310  */
311 char **RPC_make_arg_list(int family, int type, int well_known_socket,
312                    int flag, char *machine_name)
313 {
314   char arg_str[100];
315   int arg_count = 0;
316   char **arg_list;
317
318   arg_list = new char*[8];
319   sprintf(arg_str, "%s%d", "-p", well_known_socket);  
320   arg_list[arg_count++] = strdup (arg_str);  // 0
321   sprintf(arg_str, "%s%d", "-f", family);
322   arg_list[arg_count++] = strdup (arg_str);  // 1
323   sprintf(arg_str, "%s%d", "-t", type);
324   arg_list[arg_count++] = strdup (arg_str);  // 2
325   if (!machine_name) {
326       machine_name = (char *) malloc(50);
327       gethostname (machine_name, 49);
328   }
329   sprintf(arg_str, "%s%s", "-m", machine_name);
330   arg_list[arg_count++] = strdup (arg_str); // 3
331   sprintf(arg_str, "%s%d", "-l", flag);
332   arg_list[arg_count++] = strdup (arg_str);  // 4
333   arg_list[arg_count++] = 0;                 // 5
334   return arg_list;
335 }
336
337 // returns fd of socket that is listened on, or -1
338 int
339 RPC_setup_socket (int *sfd,   // return file descriptor
340                   int family, // AF_INET ...
341                   int type)   // SOCK_STREAM ...
342 {
343   struct sockaddr_in serv_addr;
344   int length;
345   char machine[50];
346
347   if (gethostname(machine, 49) != 0)
348     return -1;
349
350   if ((*sfd = socket(family, type, 0)) < 0)
351     return -1;
352
353   memset ((char*) &serv_addr, 0, sizeof(serv_addr));
354   /* bzero ((char *) &serv_addr, sizeof(servaddr)); */
355   serv_addr.sin_family = (short) family;
356   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
357   serv_addr.sin_port = htons(0);
358   
359   length = sizeof(serv_addr);
360
361   if (bind(*sfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
362     return -1;
363
364   if (getsockname (*sfd, (struct sockaddr *) &serv_addr, &length) < 0)
365     return -1;
366
367   if (listen(*sfd, 5) < 0)
368     return -1;
369
370   return (ntohs (serv_addr.sin_port));
371 }
372
373 //
374 // connect to well known socket
375 //
376 XDRrpc::XDRrpc(int family,            
377                int req_port,             
378                int type,
379                char *machine, 
380                xdrIOFunc readRoutine,
381                xdrIOFunc writeRoutine,
382                int nblock)
383      // socket, connect using machine
384 {
385   struct sockaddr_in serv_addr;
386   struct hostent *hostptr = 0;
387   struct in_addr *inadr = 0;
388
389   __xdrs__ = 0;
390
391   if ( (hostptr = gethostbyname(machine)) == 0)
392     { fd = -1; return; }
393
394   inadr = (struct in_addr *) hostptr->h_addr_list[0];
395   memset ((char*) &serv_addr, 0, sizeof(serv_addr));
396   /* bzero ((char *) &serv_addr, sizeof(serv_addr)); */
397   serv_addr.sin_family = family;
398   serv_addr.sin_addr = *inadr;
399   serv_addr.sin_port = htons(req_port);
400
401   if ( (fd = socket(family, type, 0)) < 0)
402     { fd = -1; return; }
403
404   if (connect(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
405     { fd = -1; return; }
406
407     __xdrs__ = new XDR;
408     if (!readRoutine) readRoutine = (xdrIOFunc) RPCdefaultXDRRead;
409     if (!writeRoutine) writeRoutine = (xdrIOFunc) RPCdefaultXDRWrite;
410     (void) xdrrec_create(__xdrs__, 0, 0, (char *) fd, readRoutine,writeRoutine);
411     if (nblock)
412       fcntl (fd, F_SETFL, FNDELAY);
413
414 }
415
416 //
417 // our version of string encoding that does malloc as needed.
418 //
419 bool_t xdr_char_PTR(XDR *xdrs, char **str)
420 {
421     int len;
422     unsigned char isNull=0;
423
424         // if XDR_FREE, str's memory is freed
425     switch (xdrs->x_op) {
426         case XDR_ENCODE:
427                 if (*str) {
428                     len = strlen(*str)+1;
429                     if (!xdr_u_char(xdrs, &isNull))
430                         return FALSE;
431                 } else {
432                     isNull = (unsigned char) 1;
433                     if (!xdr_u_char(xdrs, &isNull))
434                         return FALSE;
435                     else
436                         return TRUE;
437                 }
438                 return (xdr_string (xdrs, str, 65536));
439         case XDR_DECODE:
440                 *str = NULL;
441                 if (!xdr_u_char(xdrs, &isNull))
442                     return FALSE;
443                 if (isNull)
444                     return TRUE;
445                 else
446                     return (xdr_string (xdrs, str, 65536));
447         case XDR_FREE:
448                 // xdr_free (xdr_string, str);
449                 if (*str)
450                   free (*str);
451                 *str = NULL;
452                 return (TRUE);
453                 // return(TRUE);
454                 // free the memory
455         default:
456                 return (FALSE);
457                 // this should never occur      
458     }
459 }
460
461 //
462 // directly exec the command (local).
463 //
464 int execCmd(int &pid, char *command, char **arg_list, int portFd)
465 {
466     int ret;
467     int sv[2];
468     char **new_al;
469     int al_len, i;
470     int execlERROR;
471
472     ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
473     if (ret) return(ret);
474     execlERROR = 0;
475     pid = vfork();
476     if (pid == 0) {
477         close(sv[0]);
478         dup2(sv[1], 0);
479         if (!arg_list)
480           execlp(command, command);
481         else {
482           // how long is the arg_list?
483           for (al_len=0; arg_list[al_len]; al_len++) 
484             ; // not a typo
485
486           new_al = new char*[al_len+2];
487           new_al[0] = command;
488           new_al[al_len+1] = 0;
489           for (i=0; i<al_len; ++i) {
490             new_al[i+1] = arg_list[i];
491           }
492           execvp(command, new_al);
493           delete(new_al[0]);
494           delete(new_al);
495         }
496         execlERROR = errno;
497         _exit(-1);
498     } else if (pid > 0 && !execlERROR) {
499         close(sv[1]);
500         return(sv[0]);
501     } else {
502         return(-1);
503     }
504     return(-1);
505 }
506
507 int handleRemoteConnect(int &pid, int fd, int portFd)
508 {
509     char *ret;
510     int retFd;
511     FILE *pfp;
512     char line[256];
513
514     pfp = fdopen(fd, "r");
515     do {
516         ret = fgets(line, sizeof(line)-1, pfp);
517         if (ret && !strncmp(line, "PARADYND", strlen("PARADYND"))) {
518             // got the good stuff
519             sscanf(line, "PARADYND %d", &pid);
520
521             retFd = RPC_getConnect(portFd);
522             return(retFd);
523         } else if (ret) {
524             printf("%s", line);
525             return (-1);
526         }
527     }  while (ret);
528
529     return(-1);
530 }
531
532 //
533 // use rsh to get a remote process started.
534 //
535 int rshCommand(int &pid, char *hostName, char *userName, char *command, 
536     char **arg_list, int portFd)
537 {
538     int total;
539     int fd[2];
540     char **curr;
541     int shellPid;
542     char *paradyndCommand;
543
544     total = strlen(command) + 2;
545     for (curr = arg_list; *curr; curr++) {
546         total += strlen(*curr) + 2;
547     }
548     paradyndCommand = (char *) malloc(total+2);
549
550     sprintf(paradyndCommand, "%s ", command);
551     for (curr = arg_list; *curr; curr++) {
552         strcat(paradyndCommand, *curr);
553         strcat(paradyndCommand, " ");
554     }
555
556     // need to rsh to machine and setup io path.
557
558     if (pipe(fd)) {
559         perror("pipe");
560         return (-1);
561     }
562
563     shellPid = vfork();
564     if (shellPid == 0) {
565         /* child */
566         dup2(fd[1], 1);                         /* copy it onto stdout */
567         close(fd[0]);
568         close(fd[1]);
569         if (userName) {
570             execlp(RSH_COMMAND, RSH_COMMAND, hostName, "-l", 
571                 userName, "-n", paradyndCommand, "-l0", NULL);
572         } else {
573             execlp(RSH_COMMAND, RSH_COMMAND, hostName, "-n", 
574                 paradyndCommand, "-l0", NULL);
575         }
576         _exit(-1);
577     } else if (shellPid > 0) {
578         close(fd[1]);
579     } else {
580         // error situation
581     }
582     return(handleRemoteConnect(pid, fd[0], portFd));
583 }
584
585 int rexecCommand(int &pid, char *hostName, char *userName, char *command, 
586     char **arg_list, int portFd)
587 {
588     int fd;
589     int total;
590     char **curr;
591     char *paradyndCommand;
592     struct servent *inport;
593
594     total = strlen(command) + 2;
595     for (curr = arg_list; *curr; curr++) {
596         total += strlen(*curr) + 2;
597     }
598     paradyndCommand = (char *) malloc(total+2);
599
600     sprintf(paradyndCommand, "%s ", command);
601     for (curr = arg_list; *curr; curr++) {
602         strcat(paradyndCommand, *curr);
603         strcat(paradyndCommand, " ");
604     }
605
606     inport = getservbyname("exec",  "tcp");
607
608     fd = rexec(&hostName, inport->s_port, userName,NULL, paradyndCommand, NULL);
609     if (fd < 0) {
610         perror("rexec");
611         printf("rexec failed\n");
612     }
613     return(handleRemoteConnect(pid, fd, portFd));
614 }
615
616 /*
617  * 
618  * "command" will be appended to the front of the arg list
619  *
620  * if arg_list == NULL, command is the only argument used
621  */
622 int RPCprocessCreate(int &pid, const char *hostName, const char *userName,
623                      const char *command, char **arg_list, int portFd)
624 {
625     int ret;
626     char local[50];
627
628     if (gethostname(local, 49))
629         strcpy (local, " ");
630
631     if (!hostName || 
632         !strcmp(hostName, "") || 
633         !strcmp(hostName, "localhost") ||
634         !strcmp(hostName, local)) {
635       ret = execCmd(pid, command, arg_list, portFd);
636     } else if (useRexec.getValue()) {
637       ret = rexecCommand(pid, hostName, userName, command, arg_list, portFd);
638     } else {
639       ret = rshCommand(pid, hostName, userName, command, arg_list, portFd);
640     }
641     return(ret);
642   }
643
644 int
645 RPC_getConnect(int fd)
646 {
647   int clilen;
648   struct in_addr cli_addr;
649   int new_fd;
650
651   if (fd == -1)
652     return -1;
653
654   clilen = sizeof(cli_addr);
655
656   if ((new_fd = accept (fd, (struct sockaddr *) &cli_addr, &clilen)) < 0)
657     return -1;
658   else
659     return new_fd;
660 }
661
662 /*
663  *  RPCgetArg - break a string into blank separated words
664  *  Used to parse a command line.
665  *  
666  *  input --> a null terminated string, the command line
667  *  argc --> returns the number of args found
668  *  returns --> words (separated by blanks on command line)
669  *  Note --> this allocates memory 
670  */
671 char **RPCgetArg(int &argc, const char *input)
672 {
673 #define BLANK ' '
674
675   char **temp, **result;
676   const char *word_start;
677   int word_count = 0, temp_max, index, length, word_len;
678
679   argc = 0;
680   if (!input) 
681     return ((char **) 0);
682
683   length = strlen(input);
684   index = 0;
685
686   /* advance past blanks in input */
687   while ((index < length) && (input[index] == BLANK))
688     index++;
689
690   /* input is all blanks, or NULL */
691   if (index >= length) {
692     result = new char*[1];
693     if (!result) return ((char**) 0);
694     result[0] = 0;
695     return result;
696   }
697
698   temp = new char*[30];
699   if (!temp) return temp;
700   temp_max = 29;
701
702   do {
703     /* the start of each string */
704     word_len = 0; word_start = input + index;
705
706     /* find the next BLANK and the WORD size */
707     while ((index < length) && (input[index] != BLANK)) {
708       index++; word_len++;
709     }
710
711     /* copy the word */
712     temp[word_count] = new char[word_len+1];
713     strncpy(temp[word_count], word_start, word_len);
714     temp[word_count][word_len] = (char) 0;
715     word_count++;
716
717     /* skip past consecutive blanks */
718     while ((index < length) && (input[index] == BLANK))
719       index++;
720
721     /* no more room in temp, copy it to new_temp */
722     if (word_count > temp_max) {
723       char **new_temp;
724       int nt_len, i;
725       
726       /* new temp_max size */
727       nt_len = (temp_max+1) >> 1;
728       temp_max = nt_len - 1;
729
730       /* copy temp to new_temp */
731       new_temp = new char*[nt_len];
732       if (!new_temp) return ((char**) 0);
733       for (i=0; i<word_count; ++i)
734         new_temp[i] = temp[i];
735       
736       delete temp;
737       temp = new_temp;
738     }
739   } while (index < length);
740
741   argc = word_count;
742   /* null terminate the word list */
743   temp[word_count] = (char*) 0;
744   return temp;
745 }