Updated scheme for creating remote processes, implemented this scheme for
[dyninst.git] / common / src / rpcUtil.C
1 /*
2  * Copyright (c) 1996-1999 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 // This file defines a set of utility routines for RPC services.
44 // $Id: rpcUtil.C,v 1.69 1999/12/17 16:11:12 pcroth Exp $
45 //
46
47 // overcome malloc redefinition due to /usr/include/rpc/types.h declaring 
48 // malloc 
49 // This is ugly, and I hope to get rid of it -- mdc 2/2/95
50 #if defined(notdef)
51 /* prevents malloc from being redefined */
52 #ifdef MIPS
53 #define MALLOC_DEFINED_AS_VOID
54 #endif
55 #endif
56
57 #include <limits.h>
58 #include "util/h/headers.h"
59 #include "util/h/pdsocket.h"
60 #include "util/h/rpcUtil.h"
61 #include "util/h/Types.h"    // Address
62
63 const char *DEF_RSH_COMMAND="rsh";
64 const char *RSH_COMMAND_ENV="PARADYN_RSH";
65
66 //---------------------------------------------------------------------------
67 // prototypes of utility functions used in this file
68 //---------------------------------------------------------------------------
69 #if defined(i386_unknown_nt4_0)
70 bool CreateSocketPair( PDSOCKET& localSock, PDSOCKET& remoteSock );
71 #endif // defined(i386_unknown_nt4_0)
72
73
74 int RPCdefaultXDRRead(const void* handle, char *buf, const u_int len)
75 {
76     PDSOCKET sock = (PDSOCKET)(Address)handle;
77     int ret;
78
79 #if defined(i386_unknown_nt4_0)
80     ret = recv( sock, buf, len, 0 );
81 #else
82     do {
83         ret = P_read(sock, buf, len);
84     } while (ret < 0 && errno == EINTR);
85 #endif
86
87     if( (ret == PDSOCKET_ERROR) || (ret == 0))
88     {
89         return (-1);
90     }
91     return (ret);
92 }
93
94 // one counter per file descriptor to record the sequnce no the message
95 // to be received
96 vector<int> counter_2;
97
98 // One partial message record for each file descriptor
99 vector<rpcBuffer *> partialMsgs;
100
101 int RPCasyncXDRRead(const void* handle, char *buf, const u_int len)
102 {
103     /* called when paradyn/xdr detects that it needs to read a message
104        from paradynd. */
105     PDSOCKET fd = (PDSOCKET)(Address)handle;
106     unsigned header;
107     int ret;
108     int needCopy = 0;
109     char *buffer = buf;
110     u_int internal_len = 0;
111     bool newfd = true;
112     unsigned i;
113
114     for (i = 0; i< partialMsgs.size(); i++) {
115         assert(partialMsgs[i] != NULL);
116         if (partialMsgs[i] -> fd == fd) {
117             newfd = false;
118             break;
119         }
120     }
121     
122     // If new connection, allocate a partial message record
123     if (newfd) {
124         rpcBuffer *partial = new rpcBuffer;
125         partial -> fd  = fd;
126         partial -> len = 0;
127         partial -> buf = NULL;
128         partialMsgs += partial;
129         i = partialMsgs.size() - 1;
130     
131         counter_2 += 0;
132     }
133
134     // There're two situations dealt with here: with partial message
135     // left by previous read, without. The 'len' field is zero if it is
136     // the first case, nonzero if it is the second case    
137     if (partialMsgs[i] -> len) needCopy = 1;
138
139     // Allocate buffer which is four more bytes than RPC buffer for the
140     // convenience of processing message headers.
141     buffer = new char[len + sizeof(int)];
142
143     if (needCopy) {
144         P_memcpy(buffer, partialMsgs[i]->buf , partialMsgs[i]->len); 
145         delete [] partialMsgs[i]->buf;
146         partialMsgs[i]->buf = NULL;
147     }
148
149 #if defined(i386_unknown_nt4_0)
150     ret = recv(fd, buffer+partialMsgs[i]->len,
151                len + sizeof(int) - partialMsgs[i]->len, 0);
152 #else
153     do {
154         ret = P_read(fd, buffer+partialMsgs[i]->len, 
155                      len + sizeof(int) -partialMsgs[i]->len);
156     } while (ret < 0 && errno == EINTR);
157 #endif
158
159     if((ret == PDSOCKET_ERROR) || (ret == 0))
160     {
161         return(-1);
162     }
163
164     ret += partialMsgs[i]->len;
165
166     char *pstart = buffer;
167     
168     // Processing the messages received: remove those message headers;
169     // check on deliminator and sequence no to ensure the correctness;
170     // save the partial message if any
171     char *tail = buffer;
172     int is_left = ret - sizeof(int);
173     while (is_left >= 0) {
174         P_memcpy((char *)&header, buffer, sizeof(int));
175         //printf(">> header=%x, header1=%x, len=%x\n", header, ntohl(header));
176         header = ntohl(header);
177         assert(0xf == ((header >> 12)&0xf));
178         
179         short seq_no;
180         P_memcpy((char *)&(seq_no), buffer, sizeof(short));
181         seq_no = ntohs(seq_no);
182         header = (0x0fff&header);
183         
184         if (header <= (unsigned)is_left) {
185             P_memcpy(tail, buffer+sizeof(int), header);
186         
187             counter_2[i] = ((counter_2[i]+1)%SHRT_MAX);
188             assert(seq_no == counter_2[i]);
189         
190             internal_len += header;
191             if (internal_len > len) {
192                 abort();
193                 ret = ret - sizeof(int) - is_left;
194                 break;
195             }
196             tail += header;
197             buffer += header + sizeof(int);
198         
199             is_left = is_left - header - sizeof(int);
200             ret -= sizeof(int);
201         } else {
202             ret = ret - sizeof(int) - is_left;
203             break;
204         }
205     }
206
207     if (is_left >= 0) {
208         partialMsgs[i]->len = is_left + sizeof(int);
209         partialMsgs[i]->buf = new char[partialMsgs[i]->len+1];
210         P_memcpy(partialMsgs[i]->buf, buffer, partialMsgs[i]->len); 
211     } else {
212         partialMsgs[i]->len = 0;
213         assert(is_left == (0-(int)sizeof(int)));
214     }
215
216     // Copy back to internal RPC buffer
217     P_memcpy(buf, pstart, ret);
218
219 //  if (needCopy) {
220 //    buffer = pstart;
221 //    assert(buffer != buf); // make sure we aren't deleting the 2d param
222 //    delete [] buffer;
223 //  }
224
225     if (pstart != buf) delete [] pstart;
226     
227     return (ret);
228 }
229
230 int RPCdefaultXDRWrite(const void* handle, const char *buf, const u_int len)
231 {
232     PDSOCKET sock = (PDSOCKET)(Address)handle;
233     int ret;
234
235 #if defined(i386_unknown_nt4_0)
236     ret = send(sock, buf, len, 0);
237 #else
238     do {
239         ret = P_write(sock, buf, len);
240     } while (ret < 0 && errno == EINTR);
241 #endif
242
243     errno = 0;
244     if (ret != (int)len)
245       return(-1);
246     else 
247       return (ret);
248 }
249
250 vector<rpcBuffer *> rpcBuffers;
251 static short counter = 0;
252
253 int RPCasyncXDRWrite(const void* handle, const char *buf, const u_int len)
254 {
255     int ret;
256     unsigned index = 0;
257     unsigned header = len;
258
259     //printf("RPCasyncXDRWrite(handle=%p, buf=%p, len=%d)\n", handle, buf, len);
260
261     rpcBuffer *rb = new rpcBuffer;
262     rb -> fd = (int)(Address)handle;
263     rb -> len = len+sizeof(int);
264     rb -> buf = new char[rb -> len];
265
266     counter = ((counter+1)%SHRT_MAX);
267
268     assert(len <= 4040);
269
270     // Adding a header to the messages sent   
271     header = ((counter << 16) | header | 0xf000); 
272
273     // Converting to network order
274     header = htonl(header);
275     //printf(">> header=%x, counter=%x, len=%x\n", header, counter, len);
276     
277     P_memcpy(rb -> buf, (char *)&header, sizeof(int));
278     P_memcpy(rb -> buf+sizeof(int), buf, len);
279
280     rpcBuffers += rb;
281
282     // Write the items to the other end if possible with asynchrous write
283     for (int i = 0; (i < (int)rpcBuffers.size()); i++) {
284
285 #if defined(i386_unknown_nt4_0)
286         u_long ioctlsock_arg = 1; // set non-blocking
287         if (ioctlsocket(rpcBuffers[i]->fd, FIONBIO, &ioctlsock_arg) == -1)
288             perror("ioctlsocket");
289
290         ret = (int) send(rpcBuffers[i]->fd, rpcBuffers[i]->buf, 
291                          rpcBuffers[i]->len, 0);
292
293         ioctlsock_arg = 0; // set blocking
294         if (ioctlsocket(rpcBuffers[i]->fd, FIONBIO, &ioctlsock_arg) == -1)
295             perror("ioctlsocket");
296
297 #else
298         if (P_fcntl (rpcBuffers[i]->fd, F_SETFL, FNONBLOCK) == -1)
299             perror("fcntl");
300
301         ret = (int)P_write(rpcBuffers[i]->fd, rpcBuffers[i]->buf,
302                            rpcBuffers[i]->len);
303
304         if (P_fcntl (rpcBuffers[i]->fd, F_SETFL, FSYNC) == -1)
305             perror("fcntl");
306 #endif
307
308         if (rpcBuffers[i]->len != ret) {
309
310             if (ret != -1) {
311                 assert(ret < rpcBuffers[i]->len);
312                 printf("Warning: a partial message sent!\n");
313                 P_memcpy(rpcBuffers[i]->buf, rpcBuffers[i]->buf+ret,
314                          rpcBuffers[i]->len-ret);
315                 rpcBuffers[i]->len -= ret;
316             }
317             break;
318         }
319         index++;
320     
321     }
322     
323     // Delete the items sent out
324     unsigned j;
325     for (j = 0; j < index; j++) {
326         delete [] rpcBuffers[j]->buf;
327         delete rpcBuffers[j];
328         rpcBuffers[j] = NULL; // probably unnecessary
329     }    
330     
331     for (j = 0; j < rpcBuffers.size() - index; j++)
332         rpcBuffers[j] = rpcBuffers[j+index];
333     rpcBuffers.resize(rpcBuffers.size() - index);
334
335     return (ret = (int)len);
336 }
337
338 void
339 doDeferedRPCasyncXDRWrite() {
340
341     if (rpcBuffers.size() == 0)
342         return;
343
344     int ret, index = 0;
345
346     // Write the items to the other end if possible 
347     for (int i = 0; (i < (int)rpcBuffers.size()); i++) {
348
349 #if defined (i386_unknown_nt4_0)
350
351         u_long ioctlsock_arg = 1; // set non-blocking
352         if (ioctlsocket(rpcBuffers[i]->fd, FIONBIO, &ioctlsock_arg) == -1)
353             perror("ioctlsocket");
354
355         ret = (int) send(rpcBuffers[i]->fd, rpcBuffers[i]->buf,
356                          rpcBuffers[i]->len, 0);
357
358         ioctlsock_arg = 0; // set blocking
359         if (ioctlsocket(rpcBuffers[i]->fd, FIONBIO, &ioctlsock_arg) == -1)
360             perror("ioctlsocket");
361
362 #else
363         if (P_fcntl (rpcBuffers[i]->fd, F_SETFL, FNONBLOCK) == -1)
364             perror("fcntl");
365
366         ret = (int)P_write(rpcBuffers[i]->fd, rpcBuffers[i]->buf,
367                            rpcBuffers[i]->len);
368
369         if (P_fcntl (rpcBuffers[i]->fd, F_SETFL, FSYNC) == -1)
370             perror("fcntl");
371 #endif
372     
373         if (rpcBuffers[i]->len != ret) {
374
375             if (ret != -1) {
376                 assert(ret < rpcBuffers[i]->len);
377                 printf("Warning: a partial message sent!\n");
378                 P_memcpy(rpcBuffers[i]->buf, rpcBuffers[i]->buf+ret,
379                          rpcBuffers[i]->len-ret);
380                 rpcBuffers[i]->len -= ret;
381             }
382             break;
383         }
384         index++;
385
386     }
387     
388     // Delete the items sent out
389     unsigned j;
390     for (j = 0; j < (unsigned)index; j++) {
391         delete [] rpcBuffers[j]->buf;
392     }    
393
394     for (j = 0; j < rpcBuffers.size() - index; j++)
395         rpcBuffers[j] = rpcBuffers[j+index];
396     rpcBuffers.resize(rpcBuffers.size() - index);
397 }
398
399 XDRrpc::~XDRrpc()
400 {
401   if (sock != PDSOCKET_ERROR)
402     {
403 #if !defined(i386_unknown_nt4_0)
404       P_fcntl (sock, F_SETFL, FNDELAY);
405 #endif
406       CLOSEPDSOCKET( sock );
407       sock = PDSOCKET_ERROR;
408     }
409   if (xdrs) 
410     {
411       P_xdr_destroy (xdrs);
412       delete (xdrs);
413       xdrs = NULL;
414     }
415 }
416
417 //
418 // prepare for RPC's to be done/received on the passed fd.
419 //
420 XDRrpc::XDRrpc(PDSOCKET use_sock, xdr_rd_func readRoutine, xdr_wr_func writeRoutine, const int nblock)
421 : xdrs(NULL), sock(use_sock)
422 {
423     assert(use_sock != PDSOCKET_ERROR);
424     xdrs = new XDR;
425 #ifdef PURE_BUILD
426     memset(xdrs,'\0',sizeof(XDR));
427 #endif
428     assert(xdrs);
429     if (!readRoutine) {
430     if (nblock == 1) {
431         readRoutine = (xdr_rd_func) RPCasyncXDRRead;
432     } else { 
433         readRoutine = (xdr_rd_func) RPCdefaultXDRRead;
434     }
435     }   
436     if (!writeRoutine) {
437     if (nblock == 2) {
438         writeRoutine = (xdr_wr_func) RPCasyncXDRWrite;
439     } else {    
440         writeRoutine = (xdr_wr_func) RPCdefaultXDRWrite;
441     }
442     }    
443     P_xdrrec_create(xdrs, 0, 0, (char *) sock, readRoutine, writeRoutine);
444 }
445
446 //
447 // prepare for RPC's to be done/received on the passed fd.
448 //
449 XDRrpc::XDRrpc(const string &machine,
450            const string &user,
451            const string &program,
452            const string &remote_shell,
453            xdr_rd_func readRoutine, 
454            xdr_wr_func writeRoutine,
455            const vector<string> &arg_list,
456            const int nblock,
457            PDSOCKET wellKnownSocket)
458 : xdrs(NULL), sock(INVALID_PDSOCKET)
459 {
460     sock = RPCprocessCreate(machine, user, program, remote_shell, arg_list, wellKnownSocket);
461     if (sock != INVALID_PDSOCKET) {
462         xdrs = new XDR;
463 #ifdef PURE_BUILD
464         memset(xdrs,'\0',sizeof(XDR));
465 #endif
466     if (!readRoutine) {
467         if (nblock == 1) {
468         readRoutine = (xdr_rd_func) RPCasyncXDRRead;
469         } else { 
470         readRoutine = (xdr_rd_func) RPCdefaultXDRRead;
471         }
472     }
473     if (!writeRoutine) {
474         if (nblock == 2) {
475         writeRoutine = (xdr_wr_func)RPCasyncXDRWrite;
476         } else { 
477         writeRoutine = (xdr_wr_func) RPCdefaultXDRWrite;
478         }
479     }
480     P_xdrrec_create(xdrs, 0, 0, (char *) sock, readRoutine, writeRoutine);
481     }
482 }
483
484 bool
485 RPC_readReady (PDSOCKET sock, int timeout)
486 {
487   fd_set readfds;
488   struct timeval tvptr, *the_tv;
489
490   tvptr.tv_sec = timeout; tvptr.tv_usec = 0;
491   if (sock == INVALID_PDSOCKET) return false;
492   FD_ZERO(&readfds);
493   FD_SET (sock, &readfds);
494
495   // -1 timeout = blocking select
496   if (timeout == -1)
497      the_tv = 0;
498   else
499      the_tv = &tvptr;
500  
501   if (P_select (sock+1, &readfds, NULL, NULL, the_tv) == PDSOCKET_ERROR)
502     {
503       // if (errno == EBADF)
504       return false;
505     }
506   return (FD_ISSET (sock, &readfds)!=0);
507 }
508
509 /*
510  * Build an argument list starting at position 0.
511  * Note, this arg list will be used in an exec system call
512  * AND, the command name will have to be inserted at the head of the list
513  * But, a NULL space will NOT be left at the head of the list
514  */
515 bool RPC_make_arg_list(vector<string> &list,
516                const int well_known_socket, const int flag,
517                const int /* firstPVM */,
518                const string machine_name, const bool use_machine)
519 {
520   char arg_str[100];
521
522   list.resize(0);
523
524   sprintf(arg_str, "%s%d", "-p", well_known_socket);  
525   list += arg_str;
526   // arg_list[arg_count++] = strdup (arg_str);  // 0
527
528   if (!use_machine) {
529     list += string("-m") + getHostName();
530   } else {
531     list += string("-m") + machine_name;
532   }
533   // arg_list[arg_count++] = strdup (arg_str); // 3
534
535   sprintf(arg_str, "%s%d", "-l", flag);
536   list += arg_str;
537   //arg_list[arg_count++] = strdup (arg_str);  // 4
538
539   //-v option to paradynd is obsolete
540   //sprintf(arg_str, "%s%d", "-v", firstPVM);
541   //list += arg_str;
542   // arg_list[arg_count++] = strdup (arg_str);  // 5 
543
544   return true;
545 }
546
547 // returns port number of socket that is listened on, or -1
548 // returns socket descriptor through sock parameter
549 int
550 RPC_setup_socket (PDSOCKET &sock,   // return socket descriptor
551           const int family, // AF_INET ...
552           const int type)   // SOCK_STREAM ...
553 {
554   if ((sock = P_socket(family, type, 0)) == INVALID_PDSOCKET)
555     return -1;
556
557   struct sockaddr_in serv_addr;
558   P_memset ((void*) &serv_addr, 0, sizeof(serv_addr));
559   serv_addr.sin_family = (short) family;
560   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
561   serv_addr.sin_port = htons(0);
562   
563   size_t length = sizeof(serv_addr);
564
565   if (P_bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == PDSOCKET_ERROR)
566     return -1;
567
568   if (P_getsockname (sock, (struct sockaddr *) &serv_addr, &length) == PDSOCKET_ERROR)
569     return -1;
570
571   if (P_listen(sock, 128) == PDSOCKET_ERROR)  //Be prepared for lots of simultaneous connects
572     return -1;
573
574   return (ntohs (serv_addr.sin_port));
575 }
576
577 // setup a AF_UNIX domain socket of type SOCK_STREAM
578 // NT does not support AF_UNIX domain sockets
579 #if !defined(i386_unknown_nt4_0)
580 bool
581 RPC_setup_socket_un (int &sfd,   // return file descriptor
582              const char *path) // file path socket is bound to
583 {
584   struct sockaddr_un saddr;
585
586   if ((sfd = P_socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
587     return false;
588
589   saddr.sun_family = AF_UNIX;
590   P_strcpy(saddr.sun_path, path);
591   
592   if (P_bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
593     return false;
594
595   if (P_listen(sfd, 128) < 0)  //Be prepared for lots of simultaneous connects
596     return false;
597
598   return true;
599 }
600 #endif // !defined(i386_unknown_nt4_0)
601
602 //
603 // connect to well known socket
604 //
605 XDRrpc::XDRrpc(int family,            
606            int req_port,             
607            int type,
608            const string machine, 
609            xdr_rd_func readRoutine,
610            xdr_wr_func writeRoutine,
611            const int nblock) : xdrs(NULL), sock(INVALID_PDSOCKET)
612      // socket, connect using machine
613 {
614   struct sockaddr_in serv_addr;
615   struct hostent *hostptr = 0;
616   struct in_addr *inadr = 0;
617   if (!(hostptr = P_gethostbyname(machine.string_of())))
618     return;
619
620   inadr = (struct in_addr *) ((void*) hostptr->h_addr_list[0]);
621   P_memset ((void*) &serv_addr, 0, sizeof(serv_addr));
622   serv_addr.sin_family = family;
623   serv_addr.sin_addr = *inadr;
624   serv_addr.sin_port = htons(req_port);
625
626   if ( (sock = P_socket(family, type, 0)) == PDSOCKET_ERROR)
627     { sock = INVALID_PDSOCKET; return; }
628
629   //connect() may timeout if lots of Paradynd's are trying to connect to
630   //  Paradyn at the same time, so we keep retrying the connect().
631   errno = 0;
632   while (P_connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == PDSOCKET_ERROR) {
633 #if defined(i386_unknown_nt4_0)
634     if (PDSOCKET_ERRNO != WSAETIMEDOUT)
635 #else
636     if (errno != ETIMEDOUT)
637 #endif
638         { sock = INVALID_PDSOCKET; return; } 
639     CLOSEPDSOCKET(sock);
640     if ((sock = P_socket(family, type, 0)) == PDSOCKET_ERROR)
641         { sock = INVALID_PDSOCKET; return; }
642     errno = 0;
643   }
644
645     xdrs = new XDR;
646     assert(xdrs);
647     if (!readRoutine) {
648     if (nblock == 1) {
649         readRoutine = (xdr_rd_func) RPCasyncXDRRead;
650     } else {  
651         readRoutine = (xdr_rd_func) RPCdefaultXDRRead;
652     }
653     }
654     if (!writeRoutine) {
655     if (nblock == 2) {
656         writeRoutine = (xdr_wr_func)RPCasyncXDRWrite;
657     } else { 
658         writeRoutine = (xdr_wr_func) RPCdefaultXDRWrite;
659     }
660     }
661     P_xdrrec_create(xdrs, 0, 0, (char *) sock, readRoutine,writeRoutine);
662
663
664 //
665 // These xdr functions are not to be called with XDR_FREE
666 //
667 bool_t xdr_Boolean(XDR *xdrs, bool *bool_val) {
668    u_char i='\0';
669    bool_t res;
670    switch (xdrs->x_op) {
671    case XDR_ENCODE:
672       i = (u_char) *bool_val;
673       return (P_xdr_u_char(xdrs, &i));
674    case XDR_DECODE:
675       res = P_xdr_u_char(xdrs, &i);
676       *bool_val = (bool) (i!='\0');
677       return res;
678    case XDR_FREE:
679    default:
680       assert(0);
681       return FALSE;
682    }
683 }
684
685 // XDR_FREE not handled, free it yourself!
686 // our version of string encoding that does malloc as needed.
687 //
688 bool_t xdr_string_pd(XDR *xdrs, string *str)
689 {
690   unsigned int length = 0;
691   assert(str);
692
693   // if XDR_FREE, str's memory is freed
694   switch (xdrs->x_op) {
695   case XDR_ENCODE:
696     if ((length = str->length())) {
697       if (!P_xdr_u_int(xdrs, &length))
698     return FALSE;
699       else {
700     char *buffer = const_cast<char*> (str->string_of()); 
701     bool_t res = P_xdr_string (xdrs, &buffer, str->length() + 1);
702     return res;
703       }
704     } else {
705       return (P_xdr_u_int(xdrs, &length));
706     }
707   case XDR_DECODE:
708     if (!P_xdr_u_int(xdrs, &length))
709       return FALSE;
710      else if (!length) {
711        *str = NULL;
712        return TRUE;
713      } else {
714        char *temp; 
715        bool newd = false;
716        unsigned max_len = 511;
717        char stat_buf[512];
718        if (length < 512) 
719           temp = (char*) stat_buf;
720        else {
721           temp = new char[length+1];
722           max_len = length;
723           newd = true;
724        }      
725        if (!P_xdr_string (xdrs, &temp, max_len)) {
726           if (newd) 
727             delete temp;
728        return FALSE;
729        } else {
730       *str = temp;
731           if (newd)
732             delete temp; 
733       return TRUE;
734        }
735      }
736   case XDR_FREE:
737   default:
738     // this should never occur    
739     assert(0);
740     return (FALSE);
741   }
742 }
743
744 // trace data streams
745 // XDR_FREE not handled, free it yourself!
746 // our version of string encoding that does malloc as needed.
747 //
748
749 bool_t xdr_byteArray_pd(XDR *xdrs, byteArray *bArray)
750 {
751   unsigned int length = 0;
752   assert(bArray);
753
754   // if XDR_FREE, str's memory is freed
755   switch (xdrs->x_op) {
756   case XDR_ENCODE:
757     length = bArray->length();
758     if (length > 0) {
759       if (!P_xdr_u_int(xdrs, &length))
760         return FALSE;
761       else {
762         char *buffer = static_cast<char*> (const_cast<void*> (bArray->getArray()));
763         bool_t res = P_xdr_byteArray (xdrs, &buffer, &length, (bArray->length()));
764         return res;
765       }
766     } else {
767       return (P_xdr_u_int(xdrs, &length));
768     }
769   case XDR_DECODE:
770     if (!P_xdr_u_int(xdrs, &length))
771       return FALSE;
772      else if (!length) {
773        *bArray = byteArray(NULL, 0);
774        return TRUE;
775      } else {
776        char *temp;
777        unsigned int act_len;
778        unsigned int max_len = length;
779        temp = new char[length];
780        if (!P_xdr_byteArray (xdrs, &temp, &act_len, max_len)) {
781           delete [] temp;
782           return FALSE;
783        } else if (act_len != length) {
784           delete [] temp;
785           return FALSE;
786        } else {
787           *bArray = byteArray(temp,act_len);
788           delete [] temp;
789           return TRUE;
790        }
791      }
792   case XDR_FREE:
793   default:
794     // this should never occur
795     assert(0);
796     return (FALSE);
797   }
798 }
799
800 //
801 // directly exec the command (local).
802 //
803
804 PDSOCKET
805 execCmd(const string command, const vector<string> &arg_list )
806 {
807   PDSOCKET ret;
808   PDSOCKET sv[2];
809
810 #if !defined(i386_unknown_nt4_0)
811   int execlERROR;
812
813   errno = 0;
814   ret = P_socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
815   if (ret==-1) return(ret);
816   execlERROR = 0;
817   int al_len = arg_list.size();
818   char **new_al = new char*[al_len+2];
819   // TODO argv[0] should not include path
820   new_al[0] = P_strdup(command.string_of());
821   new_al[al_len+1] = NULL;
822   for (int i=0; i<al_len; ++i)
823     new_al[i+1] = P_strdup(arg_list[i].string_of());
824   ret = -1;
825
826   int pid;
827   pid = vfork();
828   // close sv[0] after exec 
829   P_fcntl(sv[0],F_SETFD,1);  
830
831   if (pid == 0) {
832     if (P_close(sv[0])) { execlERROR = errno; _exit(-1);}
833     if (P_dup2(sv[1], 0)) { execlERROR = errno; _exit(-1);}
834     P_execvp(new_al[0], new_al);
835     execlERROR = errno;
836     _exit(-1);
837   } else if (pid > 0 && !execlERROR) {
838     P_close(sv[1]);
839     ret = sv[0];
840   }
841
842   al_len=0;
843   while (new_al[al_len]) {
844     free(new_al[al_len]);
845     al_len++;
846   }
847   delete [] new_al;
848
849 #else // !defined(i386_unknown_nt4_0)
850
851     // create a pair of socket endpoints and connect them
852     // 
853     // note that we create TCP/IP sockets here rather than
854     // using a named pipe because the socket returned from
855     // this function is used to communicate with a Paradyn
856     // daemon, and the communication between the WinNT
857     // Paradyn front end and a Paradyn daemon is implemented
858     // using a socket to support communication with daemons
859     // on remote machines
860     if( !CreateSocketPair( sv[0], sv[1] ) )
861     {
862         return INVALID_PDSOCKET;
863     }
864
865     // build a command line for the new process
866     string cmdLine( command );
867     unsigned i;
868     for( i = 0; i < arg_list.size(); i++ )
869     {
870         cmdLine += " ";
871         cmdLine += arg_list[i];
872     }
873
874     // set up the standard input of the new process to 
875     // be connected to our shared socket, and make sure
876     // that the new process inherits our standard
877     // output and standard error
878     STARTUPINFO startInfo;
879     ZeroMemory( &startInfo, sizeof(startInfo) );
880     startInfo.cb = sizeof( startInfo );
881
882     startInfo.hStdInput = (HANDLE)sv[1];
883     startInfo.hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );
884     startInfo.hStdError = GetStdHandle( STD_ERROR_HANDLE );
885     startInfo.dwFlags |= STARTF_USESTDHANDLES;
886
887     // actually create the process
888     PROCESS_INFORMATION procInfo;
889     BOOL bCreated = CreateProcess( NULL,
890                                     (char*)cmdLine.string_of(),
891                                     NULL,
892                                     NULL,
893                                     TRUE,
894                                     NORMAL_PRIORITY_CLASS,
895                                     NULL,
896                                     NULL,
897                                     &startInfo,
898                                     &procInfo );
899
900     if( bCreated )
901     {
902         // We want to close our end of the connected socket
903         // pair, but we have a race condition between our 
904         // closing of the socket and the initialization of
905         // the process we just created.  We need to ensure
906         // that the other process is far enough along that
907         // our closing of the socket won't break the
908         // connection completely.
909         bool bOKToContinue = false;
910         while( !bOKToContinue )
911         {
912             HANDLE hProc = OpenProcess( PROCESS_QUERY_INFORMATION,
913                 NULL,
914                 procInfo.dwProcessId );
915             if( hProc != NULL )
916             {
917                 CloseHandle( hProc );
918                 bOKToContinue = true;
919             }
920         }
921
922         // release our hold on the child socket endpoint
923         // and keep hold of our own endpoint
924         CLOSEPDSOCKET( sv[1] );
925         ret = sv[0];
926     }
927     else
928     {
929         // cleanup
930         CLOSEPDSOCKET( sv[0] );
931         CLOSEPDSOCKET( sv[1] );
932
933         // indicate the error
934         ret = INVALID_PDSOCKET;
935     }
936
937 #endif // !defined(i386_unknown_nt4_0)
938
939     return ret;
940 }
941
942
943
944 // Parse an input string into space-delimited substrings, and add each as a
945 // separate string the given vector
946
947 void appendParsedString( vector< string > &strList, const string &str )
948 {
949     unsigned i, j, l = str.length();
950
951     for( i = 0, j = 0; j < l; ++j )
952         if( str[ j ] == ' ' )
953         {
954             if( i < j )
955                 strList += str.substr( i, j-i );
956             i = j + 1;
957         }
958
959     if( i < j )
960         strList += str.substr( i, j-i );
961 }
962
963
964 // Output a list of strings with spaces between them
965
966 void printStringList( vector< string > &strList, ostream &strout )
967 {
968     unsigned i, l = strList.size();
969
970     for( i = 0; i < l; ++i )
971     {
972         strout << strList[ i ];
973         if( i < l - 1 )
974             strout << ' ';
975     }
976 }
977
978
979 // Execute 'command' on a remote machine using 'remote_shell' (which can include
980 // arguments) passing an argument list of 'arg_list'
981
982 PDSOCKET remoteCommand(const string hostName, const string userName,
983             const string command, const string remoteExecCmd,
984             const vector<string> &arg_list, int portFd)
985 {
986     PDSOCKET ret = INVALID_PDSOCKET;
987     unsigned i;
988
989
990     // build the command line of the remote execution command we will execute
991     // note that it must support the "-l" flag to specify a username, like rsh
992     vector<string> remoteExecArgList;
993
994     // first extract any arguments specified with the remote execution command
995     vector<string> tmpArgList;
996     appendParsedString( tmpArgList, remoteExecCmd );
997     if( tmpArgList.size() > 1 )
998     {
999         // skip the remote execution command, but copy all of its
1000         // arguments into our argument list
1001         for( i = 1; i < tmpArgList.size(); i++ )
1002         {
1003             remoteExecArgList += tmpArgList[i];
1004         }
1005     }
1006
1007     // next add the host name and user name arguments to the remote execution command
1008     remoteExecArgList += hostName;
1009     if( userName.length() > 0 )
1010     {
1011         // add username specification
1012         remoteExecArgList += (string("-l ") + userName);
1013     }
1014     // add remote command and its arguments
1015     remoteExecArgList += command;
1016     for( i = 0; i < arg_list.size(); i++ )
1017     {
1018         remoteExecArgList += arg_list[i];
1019     }
1020     remoteExecArgList += "-l0";
1021
1022
1023     // execute the command
1024     PDSOCKET s = execCmd( remoteExecCmd, remoteExecArgList );
1025     if( s != INVALID_PDSOCKET )
1026     {
1027         // establish the connection
1028         ret = RPC_getConnect( portFd );
1029     }
1030
1031     return ret;
1032 }
1033
1034
1035 //
1036 // use rsh to get a remote process started.
1037 //
1038 // We do an rsh to start a process and them wait for the process 
1039 // to do a connect. There is no guarantee that the process we are waiting for
1040 // is the one that gets the connection. This can happen with paradyndPVM: the
1041 // daemon that is started by a rshCommand will start other daemons, and one of 
1042 // these daemons may get the connection that should be for the first daemon.
1043 // Daemons should always get a connection before attempting to start other
1044 // daemons.
1045 //
1046
1047 PDSOCKET rshCommand(const string hostName, const string userName, 
1048            const string command, const vector<string> &arg_list, int portFd)
1049 {
1050     // ensure we know the user's desired rsh command
1051     const char* rshCmd = getenv( RSH_COMMAND_ENV );
1052     if( rshCmd == NULL )
1053     {
1054         rshCmd = DEF_RSH_COMMAND;
1055     }
1056
1057     return remoteCommand( hostName, userName, command, rshCmd, arg_list, portFd );
1058 }
1059
1060
1061
1062
1063
1064
1065 /*
1066  * 
1067  * "command" will be appended to the front of the arg list
1068  *
1069  * if arg_list == NULL, command is the only argument used
1070  */
1071
1072 PDSOCKET RPCprocessCreate(const string hostName, const string userName,
1073              const string command, const string remote_shell,
1074              const vector<string> &arg_list,
1075              int portFd)
1076 {
1077     PDSOCKET ret;
1078
1079     if ((hostName == "") || 
1080         (hostName == "localhost") ||
1081         (hostName == getHostName()))
1082     {
1083       ret = execCmd(command, arg_list);
1084     }
1085     else if (remote_shell.length() > 0)
1086     {
1087       ret = remoteCommand(hostName, userName, command, remote_shell, arg_list,
1088                           portFd);
1089     }
1090     else
1091     {
1092       ret = rshCommand(hostName, userName, command, arg_list, portFd);
1093     }
1094
1095     return(ret);
1096 }
1097
1098 PDSOCKET RPC_getConnect(PDSOCKET sock) {
1099   if (sock == INVALID_PDSOCKET)
1100     return INVALID_PDSOCKET;
1101
1102   struct sockaddr cli_addr;
1103   size_t clilen = sizeof(cli_addr);
1104   errno = 0;
1105   PDSOCKET new_sock = P_accept (sock, (struct sockaddr *) &cli_addr, &clilen);
1106
1107   if (new_sock == PDSOCKET_ERROR) {
1108     if (PDSOCKET_ERRNO == EMFILE) {
1109       cerr << "Cannot accept more connections:  Too many open files" << endl;
1110       cerr << "Please see your documentation for `ulimit'" << endl << flush;
1111     }
1112     return INVALID_PDSOCKET;
1113   }
1114   else
1115     return new_sock;
1116 }
1117
1118 // TODO -- use vectors and strings ?
1119 /*
1120  *  RPCgetArg - break a string into blank separated words
1121  *  Used to parse a command line.
1122  *  
1123  *  input --> a null terminated string, the command line
1124  *  argc --> returns the number of args found
1125  *  returns --> words (separated by blanks on command line)
1126  *  Note --> this allocates memory 
1127  */
1128 bool RPCgetArg(vector<string> &arg, const char *input)
1129 {
1130 #define BLANK ' '
1131
1132   int word_len;
1133   if (!input) 
1134     return false;
1135
1136   int length = strlen(input);
1137   int index = 0;
1138
1139   /* advance past blanks in input */
1140   while ((index < length) && (input[index] == BLANK))
1141     index++;
1142
1143   /* input is all blanks, or NULL */
1144   if (index >= length) 
1145     return true;
1146
1147   do {
1148     /* the start of each string */
1149     word_len = 0; const char *word_start = input + index;
1150
1151     /* find the next BLANK and the WORD size */
1152     while ((index < length) && (input[index] != BLANK)) {
1153       index++; word_len++;
1154     }
1155
1156     /* copy the word */
1157     char *temp = new char[word_len+1];
1158     strncpy(temp, word_start, word_len);
1159     temp[word_len] = (char) 0;
1160     arg += temp;
1161     delete temp;
1162
1163     /* skip past consecutive blanks */
1164     while ((index < length) && (input[index] == BLANK))
1165       index++;
1166   } while (index < length);
1167   return true;
1168 }
1169
1170
1171 string getHostName() {
1172 #if defined(i386_unknown_nt4_0)
1173     char nameBuf[1000];
1174     gethostname(nameBuf,999);
1175     return string(nameBuf);
1176 #else
1177     struct utsname un;
1178     P_uname(&un);
1179     return string(un.nodename);
1180 #endif
1181 }
1182
1183
1184 #if defined(i386_unknown_nt4_0)
1185
1186
1187 //
1188 // CreateSocketPair
1189 //
1190 // Creates a connected pair of TCP/IP sockets.  The
1191 // intended use of the function is to mimic the
1192 // socketpair call available from Berkeley sockets,
1193 // so that a "child" process created by this process can
1194 // inherit one endpoint of the socket pair and 
1195 // thus be connected to the "parent" process.
1196 //
1197 // The sockets are distinguished as local and remote
1198 // because we need to make sure that the socket handle
1199 // intended to be kept at the parent process must not
1200 // be inherited by the child.  (If it were inherited,
1201 // the child couldn't tell when the parent closed
1202 // the socket, because the child would inherit both
1203 // endpoints of the connection and thus the connection
1204 // would stay open even though the parent closed its end.)
1205 //
1206 bool
1207 CreateSocketPair( PDSOCKET& localSock, PDSOCKET& remoteSock )
1208 {
1209     PDSOCKET sockListen = INVALID_PDSOCKET;
1210     bool bCreated = false;
1211     
1212
1213     // create sockets
1214     localSock = socket( AF_INET, SOCK_STREAM, PF_UNSPEC );
1215     remoteSock = INVALID_PDSOCKET;
1216     sockListen = socket( AF_INET, SOCK_STREAM, PF_UNSPEC );
1217     if( (sockListen != INVALID_PDSOCKET) && (localSock != INVALID_PDSOCKET) )
1218     {
1219         // connect sockets...
1220
1221         // ...bind one of the sockets...
1222         struct hostent* pHostData = gethostbyname( "localhost" );
1223         assert( pHostData != NULL );
1224
1225         SOCKADDR_IN sin;
1226         sin.sin_family = AF_INET;
1227         sin.sin_port = 0;            // dynamically assigned
1228         sin.sin_addr.S_un.S_addr = *(unsigned long*)pHostData->h_addr;
1229         if( bind( sockListen, (SOCKADDR*)&sin, sizeof(sin) ) != PDSOCKET_ERROR )
1230         {
1231             // ...connect the other to the bound socket
1232             sockaddr_in sain;
1233             int cbSain = sizeof( sain );
1234             if( getsockname( sockListen, (SOCKADDR*)&sain, &cbSain ) 
1235                         != PDSOCKET_ERROR )
1236             {
1237                 if( listen( sockListen, 1 ) != PDSOCKET_ERROR )
1238                 {
1239                     // issue a "deferred" connect for socket 0
1240                     sin.sin_port = sain.sin_port;
1241                     if( connect( localSock, (SOCKADDR*)&sin, sizeof(sin) )
1242                                 != PDSOCKET_ERROR )
1243                     {
1244                         // accept the connection
1245                         remoteSock = accept( sockListen, NULL, NULL );
1246                         if( remoteSock != INVALID_PDSOCKET )
1247                         {
1248                             // verify the sockets are connected -
1249                             // socket 0 should now be writable
1250                             fd_set wrset;
1251                             struct timeval tv;
1252
1253                             tv.tv_sec = 0;
1254                             tv.tv_usec = 0;
1255                             FD_ZERO( &wrset );
1256                             FD_SET( localSock, &wrset );
1257                             if( select( 0, NULL, &wrset, NULL, &tv ) == 1 )
1258                             {
1259                                 bCreated = true;
1260                             }
1261                         }
1262                     }
1263                 }
1264             }
1265         }
1266     }
1267
1268     if( bCreated )
1269     {
1270         // make sure that the remote endpoint handle
1271         // is not inheritable
1272         HANDLE hDup;
1273         if( DuplicateHandle( GetCurrentProcess(),
1274                 (HANDLE)localSock,
1275                 GetCurrentProcess(),
1276                 &hDup,
1277                 0,
1278                 FALSE,
1279                 DUPLICATE_SAME_ACCESS ) )
1280         {
1281             // we made the duplication,
1282             // so close the inheritable handle
1283             // and return the noninheritable one
1284             CLOSEPDSOCKET( localSock );
1285             localSock = (SOCKET)hDup;
1286         }
1287         else
1288         {
1289             bCreated = false;
1290         }
1291     }
1292
1293     if( !bCreated )
1294     {
1295         CLOSEPDSOCKET( localSock );
1296         localSock = INVALID_PDSOCKET;
1297         CLOSEPDSOCKET( remoteSock );
1298         remoteSock = INVALID_PDSOCKET;
1299     }
1300
1301     CLOSEPDSOCKET( sockListen );
1302
1303     return bCreated;
1304 }
1305
1306
1307 #endif // defined(i386_unknown_nt4_0)
1308