Logo ROOT  
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
60static const char *gScript = "roots";
61static const char *gScriptCmd = "\\\"%s %d localhost:%d/%s -d=%d\\\"";
62#ifndef WIN32
63static 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
73static 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
84Int_t TApplicationRemote::fgPortAttempts = 100; // number of attempts to find a port
85Int_t TApplicationRemote::fgPortLower = 49152; // lower bound for ports
86Int_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;
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;
234
235 // Set interrupt handler from now on
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
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
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)
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) {
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
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
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) {
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
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?",
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
761Int_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
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
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
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}
@ kMESS_ANY
Definition: MessageTypes.h:31
@ kMESS_NOTOK
Definition: MessageTypes.h:33
@ kMESS_OBJECT
Definition: MessageTypes.h:35
@ kMESS_CINT
Definition: MessageTypes.h:36
ROOT::R::TRInterface & r
Definition: Object.C:4
#define SafeDelete(p)
Definition: RConfig.hxx:543
const Int_t kRRemote_Protocol
@ kRRT_Terminate
@ kRRT_LogFile
@ kRRT_CheckFile
@ kRRT_GetObject
@ kRRT_File
@ kRRT_LogDone
@ kRRT_Message
@ kRRT_SendFile
@ kRRT_Fatal
@ kRRI_Shutdown
@ kRRI_Soft
@ kRRI_Hard
#define b(i)
Definition: RSha256.hxx:100
#define h(i)
Definition: RSha256.hxx:106
const Ssiz_t kNPOS
Definition: RtypesCore.h:113
int Int_t
Definition: RtypesCore.h:43
const Bool_t kFALSE
Definition: RtypesCore.h:90
long Long_t
Definition: RtypesCore.h:52
bool Bool_t
Definition: RtypesCore.h:61
R__EXTERN Int_t gDebug
Definition: RtypesCore.h:117
long long Long64_t
Definition: RtypesCore.h:71
const Bool_t kTRUE
Definition: RtypesCore.h:89
const char Option_t
Definition: RtypesCore.h:64
#define ClassImp(name)
Definition: Rtypes.h:361
static const char * gSshCmd
static const char * gScript
static const char * gScriptCmd
R__EXTERN TApplication * gApplication
Definition: TApplication.h:166
#define gDirectory
Definition: TDirectory.h:229
XFontStruct * id
Definition: TGX11.cxx:108
int type
Definition: TGX11.cxx:120
#define gROOT
Definition: TROOT.h:406
char * Form(const char *fmt,...)
void Printf(const char *fmt,...)
@ kBytesToRead
Definition: TSystem.h:224
@ kAtMark
Definition: TSystem.h:223
@ kOob
Definition: TSystem.h:229
@ kReadPermission
Definition: TSystem.h:46
R__EXTERN TSystem * gSystem
Definition: TSystem.h:556
#define O_BINARY
Definition: civetweb.c:799
#define snprintf
Definition: civetweb.c:1540
TApplicationRemote * fApplicationRemote
Bool_t Notify()
TApplicationRemote interrupt handler.
virtual void Browse(TBrowser *b)
Browse remote application (working directory and ROOT files).
void Print(Option_t *option="") const
Print some info about this instance.
Int_t CollectInput()
Collect and analyze available input from the socket.
virtual ~TApplicationRemote()
Destructor.
Int_t BroadcastRaw(const void *buffer, Int_t length)
Broadcast a raw buffer of specified length to the remote session.
Bool_t CheckFile(const char *file, Long_t modtime)
Check if a file needs to be send to the server.
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.
TRemoteObject * fWorkingDir
void Terminate(Int_t status=0)
Terminate this session.
void Interrupt(Int_t type=kRRI_Hard)
Send interrupt OOB byte to server.
TApplicationRemote(const char *url, Int_t debug=0, const char *script=0)
Main constructor: start a remote session at 'url' accepting callbacks on local port 'port'; if port i...
static Int_t fgPortUpper
void RecvLogFile(Int_t size)
Receive the log file from the server.
TSeqCollection * fRootFiles
Int_t SendFile(const char *file, Int_t opt=kAscii, const char *rfile=0)
Send a file to the server.
Int_t BroadcastObject(const TObject *obj, Int_t kind=kMESS_OBJECT)
Broadcast an object to the remote session.
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.
Int_t SendObject(const TObject *obj)
Send object to server.
static Int_t fgPortLower
Bool_t IsValid() const
Int_t Collect(Long_t timeout=-1)
Collect responses from the remote server.
static Int_t fgPortAttempts
TSignalHandler * fIntHandler
Int_t Broadcast(const TMessage &mess)
Broadcast a message to the remote session.
This class creates the ROOT Application Environment that interfaces to the windowing system eventloop...
Definition: TApplication.h:39
virtual void Help(const char *line)
The function lists useful commands (".help") or opens the online reference guide, generated with Doxy...
void InitializeGraphics()
Initialize the graphics environment.
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 "....
static void NeedGraphicsLibs()
Static method.
Using a TBrowser one can browse all ROOT objects.
Definition: TBrowser.h:37
TObject * ReadObject(const TClass *cl) override
Read object from I/O buffer.
void WriteString(const char *s) override
Write string to I/O buffer.
char * ReadString(char *s, Int_t max) override
Read string from I/O buffer.
void WriteObject(const TObject *obj, Bool_t cacheReuse=kTRUE) override
Write object to I/O buffer.
Definition: TBufferIO.cxx:530
virtual TObject * FindObject(const char *name) const
Find an object in this collection using its name.
void SetName(const char *name)
Definition: TCollection.h:204
THashList implements a hybrid collection class consisting of a hash table and a list to store TObject...
Definition: THashList.h:34
TObject * FindObject(const char *name) const
Find object using its name.
Definition: THashList.cxx:262
A doubly linked list.
Definition: TList.h:44
virtual void Add(TObject *obj)
Definition: TList.h:87
virtual TObject * First() const
Return the first object in the list. Returns 0 when list is empty.
Definition: TList.cxx:658
This code implements the MD5 message-digest algorithm.
Definition: TMD5.h:44
static TMD5 * FileChecksum(const char *file)
Returns checksum of specified file.
Definition: TMD5.cxx:474
UInt_t What() const
Definition: TMessage.h:75
TClass * GetClass() const
Definition: TMessage.h:71
virtual void ActivateAll()
Activate all de-activated sockets.
Definition: TMonitor.cxx:268
TSocket * Select()
Return pointer to socket for which an event is waiting.
Definition: TMonitor.cxx:322
virtual void Add(TSocket *sock, Int_t interest=kRead)
Add socket to the monitor's active list.
Definition: TMonitor.cxx:168
Int_t GetActive(Long_t timeout=-1) const
Return number of sockets in the active list.
Definition: TMonitor.cxx:438
virtual void DeActivateAll()
De-activate all activated sockets.
Definition: TMonitor.cxx:302
virtual void DeActivate(TSocket *sock)
De-activate a socket.
Definition: TMonitor.cxx:284
virtual const char * GetTitle() const
Returns title of object.
Definition: TNamed.h:48
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
Mother of all ROOT objects.
Definition: TObject.h:37
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:187
virtual void SysError(const char *method, const char *msgfmt,...) const
Issue system error message.
Definition: TObject.cxx:905
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition: TObject.cxx:128
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:877
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:694
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:891
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition: TObject.cxx:195
void ResetBit(UInt_t f)
Definition: TObject.h:186
@ kInvalidObject
if object ctor succeeded but object should not be used
Definition: TObject.h:68
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:865
static const char * GetMacroPath()
Get macro search path. Static utility function.
Definition: TROOT.cxx:2693
The TRemoteObject class provides protocol for browsing ROOT objects from a remote ROOT session.
Definition: TRemoteObject.h:34
const char * GetClassName() const
Definition: TRemoteObject.h:54
virtual void Add(TObject *obj)
virtual TSocket * Accept(UChar_t Opt=0)
Accept a connection on a server socket.
virtual void Add()
Add signal handler to system signal handler list.
virtual void Remove()
Remove signal handler from system signal handler list.
Option_t * GetOption() const
Definition: TSocket.h:98
virtual Int_t Recv(TMessage *&mess)
Receive a TMessage object.
Definition: TSocket.cxx:817
virtual Int_t RecvRaw(void *buffer, Int_t length, ESendRecvOptions opt=kDefault)
Receive a raw buffer of specified length bytes.
Definition: TSocket.cxx:897
virtual Int_t SendRaw(const void *buffer, Int_t length, ESendRecvOptions opt=kDefault)
Send a raw buffer of specified length.
Definition: TSocket.cxx:620
virtual Int_t SendObject(const TObject *obj, Int_t kind=kMESS_OBJECT)
Send an object.
Definition: TSocket.cxx:600
virtual Bool_t IsValid() const
Definition: TSocket.h:132
virtual Int_t Send(const TMessage &mess)
Send a TMessage object.
Definition: TSocket.cxx:522
Basic string class.
Definition: TString.h:131
TString & Insert(Ssiz_t pos, const char *s)
Definition: TString.h:644
const char * Data() const
Definition: TString.h:364
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:687
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition: TString.cxx:892
TString & Remove(Ssiz_t pos)
Definition: TString.h:668
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2289
static void ResetErrno()
Static function resetting system error number.
Definition: TSystem.cxx:274
static Int_t GetErrno()
Static function returning system error number.
Definition: TSystem.cxx:258
virtual TTime Now()
Get current time in milliseconds since 0:00 Jan 1 1995.
Definition: TSystem.cxx:461
virtual Int_t Exec(const char *shellcmd)
Execute a command.
Definition: TSystem.cxx:651
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:1393
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:1291
virtual const char * BaseName(const char *pathname)
Base name of a file name. Base name of /user/root is root.
Definition: TSystem.cxx:930
virtual Int_t GetEffectiveUid()
Returns the effective user id.
Definition: TSystem.cxx:1565
virtual void Sleep(UInt_t milliSec)
Sleep milliSec milli seconds.
Definition: TSystem.cxx:435
virtual char * Which(const char *search, const char *file, EAccessMode mode=kFileExists)
Find location of file in a search path.
Definition: TSystem.cxx:1541
virtual UserGroup_t * GetUserInfo(Int_t uid)
Returns all user info in the UserGroup_t structure.
Definition: TSystem.cxx:1594
const char * GetFile() const
Definition: TUrl.h:71
const char * GetUser() const
Definition: TUrl.h:67
const char * GetHost() const
Definition: TUrl.h:69
const char * GetHostFQDN() const
Return fully qualified domain name of url host.
Definition: TUrl.cxx:469
const char * GetOptions() const
Definition: TUrl.h:73
void SetHost(const char *host)
Definition: TUrl.h:86
Int_t GetPort() const
Definition: TUrl.h:80
TLine * line
const Int_t n
Definition: legend1.C:16
static constexpr double s
Definition: file.py:1
const char * cnt
Definition: TXMLSetup.cxx:74
static const char * what
Definition: stlLoader.cc:6
TString fUser
Definition: TSystem.h:140
auto * m
Definition: textangle.C:8