Logo ROOT   6.14/05
Reference Guide
TApplicationRemote.cxx
Go to the documentation of this file.
1 // @(#)root/net:$Id$
2 // Author: G. Ganis 10/5/2007
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2007, 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 //////////////////////////////////////////////////////////////////////////
13 // //
14 // TApplicationRemote //
15 // //
16 // TApplicationRemote maps a remote session. It starts a remote session //
17 // and takes care of redirecting the commands to be processed to the //
18 // remote session, to collect the graphic output objects and to display //
19 // them locally. //
20 // //
21 //////////////////////////////////////////////////////////////////////////
22 
23 #include <errno.h>
24 #include <random>
25 
26 #include "TApplicationRemote.h"
27 
28 #include "TBrowser.h"
29 #include "TDirectory.h"
30 #include "TError.h"
31 #include "THashList.h"
32 #include "TMonitor.h"
33 #include "TROOT.h"
34 #include "TServerSocket.h"
35 #include "TSystem.h"
36 #include "TRemoteObject.h"
37 #ifdef WIN32
38 #include <io.h>
39 #include <sys/types.h>
40 #endif
41 
42 //
43 // TApplicationRemote Interrupt signal handler
44 ////////////////////////////////////////////////////////////////////////////////
45 /// TApplicationRemote interrupt handler.
46 
48 {
49  Info("Notify","Processing interrupt signal ...");
50 
51  // Handle interrupt condition on socket(s)
53 
54  return kTRUE;
55 }
56 
57 
59 
60 static const char *gScript = "roots";
61 static const char *gScriptCmd = "\\\"%s %d localhost:%d/%s -d=%d\\\"";
62 #ifndef WIN32
63 static const char *gSshCmd = "ssh %s -f4 %s -R %d:localhost:%d sh -c \
64  \"'(sh=\\`basename \'\\\\\\$SHELL\'\\`; \
65  if test xbash = x\'\\\\\\$sh\' -o xsh = x\'\\\\\\$sh\' -o xzsh = x\'\\\\\\$sh\' -o xdash = x\'\\\\\\$sh\'; then \
66  \'\\\\\\$SHELL\' -l -c %s; \
67  elif test xcsh = x\'\\\\\\$sh\' -o xtcsh = x\'\\\\\\$sh\' -o xksh = x\'\\\\\\$sh\'; then \
68  \'\\\\\\$SHELL\' -c %s; \
69  else \
70  echo \\\"Unknown shell \'\\\\\\$SHELL\'\\\"; \
71  fi)'\"";
72 #else
73 static const char *gSshCmd = "ssh %s -f4 %s -R %d:localhost:%d sh -c \
74  \"'(sh=`basename $SHELL`; \
75  if test xbash = x$sh -o xsh = x$sh -o xzsh = x$sh -o xdash = x$sh; then \
76  $SHELL -l -c %s; \
77  elif test xcsh = x$sh -o xtcsh = x$sh -o xksh = x$sh; then \
78  $SHELL -c %s; \
79  else \
80  echo \"Unknown shell $SHELL\"; \
81  fi)'\"";
82 #endif
83 
84 Int_t TApplicationRemote::fgPortAttempts = 100; // number of attempts to find a port
85 Int_t TApplicationRemote::fgPortLower = 49152; // lower bound for ports
86 Int_t TApplicationRemote::fgPortUpper = 65535; // upper bound for ports
87 
88 ////////////////////////////////////////////////////////////////////////////////
89 /// Main constructor: start a remote session at 'url' accepting callbacks
90 /// on local port 'port'; if port is already in use scan up to 'scan - 1'
91 /// ports starting from port + 1, i.e. port + 1, ... , port + scan - 1
92 
94  const char *script)
95  : TApplication(), fUrl(url)
96 {
97  // Unique name (used also in the prompt)
98  fName = fUrl.GetHost();
99  if (strlen(fUrl.GetOptions()) > 0)
100  fName += Form("-%s", fUrl.GetOptions());
102  TString user = (pw) ? (const char*) pw->fUser : "";
103  SafeDelete(pw);
104  if (strlen(fUrl.GetUser()) > 0 && user != fUrl.GetUser())
105  fName.Insert(0,Form("%s@", fUrl.GetUser()));
106 
107  fIntHandler = 0;
108  fSocket = 0;
109  fMonitor = 0;
110  fFileList = 0;
111  fWorkingDir = 0;
112  fRootFiles = 0;
113  fReceivedObject = 0;
115 
116  // Create server socket; generate randomly a port to find a free one
117  Int_t port = -1;
118  Int_t na = fgPortAttempts;
119  Long64_t now = gSystem->Now();
120  std::default_random_engine randomEngine(now);
121  std::uniform_int_distribution<Int_t> randomPort(fgPortLower, fgPortUpper);
122  TServerSocket *ss = 0;
123  while (na--) {
124  port = randomPort(randomEngine);
125  ss = new TServerSocket(port);
126  if (ss->IsValid())
127  break;
128  }
129  if (!ss || !ss->IsValid()) {
130  Error("TApplicationRemote","unable to find a free port for connections");
132  return;
133  }
134 
135  // Create a monitor and add the socket to it
136  TMonitor *mon = new TMonitor;
137  mon->Add(ss);
138 
139  // Start the remote server
140  Int_t rport = (port < fgPortUpper) ? port + 1 : port - 1;
141  TString sc = gScript;
142  if (script && *script) {
143  // script is enclosed by " ", so ignore first " char
144  if (script[1] == '<') {
145  if (script[2])
146  sc.Form("source %s; %s", script+2, gScript);
147  else
148  Error("TApplicationRemote", "illegal script name <");
149  } else
150  sc = script;
151  }
152  sc.ReplaceAll("\"","");
153  TString userhost = fUrl.GetHost();
154  if (strlen(fUrl.GetUser()) > 0)
155  userhost.Insert(0, Form("%s@", fUrl.GetUser()));
156  const char *verb = "";
157  if (debug > 0)
158  verb = "-v";
159  TString scriptCmd;
160  scriptCmd.Form(gScriptCmd, sc.Data(), kRRemote_Protocol, rport, fUrl.GetFile(), debug);
161  TString cmd;
162  cmd.Form(gSshCmd, verb, userhost.Data(), rport, port, scriptCmd.Data(), scriptCmd.Data());
163 #ifdef WIN32
164  // make sure that the Gpad and GUI libs are loaded
167 #endif
168  if (gDebug > 0)
169  Info("TApplicationRemote", "executing: %s", cmd.Data());
170  if (gSystem->Exec(cmd) != 0) {
171  Info("TApplicationRemote", "an error occured during SSH connection");
172  mon->DeActivateAll();
173  delete mon;
174  delete ss;
177  return;
178  }
179 
180  // Wait for activity on the socket
181  mon->Select();
182 
183  // Get the connection
184  if (!(fSocket = ss->Accept())) {
185  Error("TApplicationRemote", "failed to open connection");
187  return;
188  }
189 
190  // Cleanup the monitor and the server socket
191  mon->DeActivateAll();
192  delete mon;
193  delete ss;
194 
195  // Receive the startup message
196  Int_t what;
197  char buf[512];
198  if (fSocket->Recv(buf, sizeof(buf), what) <= 0) {
199  Error("TApplicationRemote", "failed to receive startup message");
202  return;
203  }
204  Printf("%s", buf);
205 
206  // Receive the protocol version run remotely
207  if (fSocket->Recv(fProtocol, what) != 2*sizeof(Int_t)) {
208  Error("TApplicationRemote", "failed to receive remote server protocol");
211  return;
212  }
214  Info("TApplicationRemote","server runs a different protocol version: %d (vs %d)",
216 
217  TMessage *msg = 0;
218  // Receive the protocol version run remotely
219  if (fSocket->Recv(msg) < 0 || msg->What() != kMESS_ANY) {
220  Error("TApplicationRemote", "failed to receive server info - protocol error");
223  return;
224  }
225 
226  // Real host name and full path to remote log
227  TString hostname;
228  (*msg) >> hostname >> fLogFilePath;
229  fUrl.SetHost(hostname);
230 
231  // Monitor the socket
232  fMonitor = new TMonitor;
233  fMonitor->Add(fSocket);
234 
235  // Set interrupt handler from now on
236  fIntHandler = new TARInterruptHandler(this);
237 
238  // To get the right cleaning sequence
239  gROOT->GetListOfSockets()->Remove(fSocket);
240  gROOT->GetListOfSockets()->Add(this);
241 
242  fRootFiles = new TList;
243  fRootFiles->SetName("Files");
244 
245  // Collect startup notifications
246  Collect();
247 
248  // Done
249  return;
250 }
251 
252 ////////////////////////////////////////////////////////////////////////////////
253 /// Destructor
254 
256 {
257  gROOT->GetListOfSockets()->Remove(this);
258  Terminate(0);
259 }
260 
261 ////////////////////////////////////////////////////////////////////////////////
262 /// Broadcast a message to the remote session.
263 /// Returns 0 on success, -1 in case of error.
264 
266 {
267  if (!IsValid()) return -1;
268 
269  if (fSocket->Send(mess) == -1) {
270  Error("Broadcast", "could not send message");
271  return -1;
272  }
273  // Done
274  return 0;
275 }
276 
277 ////////////////////////////////////////////////////////////////////////////////
278 /// Broadcast a character string buffer to the remote session.
279 /// Use kind to set the TMessage what field.
280 /// Returns 0 on success, -1 in case of error.
281 
283 {
284  TMessage mess(kind);
285  if (kind == kMESS_ANY)
286  mess << type;
287  if (str) mess.WriteString(str);
288  return Broadcast(mess);
289 }
290 
291 ////////////////////////////////////////////////////////////////////////////////
292 /// Broadcast an object to the remote session.
293 /// Use kind to set the TMessage what field.
294 /// Returns 0 on success, -1 in case of error.
295 
297 {
298  TMessage mess(kind);
299  mess.WriteObject(obj);
300  return Broadcast(mess);
301 }
302 
303 ////////////////////////////////////////////////////////////////////////////////
304 /// Broadcast a raw buffer of specified length to the remote session.
305 /// Returns 0 on success, -1 in case of error.
306 
307 Int_t TApplicationRemote::BroadcastRaw(const void *buffer, Int_t length)
308 {
309  if (!IsValid()) return -1;
310 
311  if (fSocket->SendRaw(buffer, length) == -1) {
312  Error("Broadcast", "could not send raw buffer");
313  return -1;
314  }
315  // Done
316  return 0;
317 }
318 
319 ////////////////////////////////////////////////////////////////////////////////
320 /// Collect responses from the remote server.
321 /// Returns the number of messages received.
322 /// If timeout >= 0, wait at most timeout seconds (timeout = -1 by default,
323 /// which means wait forever).
324 
326 {
327  // Activate monitoring
329  if (!fMonitor->GetActive())
330  return 0;
331 
332  // Timeout counter
333  Long_t nto = timeout;
334  if (gDebug > 2)
335  Info("Collect","active: %d", fMonitor->GetActive());
336 
337  // On clients, handle Ctrl-C during collection
338  if (fIntHandler)
339  fIntHandler->Add();
340 
341  // We are now going to collect from the server
343 
344  Int_t rc = 0, cnt = 0;
345  while (fMonitor->GetActive() && (nto < 0 || nto > 0)) {
346 
347  // Wait for a ready socket
348  TSocket *s = fMonitor->Select(1000);
349 
350  if (s && s != (TSocket *)(-1)) {
351  // Get and analyse the info it did receive
352  if ((rc = CollectInput()) != 0) {
353  // Deactivate it if we are done with it
354  fMonitor->DeActivate(s);
355  if (gDebug > 2)
356  Info("Collect","deactivating %p", s);
357  }
358 
359  // Update counter (if no error occured)
360  if (rc >= 0)
361  cnt++;
362 
363  } else {
364  // If not timed-out, exit if not stopped or not aborted
365  // (player exits status is finished in such a case); otherwise,
366  // we still need to collect the partial output info
367  if (!s)
369  // Decrease the timeout counter if requested
370  if (s == (TSocket *)(-1) && nto > 0)
371  nto--;
372  }
373  }
374 
375  // Collection is over
377 
378  // If timed-out, deactivate everything
379  if (nto == 0)
381 
382  // Deactivate Ctrl-C special handler
383  if (fIntHandler)
384  fIntHandler->Remove();
385 
386  return cnt;
387 }
388 
389 ////////////////////////////////////////////////////////////////////////////////
390 /// Collect and analyze available input from the socket.
391 /// Returns 0 on success, -1 if any failure occurs.
392 
394 {
395  TMessage *mess;
396  Int_t rc = 0;
397 
398  char str[512];
399  TObject *obj;
400  Int_t what;
401  Bool_t delete_mess = kTRUE;
402 
403  if (fSocket->Recv(mess) < 0) {
406  return -1;
407  }
408  if (!mess) {
409  // we get here in case the remote server died
412  return -1;
413  }
414 
415  what = mess->What();
416 
417  if (gDebug > 2)
418  Info("CollectInput","what %d", what);
419 
420  switch (what) {
421 
422  case kMESS_OBJECT:
423  { // The server sent over an object: read it in memory
424  TObject *o = mess->ReadObject(mess->GetClass());
425  // If a canvas, draw it
426  if (TString(o->ClassName()) == "TCanvas")
427  o->Draw();
428  else if (TString(o->ClassName()) == "TRemoteObject") {
429  TRemoteObject *robj = (TRemoteObject *)o;
430  if (TString(robj->GetClassName()) == "TSystemDirectory") {
431  if (fWorkingDir == 0) {
432  fWorkingDir = (TRemoteObject *)o;
433  }
434  }
435  }
436  else if (TString(o->ClassName()) == "TList") {
437  TList *list = (TList *)o;
438  TRemoteObject *robj = (TRemoteObject *)list->First();
439  if (robj && (TString(robj->GetClassName()) == "TFile")) {
440  TIter next(list);
441  while ((robj = (TRemoteObject *)next())) {
442  if (!fRootFiles->FindObject(robj->GetName()))
443  fRootFiles->Add(robj);
444  }
445  gROOT->RefreshBrowsers();
446  }
447  }
448  fReceivedObject = o;
449  }
450  break;
451 
452  case kMESS_ANY:
453  // Generic message: read out the type
454  { Int_t type;
455  (*mess) >> type;
456 
457  if (gDebug > 2)
458  Info("CollectInput","type %d", type);
459 
460  switch (type) {
461 
462  case kRRT_GetObject:
463  // send server the object it asks for
464  mess->ReadString(str, sizeof(str));
465  obj = gDirectory->Get(str);
466  if (obj) {
467  fSocket->SendObject(obj);
468  } else {
469  Warning("CollectInput",
470  "server requested an object that we do not have");
472  }
473  break;
474 
475  case kRRT_Fatal:
476  // Fatal error
478  rc = -1;
479  break;
480 
481  case kRRT_LogFile:
482  { Int_t size;
483  (*mess) >> size;
484  RecvLogFile(size);
485  }
486  break;
487 
488  case kRRT_LogDone:
489  { Int_t st;
490  (*mess) >> st;
491  if (st < 0) {
492  // Problem: object should not be used
494  }
495  if (gDebug > 1)
496  Info("CollectInput","kRTT_LogDone: status %d", st);
497  rc = 1;
498  }
499  break;
500 
501  case kRRT_Message:
502  { TString msg;
503  Bool_t lfeed;
504  (*mess) >> msg >> lfeed;
505  if (lfeed)
506  fprintf(stderr,"%s\n", msg.Data());
507  else
508  fprintf(stderr,"%s\r", msg.Data());
509  }
510  break;
511 
512  case kRRT_SendFile:
513  { TString fname;
514  (*mess) >> fname;
515  // Prepare the reply
517  m << (Int_t) kRRT_SendFile;
518  // The server needs a file: we send also the related header
519  // if we have it.
520  char *imp = gSystem->Which(TROOT::GetMacroPath(), fname, kReadPermission);
521  if (!imp) {
522  Error("CollectInput", "file %s not found in path(s) %s",
523  fname.Data(), TROOT::GetMacroPath());
524  m << (Bool_t) kFALSE;
525  Broadcast(m);
526  } else {
527  TString impfile = imp;
528  delete [] imp;
529  Int_t dot = impfile.Last('.');
530 
531  // Is there any associated header file
532  Bool_t hasHeader = kTRUE;
533  TString headfile = impfile;
534  if (dot != kNPOS)
535  headfile.Remove(dot);
536  headfile += ".h";
537  if (gSystem->AccessPathName(headfile, kReadPermission)) {
538  TString h = headfile;
539  headfile.Remove(dot);
540  headfile += ".hh";
541  if (gSystem->AccessPathName(headfile, kReadPermission)) {
542  hasHeader = kFALSE;
543  if (gDebug > 0)
544  Info("CollectInput", "no associated header file"
545  " found: tried: %s %s",
546  h.Data(), headfile.Data());
547  }
548  }
549 
550  // Send files now;
551  m << (Bool_t) kTRUE;
552  Broadcast(m);
553  if (SendFile(impfile, kForce) == -1) {
554  Info("CollectInput", "problems sending file %s", impfile.Data());
555  return 0;
556  }
557  if (hasHeader) {
558  Broadcast(m);
559  if (SendFile(headfile, kForce) == -1) {
560  Info("CollectInput", "problems sending file %s", headfile.Data());
561  return 0;
562  }
563  }
564  }
565  // End of transmission
566  m.Reset(kMESS_ANY);
567  m << (Int_t) kRRT_SendFile;
568  m << (Bool_t) kFALSE;
569  Broadcast(m);
570  }
571  break;
572 
573  default:
574  Warning("CollectInput","unknown type received from server: %d", type);
575  break;
576 
577  }
578  }
579  break;
580 
581  default:
582  Error("CollectInput", "unknown command received from server: %d", what);
585  rc = -1;
586  break;
587  }
588 
589  // Cleanup
590  if (delete_mess)
591  delete mess;
592 
593  // We are done successfully
594  return rc;
595 }
596 
597 
598 ////////////////////////////////////////////////////////////////////////////////
599 /// Receive the log file from the server
600 
602 {
603  const Int_t kMAXBUF = 16384; //32768 //16384 //65536;
604  char buf[kMAXBUF];
605 
606  // Append messages to active logging unit
607  Int_t fdout = fileno(stdout);
608  if (fdout < 0) {
609  Warning("RecvLogFile", "file descriptor for outputs undefined (%d):"
610  " will not log msgs", fdout);
611  return;
612  }
613  lseek(fdout, (off_t) 0, SEEK_END);
614 
615  Int_t left, rec, r;
616  Long_t filesize = 0;
617 
618  while (filesize < size) {
619  left = Int_t(size - filesize);
620  if (left > kMAXBUF)
621  left = kMAXBUF;
622  rec = fSocket->RecvRaw(&buf, left);
623  filesize = (rec > 0) ? (filesize + rec) : filesize;
624  if (rec > 0) {
625 
626  char *p = buf;
627  r = rec;
628  while (r) {
629  Int_t w;
630 
631  w = write(fdout, p, r);
632 
633  if (w < 0) {
634  SysError("RecvLogFile", "error writing to unit: %d", fdout);
635  break;
636  }
637  r -= w;
638  p += w;
639  }
640  } else if (rec < 0) {
641  Error("RecvLogFile", "error during receiving log file");
642  break;
643  }
644  }
645 }
646 
647 ////////////////////////////////////////////////////////////////////////////////
648 /// Send object to server.
649 /// Return 0 on success, -1 in case of error.
650 
652 {
653  if (!IsValid() || !obj) return -1;
654 
655  TMessage mess(kMESS_OBJECT);
656  mess.WriteObject(obj);
657  return Broadcast(mess);
658 }
659 
660 ////////////////////////////////////////////////////////////////////////////////
661 /// Check if a file needs to be send to the server. Use the following
662 /// algorithm:
663 /// - check if file appears in file map
664 /// - if yes, get file's modtime and check against time in map,
665 /// if modtime not same get md5 and compare against md5 in map,
666 /// if not same return kTRUE.
667 /// - if no, get file's md5 and modtime and store in file map, ask
668 /// slave if file exists with specific md5, if yes return kFALSE,
669 /// if no return kTRUE.
670 /// Returns kTRUE in case file needs to be send, returns kFALSE in case
671 /// file is already on remote node.
672 
674 {
675  Bool_t sendto = kFALSE;
676 
677  if (!IsValid()) return -1;
678 
679  // The filename for the cache
680  TString fn = gSystem->BaseName(file);
681 
682  // Check if the file is already in the cache
683  TARFileStat *fs = 0;
684  if (fFileList && (fs = (TARFileStat *) fFileList->FindObject(fn))) {
685  // File in cache
686  if (fs->fModtime != modtime) {
687  TMD5 *md5 = TMD5::FileChecksum(file);
688  if (md5) {
689  if ((*md5) != fs->fMD5) {
690  sendto = kTRUE;
691  fs->fMD5 = *md5;
692  fs->fModtime = modtime;
693  }
694  delete md5;
695  } else {
696  Error("CheckFile", "could not calculate local MD5 check sum - dont send");
697  return kFALSE;
698  }
699  }
700  } else {
701  // file not in the cache
702  TMD5 *md5 = TMD5::FileChecksum(file);
703  if (md5) {
704  fs = new TARFileStat(fn, md5, modtime);
705  if (!fFileList)
706  fFileList = new THashList;
707  fFileList->Add(fs);
708  delete md5;
709  } else {
710  Error("CheckFile", "could not calculate local MD5 check sum - dont send");
711  return kFALSE;
712  }
713  TMessage mess(kMESS_ANY);
714  mess << Int_t(kRRT_CheckFile) << TString(gSystem->BaseName(file)) << fs->fMD5;
715  fSocket->Send(mess);
716 
717  TMessage *reply;
718  fSocket->Recv(reply);
719  if (reply) {
720  if (reply->What() == kMESS_ANY) {
721  // Check the type
722  Int_t type;
723  Bool_t uptodate;
724  (*reply) >> type >> uptodate;
725  if (type != kRRT_CheckFile) {
726  // Protocol error
727  Warning("CheckFile", "received wrong type:"
728  " %d (expected %d): protocol error?",
729  type, (Int_t)kRRT_CheckFile);
730  }
731  sendto = uptodate ? kFALSE : kTRUE;
732  } else {
733  // Protocol error
734  Error("CheckFile", "received wrong message: %d (expected %d)",
735  reply->What(), kMESS_ANY);
736  }
737  } else {
738  Error("CheckFile", "received empty message");
739  }
740  // Collect logs
741  Collect();
742  }
743 
744  // Done
745  return sendto;
746 }
747 
748 ////////////////////////////////////////////////////////////////////////////////
749 /// Send a file to the server. Return 0 on success, -1 in case of error.
750 /// If defined, the full path of the remote path will be rfile.
751 /// The mask 'opt' is an or of ESendFileOpt:
752 ///
753 /// kAscii (0x0) if set true ascii file transfer is used
754 /// kBinary (0x1) if set true binary file transfer is used
755 /// kForce (0x2) if not set an attempt is done to find out
756 /// whether the file really needs to be downloaded
757 /// (a valid copy may already exist in the cache
758 /// from a previous run)
759 ///
760 
761 Int_t TApplicationRemote::SendFile(const char *file, Int_t opt, const char *rfile)
762 {
763  if (!IsValid()) return -1;
764 
765 #ifndef R__WIN32
766  Int_t fd = open(file, O_RDONLY);
767 #else
768  Int_t fd = open(file, O_RDONLY | O_BINARY);
769 #endif
770  if (fd < 0) {
771  SysError("SendFile", "cannot open file %s", file);
772  return -1;
773  }
774 
775  // Get info about the file
776  Long64_t size;
777  Long_t id, flags, modtime;
778  if (gSystem->GetPathInfo(file, &id, &size, &flags, &modtime) == 1) {
779  Error("SendFile", "cannot stat file %s", file);
780  close(fd);
781  return -1;
782  }
783  if (size == 0) {
784  Error("SendFile", "empty file %s", file);
785  close(fd);
786  return -1;
787  }
788 
789  // Decode options
790  Bool_t bin = (opt & kBinary) ? kTRUE : kFALSE;
791  Bool_t force = (opt & kForce) ? kTRUE : kFALSE;
792 
793  const Int_t kMAXBUF = 32768; //16384 //65536;
794  char buf[kMAXBUF];
795 
796  const char *fnam = (rfile) ? rfile : gSystem->BaseName(file);
797 
798  Bool_t sendto = force ? kTRUE : CheckFile(file, modtime);
799 
800  // The value of 'size' is used as flag remotely, so we need to
801  // reset it to 0 if we are not going to send the file
802  size = sendto ? size : 0;
803 
804  if (gDebug > 1 && size > 0)
805  Info("SendFile", "sending file %s", file);
806 
807  snprintf(buf, kMAXBUF, "%s %d %lld", fnam, bin, size);
808  if (Broadcast(buf, kMESS_ANY, kRRT_File) == -1) {
810  close(fd);
811  return -1;
812  }
813 
814  if (sendto) {
815 
816  lseek(fd, 0, SEEK_SET);
817 
818  Int_t len;
819  do {
820  while ((len = read(fd, buf, kMAXBUF)) < 0 && TSystem::GetErrno() == EINTR)
822 
823  if (len < 0) {
824  SysError("SendFile", "error reading from file %s", file);
825  Interrupt();
826  close(fd);
827  return -1;
828  }
829 
830  if (len > 0 && fSocket->SendRaw(buf, len) == -1) {
831  SysError("SendFile", "error writing to server @ %s:%d (now offline)",
832  fUrl.GetHost(), fUrl.GetPort());
834  break;
835  }
836 
837  } while (len > 0);
838  }
839  close(fd);
840 
841  // Get the log (during collection this will be done at the end
842  if (!TestBit(kCollecting))
843  Collect();
844 
845  // Done
846  return IsValid() ? 0 : -1;
847 }
848 
849 ////////////////////////////////////////////////////////////////////////////////
850 /// Terminate this session
851 
853 {
854  TMessage mess(kMESS_ANY);
855  mess << (Int_t)kRRT_Terminate << status;
856  Broadcast(mess);
857 
861 }
862 
863 ////////////////////////////////////////////////////////////////////////////////
864 /// Set port parameters for tunnelling. A value of -1 means unchanged
865 
866 void TApplicationRemote::SetPortParam(Int_t lower, Int_t upper, Int_t attempts)
867 {
868  if (lower > -1)
869  fgPortLower = lower;
870  if (upper > -1)
871  fgPortUpper = upper;
872  if (attempts > -1)
873  fgPortAttempts = attempts;
874 
875  ::Info("TApplicationRemote::SetPortParam","port scan: %d attempts in [%d,%d]",
877 }
878 
879 ////////////////////////////////////////////////////////////////////////////////
880 /// Parse a single command line and forward the request to the remote server
881 /// where it will be processed. The line is either a C++ statement or an
882 /// interpreter command starting with a ".".
883 /// Return the return value of the command casted to a long.
884 
886 {
887  if (!line || !*line) return 0;
888 
889  if (!strncasecmp(line, ".q", 2)) {
890  // terminate the session
891  gApplication->ProcessLine(".R -close");
892  return 0;
893  }
894 
895  if (!strncmp(line, "?", 1)) {
896  Help(line);
897  return 1;
898  }
899 
900  fReceivedObject = 0;
901 
902  // Init graphics
904 
905  // Ok, now we pack the command and we send it over for processing
906  Broadcast(line, kMESS_CINT);
907 
908  // And collect the results
909  Collect();
910 
911  // Done
912  return (Long_t)fReceivedObject;
913  return 1;
914 }
915 
916 ////////////////////////////////////////////////////////////////////////////////
917 /// Print some info about this instance
918 
920 {
921  TString s(Form("OBJ: TApplicationRemote %s", fName.Data()));
922  Printf("%s", s.Data());
923  if (opt && opt[0] == 'F') {
924  s = " url: ";
925  if (strlen(fUrl.GetUser()) > 0)
926  s += Form("%s@", fUrl.GetUser());
927  s += fUrl.GetHostFQDN();
928  s += Form(" logfile: %s", fLogFilePath.Data());
929  Printf("%s", s.Data());
930  }
931 }
932 ////////////////////////////////////////////////////////////////////////////////
933 /// Send interrupt OOB byte to server.
934 /// Returns 0 if ok, -1 in case of error
935 
937 {
938  if (!IsValid()) return;
939 
940  fInterrupt = kTRUE;
941 
942 #if 1
943  Info("Interrupt", "*** Ctrl-C not yet enabled *** (type= %d)", type);
944  return;
945 #else
946 
947  char oobc = (char) type;
948  const int kBufSize = 1024;
949  char waste[kBufSize];
950 
951  // Send one byte out-of-band message to server
952  if (fSocket->SendRaw(&oobc, 1, kOob) <= 0) {
953  Error("Interrupt", "error sending oobc to server");
954  return;
955  }
956 
957  if (type == kRRI_Hard) {
958  char oob_byte;
959  int n, nch, nbytes = 0, nloop = 0;
960 
961  // Receive the OOB byte
962  while ((n = fSocket->RecvRaw(&oob_byte, 1, kOob)) < 0) {
963  if (n == -2) { // EWOULDBLOCK
964  //
965  // The OOB data has not yet arrived: flush the input stream
966  //
967  // In some systems (Solaris) regular recv() does not return upon
968  // receipt of the oob byte, which makes the below call to recv()
969  // block indefinitely if there are no other data in the queue.
970  // FIONREAD ioctl can be used to check if there are actually any
971  // data to be flushed. If not, wait for a while for the oob byte
972  // to arrive and try to read it again.
973  //
975  if (nch == 0) {
976  gSystem->Sleep(1000);
977  continue;
978  }
979 
980  if (nch > kBufSize) nch = kBufSize;
981  n = fSocket->RecvRaw(waste, nch);
982  if (n <= 0) {
983  Error("Interrupt", "error receiving waste from server");
984  break;
985  }
986  nbytes += n;
987  } else if (n == -3) { // EINVAL
988  //
989  // The OOB data has not arrived yet
990  //
991  gSystem->Sleep(100);
992  if (++nloop > 100) { // 10 seconds time-out
993  Error("Interrupt", "server does not respond");
994  break;
995  }
996  } else {
997  Error("Interrupt", "error receiving OOB from server");
998  break;
999  }
1000  }
1001 
1002  //
1003  // Continue flushing the input socket stream until the OOB
1004  // mark is reached
1005  //
1006  while (1) {
1007  int atmark;
1008 
1009  fSocket->GetOption(kAtMark, atmark);
1010 
1011  if (atmark)
1012  break;
1013 
1014  // find out number of bytes to read before atmark
1016  if (nch == 0) {
1017  gSystem->Sleep(1000);
1018  continue;
1019  }
1020 
1021  if (nch > kBufSize) nch = kBufSize;
1022  n = fSocket->RecvRaw(waste, nch);
1023  if (n <= 0) {
1024  Error("Interrupt", "error receiving waste (2) from server");
1025  break;
1026  }
1027  nbytes += n;
1028  }
1029  if (nbytes > 0)
1030  Info("Interrupt", "server synchronized: %d bytes discarded", nbytes);
1031 
1032  // Get log file from server after a hard interrupt
1033  Collect();
1034 
1035  } else if (type == kRRI_Soft) {
1036 
1037  // Get log file from server after a soft interrupt
1038  Collect();
1039 
1040  } else if (type == kRRI_Shutdown) {
1041 
1042  ; // nothing expected to be returned
1043 
1044  } else {
1045 
1046  // Unexpected message, just receive log file
1047  Collect();
1048  }
1049 #endif
1050 }
1051 
1052 ////////////////////////////////////////////////////////////////////////////////
1053 /// Browse remote application (working directory and ROOT files).
1054 
1056 {
1057  b->Add(fRootFiles, "ROOT Files");
1059  gROOT->RefreshBrowsers();
1060 }
void Add(TObject *obj, const char *name=0, Int_t check=-1)
Add object with name to browser.
Definition: TBrowser.cxx:261
TClass * GetClass() const
Definition: TMessage.h:70
virtual const char * BaseName(const char *pathname)
Base name of a file name. Base name of /user/root is root.
Definition: TSystem.cxx:932
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
virtual void SysError(const char *method, const char *msgfmt,...) const
Issue system error message.
Definition: TObject.cxx:894
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition: TSystem.cxx:1276
virtual void Add(TObject *obj)
virtual Long_t ProcessLine(const char *line, Bool_t sync=kFALSE, Int_t *error=0)
Process a single command line, either a C++ statement or an interpreter command starting with a "...
virtual void WriteString(const char *s)
Write string to I/O buffer.
virtual Bool_t IsValid() const
Definition: TSocket.h:151
void Terminate(Int_t status=0)
Terminate this session.
const char * GetClassName() const
Definition: TRemoteObject.h:56
static Int_t fgPortLower
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:854
TApplicationRemote(const char *url, Int_t debug=0, const char *script=0)
Main constructor: start a remote session at &#39;url&#39; accepting callbacks on local port &#39;port&#39;; if port i...
Int_t GetActive(Long_t timeout=-1) const
Return number of sockets in the active list.
Definition: TMonitor.cxx:438
long long Long64_t
Definition: RtypesCore.h:69
auto * m
Definition: textangle.C:8
Bool_t Notify()
TApplicationRemote interrupt handler.
static TMD5 * FileChecksum(const char *file)
Returns checksum of specified file.
Definition: TMD5.cxx:474
TLine * line
const char Option_t
Definition: RtypesCore.h:62
TApplicationRemote * fApplicationRemote
virtual Int_t Send(const TMessage &mess)
Send a TMessage object.
Definition: TSocket.cxx:527
const Ssiz_t kNPOS
Definition: RtypesCore.h:111
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:687
int GetPathInfo(const char *path, Long_t *id, Long_t *size, Long_t *flags, Long_t *modtime)
Get info about a file: id, size, flags, modification time.
Definition: TSystem.cxx:1374
virtual Int_t Recv(TMessage *&mess)
Receive a TMessage object.
Definition: TSocket.cxx:822
virtual void Add()
Add signal handler to system signal handler list.
virtual ~TApplicationRemote()
Destructor.
#define gROOT
Definition: TROOT.h:410
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:172
virtual void Add(TSocket *sock, Int_t interest=kRead)
Add socket to the monitor&#39;s active list.
Definition: TMonitor.cxx:168
#define O_BINARY
Definition: civetweb.c:652
Basic string class.
Definition: TString.h:131
Int_t CollectInput()
Collect and analyze available input from the socket.
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
const Int_t kRRemote_Protocol
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition: TObject.cxx:195
virtual Int_t SendObject(const TObject *obj, Int_t kind=kMESS_OBJECT)
Send an object.
Definition: TSocket.cxx:605
const char * GetOptions() const
Definition: TUrl.h:74
virtual char * Which(const char *search, const char *file, EAccessMode mode=kFileExists)
Find location of file in a search path.
Definition: TSystem.cxx:1522
Option_t * GetOption() const
Definition: TSocket.h:117
Bool_t CheckFile(const char *file, Long_t modtime)
Check if a file needs to be send to the server.
Int_t Broadcast(const TMessage &mess)
Broadcast a message to the remote session.
R__EXTERN TApplication * gApplication
Definition: TApplication.h:165
Int_t BroadcastObject(const TObject *obj, Int_t kind=kMESS_OBJECT)
Broadcast an object to the remote session.
virtual void DeActivateAll()
De-activate all activated sockets.
Definition: TMonitor.cxx:302
static Int_t fgPortAttempts
TObject * FindObject(const char *name) const
Find object using its name.
Definition: THashList.cxx:262
Long_t ProcessLine(const char *line, Bool_t=kFALSE, Int_t *error=0)
Parse a single command line and forward the request to the remote server where it will be processed...
TString & Insert(Ssiz_t pos, const char *s)
Definition: TString.h:644
static Int_t fgPortUpper
const char * GetHostFQDN() const
Return fully qualified domain name of url host.
Definition: TUrl.cxx:469
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:694
static const char * GetMacroPath()
Get macro search path. Static utility function.
Definition: TROOT.cxx:2743
static Int_t GetErrno()
Static function returning system error number.
Definition: TSystem.cxx:268
const char * GetFile() const
Definition: TUrl.h:72
virtual TObject * ReadObject(const TClass *cl)
Read object from I/O buffer.
virtual Int_t SendRaw(const void *buffer, Int_t length, ESendRecvOptions opt=kDefault)
Send a raw buffer of specified length.
Definition: TSocket.cxx:625
const char * GetHost() const
Definition: TUrl.h:70
TSignalHandler * fIntHandler
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition: TObject.cxx:128
The TRemoteObject class provides protocol for browsing ROOT objects from a remote ROOT session...
Definition: TRemoteObject.h:36
virtual void Sleep(UInt_t milliSec)
Sleep milliSec milli seconds.
Definition: TSystem.cxx:445
THashList implements a hybrid collection class consisting of a hash table and a list to store TObject...
Definition: THashList.h:34
This code implements the MD5 message-digest algorithm.
Definition: TMD5.h:44
virtual void DeActivate(TSocket *sock)
De-activate a socket.
Definition: TMonitor.cxx:284
virtual char * ReadString(char *s, Int_t max)
Read string from I/O buffer.
void Interrupt(Int_t type=kRRI_Hard)
Send interrupt OOB byte to server.
Int_t Collect(Long_t timeout=-1)
Collect responses from the remote server.
virtual UserGroup_t * GetUserInfo(Int_t uid)
Returns all user info in the UserGroup_t structure.
Definition: TSystem.cxx:1574
void RecvLogFile(Int_t size)
Receive the log file from the server.
virtual TSocket * Accept(UChar_t Opt=0)
Accept a connection on a server socket.
XFontStruct * id
Definition: TGX11.cxx:108
TSocket * Select()
Return pointer to socket for which an event is waiting.
Definition: TMonitor.cxx:322
A doubly linked list.
Definition: TList.h:44
const char * GetUser() const
Definition: TUrl.h:68
void Reset()
Reset the message buffer so we can use (i.e. fill) it again.
Definition: TMessage.cxx:178
Using a TBrowser one can browse all ROOT objects.
Definition: TBrowser.h:37
TSeqCollection * fRootFiles
virtual TTime Now()
Get current time in milliseconds since 0:00 Jan 1 1995.
Definition: TSystem.cxx:471
static const char * gSshCmd
virtual TObject * First() const
Return the first object in the list. Returns 0 when list is empty.
Definition: TList.cxx:655
static void NeedGraphicsLibs()
Static method.
ROOT::R::TRInterface & r
Definition: Object.C:4
R__EXTERN TSystem * gSystem
Definition: TSystem.h:540
if object ctor succeeded but object should not be used
Definition: TObject.h:68
Int_t SendObject(const TObject *obj)
Send object to server.
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2264
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
char * Form(const char *fmt,...)
virtual Int_t Exec(const char *shellcmd)
Execute a command.
Definition: TSystem.cxx:661
void SetName(const char *name)
Definition: TCollection.h:204
virtual void WriteObject(const TObject *obj, Bool_t cacheReuse=kTRUE)
Write object to I/O buffer.
Definition: TBufferIO.cxx:530
#define h(i)
Definition: RSha256.hxx:106
#define Printf
Definition: TGeoToOCC.h:18
virtual void Help(const char *line)
Print help on interpreter.
const Bool_t kFALSE
Definition: RtypesCore.h:88
UInt_t What() const
Definition: TMessage.h:74
void InitializeGraphics()
Initialize the graphics environment.
#define SafeDelete(p)
Definition: RConfig.h:529
void SetHost(const char *host)
Definition: TUrl.h:87
TString & Remove(Ssiz_t pos)
Definition: TString.h:668
long Long_t
Definition: RtypesCore.h:50
virtual Int_t GetEffectiveUid()
Returns the effective user id.
Definition: TSystem.cxx:1545
#define ClassImp(name)
Definition: Rtypes.h:359
virtual void Browse(TBrowser *b)
Browse remote application (working directory and ROOT files).
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition: TString.cxx:876
void Print(Option_t *option="") const
Print some info about this instance.
virtual void Remove()
Remove signal handler from system signal handler list.
int type
Definition: TGX11.cxx:120
static void SetPortParam(Int_t lower=-1, Int_t upper=-1, Int_t attempts=-1)
Set port parameters for tunnelling. A value of -1 means unchanged.
static constexpr double s
Definition: TSocket.h:51
Int_t GetPort() const
Definition: TUrl.h:81
Mother of all ROOT objects.
Definition: TObject.h:37
virtual void Add(TObject *obj)
Definition: TList.h:87
static const char * gScriptCmd
Definition: file.py:1
you should not use this method at all Int_t Int_t Double_t Double_t Double_t Int_t Double_t Double_t Double_t Double_t b
Definition: TRolke.cxx:630
#define snprintf
Definition: civetweb.c:1351
virtual TObject * FindObject(const char *name) const
Find an object in this collection using its name.
R__EXTERN Int_t gDebug
Definition: Rtypes.h:86
Int_t BroadcastRaw(const void *buffer, Int_t length)
Broadcast a raw buffer of specified length to the remote session.
static void ResetErrno()
Static function resetting system error number.
Definition: TSystem.cxx:284
#define gDirectory
Definition: TDirectory.h:213
static const char * gScript
void ResetBit(UInt_t f)
Definition: TObject.h:171
This class creates the ROOT Application Environment that interfaces to the windowing system eventloop...
Definition: TApplication.h:39
virtual void ActivateAll()
Activate all de-activated sockets.
Definition: TMonitor.cxx:268
virtual Int_t RecvRaw(void *buffer, Int_t length, ESendRecvOptions opt=kDefault)
Receive a raw buffer of specified length bytes.
Definition: TSocket.cxx:902
const Bool_t kTRUE
Definition: RtypesCore.h:87
const Int_t n
Definition: legend1.C:16
const char * cnt
Definition: TXMLSetup.cxx:74
TRemoteObject * fWorkingDir
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:866
Int_t SendFile(const char *file, Int_t opt=kAscii, const char *rfile=0)
Send a file to the server.
Bool_t IsValid() const
virtual const char * GetTitle() const
Returns title of object.
Definition: TNamed.h:48
const char * Data() const
Definition: TString.h:364