first version
[dyninst.git] / common / src / rpcUtil.C
1 /*
2  * Copyright (c) 1996 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * This license is for research uses.  For such uses, there is no
12  * charge. We define "research use" to mean you may freely use it
13  * inside your organization for whatever purposes you see fit. But you
14  * may not re-distribute Paradyn or parts of Paradyn, in any form
15  * source or binary (including derivatives), electronic or otherwise,
16  * to any other organization or entity without our permission.
17  * 
18  * (for other uses, please contact us at paradyn@cs.wisc.edu)
19  * 
20  * All warranties, including without limitation, any warranty of
21  * merchantability or fitness for a particular purpose, are hereby
22  * excluded.
23  * 
24  * By your use of Paradyn, you understand and agree that we (or any
25  * other person or entity with proprietary rights in Paradyn) are
26  * under no obligation to provide either maintenance services,
27  * update services, notices of latent defects, or correction of
28  * defects for Paradyn.
29  * 
30  * Even if advised of the possibility of such damages, under no
31  * circumstances shall we (or any other person or entity with
32  * proprietary rights in the software licensed hereunder) be liable
33  * to you or any third party for direct, indirect, or consequential
34  * damages of any character regardless of type of action, including,
35  * without limitation, loss of profits, loss of use, loss of good
36  * will, or computer failure or malfunction.  You agree to indemnify
37  * us (and any other person or entity with proprietary rights in the
38  * software licensed hereunder) for any and all liability it may
39  * incur to third parties resulting from your use of Paradyn.
40  */
41
42 /*
43  * $Log: rpcUtil.C,v $
44  * Revision 1.45  1996/11/26 16:09:37  naim
45  * Fixing asserts - naim
46  *
47  * Revision 1.44  1996/11/12 17:50:16  mjrg
48  * Removed warnings, changes for compiling with Visual C++ and xlc
49  *
50  * Revision 1.43  1996/08/16 21:32:02  tamches
51  * updated copyright for release 1.1
52  *
53  * Revision 1.42  1996/05/31 23:43:10  tamches
54  * removed pid from XDRrpc.  Modified handleRemoteConnect appropriately.
55  * Now paradynd doesn't try to send pid to paradyn (it wasn't being used
56  * and was one cause of paradyn UI freezing on UI startup).
57  *
58  * Revision 1.41  1995/11/22 00:06:20  mjrg
59  * Updates for paradyndPVM on solaris
60  * Fixed problem with wrong daemon getting connection to paradyn
61  * Removed -f and -t arguments to paradyn
62  * Added cleanUpAndExit to clean up and exit from pvm before we exit paradynd
63  * Fixed bug in my previous commit
64  *
65  * Revision 1.40  1995/11/12 00:44:28  newhall
66  * fix to execCmd: forked process closes it's copy of parent's file descriptors
67  *
68  * Revision 1.39  1995/08/24  15:14:29  hollings
69  * AIX/SP-2 port (including option for split instruction/data heaps)
70  * Tracing of rexec (correctly spawns a paradynd if needed)
71  * Added rtinst function to read getrusage stats (can now be used in metrics)
72  * Critical Path
73  * Improved Error reporting in MDL sematic checks
74  * Fixed MDL Function call statement
75  * Fixed bugs in TK usage (strings passed where UID expected)
76  *
77  * Revision 1.38  1995/05/18  11:12:15  markc
78  * Added flavor arg to RPC_undo_g_list
79  *
80 */
81
82 //
83 // This file defines a set of utility routines for RPC services.
84 //
85 //
86
87 // overcome malloc redefinition due to /usr/include/rpc/types.h declaring 
88 // malloc 
89 // This is ugly, and I hope to get rid of it -- mdc 2/2/95
90 #if defined(notdef)
91 /* prevents malloc from being redefined */
92 #ifdef MIPS
93 #define MALLOC_DEFINED_AS_VOID
94 #endif
95 #endif
96
97 #include "util/h/rpcUtil.h"
98
99 const char *RSH_COMMAND="rsh";
100
101 int RPCdefaultXDRRead(const void* handle, char *buf, const u_int len)
102 {
103     int fd = (int) handle;
104     int ret;
105
106     do {
107         ret = P_read(fd, buf, len);
108     } while (ret < 0 && errno == EINTR);
109
110     if (ret <= 0) { return(-1); }
111     return (ret);
112 }
113
114 int RPCdefaultXDRWrite(const void* handle, const char *buf, const u_int len)
115 {
116     int fd = (int) handle;
117     int ret;
118
119     do {
120
121         ret = P_write(fd, buf, len);
122     } while (ret < 0 && errno == EINTR);
123
124     errno = 0;
125     if (ret != (int)len)
126       return(-1);
127     else
128       return (ret);
129 }
130
131 XDRrpc::~XDRrpc()
132 {
133   if (fd >= 0)
134     {
135       P_fcntl (fd, F_SETFL, FNDELAY);
136       P_close(fd);
137       fd = -1;
138     }
139   if (xdrs) 
140     {
141       P_xdr_destroy (xdrs);
142       delete (xdrs);
143       xdrs = NULL;
144     }
145 }
146
147 //
148 // prepare for RPC's to be done/received on the passed fd.
149 //
150 XDRrpc::XDRrpc(const int f, xdr_rd_func readRoutine, xdr_wr_func writeRoutine, const bool nblock)
151 : xdrs(NULL), fd(f)
152 {
153     assert(fd >= 0);
154     xdrs = new XDR;
155     assert(xdrs);
156     if (!readRoutine) readRoutine = (xdr_rd_func) RPCdefaultXDRRead;
157     if (!writeRoutine) writeRoutine = (xdr_wr_func) RPCdefaultXDRWrite;
158     P_xdrrec_create(xdrs, 0, 0, (char *) fd, readRoutine, writeRoutine);
159     if (nblock)
160       P_fcntl (fd, F_SETFL, FNDELAY);
161 }
162
163 //
164 // prepare for RPC's to be done/received on the passed fd.
165 //
166 XDRrpc::XDRrpc(const string &machine,
167                const string &user,
168                const string &program,
169                xdr_rd_func readRoutine, 
170                xdr_wr_func writeRoutine,
171                const vector<string> &arg_list,
172                const bool nblock,
173                const int wellKnownPortFd)
174 : xdrs(NULL), fd(-1)
175 {
176     fd = RPCprocessCreate(machine, user, program, arg_list, wellKnownPortFd);
177     if (fd >= 0) {
178         xdrs = new XDR;
179         if (!readRoutine) readRoutine = (xdr_rd_func) RPCdefaultXDRRead;
180         if (!writeRoutine) writeRoutine = (xdr_wr_func) RPCdefaultXDRWrite;
181         P_xdrrec_create(xdrs, 0, 0, (char *) fd, readRoutine, writeRoutine);
182         if (nblock)
183           P_fcntl (fd, F_SETFL, FNDELAY);
184     }
185 }
186
187 bool
188 RPC_readReady (int fd, int timeout)
189 {
190   fd_set readfds;
191   struct timeval tvptr, *the_tv;
192
193   tvptr.tv_sec = timeout; tvptr.tv_usec = 0;
194   if (fd < 0) return false;
195   FD_ZERO(&readfds);
196   FD_SET (fd, &readfds);
197
198   // -1 timeout = blocking select
199   if (timeout == -1)
200      the_tv = 0;
201   else
202      the_tv = &tvptr;
203  
204   if (P_select (fd+1, &readfds, NULL, NULL, the_tv) == -1)
205     {
206       // if (errno == EBADF)
207         return false;
208     }
209   return (FD_ISSET (fd, &readfds));
210 }
211
212 // TODO
213 // mdc - I need to clean this up
214 bool
215 RPC_undo_arg_list (string& flavor, int argc, char **arg_list, string &machine,
216                    int &well_known_socket, int &flag,
217                    int &firstPVM)
218 {
219   int loop;
220   char *ptr;
221   bool b_well_known=false, b_first=false,
222   b_machine = false, b_flag = false, b_flavor=false;
223
224   for (loop=0; loop < argc; ++loop)
225     {
226       // stop at the -runme argument since the rest are for the application
227       //   process we are about to spawn
228       if (!strcmp(arg_list[loop], "-runme")) break;
229       if (!P_strncmp(arg_list[loop], "-p", 2))
230         {
231           well_known_socket = P_strtol (arg_list[loop] + 2, &ptr, 10);
232           if (ptr == (arg_list[loop] + 2))
233             return(false);
234           b_well_known = true;
235         }
236       else if (!P_strncmp(arg_list[loop], "-v", 2))
237         {
238           firstPVM = P_strtol (arg_list[loop] + 2, &ptr, 10);
239           if (ptr == (arg_list[loop] + 2))
240             return(false);
241           b_first = true;
242         }
243       else if (!P_strncmp(arg_list[loop], "-m", 2))
244         {
245           machine = (arg_list[loop] + 2);
246           if (!machine.length()) return false;
247           b_machine = true;
248         }
249       else if (!P_strncmp(arg_list[loop], "-l", 2))
250         {
251           flag = P_strtol (arg_list[loop] + 2, &ptr, 10);
252           if (ptr == (arg_list[loop] + 2))
253             return(false);
254           b_flag = true;
255         }
256       else if (!P_strncmp(arg_list[loop], "-z", 2))
257         {
258           flavor = (arg_list[loop]+2);
259           if (!flavor.length()) return false;
260           b_flavor = true;
261         }
262     }
263   return (b_flag && b_first && b_machine && b_well_known && b_flavor);
264 }
265
266 /*
267  * Build an argument list starting at position 0.
268  * Note, this arg list will be used in an exec system call
269  * AND, the command name will have to be inserted at the head of the list
270  * But, a NULL space will NOT be left at the head of the list
271  */
272 bool RPC_make_arg_list(vector<string> &list,
273                        const int well_known_socket, const int flag, const int firstPVM,
274                        const string machine_name, const bool use_machine)
275 {
276   char arg_str[100];
277
278   list.resize(0);
279
280   sprintf(arg_str, "%s%d", "-p", well_known_socket);  
281   list += arg_str;
282   // arg_list[arg_count++] = strdup (arg_str);  // 0
283
284   if (!use_machine) {
285     struct utsname unm;
286     if (P_uname(&unm) == -1)
287       assert(0);
288     sprintf(arg_str, "%s%s", "-m", unm.nodename);
289     list += arg_str;
290   } else {
291     sprintf(arg_str, "%s%s", "-m", machine_name.string_of());
292     list += arg_str;
293   }
294   // arg_list[arg_count++] = strdup (arg_str); // 3
295
296   sprintf(arg_str, "%s%d", "-l", flag);
297   list += arg_str;
298   //arg_list[arg_count++] = strdup (arg_str);  // 4
299
300   sprintf(arg_str, "%s%d", "-v", firstPVM);
301   list += arg_str;
302   // arg_list[arg_count++] = strdup (arg_str);  // 5 
303
304   return true;
305 }
306
307 // returns fd of socket that is listened on, or -1
308 // (actually, looks like it returns the port number listened on, or -1)
309 int
310 RPC_setup_socket (int &sfd,   // return file descriptor
311                   const int family, // AF_INET ...
312                   const int type)   // SOCK_STREAM ...
313 {
314   struct sockaddr_in serv_addr;
315
316   if ((sfd = P_socket(family, type, 0)) < 0)
317     return -1;
318
319   P_memset ((void*) &serv_addr, 0, sizeof(serv_addr));
320   serv_addr.sin_family = (short) family;
321   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
322   serv_addr.sin_port = htons(0);
323   
324   size_t length = sizeof(serv_addr);
325
326   if (P_bind(sfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
327     return -1;
328
329   if (P_getsockname (sfd, (struct sockaddr *) &serv_addr, &length) < 0)
330     return -1;
331
332   if (P_listen(sfd, 5) < 0)
333     return -1;
334
335   return (ntohs (serv_addr.sin_port));
336 }
337
338 //
339 // connect to well known socket
340 //
341 XDRrpc::XDRrpc(int family,            
342                int req_port,             
343                int type,
344                const string machine, 
345                xdr_rd_func readRoutine,
346                xdr_wr_func writeRoutine,
347                const bool nblock) : xdrs(NULL), fd(-1)
348      // socket, connect using machine
349 {
350   struct sockaddr_in serv_addr;
351   struct hostent *hostptr = 0;
352   struct in_addr *inadr = 0;
353   if (!(hostptr = P_gethostbyname(machine.string_of())))
354     return;
355
356   inadr = (struct in_addr *) ((void*) hostptr->h_addr_list[0]);
357   P_memset ((void*) &serv_addr, 0, sizeof(serv_addr));
358   serv_addr.sin_family = family;
359   serv_addr.sin_addr = *inadr;
360   serv_addr.sin_port = htons(req_port);
361
362   if ( (fd = P_socket(family, type, 0)) < 0)
363     { fd = -1; return; }
364
365   if (P_connect(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
366     { fd = -1; return; }
367
368     xdrs = new XDR;
369     assert(xdrs);
370     if (!readRoutine) readRoutine = (xdr_rd_func) RPCdefaultXDRRead;
371     if (!writeRoutine) writeRoutine = (xdr_wr_func) RPCdefaultXDRWrite;
372     P_xdrrec_create(xdrs, 0, 0, (char *) fd, readRoutine,writeRoutine);
373     if (nblock)
374       P_fcntl (fd, F_SETFL, FNDELAY);
375
376 }
377
378 //
379 // These xdr functions are not to be called with XDR_FREE
380 //
381 bool_t xdr_Boolean(XDR *xdrs, bool *bool_val) {
382    u_char i;
383    bool_t res;
384    switch (xdrs->x_op) {
385    case XDR_ENCODE:
386       i = (u_char) *bool_val;
387       return (P_xdr_u_char(xdrs, &i));
388    case XDR_DECODE:
389       res = P_xdr_u_char(xdrs, &i);
390       *bool_val = (bool) i;
391       return res;
392    case XDR_FREE:
393    default:
394       assert(0);
395       return FALSE;
396    }
397 }
398
399 // XDR_FREE not handled, free it yourself!
400 // our version of string encoding that does malloc as needed.
401 //
402 bool_t xdr_string_pd(XDR *xdrs, string *str)
403 {
404   unsigned int length = 0;
405   assert(str);
406
407   // if XDR_FREE, str's memory is freed
408   switch (xdrs->x_op) {
409   case XDR_ENCODE:
410     if ((length = str->length())) {
411       if (!P_xdr_u_int(xdrs, &length))
412         return FALSE;
413       else {
414         char *buffer = (char*) str->string_of(); 
415         bool_t res = P_xdr_string (xdrs, &buffer, str->length() + 1);
416         return res;
417       }
418     } else {
419       return (P_xdr_u_int(xdrs, &length));
420     }
421   case XDR_DECODE:
422     if (!P_xdr_u_int(xdrs, &length))
423       return FALSE;
424      else if (!length) {
425        *str = (char*) NULL;
426        return TRUE;
427      } else {
428        char *temp; 
429        bool newd = false;
430        unsigned max_len = 511;
431        char stat_buf[512];
432        if (length < 512) 
433           temp = (char*) stat_buf;
434        else {
435           temp = new char[length+1];
436           max_len = length;
437           newd = true;
438        }      
439        if (!P_xdr_string (xdrs, &temp, max_len)) {
440           if (newd) 
441             delete temp;
442           return FALSE;
443        } else {
444           *str = temp;
445           if (newd)
446             delete temp; 
447           return TRUE;
448        }
449      }
450   case XDR_FREE:
451   default:
452     // this should never occur  
453     assert(0);
454     return (FALSE);
455   }
456 }
457
458 //
459 // directly exec the command (local).
460 //
461
462 int execCmd(const string command, const vector<string> &arg_list, int /*portFd*/)
463 {
464   int ret;
465   int sv[2];
466   int execlERROR;
467
468   errno = 0;
469   ret = P_socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
470   if (ret==-1) return(ret);
471   execlERROR = 0;
472   int al_len = arg_list.size();
473   char **new_al = new char*[al_len+2];
474   // TODO argv[0] should not include path
475   new_al[0] = P_strdup(command.string_of());
476   new_al[al_len+1] = NULL;
477   for (int i=0; i<al_len; ++i)
478     new_al[i+1] = P_strdup(arg_list[i].string_of());
479   ret = -1;
480
481   int pid;
482   pid = vfork();
483   // close sv[0] after exec 
484   P_fcntl(sv[0],F_SETFD,1);  
485
486   if (pid == 0) {
487     if (P_close(sv[0])) { execlERROR = errno; _exit(-1);}
488     if (P_dup2(sv[1], 0)) { execlERROR = errno; _exit(-1);}
489     P_execvp(new_al[0], new_al);
490     execlERROR = errno;
491     _exit(-1);
492   } else if (pid > 0 && !execlERROR) {
493     P_close(sv[1]);
494     ret = sv[0];
495   }
496
497   al_len=0;
498   while (new_al[al_len]) {
499     delete (new_al[al_len]);
500     al_len++;
501   }
502   delete(new_al);
503   return ret;
504 }
505
506 int handleRemoteConnect(int fd, int portFd) {
507     // NOTE: This routine is not generic; it is very specific to paradyn
508     // (due to the sscanf(line, "PARADYND %d")...)
509     // Hence it makes absolutely no sense to put it in a so-called library.
510     // It should be put into the paradyn/src tree --ari
511
512     FILE *pfp = P_fdopen(fd, "r");
513     if (pfp == NULL) {
514        cerr << "handleRemoteConnect: fdopen of fd " << fd << " failed." << endl;
515        return -1;
516     }
517
518     int retFd = RPC_getConnect(portFd); // calls accept(), which blocks (sigh...)
519     return retFd;
520 }
521
522 //
523 // use rsh to get a remote process started.
524 //
525 // We do an rsh to start a process and them wait for the process 
526 // to do a connect. There is no guarantee that the process we are waiting for is
527 // the one that gets the connection. This can happen with paradyndPVM: the
528 // daemon that is started by a rshCommand will start other daemons, and one of 
529 // these daemons may get the connection that should be for the first daemon.
530 // Daemons should always get a connection before attempting to start other daemons.
531 //
532
533 int rshCommand(const string hostName, const string userName, 
534                const string command, const vector<string> &arg_list, int portFd)
535 {
536     int fd[2];
537     int shellPid;
538     int ret;
539
540     int total = command.length() + 2;
541     for (unsigned i=0; i<arg_list.size(); i++) {
542       total += arg_list[i].length() + 2;
543     }
544
545     string paradyndCommand = command + " ";
546
547     for (unsigned j=0; j < arg_list.size(); j++)
548         paradyndCommand += arg_list[j] + " ";
549
550     // need to rsh to machine and setup io path.
551
552     if (pipe(fd)) {
553         perror("pipe");
554         return (-1);
555     }
556
557     shellPid = vfork();
558     if (shellPid == 0) {
559         /* child */
560         bool aflag;
561         aflag=(-1 != dup2(fd[1], 1)); /* copy it onto stdout */
562         assert(aflag);
563         aflag=(-1 != close(fd[0]));
564         assert(aflag);
565         aflag=(-1 != close(fd[1]));
566         assert(aflag);
567         if (userName.length()) {
568             ret = execlp(RSH_COMMAND, RSH_COMMAND, hostName.string_of(), "-l", 
569                          userName.string_of(), "-n", paradyndCommand.string_of(),
570                          "-l0", NULL);
571             fprintf(stderr,"rshCommand: execlp failed (ret = %d)\n",ret);
572         } else {
573             ret = execlp(RSH_COMMAND, RSH_COMMAND, hostName.string_of(), "-n", 
574                          paradyndCommand.string_of(), "-l0", NULL);
575             fprintf(stderr,"rshCommand: execlp failed (ret = %d)\n",ret);
576         }
577         _exit(-1);
578     } else if (shellPid > 0) {
579         close(fd[1]);
580     } else {
581         // error situation
582     }
583
584     return(handleRemoteConnect(fd[0], portFd));
585 }
586
587 int rexecCommand(const string hostName, const string userName, 
588                  const string command, const vector<string> &arg_list, int portFd)
589 {
590     struct servent *inport;
591
592     int total = command.length() + 2;
593     for (unsigned i=0; i<arg_list.size(); i++) {
594       total += arg_list[i].length() + 2;
595     }
596
597     char *paradyndCommand = new char[total+2];
598     assert(paradyndCommand);
599
600     sprintf(paradyndCommand, "%s ", command.string_of());
601     for (unsigned j=0; j<arg_list.size(); j++) {
602         P_strcat(paradyndCommand, arg_list[j].string_of());
603         P_strcat(paradyndCommand, " ");
604     }
605
606     inport = P_getservbyname("exec",  "tcp");
607
608     char *hname = P_strdup(hostName.string_of());
609     char *uname = P_strdup(userName.string_of());
610     int fd = P_rexec(&hname, inport->s_port, uname, NULL,
611                    paradyndCommand, NULL);
612     delete hname; delete uname;
613
614     if (fd < 0) {
615         perror("rexec");
616         printf("rexec failed\n");
617     }
618     if (paradyndCommand)
619       delete paradyndCommand;
620
621     return(handleRemoteConnect(fd, portFd));
622 }
623
624 /*
625  * 
626  * "command" will be appended to the front of the arg list
627  *
628  * if arg_list == NULL, command is the only argument used
629  */
630
631 int RPCprocessCreate(const string hostName, const string userName,
632                      const string command, const vector<string> &arg_list,
633                      int portFd, const bool useRexec)
634 {
635     int ret;
636     struct utsname unm;
637
638     if (P_uname(&unm) == -1)
639       assert(0);
640
641     if ((hostName == "") || 
642         (hostName == "localhost") ||
643         (hostName == unm.nodename))
644       ret = execCmd(command, arg_list, portFd);
645     else if (useRexec)
646       ret = rexecCommand(hostName, userName, command, arg_list, portFd);
647     else
648       ret = rshCommand(hostName, userName, command, arg_list, portFd);
649
650     return(ret);
651 }
652
653 int RPC_getConnect(int fd) {
654   if (fd == -1)
655     return -1;
656
657   struct in_addr cli_addr;
658   size_t clilen = sizeof(cli_addr);
659   int new_fd = P_accept (fd, (struct sockaddr *) &cli_addr, &clilen);
660
661   if (new_fd < 0)
662     return -1;
663   else
664     return new_fd;
665 }
666
667 // TODO -- use vectors and strings ?
668 /*
669  *  RPCgetArg - break a string into blank separated words
670  *  Used to parse a command line.
671  *  
672  *  input --> a null terminated string, the command line
673  *  argc --> returns the number of args found
674  *  returns --> words (separated by blanks on command line)
675  *  Note --> this allocates memory 
676  */
677 bool RPCgetArg(vector<string> &arg, const char *input)
678 {
679 #define BLANK ' '
680
681   int word_len;
682   if (!input) 
683     return false;
684
685   int length = strlen(input);
686   int index = 0;
687
688   /* advance past blanks in input */
689   while ((index < length) && (input[index] == BLANK))
690     index++;
691
692   /* input is all blanks, or NULL */
693   if (index >= length) 
694     return true;
695
696   do {
697     /* the start of each string */
698     word_len = 0; const char *word_start = input + index;
699
700     /* find the next BLANK and the WORD size */
701     while ((index < length) && (input[index] != BLANK)) {
702       index++; word_len++;
703     }
704
705     /* copy the word */
706     char *temp = new char[word_len+1];
707     strncpy(temp, word_start, word_len);
708     temp[word_len] = (char) 0;
709     arg += temp;
710     delete temp;
711
712     /* skip past consecutive blanks */
713     while ((index < length) && (input[index] == BLANK))
714       index++;
715   } while (index < length);
716   return true;
717 }
718