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