Re-implemented the MPICH wrapper handling so that the machine that runs the
[dyninst.git] / paradyn / src / DMthread / DMdaemon.C
1 /*
2  * Copyright (c) 1996-1998 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  * $Id: DMdaemon.C,v 1.99 2001/01/04 22:26:25 pcroth Exp $
44  * method functions for paradynDaemon and daemonEntry classes
45  */
46
47 #include <assert.h>
48 extern "C" {
49 double   quiet_nan();
50 #include <malloc.h>
51 #include <rpc/types.h>
52 #include <rpc/xdr.h>
53 #include <stdio.h>
54 }
55
56 #include <string.h>
57 #include <sys/types.h>
58 #include <ctype.h>
59 #include <limits.h>
60
61 #include "thread/h/thread.h"
62 #include "paradyn/src/pdMain/paradyn.h"
63 #include "dataManager.thread.h"
64 #include "dyninstRPC.xdr.CLNT.h"
65 #include "DMdaemon.h"
66 #include "paradyn/src/TCthread/tunableConst.h"
67 #include "paradyn/src/UIthread/Status.h"
68 #include "DMmetric.h"
69 #include "paradyn/src/met/metricExt.h"
70 #include "DMtime.h"
71 #include "pdutilOld/h/rpcUtil.h"
72
73 #include "../UIthread/tkTools.h"
74 extern Tcl_Interp *interp;
75
76
77 // TEMP this should be part of a class def.
78 status_line *DMstatus=NULL;
79 status_line *PROCstatus=NULL;
80
81 extern debug_ostream sampleVal_cerr;
82
83 // This is from met/metMain.C
84 void metCheckDaemonProcess( const string & );
85
86
87 vector<paradynDaemon::MPICHWrapperInfo> paradynDaemon::wrappers;
88
89
90 // change a char* that points to "" to point to NULL
91 // NULL is used to signify "NO ARGUMENT"
92 // NULL is easier to detect than "" (which needs a strlen to detect)
93 /*
94 static void fixArg(char *&argToFix)
95 {
96   if (argToFix && !strlen(argToFix))
97     argToFix = (char*) 0;
98 }
99 */
100
101
102 static appState applicationState = appPaused; // status of the application.
103
104
105 metricInstance *DM_enableType::findMI(metricInstanceHandle mh){
106     for(u_int i=0; i < request->size(); i++){
107          if(mh == ((*request)[i])->getHandle()){
108              return ((*request)[i]);
109     } }
110     return 0;
111 }
112
113 void DM_enableType::setDone(metricInstanceHandle mh){
114     for(u_int i=0; i < request->size(); i++){
115         if(mh == ((*request)[i])->getHandle()){
116             (*done)[i] = true;
117             return;
118         } 
119     }
120 }
121
122 // find any matching completed mids and update the done values 
123 // if a matching value is successfully enabled done=true, else done= false
124 void DM_enableType::updateAny(vector<metricInstance *> &completed_mis,
125                               vector<bool> successful){
126
127    for(u_int i=0; i < done->size(); i++){
128        if(!(*done)[i]){  // try to update element
129            assert(not_all_done);
130            metricInstanceHandle mh = ((*request)[i])->getHandle();
131            for(u_int j=0; j < completed_mis.size(); j++){
132                if(completed_mis[j]){
133                    if(completed_mis[j]->getHandle() == mh){
134                        if(successful[j]) (*done)[i] = true;
135                        not_all_done--;
136                } } 
137            }
138    } }
139 }
140
141
142
143 // Called whenever a program is ready to run (both programs started by paradyn
144 // and programs forked by other programs). QUESTION: What about when a program does
145 // an exec syscall?
146 //
147 // The new program inherits all enabled metrics, and if the application is 
148 // running, the new program must run too.
149 // 
150 // Called by the igen routine newProgramCallbackFunc()
151
152 bool paradynDaemon::addRunningProgram (int pid,
153                                        const vector<string> &paradynd_argv,
154                                        paradynDaemon *daemon,
155                                        bool calledFromExec,
156                                        bool attached_runMe) {
157     executable *exec = NULL;
158
159     if (calledFromExec) {
160        for (unsigned i=0; i < programs.size(); i++) {
161           if ((int) programs[i]->pid == pid && programs[i]->controlPath == daemon) {
162              exec = programs[i];
163              break;
164           }
165        }
166        assert(exec);
167
168        // exec() doesn't change the pid, but paradynd_argv changes (big deal).
169        exec->argv = paradynd_argv;
170
171        // fall through (we probably want to execute the daemon->continueProcess())
172     }
173     else {
174        // the non-exec (the normal) case follows:
175        exec = new executable (pid, paradynd_argv, daemon);
176        programs += exec;
177        ++procRunning;
178
179        // the following propagates mi's to the new process IF it's the only
180        // process on the daemon.  Otherwise, the daemon can and does propagate
181        // on its own.  We don't call it in the exec case (above) since we know it
182        // wouldn't do anything.
183        daemon->propagateMetrics();
184     }
185
186     if (applicationState == appRunning || attached_runMe) {
187       //cerr << "paradyn: calling daemon->continueProcess off of addRunningProgram (machine " << daemon->getMachineName() << "; pid=" << pid << ") !" << endl;
188       daemon->continueProcess(pid);
189     }
190
191     if (procRunning == 1 && attached_runMe) {
192        // currently, Paradyn believes that the application is paused.
193        // Let's change that to 'running'.
194        if (!continueAll())
195           cerr << "warning: continueAll failed" << endl;
196        uiMgr->enablePauseOrRun();
197     }
198
199     return true;
200 }
201
202 //
203 // add a new paradyn daemon
204 // called when a new paradynd contacts the advertised socket
205 //
206 bool paradynDaemon::addDaemon (PDSOCKET sock)
207 {
208   // constructor adds new daemon to dictionary allDaemons
209   paradynDaemon *new_daemon = new paradynDaemon (sock);
210
211   if (new_daemon->errorConditionFound) {
212     //TODO: "something" has to be done for a proper clean up - naim
213     uiMgr->showError(6,"");
214     return(false);
215   }
216
217 //
218 // KLUDGE:  set socket buffer size to 64k to avoid write-write deadlock
219 //              between paradyn and paradynd
220 //
221 #if defined(sparc_sun_sunos4_1_3) || defined(hppa1_1_hp_hpux)
222    int num_bytes =0;
223    int size = sizeof(num_bytes);
224    num_bytes = 32768;
225    if(setsockopt(new_daemon->get_sock(),SOL_SOCKET,SO_SNDBUF,(char *)&num_bytes ,size) < 0){
226       perror("setsocketopt SND_BUF error");
227    }
228 #endif
229
230   msg_bind_socket (new_daemon->get_sock(), true, (int(*)(void*)) xdrrec_eof,
231                      (void*)new_daemon->net_obj(), &(new_daemon->stid));
232   assert(new_daemon);
233
234   // The pid is reported later in an upcall
235   return (true);
236 }
237
238 //  TODO : fix this so that it really does clean up state
239 // Dispose of daemon state.
240 //    This is called because someone wants to kill a daemon, or the daemon
241 //       died and we need to cleanup after it.
242 //
243 void paradynDaemon::removeDaemon(paradynDaemon *d, bool informUser)
244 {
245
246     if (informUser) {
247       string msg;
248       msg = string("paradynd has died on host <") + d->getMachineName()
249             + string(">");
250       uiMgr->showError(5, P_strdup(msg.string_of()));
251     }
252
253     d->dead = true;
254
255 #ifdef notdef
256     executable *e;
257     List<executable*> progs;
258     daemons.remove(d);
259
260     //
261     // Delete executables running on the dead paradyn daemon.
262     //
263     for (progs = programs; e = *progs; progs++) {
264        if (e->controlPath == d) {
265            programs.remove(e);
266            delete(e);
267        }
268     }
269
270 #endif
271
272     // tell the thread package to ignore the fd to the daemon.
273     msg_unbind(d->getSocketTid());
274 }
275
276
277 // get the current time of a daemon, to adjust for clock differences.
278 void getDaemonTime(paradynDaemon *pd) {
279   timeStamp t1 = getCurrentTime();
280   timeStamp dt = pd->getTime(); // daemon time
281   timeStamp t2 = getCurrentTime();
282   timeStamp delay = (t2 - t1) / 2.0;
283   pd->setTimeFactor(t1 - dt + delay);
284 }
285
286 //
287 // add a new daemon
288 // check to see if a daemon that matches the function args exists
289 // if it does exist, return a pointer to it
290 // otherwise, create a new daemon
291 //
292 paradynDaemon *paradynDaemon::getDaemonHelper(const string &machine, 
293                                               const string &login, 
294                                               const string &name) {
295
296   //cerr << "paradynDaemon::getDaemonHelper(machine=" << machine << ")" << endl;
297
298     string m = machine; 
299     // fill in machine name to default_host if empty
300     if (!m.length()) {
301         if (default_host.length()) {
302             m = getNetworkName(default_host);
303             //cerr << "Using default host <" << m << ">" << endl;
304         } else {
305             m = getNetworkName();
306             //cerr << "Using local host <" << m << ">" << endl;
307         }
308     } else {
309             m = getNetworkName(m);
310             //cerr << "Using given host <" << m << ">" << endl;
311     }
312
313     // find out if we have a paradynd on this machine+login+paradynd
314     paradynDaemon *pd;
315     for(unsigned i = 0; i < paradynDaemon::allDaemons.size(); i++){
316         pd = paradynDaemon::allDaemons[i];
317         if ((!m.string_of() || (pd->machine == m)) && 
318            (!login.string_of() || (pd->login == login))  &&
319            (name.string_of() && (pd->name == name))) {
320             //cerr << "Returning an existing daemon match!" << endl;
321             return (pd);     
322         }
323     }
324   
325     // find a matching entry in the dictionary, and start it
326     daemonEntry *def = findEntry(m, name);
327     if (!def) {
328         if (name.length()) {
329           string msg = string("Paradyn daemon \"") + name + string("\" not defined.");
330           uiMgr->showError(90,P_strdup(msg.string_of()));
331         }
332         else {
333           uiMgr->showError(91,"");
334         }
335         return ((paradynDaemon*) 0);
336     }
337
338     char statusLine[256];
339     sprintf(statusLine, "Starting daemon on %s",m.string_of());
340     uiMgr->updateStatus(DMstatus,P_strdup(statusLine));
341
342     string flav_arg(string("-z") + def->getFlavor());
343     unsigned asize = paradynDaemon::args.size();
344     paradynDaemon::args += flav_arg;
345
346     pd = new paradynDaemon(m, login, def->getCommandString(), 
347                                def->getRemoteShellString(), def->getNameString(),
348                                    def->getFlavorString());
349
350     if (pd->errorConditionFound) {
351       //TODO: "something" has to be done for a proper clean up - naim
352       string msg;
353       msg=string("Cannot create daemon process on host \"") + m + string("\"");
354       uiMgr->showError(84,P_strdup(msg.string_of())); 
355       return((paradynDaemon*) 0);
356     }
357
358 #if defined(sparc_sun_sunos4_1_3) || defined(hppa1_1_hp_hpux)
359    int num_bytes =0;
360    int nb_size = sizeof(num_bytes);
361    num_bytes = 32768;
362    if(setsockopt(pd->get_desc(),SOL_SOCKET,SO_SNDBUF,(char *)&num_bytes ,nb_size) < 0){
363       perror("setsocketopt SND_BUF error");
364    }
365 #endif
366
367     paradynDaemon::args.resize(asize);
368     uiMgr->updateStatus(DMstatus,P_strdup("ready"));
369
370     if (pd->get_sock() == PDSOCKET_ERROR) {
371         uiMgr->showError (6, "");
372         return((paradynDaemon*) 0);
373     }
374
375    // Send the initial metrics, constraints, and other neato things
376    mdl_send(pd);
377    // Send the initial metrics, constraints, and other neato things
378    vector<T_dyninstRPC::metricInfo> info = pd->getAvailableMetrics();
379    unsigned size = info.size();
380    for (unsigned u=0; u<size; u++)
381         addMetric(info[u]);
382
383     getDaemonTime(pd);
384
385     msg_bind_socket(pd->get_sock(), true, (int(*)(void*))xdrrec_eof,
386                      (void*) pd->net_obj(), &(pd->stid));
387     return (pd);
388 }
389
390 //  
391 // add a new daemon, unless a daemon is already running on that machine
392 // with the same machine, login, and program
393 //
394 bool paradynDaemon::getDaemon (const string &machine, 
395                                const string &login,
396                                const string &name){
397
398     if (!getDaemonHelper(machine, login, name)){
399         return false;
400     }
401     return true;
402 }
403
404 //
405 // define a new entry for the daemon dictionary, or change an existing entry
406 //
407 bool paradynDaemon::defineDaemon (const char *c, const char *d,
408                                   const char *l, const char *n,
409                                   const char *m, const char *r, const char *f) {
410
411   if(!n || !c)
412       return false;
413   string command = c;
414   string dir = d;
415   string login = l;
416   string name = n;
417   string machine = m;
418   string remote_shell = r;
419   string flavor = f;
420
421   daemonEntry *newE;
422   for(unsigned i=0; i < allEntries.size(); i++){
423       newE = allEntries[i];
424       if(newE->getNameString() == name){
425           if (newE->setAll(machine, command, name, login, dir, remote_shell, flavor))
426               return true;
427           else 
428               return false;
429       }
430   }
431
432   newE = new daemonEntry(machine, command, name, login, dir, remote_shell, flavor);
433   if (!newE)
434       return false;
435   allEntries += newE;
436   return true;
437 }
438
439
440 daemonEntry *paradynDaemon::findEntry(const string &, 
441                                       const string &n) {
442
443     // if (!n) return ((daemonEntry*) 0);
444     for(unsigned i=0; i < allEntries.size(); i++){
445         daemonEntry *newE = allEntries[i];
446         if(newE->getNameString() == n){
447            return(newE);
448         }
449     }
450     return ((daemonEntry*) 0);
451
452 }
453
454 void paradynDaemon::tellDaemonsOfResource(u_int parent, u_int my_id, 
455                                           const char *name, unsigned type) {
456     paradynDaemon *pd;
457     for(unsigned i = 0; i < paradynDaemon::allDaemons.size(); i++){
458         pd = paradynDaemon::allDaemons[i];
459         pd->addResource(parent,my_id,name, type);
460     }
461
462 }
463
464 void paradynDaemon::printEntries()
465 {
466     daemonEntry *entry;
467     for(unsigned i=0; i < allEntries.size(); i++){
468         entry = allEntries[i];
469         entry->print();
470     }
471 }
472
473 void paradynDaemon::print() 
474 {
475   cout << "DAEMON\n";
476   cout << "  name: " << name << endl;
477   cout << "  command: " << command << endl;
478   cout << "  login: " << login << endl;
479   cout << "  machine: " << machine << endl;
480   cout << "  flavor: " << flavor << endl;
481 }
482
483 void paradynDaemon::printDaemons()
484 {
485     paradynDaemon *pd;
486   cout << "ACTIVE DAEMONS\n";
487     for(unsigned i = 0; i < paradynDaemon::allDaemons.size(); i++){
488         pd = paradynDaemon::allDaemons[i];
489         pd->print();
490     }
491 }
492
493 void paradynDaemon::printPrograms()
494 {
495     executable *entry;
496     for(unsigned i=0; i < programs.size(); i++){
497         entry = programs[i];
498         cout << "PROGRAM ENTRY\n";
499         cout << "pid : " << entry->pid << endl;
500         entry->controlPath->print();
501     }
502 }
503
504 //
505 // Return list of names of defined daemons.  
506 //
507 vector<string> *paradynDaemon::getAvailableDaemons()
508 {
509     vector<string> *names = new vector<string>;
510
511     daemonEntry *entry;
512     for(unsigned i=0; i < allEntries.size(); i++){
513         entry = allEntries[i];
514         *names += entry->getName();
515     }
516     return(names);
517 }
518
519 // For a given machine name, find the appropriate paradynd structure(s).
520 vector<paradynDaemon*> paradynDaemon::machineName2Daemon(const string &mach) {
521    vector<paradynDaemon*> v;
522    for (unsigned i=0; i < allDaemons.size(); i++) {
523       paradynDaemon *theDaemon = allDaemons[i];
524       if (theDaemon->getMachineName() == mach)
525         v += theDaemon;
526    }
527    return v;
528 }
529
530 static bool hostIsLocal(const string &machine)
531 {
532         return (machine.length() == 0 || machine == "localhost" || 
533                 getNetworkName(machine) == getNetworkName() ||
534                 getNetworkAddr(machine) == getNetworkAddr());
535 }
536
537 static bool execPOE(const string /* &machine*/, const string /* &login */,
538                     const string /* &name   */, const string         &dir,
539                     const vector<string> &argv, const vector<string>  args,
540                     daemonEntry          *de)
541 {
542         unsigned i;
543
544
545   char **s = (char**) malloc(sizeof(char*) * (args.size() + argv.size() + 5));
546   char   t[1024];
547   assert(s != NULL);
548
549   s[0] = strdup("poe");
550   s[1] = strdup(de->getCommand());
551
552   for (i = 0; i < args.size(); i++)
553     s[i+2] = (strcmp(args[i].string_of(), "-l1")) 
554              ? strdup(args[i].string_of()) : strdup("-l0");
555
556   sprintf(t, "-z%s", de->getFlavor());
557   s[args.size()+2] = strdup(t);
558   s[args.size()+3] = strdup("-runme");
559       
560   for (i = 0; i < argv.size(); i++) 
561     s[args.size()+4+i] = (i || strcmp(argv[i].string_of(), "poe"))
562                          ? strdup(argv[i].string_of()) : strdup("");
563
564   s[args.size()+argv.size()+4] = NULL;
565
566                       //IBM POE sets remote directory same as current directory
567   if (dir.length() && (P_chdir(dir.string_of()) < 0)) 
568     cerr << "cannot chdir to " << dir.string_of() << ": " 
569          << sys_errlist[errno] << endl;
570
571   int execRetVal = execvp(s[0], s);
572
573   // Close Tk X connection to avoid conflicts with parent
574 #if !defined(i386_unknown_nt4_0)
575   Display *UIMdisplay = Tk_Display (Tk_MainWindow(interp));
576   int xfd = XConnectionNumber (UIMdisplay);
577   close(xfd);
578 #endif // !defined(i386_unknown_nt4_0)
579
580   if ( execRetVal == -1 )
581   {
582     if ( errno == ENOENT )
583       cerr << "Could not find executable." << endl;
584     else
585       cerr << "Could not start MPI on local host." << endl;
586   }
587   
588   P__exit(-1);
589   return(false);
590 }
591
592
593 static bool rshPOE(const string         &machine, const string         &login,
594                    const string      /* &name*/,  const string         &dir,
595                    const vector<string> &argv,    const vector<string>  args,
596                    daemonEntry          *de)
597 {
598   unsigned i;
599   char *s[6];
600   char  t[1024];
601   string appPath;
602   string pathResult;
603   string path = getenv("PATH");
604
605   //  create rsh command
606   if ( de->getRemoteShellString().length() > 0 )
607     s[0] = strdup(de->getRemoteShell());
608   else
609     s[0] = strdup("rsh");
610
611   s[1] = strdup(machine.string_of());
612   s[2] = (login.length()) ? strdup("-l"):              strdup("");
613   s[3] = (login.length()) ? strdup(login.string_of()): strdup("");
614
615   sprintf(t, "(");
616
617   if (dir.length()) strcat(strcat(strcat(t, "cd "), dir.string_of()), "; ");
618
619   strcat(strcat(strcat(t, "poe "), de->getCommand()), " ");
620
621   for (i = 0; i < args.size(); i++)
622     strcat(strcat(t, (strcmp(args[i].string_of(), "-l1")) 
623                      ? args[i].string_of() : "-l0"), " ");
624
625   strcat(strcat(strcat(t, "-z"), de->getFlavor()), " -runme ");
626       
627   for (i = 0; i < argv.size(); i++) 
628     strcat(strcat(t, (i || strcmp(argv[i].string_of(), "poe"))
629                      ? argv[i].string_of() : ""), " ");
630
631   strcat(t, ")");
632
633   s[4] = strdup(t);
634   s[5] = NULL;
635
636   int execRetVal = execvp(s[0], s);
637
638   // Close Tk X connection to avoid conflicts with parent
639 #if !defined(i386_unknown_nt4_0)
640   Display *UIMdisplay = Tk_Display (Tk_MainWindow(interp));
641   int xfd = XConnectionNumber (UIMdisplay);
642   close(xfd);
643 #endif // !defined(i386_unknown_nt4_0)
644
645   if ( execRetVal == -1 )
646   {
647     if ( errno == ENOENT )
648       cerr << "Could not find executable to start remote shell." << endl;
649     else
650       cerr << "Could not start MPI on remote host." << endl;
651   }
652   
653   P__exit(-1);
654   return(false);
655 }
656
657
658 static bool startPOE(const string         &machine, const string         &login,
659                      const string         &name,    const string         &dir,
660                      const vector<string> &argv,    const vector<string>  args,
661                      daemonEntry          *de)
662 {
663 #if !defined(i386_unknown_nt4_0)
664   if (DMstatus)   uiMgr->updateStatus(DMstatus,   "ready");
665   if (PROCstatus) uiMgr->updateStatus(PROCstatus, "IBM POE");
666
667   if (fork()) return(true);
668
669   if (hostIsLocal(machine))
670     return(execPOE(machine, login, name, dir, argv, args, de));
671   else
672     return( rshPOE(machine, login, name, dir, argv, args, de));
673 #else // !defined(i386_unknown_nt4_0)
674         // TODO - implement this?
675         return false;
676 #endif // !defined(i386_unknown_nt4_0)
677 }
678
679
680 bool getIrixMPICommandLine(const vector<string> argv, const vector<string> args,
681               daemonEntry* de, vector<string>& cmdLineVec, string& programNames)
682 {
683   //  This parsing implemented for mpirun IRIX 6.5, SGI MPI 3.2.0.0
684
685   typedef struct arg_entry {
686         
687     char* argFlag;
688     bool hasValue;
689     bool definedBehavior;
690     bool supported;
691     
692   } ArgEntry;
693                 
694   const unsigned int globalArgNo = 14;
695         
696   ArgEntry globalArgs[globalArgNo] =
697   {
698     {  "-a", true, true, true },
699     {  "-array", true, true, true },
700     {  "-cpr", false, false, true },
701     {  "-d", true, true, true },
702     {  "-dir", true, true, true },
703     {  "-f", true, false, true },
704     {  "-file", true, false, true },
705     {  "-h", false, false, false },
706     {  "-help", false, false, false },
707     {  "-miser", false, true, true },
708     {  "-p", true, true, true },
709     {  "-prefix", true, true, true },
710     {  "-v", false, true, true },
711     {  "-verbose", false, true, true }
712   };
713         
714         
715   const unsigned int localArgNo = 4;
716         
717   ArgEntry localArgs[localArgNo] =
718   {
719     {  "-f", true, false, true },
720     {  "-file", true, false, true },
721     {  "-np", true, true, true },
722     {  "-nt", true, true, true }
723   };
724
725   unsigned int i = 0, j = 0;
726   vector<string> progNameVec;
727   
728   //  parse past mpirun
729   bool mpirunFound = false;
730   do 
731   {
732     cmdLineVec += argv[i];
733     
734     mpirunFound = (P_strstr(argv[i].string_of(), "mpirun") > 0);
735     i++;
736   }
737   while ( !mpirunFound && i < argv.size());
738
739   if ( !mpirunFound )
740   {
741     uiMgr->showError(113, "Unable to find mpirun command.");
742     return false;
743   }
744   
745   //  parse global options
746   bool flagFound = true;
747
748   while ( i < argv.size() && flagFound )
749   {
750     flagFound = false;
751
752     for ( j = 0; j < globalArgNo && !flagFound; j++ )
753     {
754       if ( P_strcmp(argv[i].string_of(), globalArgs[j].argFlag) == 0 )
755       {
756         if ( !globalArgs[j].supported )
757         {
758           string message = "Paradyn does not support use of mpirun flag ";
759           message += globalArgs[j].argFlag;
760           uiMgr->showError(113, P_strdup(message.string_of()));
761           
762           return false;
763         }
764         
765         if ( !globalArgs[j].definedBehavior )
766         {
767           string message = "Behavior undefined with mpirun option ";
768           message += globalArgs[j].argFlag;
769           uiMgr->showError(114, P_strdup(message.string_of()));
770         }
771         
772         flagFound = true;
773         cmdLineVec += argv[i];
774         i++;
775
776         if ( globalArgs[j].hasValue )
777         {
778           cmdLineVec += argv[i];
779           i++;
780         }
781       }
782     }
783   }
784
785   while ( i < argv.size() )
786   {
787     //  We have reached the first entry
788     //  The following arguments could be host list, process count, application,
789     //    or application argument
790
791     //  Is the argument of the first entry a number?
792     //  If so, assume that this is a process count and not
793     //    a hostname
794
795     bool isNumber = true;
796     unsigned int len;
797     
798     len = argv[i].length();
799
800     for ( j = 0; j < len && isNumber; j++ )
801     {
802       if ( !isdigit(argv[i][j]) )
803         isNumber = false;
804     }
805
806     if ( isNumber )  
807     {
808       //  this must be a process count
809       cmdLineVec += argv[i];
810       i++;
811     }
812
813     if ( !isNumber )
814     {
815       //  If not a number, then this argument can either be
816       //  a local flag or a hostname.  If it is a local flag,
817       //  then there is no hostlist for this entry (it is for
818       //  the local host).
819
820       // are there any local options?
821       //  This can consist of:
822       //    -f[ile] filename
823       //    -np number
824       //    -nt number
825         
826       bool flagFound = false;
827
828       for ( j = 0; j < localArgNo && !flagFound; j++ )
829       {
830         if ( strcmp(argv[i].string_of(), localArgs[j].argFlag) == 0 )
831         {
832           if ( !localArgs[j].supported )
833           {
834             string message = "Paradyn does not support use of mpirun flag ";
835             message += globalArgs[j].argFlag;
836             uiMgr->showError(113, P_strdup(message.string_of()));
837             return false;
838           }
839           
840           if ( !localArgs[j].definedBehavior )
841           {
842             string message = "Behavior undefined with mpirun option ";
843             message += globalArgs[j].argFlag;
844             uiMgr->showError(114, P_strdup(message.string_of()));
845           }
846
847           flagFound = true;
848           
849           cmdLineVec += argv[i];
850           i++;
851         
852           if ( localArgs[j].hasValue )
853           {
854             cmdLineVec += argv[i];
855             i++;
856           }
857         }
858       }
859         
860       //  If the first arg in the entry is neither a number or a local flag,
861       //  then this must be a hostlist.
862       //    hostlist can be constructed in the following ways:
863       //      "hostname1," "hostname2"
864       //      "hostname1" ",hostname2"
865       //      "hostname1" "," "hostname2"
866       //      "hostname1,hostname2"
867
868       if ( !flagFound )
869       {
870         bool inHostList = false;
871         char* commaPtr;
872         do
873         {
874           // If curr arg contains ",", then it is a hostname
875           // If last char is ',' or next arg starts with ',', then still in hostlist
876           commaPtr = strrchr(argv[i].string_of(), ',');
877
878           if ( (commaPtr && argv[i][argv[i].length()-1] == ',') ||
879                ( (i+1) < argv.size() && argv[i+1][0] == ',') )
880           {
881             inHostList = true;
882           }
883           else
884             inHostList = false;
885
886           cmdLineVec += argv[i];
887           i++;
888         
889         } while ( inHostList );
890
891         // If there aren't anymore args, the command is invalid!
892         if ( i >= argv.size() )
893         {
894             uiMgr->showError(113, "Unable to parse mpirun command line.");
895             return false;
896         }
897         
898         // Now redo the number/flag-checking
899         isNumber = true;
900         len = argv[i].length();
901
902         for ( j = 0; j < len; j++ )
903         {
904           if ( !isdigit(argv[i][j]) )
905             isNumber = false;
906         }
907
908         if ( isNumber )  
909         {
910           //  this must be a process count
911           cmdLineVec += argv[i];
912           i++;
913         }
914         else
915         {
916           flagFound = false;
917           
918           for ( j = 0; j < localArgNo && !flagFound; j++ )
919           {
920             if ( strcmp(argv[i].string_of(), localArgs[j].argFlag) == 0 )
921             {
922               if ( !localArgs[j].supported )
923               {
924                 string message = "Paradyn does not support use of mpirun flag ";
925                 message += globalArgs[j].argFlag;
926                 uiMgr->showError(113, P_strdup(message.string_of()));
927                 return false;
928               }
929               
930               flagFound = true;
931               cmdLineVec += argv[i];
932               i++;
933         
934               if ( localArgs[j].hasValue )
935               {
936                 cmdLineVec += argv[i];
937                 i++;
938               }
939             }
940           }
941
942           if ( !flagFound )  // No process count!  Error parsing command.
943           {
944             uiMgr->showError(113, "Unable to parse mpirun command line.");
945             return false;
946           }
947         }
948       }
949     }
950
951     // If there aren't anymore args, the command is invalid!
952     if ( i >= argv.size() )
953     {
954       uiMgr->showError(113, "Unable to determine executable in mpirun command line.");
955       return false;
956     }
957
958     //  the current argument is an application, so insert daemon command & args
959     cmdLineVec += de->getCommand();
960     
961     for (j = 0; j < args.size(); j++)
962       cmdLineVec += strcmp(args[j].string_of(), "-l1") ? args[j] : "-l0";
963
964     cmdLineVec += string("-z") + de->getFlavor();
965     cmdLineVec += "-runme";
966     
967     bool progNameFound = false;
968     for ( j = 0; j < progNameVec.size(); j++ )
969     {
970       if ( progNameVec[j] == argv[i] )
971         progNameFound = true;
972     }
973
974     if ( !progNameFound )
975       progNameVec += argv[i];
976
977     cmdLineVec += argv[i];
978     
979     i++;
980
981     //  Go past program arguments to find the next entry
982     while ( i < argv.size() && strcmp(argv[i].string_of(), ":") )
983     {
984       cmdLineVec += argv[i];
985       i++;
986     }
987
988     if ( i < argv.size() && !strcmp(argv[i].string_of(), ":") )
989     {
990       cmdLineVec += argv[i];
991       i++;
992     }
993   }
994
995   for ( j = 0; j < progNameVec.size(); j++ )
996   {
997     if ( programNames.length() )
998       programNames += ", ";
999     programNames += progNameVec[j];
1000   }
1001
1002   return true;
1003 }
1004
1005
1006 static bool execIrixMPI(const string &dir, vector<string>& cmdLineVec)
1007 {
1008   unsigned int j;
1009   
1010   char **s = new char*[cmdLineVec.size()+1]();
1011
1012   for ( j = 0; j < cmdLineVec.size(); j++ ) {
1013     s[j] = P_strdup(cmdLineVec[j].string_of());
1014   }
1015
1016   s[j] = NULL;
1017   
1018   if (dir.length()) {
1019       // mpirun cds to "current directory" based on PWD environment variable
1020       char* buf = new char[dir.length()+5]();
1021       sprintf(buf, "PWD=%s",dir.string_of());
1022       putenv(buf);
1023   }
1024
1025   int execRetVal = execvp(s[0], s);
1026
1027   // Close Tk X connection to avoid conflicts with parent
1028 #if !defined(i386_unknown_nt4_0)
1029   Display *UIMdisplay = Tk_Display (Tk_MainWindow(interp));
1030   int xfd = XConnectionNumber (UIMdisplay);
1031   close(xfd);
1032 #endif // !defined(i386_unknown_nt4_0)
1033
1034   if ( execRetVal == -1 )
1035   {
1036     if ( errno == ENOENT )
1037       cerr << "Could not find executable." << endl;
1038     else
1039       cerr << "Could not start MPI on local host." << endl;
1040   }
1041   
1042   P__exit(-1);
1043   return(false);
1044 }
1045
1046
1047 static bool rshIrixMPI(const string &machine, const string &login,
1048                        const string &dir, daemonEntry* de,
1049                        vector<string>& cmdLineVec)
1050 {
1051   char *s[6];
1052   char  t[1024];
1053   unsigned int i;
1054   
1055   //  create rsh command
1056   if ( de->getRemoteShellString().length() > 0 )
1057     s[0] = strdup(de->getRemoteShell());
1058   else
1059     s[0] = strdup("rsh");
1060
1061   s[1] = strdup(machine.string_of());
1062   s[2] = (login.length()) ? strdup("-l"):              strdup("");
1063   s[3] = (login.length()) ? strdup(login.string_of()): strdup("");
1064
1065   strcpy(t, "(");
1066
1067   if (dir.length())
1068   {
1069     strcat(t, "cd ");
1070     strcat(t, dir.string_of());
1071     strcat(t, "; ");
1072   }
1073
1074   for ( i = 0; i < cmdLineVec.size(); i++ )
1075   {
1076     strcat(t, cmdLineVec[i].string_of());
1077     strcat(t, " ");
1078   }
1079   
1080   strcat(t, ")");
1081
1082   s[4] = strdup(t);
1083   s[5] = NULL;
1084
1085   int execRetVal = execvp(s[0], s);
1086
1087   // Close Tk X connection to avoid conflicts with parent
1088 #if !defined(i386_unknown_nt4_0)
1089   Display *UIMdisplay = Tk_Display (Tk_MainWindow(interp));
1090   int xfd = XConnectionNumber (UIMdisplay);
1091   close(xfd);
1092 #endif // !defined(i386_unknown_nt4_0)
1093
1094   if ( execRetVal == -1 )
1095   {
1096     if ( errno == ENOENT )
1097       cerr << "Could not find executable to start remote shell." << endl;
1098     else
1099       cerr << "Could not start MPI on remote host." << endl;
1100   }
1101   
1102   P__exit(-1);
1103   return(false);
1104 }
1105
1106
1107 static bool startIrixMPI(const string     &machine, const string         &login,
1108                      const string         &name,    const string         &dir,
1109                      const vector<string> &argv,    const vector<string>  args,
1110                      daemonEntry          *de)
1111 {
1112 #if !defined(i386_unknown_nt4_0)
1113    if (DMstatus)   uiMgr->updateStatus(DMstatus,   "ready");
1114    if (PROCstatus) uiMgr->updateStatus(PROCstatus, "IRIX MPI");
1115
1116    string programNames;
1117    vector<string> cmdLineVec;
1118    
1119    if ( !getIrixMPICommandLine(argv, args, de, cmdLineVec, programNames) )
1120      return false;
1121    else
1122    {
1123       string newStatus;
1124       newStatus = string("program: ") + programNames + " ";
1125       newStatus += string("machine: ");
1126       if ( machine.length() )
1127          newStatus += machine;
1128       else
1129          newStatus += "(local_host)";
1130       newStatus += " ";
1131       newStatus += string("user: ");
1132       if ( login.length() )
1133          newStatus += login;
1134       else
1135          newStatus += "(self)";
1136       newStatus += " ";
1137       newStatus += string("daemon: ") + name + " ";
1138
1139       extern status_line* app_name;
1140       uiMgr->updateStatus(app_name, P_strdup(newStatus.string_of()));
1141    }
1142    
1143    if (fork()) return(true);
1144
1145    if (hostIsLocal(machine))
1146       return(execIrixMPI(dir, cmdLineVec));
1147    else
1148       return(rshIrixMPI(machine, login, dir, de, cmdLineVec));
1149 #else // !defined(i386_unknown_nt4_0)
1150    // TODO - implement this?
1151    return false;
1152 #endif // !defined(i386_unknown_nt4_0)
1153 }
1154
1155 /*
1156   Pick a unique name for the wrapper
1157 */
1158 string mpichNameWrapper( bool localMachine, const string& dir )
1159 {
1160         string rv;
1161         
1162
1163         if( localMachine )
1164         {
1165                 // the mpirun process is to be run on our machine - 
1166                 // we can use a local temp file
1167                 rv = tmpnam( NULL );
1168         }
1169         else
1170         {
1171                 // the mpirun process is to be run on a remote machine -
1172                 // we need to generate a name that is likely to be unique on
1173                 // the remote system
1174                 //
1175                 // our format for this name is
1176                 //
1177                 //   pdd-<name>-<pid>-<usec>
1178                 //
1179                 // where:
1180                 //   <name> is the network name of this machine (the one running the
1181                 //     front end process
1182                 //   <pid> is the process ID of the front-end process on that machine
1183                 //   <usec> is the result of gettimeofday() expressed in microseconds
1184                 // 
1185                 rv += dir;
1186                 rv += "/";
1187                 rv += "pdd-";
1188                 rv += getNetworkName();
1189                 rv += "-";
1190                 rv += getpid();
1191                 rv += "-";
1192
1193                 struct timeval tv;
1194                 gettimeofday( &tv, NULL );
1195                 rv += tv.tv_sec * 1000000 + tv.tv_usec;
1196         }
1197
1198         return rv;
1199 }
1200
1201
1202 #if !defined(i386_unknown_nt4_0)
1203
1204 /*
1205   Perform a cleanup when the frontend exits
1206 */
1207 bool
1208 mpichUnlinkWrappers()
1209 {
1210         bool rv=true;
1211         for (unsigned int i=0; i< paradynDaemon::wrappers.size(); i++)
1212         {
1213                 paradynDaemon::MPICHWrapperInfo& wrapper = paradynDaemon::wrappers[i];
1214                 
1215                 if( wrapper.isLocal )
1216                 {
1217                         // remove the local wrapper file
1218                         if (remove(wrapper.filename.string_of()) < 0) { // Best effort
1219                                 cerr << "mpichUnlinkWrappers: Failed to remove " 
1220                                          << wrapper.filename << ": " << strerror(errno) << endl;
1221                                 rv=false;
1222                         }
1223
1224                 }
1225                 else
1226                 {
1227                         // remove the remote wrapper file
1228                         bool removeFailed = false;
1229                         int pid = fork();
1230                         if( pid > 0 )
1231                         {
1232                                 // we're the parent - 
1233                                 // wait for our child's remove command to finish
1234                                 int rmStatus;
1235                                 if( (waitpid( pid, &rmStatus, 0 ) != pid) ||
1236                                         (rmStatus != 0) )
1237                                 {
1238                                         removeFailed = true;
1239                                         rv=false;
1240                                 }
1241                         }
1242                         else if( pid == 0 )
1243                         {
1244                                 // we're the child -
1245                                 // exec the remote command to remove the wrapper file
1246                                 string cmd;
1247                                 
1248                                 cmd += wrapper.remoteShell;
1249                                 cmd += " ";
1250                                 cmd += wrapper.remoteMachine;
1251                                 cmd += " rm ";
1252                                 cmd += wrapper.filename;
1253                                 execl( "/bin/sh",
1254                                         "sh",
1255                                         "-c",
1256                                         cmd.string_of(),
1257                                         NULL );
1258
1259                                 // if we reached here, our exec failed
1260                                 removeFailed = true;
1261                         }
1262                         else
1263                         {
1264                                 // our fork failed
1265                                 removeFailed = true;
1266                                 rv=false;
1267                         }
1268
1269                         if( removeFailed )
1270                         {
1271                                 cerr << "mpichUnlinkWrappers: Failed to remove " 
1272                                          << wrapper.filename << ": " << strerror(errno) << endl;
1273                         }
1274                 }
1275         }
1276         return rv;
1277 }
1278                                 
1279
1280
1281 bool
1282 writeMPICHWrapper( const string& fileName, const string& buffer )
1283 {
1284         FILE *f;
1285
1286         if ((f = fopen (fileName.string_of(), "wt")) == 0 ||
1287                 fputs (buffer.string_of(), f) < 0 ||
1288                 fclose (f) < 0 ||
1289                 chmod (fileName.string_of(), S_IRWXU) < 0) {
1290                 uiMgr->showError(113, "mpichCreateWrapper: I/O error");
1291                 return false;
1292         }
1293         return true;
1294 }
1295
1296
1297
1298 /*
1299   Create a script file which will start paradynd with appropriate
1300   parameters in a given directory. MPICH hides the user cmdline from
1301   slave processes until the MPI_Init() moment, so we pass these parameters
1302   in a script
1303 */
1304 bool mpichCreateWrapper(const string& machine,
1305                         bool                 localMachine,
1306                         const string&        script,
1307                         const char           *dir,
1308                         const string&        app_name,
1309                         const vector<string> args,
1310                         daemonEntry          *de)
1311 {
1312         const char *preamble = "#!/bin/sh\ncd ";
1313         string buffer;
1314         unsigned int j;
1315
1316         buffer = string(preamble) + string(dir) + 
1317                 string("\nPWD=") + string(dir) + string("; export PWD\n") + 
1318                 string(de->getCommand());
1319
1320         for (j=0; j < args.size(); j++) {
1321                 if (!strcmp(args[j].string_of(), "-l1")) {
1322                         buffer += " -l0";
1323                 }
1324                 else {
1325                         buffer += string(" ") + args[j];
1326                 }
1327         }
1328
1329         buffer += string(" -z") + string(de->getFlavor()) + 
1330                   string(" -runme ") + app_name +
1331                   string(" $*\n");
1332         
1333
1334
1335         if( localMachine )
1336         {
1337                 // the file named by script is our wrapper -
1338                 // write the script to the wrapper file
1339                 //
1340                 if( !writeMPICHWrapper( script, buffer ) )
1341                 {
1342                         return false;
1343                 }
1344
1345                 paradynDaemon::wrappers += paradynDaemon::MPICHWrapperInfo( script );
1346         }
1347         else
1348         {
1349                 // the file named by script is the wrapper's remote name -
1350                 // we write the script to a local temporary file so as to 
1351                 // copy it to the remote system
1352                 //
1353                 string localWrapper = tmpnam( NULL );
1354                 if( !writeMPICHWrapper( localWrapper, buffer ) )
1355                 {
1356                         return false;
1357                 }
1358
1359                 // try to copy the file to the desired location on the remote system
1360                 // ideally, we'd use execCmd() to execute this copy command on the local
1361                 // system, but execCmd has some side effects we'd rather not try
1362                 // to work around
1363                 //
1364                 bool copySucceeded = true;
1365                 int pid = fork();
1366                 if( pid > 0 )
1367                 {
1368                         // we are the parent - 
1369                         // wait for our child process (which will do the copy) to complete
1370                         int copyStatus;
1371                         if( waitpid( pid, &copyStatus, 0 ) != pid )
1372                         {
1373                                 cerr << "mpichCreateWrapper: Failed to copy temporary local wrapper " 
1374                              << localWrapper
1375                                  << " to remote system: " 
1376                                  << strerror(errno) 
1377                                  << endl;
1378                                 copySucceeded = false;
1379                         }
1380                         else
1381                         {
1382                                 if( copyStatus != 0 )
1383                                 {
1384                                         copySucceeded = false;
1385                                 }
1386                         }
1387                 }
1388                 else if( pid == 0 )
1389                 {
1390                         // we are the child - 
1391                         // we exec our copy command...
1392
1393                         // ...build the command string...
1394                         string cmd;
1395                         
1396                         cmd += de->getRemoteShell();
1397                         cmd += " ";
1398                         cmd += machine;                 // name of the remote system
1399                         cmd += " cat - \">\" ";
1400                         cmd += script;                  // name of wrapper on remote system
1401                         cmd += " \";\" chmod 755 ";
1402                         cmd += script;
1403                         cmd += " < ";
1404                         cmd += localWrapper;    // name of wrapper on local system
1405
1406
1407                         // ...exec a shell to execute it
1408                         execl( "/bin/sh",
1409                                 "sh",
1410                                 "-c",
1411                                 cmd.string_of(),
1412                                 NULL );
1413                         copySucceeded = false;  // if we got here, the exec failed
1414                 }
1415                 else
1416                 {
1417                         cerr << "mpichCreateWrapper: fork failed." << strerror(errno) << endl;
1418                         copySucceeded = false;
1419                 }
1420
1421                 // release the local temporary wrapper file
1422                 if( remove(localWrapper.string_of()) < 0 )
1423                 {
1424                         cerr << "mpichCreateWrapper: Failed to remove temporary local wrapper " 
1425                              << localWrapper
1426                                  << ": " 
1427                                  << strerror(errno) 
1428                                  << endl;
1429                 }
1430
1431                 if( copySucceeded )
1432                 {
1433                         // keep info around till later about script that needs to be removed
1434                         paradynDaemon::wrappers += paradynDaemon::MPICHWrapperInfo( script, machine, de->getRemoteShell() );
1435                 }
1436                 
1437                 if( !copySucceeded )
1438                 {
1439                         return false;
1440                 }
1441         }
1442
1443         return true;
1444 }
1445
1446 extern void appendParsedString(vector<string> &strList, const string &str);
1447
1448 /*
1449   Handle remote startup case
1450 */
1451 void mpichRemote(const string &machine, const string &login, 
1452                  const char *cwd, daemonEntry *de, 
1453                  vector<string> &params)
1454 {
1455         string rsh = de->getRemoteShellString();
1456
1457         if (rsh.length() > 0) {
1458                 appendParsedString(params, rsh);
1459         } else {
1460                 params += string("rsh");
1461         }
1462         if (login.length() != 0) {
1463                 params += string("-l");
1464                 params += login;
1465         }
1466         params += machine;
1467         params += string("cd");
1468         params += string(cwd);
1469         params += string(";");
1470 }
1471         
1472 struct known_arguments {
1473         char* name;
1474         bool has_value;
1475         bool supported;
1476 };
1477
1478 /*
1479   Split the command line into three parts:
1480   (mpirun arguments, the application name, application parameters)
1481   Insert the script name instead of the application name
1482 */
1483 bool mpichParseCmdline(const string& script, const vector<string> &argv,
1484                        string& app_name, vector<string> &params)
1485 {
1486         const unsigned int NKEYS = 34;
1487         struct known_arguments known[NKEYS] = {
1488                 {"-arch", true, true},
1489                 {"-h", false, false},
1490                 {"-machine", true, true},
1491                 {"-machinefile", true, true},
1492                 {"-np", true, true},
1493                 {"-nolocal", false, true},
1494                 {"-stdin", true, true},
1495                 {"-t", false, false},
1496                 {"-v", false, true},
1497                 {"-dbx", false, false},
1498                 {"-gdb", false, false},
1499                 {"-xxgdb", false, false},
1500                 {"-tv", false, false},
1501                 {"-batch", true, true},
1502                 {"-stdout", true, true},
1503                 {"-stderr", true, true},
1504                 {"-nexuspg", true, true},
1505                 {"-nexusdb", true, true},
1506                 {"-e", false, true},
1507                 {"-pg", false, true},
1508                 {"-leave_pg", false, true},
1509                 {"-p4pg", true, false},
1510                 {"-tcppg", true, false},
1511                 {"-p4ssport", true, true},
1512                 {"-mvhome", false, false},
1513                 {"-mvback", true, false},
1514                 {"-maxtime", true, true},
1515                 {"-nopoll", false, true},
1516                 {"-mem", true, true},
1517                 {"-cpu", true, true},
1518                 {"-cac", true, true},
1519                 {"-paragontype", true, true},
1520                 {"-paragonname", true, true},
1521                 {"-paragonpn", true, true}
1522         };
1523         bool app = false, found_mpirun = false;
1524         unsigned int i = 0, k;
1525
1526         while (i < argv.size() && !found_mpirun) {
1527                 found_mpirun = (strstr(argv[i].string_of(), "mpirun") != 0);
1528                 params += argv[i++];
1529         }
1530         if (!found_mpirun) {
1531                 uiMgr->showError(113, "Expected: \"mpirun <command>\"");
1532                 return false;
1533         }
1534         while (!app && i < argv.size()) {
1535                 app = true;
1536
1537                 for (k=0; k<NKEYS; k++) {
1538                         if (argv[i] == known[k].name) {
1539
1540                                 app = false;
1541
1542                                 if (!known[k].supported) {
1543                                         string msg = string("Argument \"") +
1544                                                 string(known[k].name) + 
1545                                                 string("\" is not supported");
1546                                         uiMgr->showError(113, strdup(msg.
1547                                                               string_of()));
1548                                         return false;
1549                                 }
1550
1551                                 params += argv[i++];
1552
1553                                 if (known[k].has_value) {
1554                                         // Skip the next arg
1555                                         if (i >= argv.size()) {
1556                                                 uiMgr->showError(113, "MPICH "
1557                                                    "command line parse error");
1558                                                 return false;
1559                                         }
1560                                         params += argv[i++];
1561                                 }
1562                                 break;
1563                         }
1564                 }
1565         }
1566         if (!app) {
1567                 uiMgr->showError(113, "MPICH command line parse error");
1568                 return false;
1569         }
1570
1571         params += script;
1572         app_name = argv[i++];
1573
1574         for (; i < argv.size(); i++) {
1575                 params += argv[i];
1576         }
1577
1578         return true;
1579 }
1580
1581 /*
1582   Initiate the MPICH startup process: start the master application
1583   under paradynd
1584 */
1585 static bool startMPICH(const string &machine, const string &login,
1586                        const string &/*name*/, const string &dir,
1587                        const vector<string> &argv, const vector<string> &args,
1588                        daemonEntry *de)
1589 {
1590         string app_name;
1591         vector<string> params;
1592         unsigned int i;
1593         char cwd[PATH_MAX];
1594         char **s;
1595         bool localMachine = hostIsLocal(machine);
1596
1597         if (DMstatus)   uiMgr->updateStatus(DMstatus,   "ready");
1598         if (PROCstatus) uiMgr->updateStatus(PROCstatus, "MPICH");
1599    
1600         if (dir.length()) {
1601                 strcpy(cwd, dir.string_of());
1602         } else {
1603                 getcwd(cwd, PATH_MAX);
1604         }
1605         if (!localMachine) {
1606                 // Prepend "rsh ..." to params
1607                 mpichRemote(machine, login, cwd, de, params);
1608         }
1609         string script = mpichNameWrapper( localMachine, cwd );
1610         if (!mpichParseCmdline(script, argv, app_name, params)) {
1611                 return false;
1612         }
1613         if (!mpichCreateWrapper(machine, localMachine, script, cwd, app_name, args, de)) {
1614                 return false;
1615         }
1616         if ((s = (char **)malloc((params.size() + 1) * sizeof(char *))) == 0) {
1617                 uiMgr->showError(113, "Out of memory");
1618                 return false;
1619         }
1620
1621         for (i=0; i<params.size(); i++) {
1622                 s[i] = strdup(params[i].string_of());
1623         }
1624         s[i] = 0;
1625
1626         if (fork()) {
1627                 return true;
1628         }
1629
1630         // Close Tk X connection to avoid conflicts with parent
1631         Display *UIMdisplay = Tk_Display (Tk_MainWindow(interp));
1632         int xfd = XConnectionNumber(UIMdisplay);
1633         close(xfd);
1634
1635         if (execvp(s[0], s) < 0) {
1636                 uiMgr->showError(113, "Failed to start MPICH");
1637         }
1638         return false;
1639 }
1640 #endif // !defined(i386_unknown_nt4_0)
1641
1642 // TODO: fix this
1643 //
1644 // add a new executable (binary) to a program.
1645 //
1646 bool paradynDaemon::newExecutable(const string &machine,
1647                                   const string &login,
1648                                   const string &name, 
1649                                   const string &dir, 
1650                                   const vector<string> &argv){
1651
1652    if (! DMstatus)
1653       DMstatus = new status_line("Data Manager");
1654
1655    if (! PROCstatus)
1656       PROCstatus = new status_line("Processes");
1657
1658    daemonEntry *def = findEntry(machine, name) ;
1659    if (!def) {
1660       if (name.length()) {
1661          string msg = string("Paradyn daemon \"") + name + string("\" not defined.");
1662          uiMgr->showError(90,P_strdup(msg.string_of()));
1663       }
1664       else {
1665          uiMgr->showError(91,"");
1666       }
1667       return false;
1668    }
1669
1670    if ( def->getFlavorString() == "mpi" )
1671    {
1672 #if defined(i386_unknown_nt4_0)
1673      string message = "Paradyn does not yet support MPI applications started from OS WinNT";
1674          
1675      uiMgr->showError(113, strdup(message.string_of()));
1676      return false;
1677 #else
1678       string os;
1679
1680       if (hostIsLocal(machine))
1681       {
1682           struct utsname unameInfo;
1683           if ( P_uname(&unameInfo) == -1 )
1684           {
1685               perror("uname");
1686               return false;
1687           }
1688
1689           os = unameInfo.sysname;
1690       }
1691       else
1692       {
1693           // get OS name through remote uname
1694           
1695           char comm[256];
1696           FILE* commStream;
1697           string remoteShell;
1698
1699           remoteShell = def->getRemoteShellString();
1700
1701           sprintf(comm, "%s%s%s %s uname -s", 
1702                   remoteShell.length() ? remoteShell.string_of() : "rsh",
1703                   login.length() ? " -l " : "",
1704                   login.length() ? login.string_of() : "",
1705                   machine.string_of());
1706
1707           commStream = P_popen(comm, "r");
1708
1709           bool foundOS = false;
1710       
1711           if ( commStream )
1712           {
1713               if ( !P_fgets(comm, 256, commStream) )
1714                   fclose(commStream);
1715               else
1716               {
1717                   os = comm;
1718                   fclose(commStream);
1719                   foundOS = true;
1720               }
1721           }
1722       
1723           if ( !foundOS )
1724           {
1725               uiMgr->showError(113, "Could not determine OS on remote host.");
1726               return false;
1727           }
1728       }
1729
1730       if ( os.prefixed_by("IRIX") )
1731       {
1732          return(startIrixMPI(machine, login, name, dir, argv, args, def));
1733       }
1734       else if ( os.prefixed_by ("AIX") )
1735       {
1736          return(startPOE(machine, login, name, dir, argv, args, def));
1737       }
1738       else
1739       {
1740          return(startMPICH(machine, login, name, dir, argv, args, def));
1741       }
1742 #endif
1743    }
1744    
1745    paradynDaemon *daemon;
1746    if ((daemon=getDaemonHelper(machine, login, name)) == (paradynDaemon*) NULL)
1747       return false;
1748
1749    performanceStream::ResourceBatchMode(batchStart);
1750    int pid = daemon->addExecutable(argv, dir);
1751    performanceStream::ResourceBatchMode(batchEnd);
1752
1753    // did the application get started ok?
1754    if (pid > 0 && !daemon->did_error_occur()) {
1755       // TODO
1756       char tmp_buf[80];
1757       sprintf (tmp_buf, "PID=%d", pid);
1758       uiMgr->updateStatus(PROCstatus, P_strdup(tmp_buf));
1759 #ifdef notdef
1760       executable *exec = new executable(pid, argv, daemon);
1761       paradynDaemon::programs += exec;
1762       ++procRunning;
1763 #endif
1764       return (true);
1765    } else {
1766       return(false);
1767    }
1768 }
1769
1770 bool paradynDaemon::attachStub(const string &machine,
1771                                const string &userName,
1772                                const string &cmd, // program name (full path)
1773                                int the_pid,
1774                                const string &daemonName,
1775                                int afterAttach // 0 --> as is, 1 --> pause, 2 --> run
1776                                ) {
1777   // Note: by this time, both the RUN and PAUSE buttons have been disabled in the
1778   // user interface...
1779
1780   if (! DMstatus)
1781       DMstatus = new status_line("Data Manager");
1782
1783   if (! PROCstatus)
1784       PROCstatus = new status_line("Processes");
1785
1786   paradynDaemon *daemon = getDaemonHelper(machine, userName, daemonName);
1787   if (daemon == NULL)
1788       return false;
1789
1790   char tmp_buf[128];
1791   sprintf (tmp_buf, "attaching to PID=%d...", the_pid);
1792   uiMgr->updateStatus(PROCstatus, P_strdup(tmp_buf));
1793
1794   performanceStream::ResourceBatchMode(batchStart);
1795   bool success = daemon->attach(cmd, the_pid, afterAttach);
1796   performanceStream::ResourceBatchMode(batchEnd);
1797
1798   if (daemon->did_error_occur())
1799      return false;
1800
1801   if (!success)
1802      return false;
1803
1804   sprintf (tmp_buf, "PID=%d", the_pid);
1805   uiMgr->updateStatus(PROCstatus, P_strdup(tmp_buf));
1806   return true; // success
1807 }
1808
1809 //
1810 // start the programs running.
1811 //
1812 bool paradynDaemon::startApplication()
1813 {
1814     executable *prog;
1815     for(unsigned i=0; i < programs.size(); i++){
1816         prog = programs[i];
1817         prog->controlPath->startProgram(prog->pid);   
1818     }
1819     return(true);
1820 }
1821
1822 //
1823 // pause all processes.
1824 //
1825 bool paradynDaemon::pauseAll()
1826 {
1827     paradynDaemon *pd;
1828     for(unsigned i = 0; i < paradynDaemon::allDaemons.size(); i++){
1829         pd = paradynDaemon::allDaemons[i];
1830         pd->pauseApplication();
1831     }
1832     // tell perf streams about change.
1833     performanceStream::notifyAllChange(appPaused);
1834     applicationState = appPaused;
1835     return(true);
1836 }
1837
1838 //
1839 // pause one processes.
1840 //
1841 bool paradynDaemon::pauseProcess(unsigned pid)
1842 {
1843     executable *exec = 0;
1844     for(unsigned i=0; i < programs.size(); i++){
1845         exec = programs[i];
1846         if(exec->pid == pid)
1847             break;
1848         exec = 0;
1849     }
1850     if (exec) {
1851         exec->controlPath->pauseProgram(exec->pid);
1852         return(true); 
1853     } else
1854         return (false);
1855 }
1856
1857 //Send message to each daemon to instrument the dynamic call
1858 //sites in function "func_name"
1859 bool paradynDaemon::AllMonitorDynamicCallSites(string func_name){
1860   paradynDaemon *pd;
1861   for(unsigned int i = 0; i < paradynDaemon::allDaemons.size(); i++)
1862     {
1863       pd = paradynDaemon::allDaemons[i];
1864       pd->MonitorDynamicCallSites(func_name);
1865     }
1866
1867   return true;
1868 }
1869
1870 //
1871 // continue all processes.
1872 //
1873 bool paradynDaemon::continueAll()
1874 {
1875     paradynDaemon *pd;
1876
1877     if (programs.size() == 0)
1878        return false; // no program to pause
1879
1880     if (procRunning == 0)
1881        return false;
1882
1883     for(int i = paradynDaemon::allDaemons.size()-1; i >= 0; i--) 
1884     {
1885         pd = paradynDaemon::allDaemons[i];
1886         pd->continueApplication();
1887     }
1888     // tell perf streams about change.
1889     performanceStream::notifyAllChange(appRunning);
1890     applicationState = appRunning;
1891     return(true);
1892 }
1893
1894 //
1895 // continue one processes.
1896 //
1897 bool paradynDaemon::continueProcess(unsigned pid)
1898 {
1899     executable *exec = 0;
1900     for(unsigned i=0; i < programs.size(); i++){
1901         exec = programs[i];
1902         if(exec->pid == pid && exec->controlPath == this)
1903             break;
1904         exec = 0;
1905     }
1906     if (exec) {
1907         exec->controlPath->continueProgram(exec->pid);
1908         return(true); 
1909     } else
1910         return (false);
1911 }
1912
1913 //
1914 // detach the paradyn tool from a running program.  This should clean all
1915 //   of the dynamic instrumentation that has been inserted.
1916 //
1917 bool paradynDaemon::detachApplication(bool pause)
1918 {
1919     executable *exec = 0;
1920     for(unsigned i=0; i < programs.size(); i++){
1921         exec = programs[i];
1922         exec->controlPath->detachProgram(exec->pid,pause);
1923     }
1924     return(true);
1925 }
1926
1927 //
1928 // print the status of each process.  This is used mostly for debugging.
1929 //
1930 void paradynDaemon::printStatus()
1931 {
1932     executable *exec = 0;
1933     for(unsigned i=0; i < programs.size(); i++){
1934         exec = programs[i];
1935         string status = exec->controlPath->getStatus(exec->pid);
1936             if (!exec->controlPath->did_error_occur()) {
1937                 cout << status << endl;
1938             }
1939     }
1940 }
1941
1942 //
1943 // Cause the passed process id to dump a core file.  This is also used for
1944 //    debugging.
1945 // If pid = -1, all processes will dump core files.
1946 //
1947 void paradynDaemon::dumpCore(int pid)
1948 {
1949     executable *exec = 0;
1950     for(unsigned i=0; i < programs.size(); i++){
1951         exec = programs[i];
1952         if ((exec->pid == (unsigned)pid) || (pid == -1)) {
1953             exec->controlPath->coreProcess(exec->pid);
1954             printf("found process and coreing it\n");
1955         }
1956     }
1957 }
1958
1959
1960 bool paradynDaemon::setInstSuppress(resource *res, bool newValue)
1961 {
1962     bool ret = false;
1963     paradynDaemon *pd;
1964     for(unsigned i = 0; i < paradynDaemon::allDaemons.size(); i++){
1965         pd = paradynDaemon::allDaemons[i];
1966         ret |= pd->setTracking(res->getHandle(), newValue);
1967     }
1968     return(ret);
1969 }
1970
1971 //
1972 // signal from daemon that is is about to start or end a set 
1973 // of new resource definitions
1974 //
1975 void paradynDaemon::resourceBatchMode(bool onNow){
1976     int prev = count;
1977     if (onNow) {
1978         count++;
1979     } else {
1980         assert(count > 0);
1981         count--;
1982     }
1983
1984     if (count == 0) {
1985         for(u_int i=0; i < allDaemons.size(); i++){
1986             (allDaemons[i])->reportResources();
1987         }
1988         performanceStream::ResourceBatchMode(batchEnd);
1989     } else if (!prev) {
1990         performanceStream::ResourceBatchMode(batchStart);
1991     }
1992 }
1993
1994 //
1995 //  reportResources:  send new resource ids to daemon
1996 //
1997 void  paradynDaemon::reportResources(){
1998     assert(newResourceTempIds.size() == newResourceHandles.size());
1999     resourceInfoResponse(newResourceTempIds, newResourceHandles);
2000     newResourceTempIds.resize(0);
2001     newResourceHandles.resize(0);
2002 }
2003
2004 //
2005 // upcall from paradynd reporting new resource
2006 //
2007 void paradynDaemon::resourceInfoCallback(u_int temporaryId,
2008                               vector<string> resource_name,
2009                               string abstr, u_int type) {
2010
2011     resourceHandle r = createResource(temporaryId, resource_name, abstr, type);
2012     if(!count){
2013       if (r != temporaryId) {
2014         vector<u_int>tempIds; vector<u_int>rIds;
2015         tempIds += temporaryId; rIds += r;
2016         resourceInfoResponse(tempIds, rIds);
2017       }
2018     }
2019     else {
2020         if (r != temporaryId) {
2021           newResourceTempIds += temporaryId;
2022           newResourceHandles += r;
2023           assert(newResourceTempIds.size() == newResourceHandles.size());
2024         }
2025     }
2026 }
2027
2028 void paradynDaemon::severalResourceInfoCallback(vector<T_dyninstRPC::resourceInfoCallbackStruct> items) {
2029    for (unsigned lcv=0; lcv < items.size(); lcv++)
2030       resourceInfoCallback(items[lcv].temporaryId,
2031                            items[lcv].resource_name,
2032                            items[lcv].abstraction,
2033                            items[lcv].type);
2034 }
2035
2036
2037 //
2038 // Get the expected delay (as a fraction of the running program) for the passed
2039 //   resource list (focus) and metric.
2040 //
2041 void paradynDaemon::getPredictedDataCostCall(perfStreamHandle ps_handle,
2042                                       metricHandle m_handle,
2043                                       resourceListHandle rl_handle,
2044                                       resourceList *rl, 
2045                                       metric *m,
2046                                       u_int clientID)
2047 {
2048     if(rl && m){
2049         vector<u_int> focus;
2050         bool aflag;
2051         aflag=rl->convertToIDList(focus);
2052         assert(aflag);
2053         const char *metName = m->getName();
2054         assert(metName);
2055         u_int requestId;
2056         if(performanceStream::addPredCostRequest(ps_handle,requestId,m_handle,
2057                                 rl_handle, paradynDaemon::allDaemons.size())){
2058             paradynDaemon *pd;
2059             for(unsigned i = 0; i < paradynDaemon::allDaemons.size(); i++){
2060                 pd = paradynDaemon::allDaemons[i];
2061                 pd->getPredictedDataCost(ps_handle,requestId,focus, 
2062                                          metName,clientID);
2063             }
2064             return;
2065         }
2066     }
2067     // TODO: change this to do the right thing
2068     // this should make the response upcall to the correct calling thread
2069     // perfConsult->getPredictedDataCostCallbackPC(0,0.0);
2070     assert(0);
2071 }
2072
2073 //
2074 // make data enable request to paradynds, and add request entry to
2075 // list of outstanding enable requests
2076 //
2077 void paradynDaemon::enableData(vector<metricInstance *> *miVec,
2078                                vector<bool> *done,
2079                                vector<bool> *enabled,
2080                                DM_enableType *new_entry,
2081                                bool need_to_enable){
2082
2083     // make enable request, pass only pairs that need to be enabled to daemons
2084     if(need_to_enable){  
2085         bool whole_prog_focus = false;
2086         vector<paradynDaemon*> daemon_subset; // which daemons to send request
2087         vector<T_dyninstRPC::focusStruct> foci; 
2088         vector<string> metrics; 
2089         vector<u_int> mi_ids;  
2090
2091         for(u_int i=0; i < miVec->size(); i++){
2092             if(!(*enabled)[i] && !(*done)[i]){
2093                 // create foci, metrics, and mi_ids entries for this mi
2094                 T_dyninstRPC::focusStruct focus;
2095                 string met_name;
2096                 bool aflag;
2097                 aflag=((*miVec)[i]->convertToIDList(focus.focus));
2098                 assert(aflag);
2099                 met_name = (*miVec)[i]->getMetricName();
2100                 foci += focus;
2101                 metrics += met_name;
2102                 mi_ids += (*miVec)[i]->getHandle();
2103                 // set curretly enabling flag on mi 
2104                 (*miVec)[i]->setCurrentlyEnabling();
2105
2106                 // check to see if this focus is refined on the machine
2107                 // or process heirarcy, if so then add the approp. daemon
2108                 // to the daemon_subset, else set whole_prog_focus to true
2109                 if(!whole_prog_focus){
2110                     string machine_name;
2111                     resourceList *rl = (*miVec)[i]->getresourceList(); 
2112                     assert(rl);
2113                     // focus is refined on machine or process heirarchy 
2114                     if(rl->getMachineNameReferredTo(machine_name)){
2115                         // get the daemon corr. to this focus and add it
2116                         // to the list of daemons
2117                         vector<paradynDaemon*> vpd = 
2118                                 paradynDaemon::machineName2Daemon(machine_name);
2119                         assert(vpd.size());
2120                         for(u_int j=0; j < vpd.size(); j++){
2121                           bool found = false;
2122
2123                           for(u_int k=0; k<daemon_subset.size() && !found; k++)
2124                             if(vpd[j]->id == daemon_subset[k]->id) 
2125                               found = true;    
2126
2127                           if(!found)  // add new daemon to subset list
2128                             daemon_subset += vpd[j];
2129                         }  
2130                     }
2131                     else {  // foucs is not refined on process or machine 
2132                         whole_prog_focus = true;
2133                     }
2134                 }
2135         } }
2136         assert(foci.size() == metrics.size());
2137         assert(metrics.size() == mi_ids.size());
2138         assert(daemon_subset.size() <= paradynDaemon::allDaemons.size());
2139         // if there is a whole_prog_focus then make the request to all 
2140         // the daemons, else make the request to the daemon subset
2141         // make enable requests to all daemons
2142         if(whole_prog_focus) {
2143             for(u_int j=0; j < paradynDaemon::allDaemons.size(); j++){
2144                paradynDaemon *pd = paradynDaemon::allDaemons[j]; 
2145                pd->enableDataCollection(foci,metrics,mi_ids,j,
2146                                      new_entry->request_id);
2147             }
2148         }
2149         else {  
2150             // change the enable number in the entry 
2151             new_entry->how_many = daemon_subset.size();
2152             for(u_int j=0; j < daemon_subset.size(); j++){
2153                daemon_subset[j]->enableDataCollection(foci,metrics,mi_ids,
2154                                  daemon_subset[j]->id,new_entry->request_id);
2155             }
2156         }
2157     }
2158     // add entry to outstanding_enables list
2159     paradynDaemon::outstanding_enables += new_entry;
2160     new_entry = 0; 
2161     miVec = 0;
2162     done = 0; 
2163     enabled = 0;
2164 }
2165
2166
2167 // propagateMetrics:
2168 // called when a new process is started, to propagate all enabled metrics to
2169 // the new process.  (QUESTION: should this include when a process makes
2170 // an exec syscall, thus 'starting' another process?)
2171 // Metrics are propagated only if the new process is the only process running 
2172 // on a daemon (this is why we don't need the pid here). If there are already
2173 // other processes running on a daemon, than it is up to the daemon to do the
2174 // propagation (we can't do it here because the daemon has to do the aggregation).
2175 // Calling this function has no effect if there are no metrics enabled.
2176 void paradynDaemon::propagateMetrics() {
2177
2178     vector<metricInstanceHandle> allMIHs = metricInstance::allMetricInstances.keys();
2179
2180     for (unsigned i = 0; i < allMIHs.size(); i++) {
2181
2182       metricInstance *mi = metricInstance::getMI(allMIHs[i]);
2183
2184       if (!mi->isEnabled())
2185          continue;
2186
2187       // first we must find if the daemon already has this metric enabled for
2188       // some process. In this case, we don't need to do anything, the
2189       // daemon will do the propagation by itself.
2190       bool found = false;
2191       for (unsigned j = 0; j < mi->components.size(); j++) {
2192          if (mi->components[j]->getDaemon() == this) {
2193             found = true;
2194             break;
2195          }
2196       }
2197
2198       if (found)
2199          continue; // we don't enable this mi; let paradynd do it
2200
2201       resourceListHandle r_handle = mi->getFocusHandle();
2202       metricHandle m_handle = mi->getMetricHandle();
2203       resourceList *rl = resourceList::getFocus(r_handle);
2204       metric *m = metric::getMetric(m_handle);
2205
2206       vector<u_int> vs;
2207       bool aflag = rl->convertToIDList(vs);
2208       assert(aflag);
2209
2210       int id = enableDataCollection2(vs, (const char *) m->getName(), mi->id);
2211
2212       if (id > 0 && !did_error_occur()) {
2213          component *comp = new component(this, id, mi);
2214          if (!mi->addComponent(comp)) {
2215             cout << "internal error in paradynDaemon::addRunningProgram" << endl;
2216             abort();
2217          }
2218       }
2219     }
2220 }
2221
2222
2223 bool paradynDaemon::setDefaultArgs(char *&name)
2224 {
2225   if (!name)
2226     name = strdup("defd");
2227   if (name)
2228     return true;
2229   else 
2230     return false;
2231 }
2232
2233
2234 bool daemonEntry::setAll (const string &m, const string &c, const string &n,
2235                           const string &l, const string &d, const string &r, const string &f)
2236 {
2237   if(!n.string_of() || !c.string_of())
2238       return false;
2239
2240   if (m.string_of()) machine = m;
2241   if (c.string_of()) command = c;
2242   if (n.string_of()) name = n;
2243   if (l.string_of()) login = l;
2244   if (d.string_of()) dir = d;
2245   if (r.string_of()) remote_shell = r;
2246   if (f.string_of()) flavor = f;
2247
2248   return true;
2249 }
2250 void daemonEntry::print() 
2251 {
2252   cout << "DAEMON ENTRY\n";
2253   cout << "  name: " << name << endl;
2254   cout << "  command: " << command << endl;
2255   cout << "  dir: " << dir << endl;
2256   cout << "  login: " << login << endl;
2257   cout << "  machine: " << machine << endl;
2258   cout << "  remote_shell: " << remote_shell << endl;
2259   cout << "  flavor: " << flavor << endl;
2260 }
2261
2262 #ifdef notdef
2263 int paradynDaemon::read(const void* handle, char *buf, const int len) {
2264   assert(0);
2265   int ret, err;
2266
2267   assert(len > 0);
2268   assert((int)handle<200);
2269   assert((int)handle >= 0);
2270   static vector<unsigned> fd_vect(200);
2271
2272   // must handle the msg_bind_buffered call here because xdr_read will be
2273   // called in the constructor for paradynDaemon, before the previous call
2274   // to msg_bind had been called
2275
2276   if (!fd_vect[(unsigned)handle]) {
2277     paradynDaemon *pd;
2278     for(unsigned i = 0; i < paradynDaemon::allDaemons.size(); i++){
2279         pd = paradynDaemon::allDaemons[i];
2280         if(pd->get_sock() == (int)handle)
2281             break;
2282     }
2283     if (!(pd))
2284       return -1;
2285
2286     msg_bind((int)handle, true, (int(*)(void*))xdrrec_eof,
2287                       (void*)(pd)->net_obj(), &???);
2288     fd_vect[(unsigned)handle] = 1;
2289   }
2290
2291   do {
2292     tag_t tag = MSG_TAG_FILE;
2293         thread_t ready_tid;
2294
2295     do
2296                 ready_tid = THR_TID_UNSPEC;
2297                 err = msg_poll(&ready_tid, &tag, true);
2298         while ((err != THR_ERR) && (ready_tid != (thread_t)handle))
2299     
2300     if (ready_tid == (thread_t) handle) {
2301       errno = 0;
2302       ret = P_read((int)handle, buf, len);
2303     } else 
2304       return -1;
2305   } while (ret < 0 && errno == EINTR);
2306
2307   if (ret <= 0)
2308     return (-1);
2309   else
2310     return ret;
2311 }
2312 #endif // notdef
2313
2314 void paradynDaemon::firstSampleCallback(int, double firstTime) {
2315   static bool done = false;
2316   if (!done) {
2317 //  cerr << "paradyn: welcome to firstSampleCallback; firstTime=" << firstTime << "; adjusted time=" << getAdjustedTime(firstTime) << endl;
2318
2319     setEarliestFirstTime(getAdjustedTime(firstTime));
2320   }
2321   done = true;
2322 }
2323
2324
2325 paradynDaemon::paradynDaemon(const string &m, const string &u, const string &c,
2326                            const string &r, const string &n, const string &f)
2327 : dynRPCUser(m, u, c, r, NULL, NULL, args, 1, dataManager::sock_desc),
2328   machine(m), login(u), command(c), name(n), flavor(f), activeMids(uiHash)
2329 {
2330   if (!this->errorConditionFound) {
2331     // No problems found in order to create this new daemon process - naim
2332     assert(m.length());
2333     assert(c.length());
2334     assert(n.length());
2335     assert(f.length());
2336
2337     // if c includes a pathname, lose the pathname
2338     const char *loc = P_strrchr(c.string_of(), '/');
2339     if (loc) {
2340       loc = loc + 1;
2341       command = loc;
2342     }
2343   
2344     if (machine.suffixed_by(local_domain)) {
2345         const unsigned namelength = machine.length()-local_domain.length()-1;
2346         const string localname = machine.substr(0,namelength);
2347         status = new status_line(localname.string_of(), status_line::PROCESS);
2348     } else
2349         status = new status_line(machine.string_of(), status_line::PROCESS);
2350     paradynDaemon *pd = this;
2351     paradynDaemon::allDaemons+=pd;
2352     id = paradynDaemon::allDaemons.size()-1;
2353     assert(paradynDaemon::allDaemons.size() > id); 
2354
2355     metCheckDaemonProcess(machine);
2356   }
2357   // else...we leave "errorConditionFound" for the caller to check...
2358   //        don't forget to check!
2359 }
2360
2361 // machine, name, command, flavor and login are set via a callback
2362 paradynDaemon::paradynDaemon(PDSOCKET use_sock)
2363 : dynRPCUser(use_sock, NULL, NULL, 1), flavor(0), activeMids(uiHash){
2364   if (!this->errorConditionFound) {
2365     // No problems found in order to create this new daemon process - naim 
2366     paradynDaemon *pd = this;
2367     paradynDaemon::allDaemons += pd;
2368     id = paradynDaemon::allDaemons.size()-1;
2369   }
2370   // else...we leave "errorConditionFound" for the caller to check...
2371   //        don't forget to check!
2372 }
2373
2374 bool our_print_sample_arrival = false;
2375 void printSampleArrivalCallback(bool newVal) {
2376    our_print_sample_arrival = newVal;
2377 }
2378
2379 // batched version of sampleCallbackFunc
2380 void paradynDaemon::batchSampleDataCallbackFunc(int ,
2381                 vector<T_dyninstRPC::batch_buffer_entry> theBatchBuffer)
2382 {
2383     // get the earliest first time that had been reported by any paradyn
2384     // daemon to use as the base (0) time
2385     bool aflag;
2386     aflag=(getEarliestFirstTime()>0);
2387     assert(aflag);
2388
2389     
2390     sampleVal_cerr << "batchSampleDataCallbackFunc(), burst size: " 
2391                    << theBatchBuffer.size() << "   earliestFirstTime: " 
2392                    << getEarliestFirstTime() << "\n";
2393     // Go through every item in the batch buffer we've just received and
2394     // process it.
2395     for (unsigned index=0; index < theBatchBuffer.size(); index++) {
2396         const T_dyninstRPC::batch_buffer_entry &entry = theBatchBuffer[index] ; 
2397
2398         unsigned mid          = entry.mid ;
2399         double startTimeStamp = entry.startTimeStamp ;
2400         double endTimeStamp   = entry.endTimeStamp ;
2401         double value          = entry.value ;
2402         u_int  weight         = entry.weight;
2403         //bool   internal_metric = entry.internal_met;
2404
2405         if (our_print_sample_arrival || sampleVal_cerr.isOn()) {
2406           cerr << "[" << index << "] mid " << mid << "-   from: " 
2407                << startTimeStamp << "  to: " << endTimeStamp << "   val: " 
2408                << value << "  weight: " << weight << "  machine: " 
2409                << machine.string_of() << "\n";
2410         }
2411
2412         startTimeStamp = 
2413             this->getAdjustedTime(startTimeStamp) - getEarliestFirstTime();
2414         endTimeStamp = 
2415             this->getAdjustedTime(endTimeStamp) - getEarliestFirstTime();
2416
2417         sampleVal_cerr << "   after intermediate adjustment-  " << "from: " 
2418                        << this->getAdjustedTime(startTimeStamp) << "  to: "
2419                        << this->getAdjustedTime(endTimeStamp) << "\n";
2420
2421         if (our_print_sample_arrival || sampleVal_cerr.isOn()) {
2422           cerr << "    after adjustment-  from: " << startTimeStamp << "  to: "
2423                << endTimeStamp << "  val: " << value << "\n";
2424         }
2425
2426         // Okay, the sample is not an error; let's process it.
2427         metricInstance *mi;
2428         bool found = activeMids.find(mid, mi);
2429         if (!found) {
2430            // this can occur due to asynchrony of enable or disable requests
2431            // so just ignore the data
2432            if (our_print_sample_arrival)
2433               cout << "ignoring that sample since it's no longer active" << endl;
2434
2435            continue;
2436         }
2437         assert(mi);
2438
2439         // Any sample sent by a daemon should not have the start time
2440         // less than lastSampleEnd for the aggregate sample. When a new
2441         // component is added to a metric, the first sample could have
2442         // the startTime less than lastSampleEnd. If this happens,
2443         // the daemon clock must be late (or the time adjustment
2444         // factor is not good enough), and so we must update
2445         // the time adjustment factor for this daemon.
2446         if (startTimeStamp < mi->aggSample.currentTime()) {
2447           timeStamp diff = mi->aggSample.currentTime() - startTimeStamp;
2448           startTimeStamp += diff;
2449           endTimeStamp += diff;
2450           this->setTimeFactor(this->getTimeFactor() + diff);
2451           //printf("*** Adjusting time for %s: diff = %f\n", this->machine.string_of(), diff);
2452         }
2453
2454         struct sampleInterval ret;
2455         if (mi->components.size()){
2456            // find the right component.
2457            component *part = 0;
2458            for(unsigned i=0; i < mi->components.size(); i++) {
2459               if(mi->components[i]->daemon == this){
2460                  part = mi->components[i];
2461                  // update the weight associated with this component
2462                  // this does not necessarily need to be updated with
2463                  // each new value as long as we can distinguish between
2464                  // internal and non-internal metric values in some way
2465                  // (internal metrics weight is 1 and regular metrics 
2466                  // weight is the number of processes for this daemon),
2467                  // and the weight is changed when the number of processes 
2468                  // changes (we are not currently doing this part)
2469                  //if(!internal_metric){
2470                  //    mi->num_procs_per_part[i] = weight;
2471                  //}
2472               }
2473            }
2474            if (!part) {
2475               uiMgr->showError(3, "");
2476               return;
2477               //exit(-1);
2478            }
2479
2480            // update the sampleInfo value associated with 
2481            // the daemon that sent the value 
2482            //
2483        
2484            assert(part->sample);
2485            if (!part->sample->firstValueReceived()) {
2486               //part->sample->startTime(startTimeStamp);
2487               part->sample->firstTimeAndValue(startTimeStamp, (float)0.0);
2488            }
2489
2490            part->sample->newValue(endTimeStamp, (float)value, weight);
2491         }
2492
2493         // don't aggregate if this metric is still being enabled (we may 
2494         // not have received replies for the enable requests from all the daemons)
2495         if (mi->isCurrentlyEnabling())
2496           continue;
2497
2498         //
2499         // update the metric instance sample value if there is a new
2500         // interval with data for all parts, otherwise this routine
2501         // returns false for ret.valid and the data cannot be bucketed
2502         // by the histograms yet (not all components have sent data for
2503         // this interval)
2504         // newValue will aggregate the parts according to mi's aggOp
2505         //
2506         ret = mi->aggSample.aggregateValues();
2507         
2508         if (ret.valid) {  // there is new data from all components 
2509            assert(ret.end >= 0.0);
2510            assert(ret.start >= 0.0);
2511            assert(ret.end >= ret.start);
2512            mi->enabledTime += ret.end - ret.start;
2513            sampleVal_cerr << "calling mi->addInterval- st:" << ret.start 
2514                           << "  end: " << ret.end << "  val: " << ret.value 
2515                           << "\n";
2516            mi->addInterval(ret.start, ret.end, ret.value, false);
2517         }
2518     } // the main for loop
2519 }
2520
2521 // trace data streams
2522 void paradynDaemon::batchTraceDataCallbackFunc(int ,
2523                 vector<T_dyninstRPC::trace_batch_buffer_entry> theTraceBatchBuffer)
2524 {
2525     // get the earliest first time that had been reported by any paradyn
2526     // daemon to use as the base (0) time
2527     // assert(getEarliestFirstTime());
2528
2529   // Just for debugging:
2530   //fprintf(stderr, "in DMdaemon.C, burst size = %d\n", theTraceBatchBuffer.size());
2531
2532     // Go through every item in the batch buffer we've just received and
2533     // process it.
2534     for (unsigned index =0; index < theTraceBatchBuffer.size(); index++) {
2535         T_dyninstRPC::trace_batch_buffer_entry &entry = theTraceBatchBuffer[index] ;
2536
2537         unsigned mid          = entry.mid ;
2538         unsigned length       = entry.length;
2539
2540         if (our_print_sample_arrival) {
2541             cout << "mid " << mid << " : length = " << length << "\n";
2542         }
2543
2544         // Okay, the sample is not an error; let's process it.
2545         metricInstance *mi;
2546         bool found = activeMids.find(mid, mi);
2547         if (!found) {
2548            // this can occur due to asynchrony of enable or disable requests
2549            // so just ignore the data
2550           continue;
2551         }
2552         assert(mi);
2553         byteArray *localTraceData = new byteArray(entry.traceRecord.getArray(),
2554         length);
2555         mi->sendTraceData(localTraceData->getArray(),length);
2556
2557         delete localTraceData;
2558
2559     } // the main for loop
2560 }
2561
2562 //
2563 // paradyn daemon should never go away.  This represents an error state
2564 //    due to a paradynd being killed for some reason.
2565 //
2566 // TODO -- handle this better
2567 paradynDaemon::~paradynDaemon() {
2568
2569 #ifdef notdef
2570     metricInstance *mi;
2571     HTable<metricInstance*> curr;
2572
2573     allDaemons.remove(this);
2574
2575     // remove the metric ID as required.
2576     for (curr = activeMids; mi = *curr; curr++) {
2577         mi->parts.remove(this);
2578         mi->components.remove(this);
2579     }
2580 #endif
2581     printf("Inconsistant state\n");
2582     abort();
2583 }
2584
2585 //
2586 // When an error is determined on an igen call, this function is
2587 // called, since the default error handler will exit, and we don't
2588 // want paradyn to exit.
2589 //
2590 void paradynDaemon::handle_error()
2591 {
2592    removeDaemon(this, true);
2593 }
2594
2595 //
2596 // When a paradynd is started remotely, ie not by paradyn, this upcall
2597 // reports the information for that paradynd to paradyn
2598 //
2599 // This must set command, name, machine and flavor fields
2600 // (pid no longer used --ari)
2601 //
2602 void 
2603 paradynDaemon::reportSelf (string m, string p, int /*pid*/, string flav)
2604 {
2605   flavor = flav;
2606   if (!m.length() || !p.length()) {
2607     removeDaemon(this, true);
2608     printf("paradyn daemon reported bad info, removed\n");
2609     // error
2610   } else {
2611     machine = m.string_of();
2612     command = p.string_of();
2613
2614     if (machine.suffixed_by(local_domain)) {
2615         const unsigned namelength = machine.length()-local_domain.length()-1;
2616         const string localname = machine.substr(0,namelength);
2617         status = new status_line(localname.string_of(), status_line::PROCESS);
2618     } else
2619         status = new status_line(machine.string_of(), status_line::PROCESS);
2620
2621     if(flavor == "pvm") {
2622       name = "pvmd";
2623     } else if(flavor == "unix") {
2624       name = "defd";
2625     } else if(flavor == "mpi") {
2626       name = "mpid";
2627     } else if (flavor == "winnt") {
2628       name = "winntd";
2629     } else {
2630       name = flavor;
2631     }
2632
2633         metCheckDaemonProcess( machine );
2634   }
2635
2636   // Send the initial metrics, constraints, and other neato things
2637   mdl_send(this);
2638   vector<T_dyninstRPC::metricInfo> info = this->getAvailableMetrics();
2639   unsigned size = info.size();
2640   for (unsigned u=0; u<size; u++)
2641       addMetric(info[u]);
2642
2643   getDaemonTime(this);
2644
2645   return;
2646 }
2647
2648 //
2649 // When a paradynd reports status, send the status to the user
2650 //
2651 void 
2652 paradynDaemon::reportStatus (string line)
2653 {
2654   if (status)
2655     uiMgr->updateStatus(status, P_strdup(line.string_of()));
2656 }
2657
2658 /***
2659  This call is used by a daemon to report a change in the status of a process
2660  such as when the process exits.
2661  When one process exits, we just decrement procRunning, a counter of the number
2662  of processes running. If procRunning is zero, there are no more processes running,
2663  and the status of the application is set to appExited.
2664 ***/
2665 void
2666 paradynDaemon::processStatus(int pid, u_int stat) {
2667   if (stat == procExited) { // process exited
2668     for(unsigned i=0; i < programs.size(); i++) {
2669         if ((programs[i]->pid == (unsigned)pid) && programs[i]->controlPath == this) {
2670         programs[i]->exited = true;
2671         if (--procRunning == 0)
2672           performanceStream::notifyAllChange(appExited);
2673         break;
2674         }
2675     }
2676   }
2677 }
2678  
2679
2680 // Called by a daemon when there is no more data to be sent for a metric
2681 // instance (because the processes have exited).
2682 void
2683 paradynDaemon::endOfDataCollection(int mid) {
2684   sampleVal_cerr << "endOfDataCollection-  mid: " << mid << "\n";
2685
2686     if(activeMids.defines(mid)){
2687         metricInstance *mi = activeMids[mid];
2688         assert(mi);
2689         bool aflag;
2690         aflag=(mi->removeComponent(this));
2691         assert(aflag);
2692     }
2693     else{  // check if this mid is for a disabled metric 
2694         bool found = false;
2695         for (unsigned ve=0; ve<disabledMids.size(); ve++) {
2696             if ((int) disabledMids[ve] == mid) {
2697                 found = true;
2698                 break;
2699             }
2700         }
2701         if (!found) {
2702             cout << "Ending data collection for unknown metric" << endl;
2703             uiMgr->showError (2, "Ending data collection for unknown metric");
2704         }
2705     }
2706 }