Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TPackMgr.cxx
Go to the documentation of this file.
1 // @(#)root/proof:$Id$
2 // Author: G. Ganis, Oct 2015
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TPackMgr
13 \ingroup proofkernel
14 
15 The PROOF package manager contains tools to manage packages.
16 This class has been created to eliminate duplications, and to allow for
17 standalone usage.
18 
19 */
20 
21 #include "TPackMgr.h"
22 
23 #include "TError.h"
24 #include "TFile.h"
25 #include "TFunction.h"
26 #include "THashList.h"
27 #include "TList.h"
28 #include "TMacro.h"
29 #include "TMD5.h"
30 #include "TMethodArg.h"
31 #include "TMethodCall.h"
32 #include "TObjString.h"
33 #include "TParameter.h"
34 #include "TMap.h"
35 #include "TProof.h" // for constants such as kRM and kLS.
36 #include "TROOT.h"
37 #include "TSystem.h"
38 
39 ClassImp(TPackMgr);
40 
41 
42 static void DefaultLogger(const char *msg) { Printf("%s", msg); }
43 
44 THashList *TPackMgr::fgGlobalPackMgrList = 0; // list of package managers for global packages
45 
46 ////////////////////////////////////////////////////////////////////////////////
47 /// Create a PROOF package manager
48 
49 TPackMgr::TPackMgr(const char *dir, const char *key)
50  : fLogger(DefaultLogger), fName(key), fDir(dir), fLock(dir), fEnabledPackages(0)
51 {
52  // Work with full names
53  if (gSystem->ExpandPathName(fDir))
54  Warning("TPackMgr", "problems expanding path '%s'", fDir.Data());
55  // The lock file in temp
56  TString lockname = TString::Format("%s/packdir-lock-%s",
57  gSystem->TempDirectory(), TString(fDir).ReplaceAll("/","%").Data());
58  fLock.SetName(lockname);
59 }
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 /// Destroy a TPackMgr instance
63 
64 TPackMgr::~TPackMgr()
65 {
66  // Destroy the lock file
67  if (fEnabledPackages) delete fEnabledPackages;
68 }
69 
70 ////////////////////////////////////////////////////////////////////////////////
71 /// Wrapper to notofuer / logger
72 
73 void TPackMgr::Log(const char *msg)
74 {
75  if (fLogger) {
76  if (fPfx.IsNull())
77  (*fLogger)(msg);
78  else
79  (*fLogger)(TString::Format("%s: %s", fPfx.Data(), msg));
80  }
81 }
82 
83 ////////////////////////////////////////////////////////////////////////////////
84 /// Method to build a package.
85 /// Return -1 on error, 0 otherwise
86 
87 Int_t TPackMgr::Build(const char *pack, Int_t opt)
88 {
89  Int_t rc = 0;
90 
91  TLockPathGuard lp(&fLock);
92 
93  if (gDebug > 0)
94  Info("Build", "building package %s ...", pack);
95 
96  TString ocwd = gSystem->WorkingDirectory();
97  TString pdir = TString::Format("%s/%s", fDir.Data(), pack);
98  gSystem->ChangeDirectory(pdir);
99 
100  // check for BUILD.sh and execute
101  if (!gSystem->AccessPathName("PROOF-INF/BUILD.sh")) {
102  // Notify the upper level
103  Log(TString::Format("building %s ...", pack));
104 
105  // Read version from file proofvers.txt, and if current version is
106  // not the same do a "BUILD.sh clean"
107  Bool_t goodver = kTRUE;
108  Bool_t savever = kFALSE;
109  TString v, r;
110  FILE *f = fopen("PROOF-INF/proofvers.txt", "r");
111  if (f) {
112  v.Gets(f);
113  r.Gets(f);
114  fclose(f);
115  if (opt == TPackMgr::kCheckROOT && v != gROOT->GetVersion()) goodver = kFALSE;
116  }
117  if (!f || !goodver) {
118  if (!gSystem->AccessPathName(pdir, kWritePermission)) {
119  savever = kTRUE;
120  Log(TString::Format("%s: version change"
121  " (current: %s, build: %s): cleaning ... ",
122  pack, gROOT->GetVersion(), v.Data()));
123  // Hard cleanup: go up the dir tree
124  gSystem->ChangeDirectory(fDir);
125  // remove package directory
126  gSystem->Exec(TString::Format("%s %s", kRM, pdir.Data()));
127  // find gunzip...
128  char *gunzip = gSystem->Which(gSystem->Getenv("PATH"), kGUNZIP,
129  kExecutePermission);
130  if (gunzip) {
131  TString par;
132  par.Form("%s.par", pdir.Data());
133  // untar package
134  TString cmd;
135  cmd.Form(kUNTAR3, gunzip, par.Data());
136  rc = gSystem->Exec(cmd);
137  if (rc != 0) {
138  Error("Build", "failure executing: %s", cmd.Data());
139  } else {
140  // Store md5 in package/PROOF-INF/md5.txt
141  TMD5 *md5local = TMD5::FileChecksum(par);
142  if (md5local) {
143  TString md5f = pdir + "/PROOF-INF/md5.txt";
144  TMD5::WriteChecksum(md5f, md5local);
145  // Go down to the package directory
146  gSystem->ChangeDirectory(pdir);
147  // Cleanup
148  SafeDelete(md5local);
149  } else {
150  Warning("Build", "failure calculating/saving MD5sum for '%s'", par.Data());
151  }
152  }
153  delete [] gunzip;
154  } else {
155  Error("Build", "%s not found", kGUNZIP);
156  rc = -1;
157  }
158  } else {
159  Log(TString::Format("%s: ROOT version inconsistency (current: %s, build: %s):"
160  " directory not writable: cannot re-build!!! ",
161  pack, gROOT->GetVersion(), v.Data()));
162  rc = -1;
163  }
164 
165  if (rc == 0) {
166  // To build the package we execute PROOF-INF/BUILD.sh via a pipe
167  // so that we can send back the log in (almost) real-time to the
168  // (impatient) client. Note that this operation will block, so
169  // the messages from builds on the workers will reach the client
170  // shortly after the master ones.
171  TString ipath(gSystem->GetIncludePath());
172  ipath.ReplaceAll("\"","");
173  TString cmd;
174  cmd.Form("export ROOTINCLUDEPATH=\"%s\" ; PROOF-INF/BUILD.sh", ipath.Data());
175  rc = gSystem->Exec(cmd);
176  if (rc != 0) {
177  Error("Build", "failure executing: %s", cmd.Data());
178  } else {
179  // Success: write version file
180  if (savever) {
181  f = fopen("PROOF-INF/proofvers.txt", "w");
182  if (f) {
183  fputs(gROOT->GetVersion(), f);
184  fputs(TString::Format("\n%s", gROOT->GetGitCommit()), f);
185  fclose(f);
186  }
187  }
188  }
189  }
190  } else {
191  // Notify the user
192  if (gDebug > 0)
193  Info("Build", "no PROOF-INF/BUILD.sh found for package %s", pack);
194  }
195  }
196  // Always return to the initial directory
197  gSystem->ChangeDirectory(ocwd);
198 
199  return rc;
200 }
201 
202 ////////////////////////////////////////////////////////////////////////////////
203 /// Method to load a package taking an option const char *
204 /// Return -1 on error, 0 otherwise
205 
206 Int_t TPackMgr::Load(const char *pack, const char *opts)
207 {
208  TList *optls = new TList;
209  optls->Add(new TObjString(opts));
210  Int_t rc = Load(pack, optls);
211  optls->SetOwner();
212  delete optls;
213  return rc;
214 }
215 
216 ////////////////////////////////////////////////////////////////////////////////
217 /// Method to load a package taking an option list
218 /// Return -1 on error, 0 otherwise
219 
220 Int_t TPackMgr::Load(const char *pack, TList *optls)
221 {
222  Int_t rc = 0;
223  TString emsg;
224 
225  // If already loaded don't do it again
226  if (fEnabledPackages && fEnabledPackages->FindObject(pack)) {
227  Log(TString::Format("error: TPackMgr::Load: package %s already loaded", pack));
228  return 0;
229  }
230 
231  // Which pack mgr has the package?
232  if (!Has(pack)) {
233  // Check the global packages
234  TPackMgr *packmgr = 0;
235  if (!(packmgr = TPackMgr::GetPackMgr(pack, nullptr))) {
236  // Package not found
237  Log(TString::Format("error: TPackMgr::Load: failure locating %s ...", pack));
238  return -1;
239  }
240  // Load from there
241  return packmgr->Load(pack, optls);
242  }
243 
244  // We have the package
245  TString pdir = TString::Format("%s/%s", fDir.Data(), pack);
246 
247  // Check dependencies
248  TString deps = TString::Format("%s/PROOF-INF/depends", pdir.Data());
249  if (!gSystem->AccessPathName(deps)) {
250  TMacro mdeps("deps");
251  if (mdeps.ReadFile(deps) > 0) {
252  Log(TString::Format("info: TPackMgr::Load: checking dependencies for package %s ...", pack));
253  TIter nxl(mdeps.GetListOfLines());
254  TObjString *os = 0;
255  while ((os = (TObjString *)nxl())) {
256  if (!TPackMgr::IsEnabled(os->GetName(), this)) {
257  if (Load(os->GetName(), optls) < 0) {
258  // Package loading failure
259  Log(TString::Format("error: TPackMgr::Load: failure loading dep %s ...", os->GetName()));
260  return -1;
261  }
262  }
263  }
264  }
265  }
266 
267  // Make sure it has been build
268  Int_t chkveropt = kCheckROOT;
269  if (optls) {
270  TParameter<Int_t> *pcv = (TParameter<Int_t> *) optls->FindObject("PROOF_Package_CheckVersion");
271  if (pcv) {
272  chkveropt = pcv->GetVal();
273  optls->Remove(pcv);
274  delete pcv;
275  }
276  }
277  if (Build(pack, chkveropt) < 0) {
278  // Package not found
279  Log(TString::Format("error: TPackMgr::Load: package %s oes not build! ", pack));
280  return -1;
281  }
282 
283  TString ocwd = gSystem->WorkingDirectory();
284  gSystem->ChangeDirectory(pdir);
285 
286  // Shared lock from here
287  TLockPathGuard lp(&fLock, kTRUE);
288 
289  // Check for SETUP.C and execute
290  if (!gSystem->AccessPathName("PROOF-INF/SETUP.C")) {
291  // We need to change the name of the function to avoid problems when we load more packages
292  TString setup;
293  setup.Form("SETUP_%d_%x", gSystem->GetPid(), TString(pack).Hash());
294  // Remove special characters
295  TMacro setupmc("PROOF-INF/SETUP.C");
296  TObjString *setupline = setupmc.GetLineWith("SETUP(");
297  if (setupline) {
298  TString setupstring(setupline->GetString());
299  setupstring.ReplaceAll("SETUP(", TString::Format("%s(", setup.Data()));
300  setupline->SetString(setupstring);
301  } else {
302  // Macro does not contain SETUP()
303  Log(TString::Format("warning: macro '%s/PROOF-INF/SETUP.C' does not contain a SETUP()"
304  " function", pack));
305  }
306 
307  // Load the macro
308  if (!setupmc.Load()) {
309  // Macro could not be loaded
310  Log(TString::Format("error: macro '%s/PROOF-INF/SETUP.C' could not be loaded:"
311  " cannot continue", pack));
312  rc = -1;
313  } else {
314  // Check the signature
315  TFunction *fun = (TFunction *) gROOT->GetListOfGlobalFunctions()->FindObject(setup);
316  if (!fun) {
317  // Notify the upper level
318  Log(TString::Format("error: function SETUP() not found in macro '%s/PROOF-INF/SETUP.C':"
319  " cannot continue", pack));
320  rc = -1;
321  } else {
322  TMethodCall callEnv;
323  // Check the number of arguments
324  if (fun->GetNargs() == 0) {
325  // No arguments (basic signature)
326  callEnv.Init(fun);
327  if (optls && optls->GetSize() > 0) {
328  Log(TString::Format("warning: loaded SETUP() for '%s' does not take any argument:"
329  " the specified argument will be ignored", pack));
330  }
331  } else if (fun->GetNargs() == 1) {
332  TMethodArg *arg = (TMethodArg *) fun->GetListOfMethodArgs()->First();
333  if (arg) {
334  callEnv.Init(fun);
335  // Check argument type
336  TString argsig(arg->GetTitle());
337  if (argsig.BeginsWith("TList")) {
338  callEnv.ResetParam();
339  callEnv.SetParam((Long_t) optls);
340  } else if (argsig.BeginsWith("const char")) {
341  callEnv.ResetParam();
342  TObjString *os = optls ? dynamic_cast<TObjString *>(optls->First()) : 0;
343  if (os) {
344  callEnv.SetParam((Long_t) os->GetName());
345  } else {
346  if (optls && optls->First()) {
347  Log(TString::Format("warning: found object argument of type %s:"
348  " SETUP expects 'const char *': ignoring",
349  optls->First()->ClassName()));
350  }
351  callEnv.SetParam((Long_t) 0);
352  }
353  } else {
354  // Notify the upper level
355  Log(TString::Format("error: unsupported SETUP signature: SETUP(%s)"
356  " cannot continue", arg->GetTitle()));
357  rc = -1;
358  }
359  } else {
360  // Notify the upper level
361  Log("error: cannot get information about the SETUP() argument:"
362  " cannot continue");
363  rc = -1;
364  }
365  } else if (fun->GetNargs() > 1) {
366  // Notify the upper level
367  Log("error: function SETUP() can have at most a 'TList *' argument:"
368  " cannot continue");
369  rc = -1;
370  }
371  // Execute
372  Long_t setuprc = (rc == 0) ? 0 : -1;
373  if (rc == 0) {
374  callEnv.Execute(setuprc);
375  if (setuprc < 0) rc = -1;
376  }
377  }
378  }
379  }
380 
381  gSystem->ChangeDirectory(ocwd);
382 
383  if (rc == 0) {
384  // create link to package in working directory
385  gSystem->Symlink(pdir, pack);
386 
387  // add package to list of include directories to be searched
388  // by ACliC
389  gSystem->AddIncludePath(TString::Format("-I%s", pack));
390 
391  // add package to list of include directories to be searched by CINT
392  gROOT->ProcessLine(TString::Format(".I %s", pack));
393 
394  TPair *pck = (optls && optls->GetSize() > 0) ? new TPair(new TObjString(pack), optls->Clone())
395  : new TPair(new TObjString(pack), 0);
396  if (!fEnabledPackages) {
397  fEnabledPackages = new TList;
398  fEnabledPackages->SetOwner();
399  }
400  fEnabledPackages->Add(pck);
401  }
402 
403  return rc;
404 }
405 
406 
407 ////////////////////////////////////////////////////////////////////////////////
408 /// Method to unload a package.
409 /// Return -1 on error, 0 otherwise
410 
411 Int_t TPackMgr::Unload(const char *pack)
412 {
413  Int_t rc = 0;
414 
415  if (fEnabledPackages && fEnabledPackages->GetSize() > 0) {
416  TPair *ppack = 0;
417  if (pack && strlen(pack) > 0) {
418  if ((ppack = (TPair *) fEnabledPackages->FindObject(pack))) {
419 
420  // Remove entry from include path
421  TString aclicincpath = gSystem->GetIncludePath();
422  TString cintincpath = gInterpreter->GetIncludePath();
423  // remove interpreter part of gSystem->GetIncludePath()
424  aclicincpath.Remove(aclicincpath.Length() - cintincpath.Length() - 1);
425  // remove package's include path
426  aclicincpath.ReplaceAll(TString(" -I") + pack, "");
427  gSystem->SetIncludePath(aclicincpath);
428 
429  //TODO reset interpreter include path
430 
431  // remove entry from enabled packages list
432  delete fEnabledPackages->Remove(ppack);
433  }
434 
435  // Cleanup the link, if there
436  if (!gSystem->AccessPathName(pack))
437  if (gSystem->Unlink(pack) != 0) rc = -1;
438 
439  } else {
440 
441  // Iterate over packages and remove each package
442  TIter nxp(fEnabledPackages);
443  while ((ppack = (TPair *) nxp())) {
444  if (Unload(ppack->GetName()) != 0) rc = -1;
445  }
446 
447  }
448  }
449 
450  // We are done
451  return rc;
452 }
453 
454 ////////////////////////////////////////////////////////////////////////////////
455 /// Method to check if this package manager has package 'pack'.
456 /// Return kTRUE or kFALSE
457 
458 Bool_t TPackMgr::Has(const char *pack)
459 {
460  // always follows BuildPackage so no need to check for PROOF-INF
461  TString pdir = TString::Format("%s/%s", fDir.Data(), pack);
462 
463  // Shared lock from here
464  TLockPathGuard lp(&fLock, kTRUE);
465 
466  if (gSystem->AccessPathName(pdir, kReadPermission) ||
467  gSystem->AccessPathName(pdir + "/PROOF-INF", kReadPermission))
468  return kFALSE;
469 
470  // Relevant directories exist and ar readable
471  return kTRUE;
472 }
473 
474 ////////////////////////////////////////////////////////////////////////////////
475 /// Method to check if 'path' is in the managed directory
476 /// Return kTRUE or kFALSE
477 
478 Bool_t TPackMgr::IsInDir(const char *path)
479 {
480  return strncmp(fDir.Data(), path, fDir.Length()) ? kFALSE : kTRUE ;
481 }
482 
483 ////////////////////////////////////////////////////////////////////////////////
484 /// Method to get the path of the dir for package 'pack'.
485 /// Return -1 in case of error (not found), 0 otherwise
486 
487 Int_t TPackMgr::GetPackDir(const char *pack, TString &pdir)
488 {
489  // Make sure the extension is not ".par"
490  TString pn(pack);
491  if (strstr(pack, ".par")) pn.Remove(pn.Last('.'));
492  pdir.Form("%s/%s", fDir.Data(), pn.Data());
493  if (gSystem->AccessPathName(pdir, kReadPermission)) return -1;
494  return 0;
495 }
496 
497 ////////////////////////////////////////////////////////////////////////////////
498 /// Method to get a semi-colon separated list with the names of the enabled
499 /// packages.
500 
501 void TPackMgr::GetEnabledPackages(TString &packlist)
502 {
503  packlist = "";
504  if (!fEnabledPackages) return;
505 
506  TIter nxp(fEnabledPackages);
507  TPair *pck= 0;
508  while ((pck = (TPair *)nxp())) {
509  if (packlist.Length() <= 0)
510  packlist = pck->GetName();
511  else
512  packlist += TString::Format(";%s", pck->GetName());
513  }
514  return;
515 }
516 
517 ////////////////////////////////////////////////////////////////////////////////
518 /// Method to get the path of the PAR file for package 'pack'.
519 /// Return -1 in case of error (not found), 0 otherwise
520 
521 Int_t TPackMgr::GetParPath(const char *pack, TString &path)
522 {
523  // Make sure the extension is ".par"
524  const char *fm = (strstr(pack, ".par")) ? "%s/%s" : "%s/%s.par";
525  path.Form(fm, fDir.Data(), pack);
526  if (gSystem->AccessPathName(path, kReadPermission)) return -1;
527  return 0;
528 }
529 
530 ////////////////////////////////////////////////////////////////////////////////
531 /// Method to get the download dir; create if not existing
532 /// Return -1 in case of error (not found; not created), 0 otherwise
533 
534 Int_t TPackMgr::GetDownloadDir(TString &dldir)
535 {
536  dldir.Form("%s/downloaded", fDir.Data());
537  if (gSystem->AccessPathName(dldir, kReadPermission)) {
538  if (gSystem->mkdir(dldir, kTRUE) != 0) return -1;
539  if (gSystem->AccessPathName(dldir, kReadPermission)) return -1;
540  }
541  return 0;
542 }
543 
544 ////////////////////////////////////////////////////////////////////////////////
545 /// Show available packages
546 ///
547 
548 void TPackMgr::Show(const char *title)
549 {
550  if (fgGlobalPackMgrList && fgGlobalPackMgrList->GetSize() > 0) {
551  // Scan the list of global packages dirs
552  TIter nxpm(fgGlobalPackMgrList);
553  TPackMgr *pm = 0;
554  while ((pm = (TPackMgr *)nxpm())) {
555  pm->Show(TString::Format("*** Global Package cache %s %s:%s ***\n",
556  pm->GetName(), gSystem->HostName(), pm->GetTitle()));
557  }
558  }
559 
560  if (title && strlen(title) > 0)
561  printf("%s\n", title);
562  else
563  printf("*** Package cache %s:%s ***\n", gSystem->HostName(), fDir.Data());
564  fflush(stdout);
565  // Shared lock from here
566  TLockPathGuard lp(&fLock, kTRUE);
567  gSystem->Exec(TString::Format("%s %s", kLS, fDir.Data()));
568  printf("\n");
569 }
570 
571 ////////////////////////////////////////////////////////////////////////////////
572 /// Clean dir for package 'pack'
573 /// Return -1 in case of error, 0 otherwise
574 ///
575 
576 Int_t TPackMgr::Clean(const char *pack)
577 {
578  // Shared lock from here
579  TLockPathGuard lp(&fLock);
580  Int_t rc = 0;
581  if (pack && strlen(pack)) {
582  // remove package directory and par file
583  rc = gSystem->Exec(TString::Format("%s %s/%s/*", kRM, fDir.Data(), pack));
584  }
585  return rc;
586 }
587 
588 ////////////////////////////////////////////////////////////////////////////////
589 /// Remove package 'pack'
590 /// If 'pack' is null or empty all packages are cleared
591 ///
592 
593 Int_t TPackMgr::Remove(const char *pack, Bool_t dolock)
594 {
595  // Shared lock from here
596  if (dolock) fLock.Lock();
597  Int_t rc1 = 0, rc2 = 0, rc3 = 0;
598  if (pack && strlen(pack)) {
599  // remove package directory and par file
600  TString path = TString::Format("%s/downloaded/%s.par", fDir.Data(), pack);
601  gSystem->Exec(TString::Format("%s %s", kRM, path.Data()));
602  if (!gSystem->AccessPathName(path, kFileExists)) rc1 = -1;
603  path.ReplaceAll("/downloaded/", "/");
604  gSystem->Exec(TString::Format("%s %s", kRM, path.Data()));
605  if (!gSystem->AccessPathName(path, kFileExists)) rc2 = -1;
606  path.Remove(path.Last('.'));
607  gSystem->Exec(TString::Format("%s %s", kRM, path.Data()));
608  if (!gSystem->AccessPathName(path, kFileExists)) rc3 = -1;
609  } else {
610  // Clear all packages
611  rc1 = gSystem->Exec(TString::Format("%s %s/*", kRM, fDir.Data()));
612  }
613  if (dolock) fLock.Unlock();
614  return (rc1 + rc2 + rc3);
615 }
616 
617 ////////////////////////////////////////////////////////////////////////////////
618 /// Get list of available packages
619 /// Returns a pointer to a TList object, transferring ownership to the caller
620 
621 TList *TPackMgr::GetList() const
622 {
623  TList *plist = new TList;
624  void *dir = gSystem->OpenDirectory(fDir);
625  if (dir) {
626  TString pac(gSystem->GetDirEntry(dir));
627  while (pac.Length() > 0) {
628  if (pac.EndsWith(".par")) {
629  pac.ReplaceAll(".par","");
630  plist->Add(new TObjString(pac.Data()));
631  }
632  pac = gSystem->GetDirEntry(dir);
633  }
634  }
635  gSystem->FreeDirectory(dir);
636 
637  return plist;
638 }
639 
640 ////////////////////////////////////////////////////////////////////////////////
641 /// Get list of enabled packages
642 /// Returns a pointer to a TList object, transferring ownership to the caller
643 
644 TList *TPackMgr::GetListOfEnabled() const
645 {
646  TList *epl = nullptr;
647  if (fEnabledPackages && fEnabledPackages->GetSize() > 0) {
648  epl = new TList;
649  TIter nxp(fEnabledPackages);
650  TObject *o = 0;
651  while ((o = nxp())) {
652  epl->Add(new TObjString(o->GetName()));
653  }
654  }
655  return epl;
656 }
657 
658 ////////////////////////////////////////////////////////////////////////////////
659 /// Show enabled packages
660 ///
661 
662 void TPackMgr::ShowEnabled(const char *title)
663 {
664  if (fgGlobalPackMgrList && fgGlobalPackMgrList->GetSize() > 0) {
665  // Scan the list of global packages dirs
666  TIter nxpm(fgGlobalPackMgrList);
667  TPackMgr *pm = 0;
668  while ((pm = (TPackMgr *)nxpm())) {
669  pm->ShowEnabled(TString::Format("*** Global Package cache %s %s:%s ***\n",
670  pm->GetName(), gSystem->HostName(), pm->GetTitle()));
671  }
672  }
673 
674  if (!fEnabledPackages || fEnabledPackages->GetSize() <= 0) return;
675 
676  if (title && strlen(title) > 0)
677  printf("%s\n", title);
678  else
679  printf("*** Package enabled on %s ***\n", gSystem->HostName());
680  fflush(stdout);
681 
682  TIter next(fEnabledPackages);
683  while (TPair *pck = (TPair *) next()) {
684  printf("%s\n", pck->GetName());
685  }
686 }
687 
688 ////////////////////////////////////////////////////////////////////////////////
689 /// Get MD5 checksum of the PAR file corresponding to given package
690 /// Returns a pointer to a TMD5 object, transferring ownership to the caller
691 
692 TMD5 *TPackMgr::GetMD5(const char *pack)
693 {
694  // Shared lock from here
695  TLockPathGuard lp(&fLock, kTRUE);
696  // PAR file path
697  const char *fm = (strstr(pack, ".par")) ? "%s/%s" : "%s/%s.par";
698  TString parfile = TString::Format(fm, fDir.Data(), pack);
699 
700  return TMD5::FileChecksum(parfile);
701 }
702 
703 
704 ////////////////////////////////////////////////////////////////////////////////
705 /// Read MD5 checksum of the PAR file from the PROOF-INF/md5.txt file.
706 /// Returns a pointer to a TMD5 object, transferring ownership to the caller
707 
708 TMD5 *TPackMgr::ReadMD5(const char *pack)
709 {
710  TString pn(pack);
711  if (pn.EndsWith(".par")) pn.Remove(pn.Last('.'));
712 
713  TString md5f = TString::Format("%s/%s/PROOF-INF/md5.txt", fDir.Data(), pn.Data());
714  TLockPathGuard lp(&fLock, kTRUE);
715  return TMD5::ReadChecksum(md5f);
716 }
717 
718 
719 ////////////////////////////////////////////////////////////////////////////////
720 /// Read MD5 checksum of the PAR file from the PROOF-INF/md5.txt file.
721 /// Returns a pointer to a TMD5 object, transferring ownership to the caller
722 
723 Int_t TPackMgr::Unpack(const char *pack, TMD5 *sum)
724 {
725  Int_t rc = 0;
726  TString fn(pack), pn(pack);
727  if (!fn.EndsWith(".par")) fn += ".par";
728  if (pn.EndsWith(".par")) pn.Remove(pn.Last('.'));
729 
730  // Find gunzip...
731  char *gunzip = gSystem->Which(gSystem->Getenv("PATH"), kGUNZIP, kExecutePermission);
732  if (gunzip) {
733  // untar package
734  TString cmd;
735  cmd.Form(kUNTAR, gunzip, fDir.Data(), fn.Data(), fDir.Data());
736  rc = gSystem->Exec(cmd);
737  if (rc != 0)
738  Error("Unpack", "failure executing: %s (rc: %d)", cmd.Data(), rc);
739  delete [] gunzip;
740  } else {
741  Error("Unpack", "%s not found", kGUNZIP);
742  rc = -2;
743  }
744  // check that fDir/pack now exists
745  if (gSystem->AccessPathName(TString::Format("%s/%s", fDir.Data(), pn.Data()), kWritePermission)) {
746  // par file did not unpack itself in the expected directory, failure
747  rc = -1;
748  Error("Unpack", "package %s did not unpack into %s", fn.Data(), pn.Data());
749  } else {
750  // store md5 in package/PROOF-INF/md5.txt
751  if (sum) {
752  TString md5f = TString::Format("%s/%s/PROOF-INF/md5.txt", fDir.Data(), pn.Data());
753  TMD5::WriteChecksum(md5f, sum);
754  }
755  }
756 
757  return rc;
758 }
759 
760 ////////////////////////////////////////////////////////////////////////////////
761 /// Install package from par (unpack the file in the directory); par can be an
762 /// URL for remote retrieval. If rmold is kTRUE an existing version of the package
763 /// is removed if existing.
764 /// Returns 0 on success, <0 otherwise
765 
766 Int_t TPackMgr::Install(const char *parpath, Bool_t rmold)
767 {
768  Int_t rc = 0;
769 
770  Info("Install", "installing %s ...", parpath);
771  const char *par = gSystem->ExpandPathName(parpath);
772 
773  // Does par exists?
774  if (gSystem->AccessPathName(par, kReadPermission)) {
775  Error("Install", "%s is invalid", par);
776  return -1;
777  }
778  TString parname = gSystem->BaseName(par);
779  TString pack = parname(0, parname.Last('.'));
780  TString dest = TString::Format("%s/%s", fDir.Data(), parname.Data());
781  TString psrc = par, ssrc;
782  TMD5 *sums = 0, *md5 = 0, *md5d = 0;
783 
784  // Check if we need to download: get the remote checksum
785  // Retrieve the checksum of the file, if available
786  // Dowload checksum file, if available
787  TString dldir;
788  if (GetDownloadDir(dldir) != 0) {
789  Error("Install", "could not create/get download directory");
790  return -1;
791  }
792 
793  TLockPathGuard lp(&fLock, kFALSE);
794 
795  TString parsum(par);
796  parsum.ReplaceAll(".par", ".md5sum");
797  if (!gSystem->AccessPathName(parsum, kReadPermission)) {
798  ssrc.Form("%s/%s", dldir.Data(), gSystem->BaseName(parsum));
799  if (!TFile::Cp(parsum, ssrc)) {
800  Warning("Install", "could not retrieve %s", parsum.Data());
801  } else {
802  md5 = TMD5::ReadChecksum(ssrc);
803  }
804  }
805 
806  // Do we have already the file?
807  Bool_t parexists = (!gSystem->AccessPathName(dest)) ? kTRUE : kFALSE;
808 
809  Bool_t install = kTRUE;
810  // If yes and we are asked to clean the old one, do it
811  if (parexists) {
812  install = kFALSE;
813  if (rmold) {
814  // Asked to remove: do it
815  if (Remove(pack, kFALSE) < 0) {
816  Error("Install", "could not remove existing version of '%s'", pack.Data());
817  if (md5) delete md5;
818  return -1;
819  }
820  install = kTRUE;
821  } else {
822  if (!md5) {
823  TFile::EFileType ft = TFile::GetType(par);
824  if (ft == TFile::kWeb || ft == TFile::kNet) {
825  psrc.Form("%s/%s", dldir.Data(), parname.Data());
826  if (!TFile::Cp(par, psrc)) {
827  Error("Install", "could not retrieve %s", par);
828  return -1;
829  }
830  }
831  // psrc is either the original par or the downloaded path
832  md5 = TMD5::FileChecksum(psrc);
833  }
834  // Now we need to compare with the local one
835  sums = TMD5::FileChecksum(dest);
836  if (sums && md5 && (*sums != *md5)) install = kTRUE;
837  }
838  }
839  if (sums) delete sums;
840 
841  // Install if required
842  if (install) {
843  if (!TFile::Cp(psrc, dest)) {
844  Error("Install", "could not copy %s to %s", psrc.Data(), dest.Data());
845  if (md5) delete md5;
846  return -1;
847  }
848  }
849  md5d = TMD5::FileChecksum(dest);
850 
851  if (md5 && *md5 != *md5d)
852  Warning("Install", "checksums do not match:\n\tdownloaded:\t%s\n\texpected:\t%s",
853  md5d->AsString(), md5->AsString());
854  if (Unpack(pack, md5d) != 0) {
855  Error("Install", "could not unpack %s", dest.Data());
856  rc = -1;
857  }
858  if (md5) delete md5;
859  if (md5d) delete md5d;
860  return rc;
861 }
862 
863 //---------------------------------------------------------------------------------------------------
864 // Static methods
865 //---------------------------------------------------------------------------------------------------
866 
867 ////////////////////////////////////////////////////////////////////////////////
868 /// Parse one or more paths as possible sources of packages
869 /// Returns number of paths added; or -1 in case of problems
870 
871 Int_t TPackMgr::RegisterGlobalPath(const char *paths)
872 {
873  Int_t ng = 0;
874  // List of directories where to look for global packages
875  TString globpack(paths);
876  if (globpack.Length() > 0) {
877  Int_t from = 0;
878  TString ldir;
879  while (globpack.Tokenize(ldir, from, ":")) {
880  if (gSystem->AccessPathName(ldir, kReadPermission)) {
881  ::Warning("TPackMgr::RegisterGlobalPath",
882  "directory for global packages %s does not"
883  " exist or is not readable", ldir.Data());
884  } else {
885  // Add to the list, key will be "G<ng>", i.e. "G0", "G1", ...
886  TString key;
887  key.Form("G%d", ng++);
888  if (!fgGlobalPackMgrList) {
889  fgGlobalPackMgrList = new THashList();
890  fgGlobalPackMgrList->SetOwner();
891  }
892  TPackMgr *pmgr = new TPackMgr(ldir);
893  pmgr->SetName(key);
894  fgGlobalPackMgrList->Add(pmgr);
895  ::Info("TPackMgr::RegisterGlobalPath",
896  "manager for global packages directory %s added to the list",
897  ldir.Data());
898  }
899  }
900  }
901  // Number of registered packages
902  return ng;
903 }
904 
905 
906 ////////////////////////////////////////////////////////////////////////////////
907 /// Get the package manager having 'pack'; priority is given to packmgr, if
908 /// defined.
909 /// Returns packmgr or nullptr
910 
911 TPackMgr *TPackMgr::GetPackMgr(const char *pack, TPackMgr *packmgr)
912 {
913  if (packmgr && packmgr->Has(pack)) return packmgr;
914 
915  if (fgGlobalPackMgrList && fgGlobalPackMgrList->GetSize() > 0) {
916  // Scan the list of global packages managers
917  TIter nxpm(fgGlobalPackMgrList);
918  TPackMgr *pm = 0;
919  while ((pm = (TPackMgr *)nxpm())) {
920  if (pm->Has(pack)) return pm;
921  }
922  }
923  return nullptr;
924 }
925 
926 
927 ////////////////////////////////////////////////////////////////////////////////
928 /// Get the full path to PAR, looking also in the global dirs.
929 /// Returns -1 if not found, 0 if available in global dirs, 1 if it can be
930 /// uploaded from the local package dir.
931 /// For the cases >= 0, par is filled with the path of the PAR file
932 
933 Int_t TPackMgr::FindParPath(TPackMgr *packmgr, const char *pack, TString &par)
934 {
935  // Try the package dir
936  if (packmgr && packmgr->GetParPath(pack, par) == 0) return 1;
937 
938  // Try global package dirs
939  if (fgGlobalPackMgrList && fgGlobalPackMgrList->GetSize() > 0) {
940  // Scan the list of global packages dirs
941  TIter nxpm(fgGlobalPackMgrList);
942  TPackMgr *pm = 0;
943  while ((pm = (TPackMgr *)nxpm())) {
944  if (pm->GetParPath(pack, par) == 0) {
945  // Package found, stop searching
946  break;
947  }
948  par = "";
949  }
950  if (par.Length() > 0) return 0;
951  }
952  return -1;
953 }
954 
955 ////////////////////////////////////////////////////////////////////////////////
956 /// Check if the package is enabled; priority is given to packmgr, if
957 /// defined.
958 /// Returns kTRUE if enabled
959 
960 Bool_t TPackMgr::IsEnabled(const char *pack, TPackMgr *packmgr)
961 {
962  if (packmgr && packmgr->IsPackageEnabled(pack)) return kTRUE;
963 
964  if (fgGlobalPackMgrList && fgGlobalPackMgrList->GetSize() > 0) {
965  // Scan the list of global packages managers
966  TIter nxpm(fgGlobalPackMgrList);
967  TPackMgr *pm = 0;
968  while ((pm = (TPackMgr *)nxpm())) {
969  if (pm->IsPackageEnabled(pack)) return kTRUE;
970  }
971  }
972  // Not Enabled
973  return kFALSE;
974 }