- added support for (mips-sgi-irix6.4) native compiler build
[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.68 1999/08/09 05:45:05 csserra 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, int /*portFd*/)
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 int handleRemoteConnect(int fd, int portFd) {
943     // NOTE: This routine is not generic; it is very specific to paradyn
944     // (due to the sscanf(line, "PARADYND %d")...)
945     // Hence it makes absolutely no sense to put it in a so-called library.
946     // It should be put into the paradyn/src tree --ari
947
948     FILE *pfp = P_fdopen(fd, "r");
949     if (pfp == NULL) {
950        cerr << "handleRemoteConnect: fdopen of fd " << fd << " failed." << endl;
951        return -1;
952     }
953
954     int retFd = RPC_getConnect(portFd); // calls accept(), which blocks (sigh)
955     return retFd;
956 }
957
958
959 // Parse an input string into space-delimited substrings, and add each as a
960 // separate string the given vector
961
962 void appendParsedString( vector< string > &strList, const string &str )
963 {
964     unsigned i, j, l = str.length();
965
966     for( i = 0, j = 0; j < l; ++j )
967         if( str[ j ] == ' ' )
968         {
969             if( i < j )
970                 strList += str.substr( i, j-i );
971             i = j + 1;
972         }
973
974     if( i < j )
975         strList += str.substr( i, j-i );
976 }
977
978
979 // Output a list of strings with spaces between them
980
981 void printStringList( vector< string > &strList, ostream &strout )
982 {
983     unsigned i, l = strList.size();
984
985     for( i = 0; i < l; ++i )
986     {
987         strout << strList[ i ];
988         if( i < l - 1 )
989             strout << ' ';
990     }
991 }
992
993
994 // Execute 'command' on a remote machine using 'remote_shell' (which can include
995 // arguments) passing an argument list of 'arg_list'
996
997 int remoteCommand(const string hostName, const string userName, const string command,
998                   const string remote_shell, const vector<string> &arg_list, int portFd)
999 {
1000 #if defined(i386_unknown_nt4_0)
1001     // TODO
1002     assert(0);
1003     return 0;
1004 #else
1005     int fd[2];
1006     int shellPid;
1007     int ret;
1008     unsigned i, j;
1009
1010     int total = command.length() + 2;
1011     for ( i = 0; i < arg_list.size(); ++i )
1012       total += arg_list[i].length() + 2;
1013
1014     string paradyndCommand = command + " ";
1015
1016     for ( j = 0; j < arg_list.size(); ++j )
1017         paradyndCommand += arg_list[j] + " ";
1018
1019     // remote_shell command needs to recognize the '-l user' flag to log onto 
1020     // a remote machine as a user other than the default
1021
1022     vector<string> execArgs;
1023     appendParsedString( execArgs, remote_shell );
1024     string remote_shell_command = execArgs[ 0 ];
1025     execArgs += hostName;
1026     if( userName.length() ) {
1027         execArgs += string( "-l" );
1028         execArgs += userName;
1029     }
1030     execArgs += command;
1031     execArgs += arg_list;
1032     //appendParsedString( execArgs, paradyndCommand );
1033     execArgs += string( "-l0" );
1034
1035     char **argv = new char *[ execArgs.size() + 1 ];
1036     for( i = 0; i < execArgs.size(); ++i ) {
1037         argv[ i ] = strdup( execArgs[ i ].string_of() );
1038     }
1039     argv[ i ] = NULL;
1040
1041     // need to rsh to machine and setup io path.
1042
1043     if (pipe(fd)) {
1044         perror("pipe");
1045         return (-1);
1046     }
1047
1048     shellPid = vfork();
1049     if (shellPid == 0) {
1050         /* child */
1051         bool aflag;
1052         aflag=(-1 != dup2(fd[1], 1)); /* copy it onto stdout */
1053         assert(aflag);
1054         aflag=(-1 != close(fd[0]));
1055         assert(aflag);
1056         aflag=(-1 != close(fd[1]));
1057         assert(aflag);
1058
1059         ret = execvp( remote_shell_command.string_of(), argv );
1060         fprintf(stderr,"remoteCommand: execvp failed (ret = %d)\n",ret);
1061         for( i = 0; i < execArgs.size(); ++i )
1062             cerr << "argv[" << i << "] = \"" << argv[ i ] << '\"' << endl;
1063         _exit(-1);
1064     } else if (shellPid > 0) {
1065         close(fd[1]);
1066     } else {
1067         // error situation
1068     }
1069     
1070     for( i = 0; i < execArgs.size(); ++i )
1071         free( argv[ i ] );
1072     delete [] argv;
1073
1074     return(handleRemoteConnect(fd[0], portFd));
1075 #endif
1076 }
1077
1078 //
1079 // use rsh to get a remote process started.
1080 //
1081 // We do an rsh to start a process and them wait for the process 
1082 // to do a connect. There is no guarantee that the process we are waiting for
1083 // is the one that gets the connection. This can happen with paradyndPVM: the
1084 // daemon that is started by a rshCommand will start other daemons, and one of 
1085 // these daemons may get the connection that should be for the first daemon.
1086 // Daemons should always get a connection before attempting to start other
1087 // daemons.
1088 //
1089
1090 int rshCommand(const string hostName, const string userName, 
1091            const string command, const vector<string> &arg_list, int portFd)
1092 {
1093 #if defined(i386_unknown_nt4_0)
1094     // TODO
1095     assert(0);
1096     return 0;
1097 #else
1098     int fd[2];
1099     int shellPid;
1100     int ret;
1101
1102     int total = command.length() + 2;
1103     for (unsigned i=0; i<arg_list.size(); i++) {
1104       total += arg_list[i].length() + 2;
1105     }
1106
1107     string paradyndCommand = command + " ";
1108
1109     for (unsigned j=0; j < arg_list.size(); j++)
1110         paradyndCommand += arg_list[j] + " ";
1111
1112     // need to rsh to machine and setup io path.
1113
1114     if (pipe(fd)) {
1115     perror("pipe");
1116     return (-1);
1117     }
1118
1119     char *rsh_env, rsh_command[256];
1120     rsh_env = getenv( RSH_COMMAND_ENV );
1121     if( rsh_env )
1122         strcpy( (char*)rsh_command, rsh_env );
1123     else
1124         strcpy( (char*)rsh_command, DEF_RSH_COMMAND );
1125
1126     //cerr << "Using rsh = \"" << rsh_command << '\"' << endl;
1127
1128     shellPid = vfork();
1129     if (shellPid == 0) {
1130     /* child */
1131     bool aflag;
1132     aflag=(-1 != dup2(fd[1], 1)); /* copy it onto stdout */
1133     assert(aflag);
1134     aflag=(-1 != close(fd[0]));
1135     assert(aflag);
1136     aflag=(-1 != close(fd[1]));
1137     assert(aflag);
1138     if (userName.length()) {
1139         ret = execlp(rsh_command, rsh_command, hostName.string_of(), "-l", 
1140              userName.string_of(), "-n", paradyndCommand.string_of(),
1141              "-l0", NULL);
1142             fprintf(stderr,"rshCommand: execlp failed (ret = %d)\n",ret);
1143     } else {
1144         ret = execlp(rsh_command, rsh_command, hostName.string_of(), "-n", 
1145              paradyndCommand.string_of(), "-l0", NULL);
1146             fprintf(stderr,"rshCommand: execlp failed (ret = %d)\n",ret);
1147     }
1148     _exit(-1);
1149     } else if (shellPid > 0) {
1150     close(fd[1]);
1151     } else {
1152     // error situation
1153     }
1154
1155     return(handleRemoteConnect(fd[0], portFd));
1156 #endif
1157 }
1158
1159 int rexecCommand(const string hostName, const string userName, 
1160          const string command, const vector<string> &arg_list, int portFd)
1161 {
1162 #if defined(i386_unknown_nt4_0)
1163     // TODO
1164     assert(0);
1165     return 0;
1166 #else
1167     struct servent *inport;
1168
1169     int total = command.length() + 2;
1170     for (unsigned i=0; i<arg_list.size(); i++) {
1171       total += arg_list[i].length() + 2;
1172     }
1173
1174     char *paradyndCommand = new char[total+2];
1175     assert(paradyndCommand);
1176
1177     sprintf(paradyndCommand, "%s ", command.string_of());
1178     for (unsigned j=0; j<arg_list.size(); j++) {
1179     P_strcat(paradyndCommand, arg_list[j].string_of());
1180     P_strcat(paradyndCommand, " ");
1181     }
1182
1183     inport = P_getservbyname("exec",  "tcp");
1184
1185     char *hname = P_strdup(hostName.string_of());
1186     char *uname = P_strdup(userName.string_of());
1187     int fd = P_rexec(&hname, inport->s_port, uname, NULL,
1188         paradyndCommand, NULL);
1189     delete hname; delete uname;
1190
1191     if (fd < 0) {
1192     perror("rexec");
1193     printf("rexec failed\n");
1194     }
1195     if (paradyndCommand)
1196       delete paradyndCommand;
1197
1198     return(handleRemoteConnect(fd, portFd));
1199 #endif
1200 }
1201
1202 /*
1203  * 
1204  * "command" will be appended to the front of the arg list
1205  *
1206  * if arg_list == NULL, command is the only argument used
1207  */
1208
1209 PDSOCKET RPCprocessCreate(const string hostName, const string userName,
1210              const string command, const string remote_shell,
1211              const vector<string> &arg_list,
1212              int portFd, const bool useRexec)
1213 {
1214     PDSOCKET ret;
1215
1216     if ((hostName == "") || 
1217     (hostName == "localhost") ||
1218     (hostName == getHostName()))
1219       ret = execCmd(command, arg_list, portFd);
1220     else if (remote_shell.length() > 0)
1221       ret = remoteCommand(hostName, userName, command, remote_shell, arg_list,
1222                           portFd);
1223     else if (useRexec)
1224       ret = rexecCommand(hostName, userName, command, arg_list, portFd);
1225     else
1226       ret = rshCommand(hostName, userName, command, arg_list, portFd);
1227
1228     return(ret);
1229 }
1230
1231 PDSOCKET RPC_getConnect(PDSOCKET sock) {
1232   if (sock == INVALID_PDSOCKET)
1233     return INVALID_PDSOCKET;
1234
1235   struct sockaddr cli_addr;
1236   size_t clilen = sizeof(cli_addr);
1237   errno = 0;
1238   PDSOCKET new_sock = P_accept (sock, (struct sockaddr *) &cli_addr, &clilen);
1239
1240   if (new_sock == PDSOCKET_ERROR) {
1241     if (PDSOCKET_ERRNO == EMFILE) {
1242       cerr << "Cannot accept more connections:  Too many open files" << endl;
1243       cerr << "Please see your documentation for `ulimit'" << endl << flush;
1244     }
1245     return INVALID_PDSOCKET;
1246   }
1247   else
1248     return new_sock;
1249 }
1250
1251 // TODO -- use vectors and strings ?
1252 /*
1253  *  RPCgetArg - break a string into blank separated words
1254  *  Used to parse a command line.
1255  *  
1256  *  input --> a null terminated string, the command line
1257  *  argc --> returns the number of args found
1258  *  returns --> words (separated by blanks on command line)
1259  *  Note --> this allocates memory 
1260  */
1261 bool RPCgetArg(vector<string> &arg, const char *input)
1262 {
1263 #define BLANK ' '
1264
1265   int word_len;
1266   if (!input) 
1267     return false;
1268
1269   int length = strlen(input);
1270   int index = 0;
1271
1272   /* advance past blanks in input */
1273   while ((index < length) && (input[index] == BLANK))
1274     index++;
1275
1276   /* input is all blanks, or NULL */
1277   if (index >= length) 
1278     return true;
1279
1280   do {
1281     /* the start of each string */
1282     word_len = 0; const char *word_start = input + index;
1283
1284     /* find the next BLANK and the WORD size */
1285     while ((index < length) && (input[index] != BLANK)) {
1286       index++; word_len++;
1287     }
1288
1289     /* copy the word */
1290     char *temp = new char[word_len+1];
1291     strncpy(temp, word_start, word_len);
1292     temp[word_len] = (char) 0;
1293     arg += temp;
1294     delete temp;
1295
1296     /* skip past consecutive blanks */
1297     while ((index < length) && (input[index] == BLANK))
1298       index++;
1299   } while (index < length);
1300   return true;
1301 }
1302
1303
1304 string getHostName() {
1305 #if defined(i386_unknown_nt4_0)
1306     char nameBuf[1000];
1307     gethostname(nameBuf,999);
1308     return string(nameBuf);
1309 #else
1310     struct utsname un;
1311     P_uname(&un);
1312     return string(un.nodename);
1313 #endif
1314 }
1315
1316
1317 #if defined(i386_unknown_nt4_0)
1318
1319
1320 //
1321 // CreateSocketPair
1322 //
1323 // Creates a connected pair of TCP/IP sockets.  The
1324 // intended use of the function is to mimic the
1325 // socketpair call available from Berkeley sockets,
1326 // so that a "child" process created by this process can
1327 // inherit one endpoint of the socket pair and 
1328 // thus be connected to the "parent" process.
1329 //
1330 // The sockets are distinguished as local and remote
1331 // because we need to make sure that the socket handle
1332 // intended to be kept at the parent process must not
1333 // be inherited by the child.  (If it were inherited,
1334 // the child couldn't tell when the parent closed
1335 // the socket, because the child would inherit both
1336 // endpoints of the connection and thus the connection
1337 // would stay open even though the parent closed its end.)
1338 //
1339 bool
1340 CreateSocketPair( PDSOCKET& localSock, PDSOCKET& remoteSock )
1341 {
1342     PDSOCKET sockListen = INVALID_PDSOCKET;
1343     bool bCreated = false;
1344     
1345
1346     // create sockets
1347     localSock = socket( AF_INET, SOCK_STREAM, PF_UNSPEC );
1348     remoteSock = INVALID_PDSOCKET;
1349     sockListen = socket( AF_INET, SOCK_STREAM, PF_UNSPEC );
1350     if( (sockListen != INVALID_PDSOCKET) && (localSock != INVALID_PDSOCKET) )
1351     {
1352         // connect sockets...
1353
1354         // ...bind one of the sockets...
1355         struct hostent* pHostData = gethostbyname( "localhost" );
1356         assert( pHostData != NULL );
1357
1358         SOCKADDR_IN sin;
1359         sin.sin_family = AF_INET;
1360         sin.sin_port = 0;            // dynamically assigned
1361         sin.sin_addr.S_un.S_addr = *(unsigned long*)pHostData->h_addr;
1362         if( bind( sockListen, (SOCKADDR*)&sin, sizeof(sin) ) != PDSOCKET_ERROR )
1363         {
1364             // ...connect the other to the bound socket
1365             sockaddr_in sain;
1366             int cbSain = sizeof( sain );
1367             if( getsockname( sockListen, (SOCKADDR*)&sain, &cbSain ) 
1368                         != PDSOCKET_ERROR )
1369             {
1370                 if( listen( sockListen, 1 ) != PDSOCKET_ERROR )
1371                 {
1372                     // issue a "deferred" connect for socket 0
1373                     sin.sin_port = sain.sin_port;
1374                     if( connect( localSock, (SOCKADDR*)&sin, sizeof(sin) )
1375                                 != PDSOCKET_ERROR )
1376                     {
1377                         // accept the connection
1378                         remoteSock = accept( sockListen, NULL, NULL );
1379                         if( remoteSock != INVALID_PDSOCKET )
1380                         {
1381                             // verify the sockets are connected -
1382                             // socket 0 should now be writable
1383                             fd_set wrset;
1384                             struct timeval tv;
1385
1386                             tv.tv_sec = 0;
1387                             tv.tv_usec = 0;
1388                             FD_ZERO( &wrset );
1389                             FD_SET( localSock, &wrset );
1390                             if( select( 0, NULL, &wrset, NULL, &tv ) == 1 )
1391                             {
1392                                 bCreated = true;
1393                             }
1394                         }
1395                     }
1396                 }
1397             }
1398         }
1399     }
1400
1401     if( bCreated )
1402     {
1403         // make sure that the remote endpoint handle
1404         // is not inheritable
1405         HANDLE hDup;
1406         if( DuplicateHandle( GetCurrentProcess(),
1407                 (HANDLE)localSock,
1408                 GetCurrentProcess(),
1409                 &hDup,
1410                 0,
1411                 FALSE,
1412                 DUPLICATE_SAME_ACCESS ) )
1413         {
1414             // we made the duplication,
1415             // so close the inheritable handle
1416             // and return the noninheritable one
1417             CLOSEPDSOCKET( localSock );
1418             localSock = (SOCKET)hDup;
1419         }
1420         else
1421         {
1422             bCreated = false;
1423         }
1424     }
1425
1426     if( !bCreated )
1427     {
1428         CLOSEPDSOCKET( localSock );
1429         localSock = INVALID_PDSOCKET;
1430         CLOSEPDSOCKET( remoteSock );
1431         remoteSock = INVALID_PDSOCKET;
1432     }
1433
1434     CLOSEPDSOCKET( sockListen );
1435
1436     return bCreated;
1437 }
1438
1439
1440 #endif // defined(i386_unknown_nt4_0)
1441