Fixed bug in XDRrpc constructor that clobbered the fd value. Added feature
[dyninst.git] / common / src / rpcUtil.C
1 /*
2 $Log: rpcUtil.C,v $
3 Revision 1.18  1994/04/06 22:46:12  markc
4 Fixed bug in XDRrpc constructor that clobbered the fd value.  Added feature
5 to RPC_readReady to do blocking select.
6
7  * Revision 1.17  1994/04/01  20:05:27  hollings
8  * Removed kill of rsh process (not needed and it causes a race condition).
9  *
10  * Revision 1.16  1994/04/01  04:59:13  markc
11  * Put in support to encode NULL ptrs to strings in xdr_String.
12  *
13  * Revision 1.15  1994/03/31  22:59:08  hollings
14  * added well known port as a paramter to xdrRPC constructor.
15  *
16  * Revision 1.14  1994/03/31  22:45:04  markc
17  * Added Log for rcs.
18  *
19 */
20
21 //
22 // This file defines a set of utility routines for RPC services.
23 //
24 //
25
26 // overcome malloc redefinition due to /usr/include/rpc/types.h declaring 
27 // malloc 
28
29 #include <signal.h>
30 #include <sys/wait.h>
31
32 /* prevents malloc from being redefined */
33 #ifdef MIPS
34 #define MALLOC_DEFINED_AS_VOID
35 #endif
36
37 #include "util/h/rpcUtil.h"
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 #include <assert.h>
44 #include <fcntl.h>
45 #include <memory.h>
46 #include <netinet/in.h>
47 #include <netdb.h>
48 #include <sys/time.h>
49 #include <unistd.h>
50 #include <sys/file.h>
51
52 extern "C" {
53 #include <rpc/types.h>
54 #include <rpc/xdr.h>
55 }
56
57 // functions that g++-fixincludes missed
58 #ifdef MIPS
59 extern "C" {
60 void bzero (char*, int);
61 int select (int, fd_set*, fd_set*, fd_set*, struct timeval*);
62 char *strdup (char*);
63 int gethostname(char*, int);
64 int socket(int, int, int);
65 int bind(int s, struct sockaddr *, int);
66 int getsockname(int, struct sockaddr*, int *);
67 int listen(int, int);
68 int connect(int s, struct sockaddr*, int);
69 int socketpair(int, int, int, int sv[2]);
70 int vfork();
71 int accept(int, struct sockaddr *addr, int *); 
72 }
73 #endif
74
75
76 #define RSH_COMMAND     "rsh"
77
78 int RPCdefaultXDRRead(int handle, char *buf, u_int len)
79 {
80     int ret;
81
82     do {
83         ret = read(handle, buf, len);
84     } while (ret < 0 && errno == EINTR);
85
86     if (ret <= 0) return(-1);
87     return (ret);
88 }
89
90 int RPCdefaultXDRWrite(int handle, char *buf, u_int len)
91 {
92     int ret;
93
94     do {
95         ret = write(handle, buf, len);
96     } while (ret < 0 && errno == EINTR);
97
98     if (ret != len) 
99         return(-1);
100     else
101         return (ret);
102 }
103
104 XDRrpc::~XDRrpc()
105 {
106   if (fd >= 0)
107     {
108       fcntl (fd, F_SETFL, FNDELAY);
109       close(fd);
110     }
111   if (__xdrs__) 
112     {
113       xdr_destroy (__xdrs__);
114       delete (__xdrs__);
115     }
116 }
117
118 //
119 // prepare for RPC's to be done/received on the passed fd.
120 //
121 XDRrpc::XDRrpc(int f, xdrIOFunc readRoutine, xdrIOFunc writeRoutine, int nblock)
122 {
123     fd = f;
124     __xdrs__ = new XDR;
125     if (!readRoutine) readRoutine = RPCdefaultXDRRead;
126     if (!writeRoutine) writeRoutine = RPCdefaultXDRWrite;
127     (void) xdrrec_create(__xdrs__, 0, 0, (char *) fd, readRoutine,writeRoutine);
128     if (nblock)
129       fcntl (fd, F_SETFL, FNDELAY);
130 }
131
132 //
133 // prepare for RPC's to be done/received on the passed fd.
134 //
135 XDRrpc::XDRrpc(char *machine,
136                char *user,
137                char *program,
138                xdrIOFunc readRoutine, 
139                xdrIOFunc writeRoutine,
140                char **arg_list,
141                int nblock,
142                int wellKnownPortFd)
143 {
144     fd = RPCprocessCreate(&pid, machine, user, program, arg_list, 
145         wellKnownPortFd);
146     if (fd >= 0) {
147         __xdrs__ = new XDR;
148         if (!readRoutine) readRoutine = RPCdefaultXDRRead;
149         if (!writeRoutine) writeRoutine = RPCdefaultXDRWrite;
150         (void) xdrrec_create(__xdrs__, 0, 0, (char *) fd, 
151                 readRoutine, writeRoutine);
152         if (nblock)
153           fcntl (fd, F_SETFL, FNDELAY);
154     } else {
155         __xdrs__ = NULL;
156         fd = -1;
157     }
158 }
159
160 int
161 RPC_readReady (int fd, int timeout)
162 {
163   fd_set readfds;
164   struct timeval tvptr, *the_tv;
165
166   tvptr.tv_sec = timeout; tvptr.tv_usec = 0;
167   if (fd < 0) return -1;
168   FD_ZERO(&readfds);
169   FD_SET (fd, &readfds);
170
171   // -1 timeout = blocking select
172   if (timeout == -1)
173      the_tv = 0;
174   else
175      the_tv = &tvptr;
176  
177   if (select (fd+1, &readfds, NULL, NULL, the_tv) == -1)
178     {
179       // if (errno == EBADF)
180         return -1;
181     }
182   return (FD_ISSET (fd, &readfds));
183 }
184
185 int 
186 RPC_undo_arg_list (int argc, char **arg_list, char **machine, int &family,
187                    int &type, int &well_known_socket, int &flag)
188 {
189   int loop;
190   char *ptr;
191   int sum = 0;
192
193   for (loop=0; loop < argc; ++loop)
194     {
195       if (!strncmp(arg_list[loop], "-p", 2))
196         {
197           well_known_socket = (int) strtol (arg_list[loop] + 2, &ptr, 10);
198           if (!ptr)
199             return(-1);
200           sum |= 1;
201         }
202       else if (!strncmp(arg_list[loop], "-f", 2))
203         {
204           family = (int) strtol (arg_list[loop] + 2, &ptr, 10);
205           if (!ptr)
206             return(-1);
207           sum |= 2;
208         }
209       else if (!strncmp(arg_list[loop], "-t", 2))
210         {
211           type = (int) strtol (arg_list[loop] + 2, &ptr, 10);
212           if (!ptr)
213             return(-1);
214           sum |= 4;
215         }
216       else if (!strncmp(arg_list[loop], "-m", 2))
217         {
218           *machine = strdup (arg_list[loop] + 2);
219           if (!(*machine)) return -1;
220           sum |= 8;
221         }
222       else if (!strncmp(arg_list[loop], "-l", 2))
223         {
224           flag = (int) strtol (arg_list[loop] + 2, &ptr, 10);
225           if (!ptr)
226             return(-1);
227           sum |= 16;
228         }
229     }
230   if (sum == (16 + 8 + 4 + 2 + 1))
231         return 0;
232   else
233         return -1;
234 }
235
236 char **
237 RPC_make_arg_list (char *program, int family, int type, int well_known_socket,
238                    int flag)
239 {
240   char arg_str[100];
241   int arg_count = 0;
242   char **arg_list;
243   char machine_name[50];
244
245   arg_list = new char*[7];
246   arg_list[arg_count++] = strdup (program);
247   sprintf(arg_str, "%s%d", "-p", well_known_socket);
248   arg_list[arg_count++] = strdup (arg_str);
249   sprintf(arg_str, "%s%d", "-f", family);
250   arg_list[arg_count++] = strdup (arg_str);
251   sprintf(arg_str, "%s%d", "-t", type);
252   arg_list[arg_count++] = strdup (arg_str);
253   gethostname (machine_name, 49);
254   sprintf(arg_str, "%s%s", "-m", machine_name);
255   arg_list[arg_count++] = strdup (arg_str);
256   sprintf(arg_str, "%s%d", "-l", flag);
257   arg_list[arg_count++] = strdup (arg_str);
258   arg_list[arg_count++] = 0;
259   return arg_list;
260 }
261
262 // returns fd of socket that is listened on, or -1
263 int
264 RPC_setup_socket (int *sfd,   // return file descriptor
265                   int family, // AF_INET ...
266                   int type)   // SOCK_STREAM ...
267 {
268   struct sockaddr_in serv_addr;
269   int length;
270   char machine[50];
271
272   if (gethostname(machine, 49) != 0)
273     return -1;
274
275   if ((*sfd = socket(family, type, 0)) < 0)
276     return -1;
277   
278   bzero ((char *) &serv_addr, sizeof(serv_addr));
279   serv_addr.sin_family = (short) family;
280   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
281   serv_addr.sin_port = htons(0);
282   
283   length = sizeof(serv_addr);
284
285   if (bind(*sfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
286     return -1;
287
288   if (getsockname (*sfd, (struct sockaddr *) &serv_addr, &length) < 0)
289     return -1;
290
291   if (listen(*sfd, 5) < 0)
292     return -1;
293
294   return (ntohs (serv_addr.sin_port));
295 }
296
297 //
298 // connect to well known socket
299 //
300 XDRrpc::XDRrpc(int family,            
301                int req_port,             
302                int type,
303                char *machine, 
304                xdrIOFunc readRoutine,
305                xdrIOFunc writeRoutine,
306                int nblock)
307      // socket, connect using machine
308 {
309   struct sockaddr_in serv_addr;
310   struct hostent *hostptr = 0;
311   struct in_addr *inadr = 0;
312
313   __xdrs__ = 0;
314
315   if ( (hostptr = gethostbyname(machine)) == 0)
316     { fd = -1; return; }
317
318   inadr = (struct in_addr *) hostptr->h_addr_list[0];
319   bzero ((char *) &serv_addr, sizeof(serv_addr));
320   serv_addr.sin_family = family;
321   serv_addr.sin_addr = *inadr;
322   serv_addr.sin_port = htons(req_port);
323
324   if ( (fd = socket(family, type, 0)) < 0)
325     { fd = -1; return; }
326
327   if (connect(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
328     { fd = -1; return; }
329
330     __xdrs__ = new XDR;
331     if (!readRoutine) readRoutine = RPCdefaultXDRRead;
332     if (!writeRoutine) writeRoutine = RPCdefaultXDRWrite;
333     (void) xdrrec_create(__xdrs__, 0, 0, (char *) fd, readRoutine,writeRoutine);
334     if (nblock)
335       fcntl (fd, F_SETFL, FNDELAY);
336
337 }
338
339 //
340 // prepare for RPC's to be done/received on the passed thread id.
341 //
342 THREADrpc::THREADrpc(int thread)
343 {
344     tid = thread;
345 }
346
347 //
348 // This should never be called, it should be replaced by a virtual function
349 //    from the derived class created by igen.
350 //
351 void RPCUser::verifyProtocolAndVersion()
352 {
353     abort();
354 }
355
356 //
357 // our version of string encoding that does malloc as needed.
358 //
359 bool_t xdr_String(XDR *xdrs, String *str)
360 {
361     int len;
362     unsigned char isNull=0;
363
364         // if XDR_FREE, str's memory is freed
365     switch (xdrs->x_op) {
366         case XDR_ENCODE:
367                 if (*str) {
368                     len = strlen(*str)+1;
369                     if (!xdr_u_char(xdrs, &isNull))
370                         return FALSE;
371                 } else {
372                     isNull = (unsigned char) 1;
373                     if (!xdr_u_char(xdrs, &isNull))
374                         return FALSE;
375                     else
376                         return TRUE;
377                 }
378                 return (xdr_string (xdrs, str, 65536));
379         case XDR_DECODE:
380                 *str = NULL;
381                 if (!xdr_u_char(xdrs, &isNull))
382                     return FALSE;
383                 if (isNull)
384                     return TRUE;
385                 else
386                     return (xdr_string (xdrs, str, 65536));
387         case XDR_FREE:
388                 // xdr_free (xdr_string, str);
389                 if (*str)
390                   free (*str);
391                 *str = NULL;
392                 return (TRUE);
393                 // return(TRUE);
394                 // free the memory
395         default:
396                 assert(0);
397                 // this should never occur      
398     }
399 }
400
401 int RPCprocessCreate(int *pid, char *hostName, char *userName,
402                      char *command, char **arg_list, int portFd)
403 {
404     int ret;
405     int sv[2];
406     int execlERROR;
407     char local[50];
408
409     if (gethostname(local, 49))
410         strcpy (local, " ");
411
412     if (!hostName || 
413         !strcmp(hostName, "") || 
414         !strcmp(hostName, "localhost") ||
415         !strcmp(hostName, local)) {
416         ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
417         if (ret) return(ret);
418         execlERROR = 0;
419         *pid = vfork();
420         if (*pid == 0) {
421             close(sv[0]);
422             dup2(sv[1], 0);
423             if (!arg_list)
424               execl(command, command);
425             else
426               execv(command, arg_list);
427             execlERROR = errno;
428             _exit(-1);
429         } else if (*pid > 0 && !execlERROR) {
430             close(sv[1]);
431             return(sv[0]);
432         } else {
433             return(-1);
434         }
435     } else {
436         int total;
437         int fd[2];
438         int retFd;
439         char *ret;
440         FILE *pfp;
441         char **curr;
442         int shellPid;
443         char line[256];
444         char *paradyndCommand;
445
446         total = strlen(command) + 2;
447         for (curr = arg_list+1; *curr; curr++) {
448             total += strlen(*curr) + 2;
449         }
450         paradyndCommand = (char *) malloc(total+2);
451
452         sprintf(paradyndCommand, "%s ", command);
453         for (curr = arg_list+1; *curr; curr++) {
454             strcat(paradyndCommand, *curr);
455             strcat(paradyndCommand, " ");
456         }
457
458         // need to rsh to machine and setup io path.
459
460         if (pipe(fd)) {
461             perror("pipe");
462             return (-1);
463         }
464
465         shellPid = vfork();
466         if (shellPid == 0) {
467             /* child */
468             dup2(fd[1], 1);                         /* copy it onto stdout */
469             close(fd[0]);
470             close(fd[1]);
471             if (userName) {
472                 execlp(RSH_COMMAND, RSH_COMMAND, hostName, "-l", 
473                     userName, "-n", paradyndCommand, "-l0", NULL);
474             } else {
475                 execlp(RSH_COMMAND, RSH_COMMAND, hostName, "-n", 
476                     paradyndCommand, "-l0", NULL);
477             }
478             _exit(-1);
479         } else if (shellPid > 0) {
480             close(fd[1]);
481         } else {
482             // error situation
483         }
484
485         pfp = fdopen(fd[0], "r");
486         do {
487             ret = fgets(line, sizeof(line)-1, pfp);
488             if (ret && !strncmp(line, "PARADYND", strlen("PARADYND"))) {
489                 // got the good stuff
490                 sscanf(line, "PARADYND %d", pid);
491
492                 retFd = RPC_getConnect(portFd);
493                 return(retFd);
494             } else if (ret) {
495                 // some sort of error message from rsh.
496                 printf("%s", line);
497             }
498         }  while (ret);
499
500         return(-1);
501     }
502 }
503
504 //
505 // wait for an expected RPC responce, but also handle upcalls while waiting.
506 //    Should not be called directly !!!
507 //
508 void RPCUser::awaitResponce(int tag)
509 {
510     abort();
511 }
512
513 void
514 XDRrpc::setNonBlock()
515 {
516   if (fd >= 0)
517     fcntl (fd, F_SETFL, FNDELAY);
518 }
519
520 int
521 XDRrpc::readReady(int timeout)
522 {
523   return RPC_readReady (fd, timeout);
524 }
525
526 int
527 RPC_getConnect(int fd)
528 {
529   int clilen;
530   struct in_addr cli_addr;
531   int new_fd;
532
533   if (fd == -1)
534     return -1;
535
536   clilen = sizeof(cli_addr);
537
538   if ((new_fd = accept (fd, (struct sockaddr *) &cli_addr, &clilen)) < 0)
539     return -1;
540   else
541     return new_fd;
542 }
543