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