Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
TApplicationServer.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// TApplicationServer //
15// //
16// TApplicationServer is the remote application run by the roots main //
17// program. The input is taken from the socket connection to the client.//
18// //
19//////////////////////////////////////////////////////////////////////////
20
21#include "RConfigure.h"
22#include <ROOT/RConfig.hxx>
23#include "snprintf.h"
24#include <iostream>
25
26#ifdef WIN32
27 #include <io.h>
28 typedef long off_t;
29#endif
30#include <cstdlib>
31#include <cerrno>
32#include <ctime>
33#include <fcntl.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36
37#if (defined(__FreeBSD__) && (__FreeBSD__ < 4)) || \
38 (defined(__APPLE__) && (!defined(MAC_OS_X_VERSION_10_3) || \
39 (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3)))
40#include <sys/file.h>
41#define lockf(fd, op, sz) flock((fd), (op))
42#ifndef F_LOCK
43#define F_LOCK (LOCK_EX | LOCK_NB)
44#endif
45#ifndef F_ULOCK
46#define F_ULOCK LOCK_UN
47#endif
48#endif
49
50#include "RRemoteProtocol.h"
51
52#include "TApplicationServer.h"
53#include "TBenchmark.h"
54#include "TEnv.h"
55#include "TError.h"
56#include "TException.h"
57#include "TInterpreter.h"
58#include "TMD5.h"
59#include "TMessage.h"
60#include "TROOT.h"
61#include "TSocket.h"
62#include "TSystem.h"
63#include "TRemoteObject.h"
64#include "TUrl.h"
65#include "compiledata.h"
66#include "TClass.h"
67
68
69//----- Interrupt signal handler -----------------------------------------------
70////////////////////////////////////////////////////////////////////////////////
71
79
80////////////////////////////////////////////////////////////////////////////////
81/// Handle this interrupt
82
84{
85 fServ->HandleUrgentData();
86 if (TROOT::Initialized()) {
88 }
89 return kTRUE;
90}
91
92//----- SigPipe signal handler -------------------------------------------------
93////////////////////////////////////////////////////////////////////////////////
94
102
103////////////////////////////////////////////////////////////////////////////////
104/// Handle this signal
105
107{
108 fServ->HandleSigPipe();
109 return kTRUE;
110}
111
112//----- Input handler for messages from client -----------------------
113////////////////////////////////////////////////////////////////////////////////
114
117public:
120 Bool_t Notify() override;
121 Bool_t ReadNotify() override { return Notify(); }
122};
123
124////////////////////////////////////////////////////////////////////////////////
125/// Handle this input
126
128{
129 fServ->HandleSocketInput();
130 return kTRUE;
131}
132
133TString TASLogHandler::fgPfx = ""; // Default prefix to be prepended to messages
134////////////////////////////////////////////////////////////////////////////////
135/// Execute 'cmd' in a pipe and handle output messages from the related file
136
137TASLogHandler::TASLogHandler(const char *cmd, TSocket *s, const char *pfx)
138 : TFileHandler(-1, 1), fSocket(s), fPfx(pfx)
139{
141 fFile = 0;
142 if (s && cmd) {
143 fFile = gSystem->OpenPipe(cmd, "r");
144 if (fFile) {
145 SetFd(fileno(fFile));
146 // Notify what already in the file
147 Notify();
148 // Used in the destructor
150 } else {
151 fSocket = 0;
152 Error("TASLogHandler", "executing command in pipe");
153 }
154 } else {
155 Error("TASLogHandler",
156 "undefined command (%p) or socket (%p)", (int *)cmd, s);
157 }
158}
159////////////////////////////////////////////////////////////////////////////////
160/// Handle available message from the open file 'f'
161
162TASLogHandler::TASLogHandler(FILE *f, TSocket *s, const char *pfx)
163 : TFileHandler(-1, 1), fSocket(s), fPfx(pfx)
164{
166 fFile = 0;
167 if (s && f) {
168 fFile = f;
169 SetFd(fileno(fFile));
170 // Notify what already in the file
171 Notify();
172 } else {
173 Error("TASLogHandler", "undefined file (%p) or socket (%p)", f, s);
174 }
175}
176////////////////////////////////////////////////////////////////////////////////
177/// Handle available message in the open file
178
180{
181 if (TestBit(kFileIsPipe) && fFile)
182 gSystem->ClosePipe(fFile);
183 fFile = 0;
184 fSocket = 0;
186}
187////////////////////////////////////////////////////////////////////////////////
188/// Handle available message in the open file
189
191{
192 if (IsValid()) {
194 // Read buffer
195 char line[4096];
196 char *plf = 0;
197 while (fgets(line, sizeof(line), fFile)) {
198 if ((plf = strchr(line, '\n')))
199 *plf = 0;
200 // Send the message one level up
201 m.Reset(kMESS_ANY);
202 m << (Int_t)kRRT_Message;
203 if (fPfx.Length() > 0) {
204 // Prepend prefix specific to this instance
205 m << TString(Form("%s: %s", fPfx.Data(), line));
206 } else if (fgPfx.Length() > 0) {
207 // Prepend default prefix
208 m << TString(Form("%s: %s", fgPfx.Data(), line));
209 } else {
210 // Nothing to prepend
211 m << TString(line);
212 }
213 fSocket->Send(m);
214 }
215 }
216 return kTRUE;
217}
218////////////////////////////////////////////////////////////////////////////////
219/// Static method to set the default prefix
220
222{
223 fgPfx = pfx;
224}
225
226////////////////////////////////////////////////////////////////////////////////
227/// Init a guard for executing a command in a pipe
228
230 const char *pfx, Bool_t on)
231{
232 fExecHandler = 0;
233 if (cmd && on) {
234 fExecHandler = new TASLogHandler(cmd, s, pfx);
235 if (fExecHandler->IsValid()) {
236 gSystem->AddFileHandler(fExecHandler);
237 } else {
238 Error("TASLogHandlerGuard","invalid handler");
239 }
240 } else {
241 if (on)
242 Error("TASLogHandlerGuard","undefined command");
243 }
244}
245
246////////////////////////////////////////////////////////////////////////////////
247/// Init a guard for executing a command in a pipe
248
250 const char *pfx, Bool_t on)
251{
252 fExecHandler = 0;
253 if (f && on) {
254 fExecHandler = new TASLogHandler(f, s, pfx);
255 if (fExecHandler->IsValid()) {
256 gSystem->AddFileHandler(fExecHandler);
257 } else {
258 Error("TASLogHandlerGuard","invalid handler");
259 }
260 } else {
261 if (on)
262 Error("TASLogHandlerGuard","undefined file");
263 }
264}
265
266////////////////////////////////////////////////////////////////////////////////
267/// Close a guard for executing a command in a pipe
268
270{
271 if (fExecHandler && fExecHandler->IsValid()) {
272 gSystem->RemoveFileHandler(fExecHandler);
274 }
275}
276
277
278////////////////////////////////////////////////////////////////////////////////
279/// Main constructor. Create an application environment. The TApplicationServer
280/// environment provides an eventloop via inheritance of TApplication.
281
283 FILE *flog, const char *logfile)
284 : TApplication("server", argc, argv, 0, -1)
285{
286 // Parse options
287 GetOptions(argc, argv);
288
289 // Abort on higher than kSysError's and set error handler
292
294 fSocket = 0;
295 fWorkingDir = 0;
296
297 fLogFilePath = logfile;
298 fLogFile = flog;
299 fLogFileDes = -1;
300 if (!fLogFile || (fLogFileDes = fileno(fLogFile)) < 0)
301 // For some reason we failed setting a redirection; we cannot continue
302 Terminate(0);
304 fSentCanvases = 0;
305
306 // Default prefix for notifications
307 TASLogHandler::SetDefaultPrefix(Form("roots:%s", gSystem->HostName()));
308
309 // Now we contact back the client: if we fail we set ourselves
310 // as invalid
312
313 if (!(fSocket = new TSocket(GetHost(), GetPort()))) {
314 Terminate(0);
315 return;
316 }
317 Int_t sock = fSocket->GetDescriptor();
318
319 if (Setup() != 0) {
320 Error("TApplicationServer", "failed to setup - quitting");
321 SendLogFile(-98);
322 Terminate(0);
323 }
324
325 // Everybody expects std::iostream to be available, so load it...
326 ProcessLine("#include <iostream>", kTRUE);
327 ProcessLine("#include <string>",kTRUE); // for std::string std::iostream.
328
329 // Load user functions
330 const char *logon;
331 logon = gEnv->GetValue("Rint.Load", (char *)0);
332 if (logon) {
333 char *mac = gSystem->Which(TROOT::GetMacroPath(), logon, kReadPermission);
334 if (mac)
335 ProcessLine(Form(".L %s", logon), kTRUE);
336 delete [] mac;
337 }
338
339 // Execute logon macro
340 ExecLogon();
341
342 // Init benchmarking
343 gBenchmark = new TBenchmark();
344
345 // Save current interpreter context
346 gInterpreter->SaveContext();
347 gInterpreter->SaveGlobalsContext();
348
349 // Install interrupt and message input handlers
350 gSystem->AddSignalHandler(new TASInterruptHandler(this));
351 gSystem->AddFileHandler(new TASInputHandler(this, sock));
352
353 // We are done
354 fIsValid = kTRUE;
355
356 // Startup notification
358 SendLogFile();
359}
360
361////////////////////////////////////////////////////////////////////////////////
362/// Print the Remote Server logo on standard output.
363/// Return 0 on success, -1 on failure
364
366{
367 char str[512];
368 snprintf(str, 512, "**** Remote session @ %s started ****", gSystem->HostName());
369 if (fSocket->Send(str) != 1+static_cast<Int_t>(strlen(str))) {
370 Error("Setup", "failed to send startup message");
371 return -1;
372 }
373
374 // Send our protocol level to the client
375 if (fSocket->Send(kRRemote_Protocol, kROOTD_PROTOCOL) != 2*sizeof(Int_t)) {
376 Error("Setup", "failed to send local protocol");
377 return -1;
378 }
379
380 // Send the host name and full path to log file
381 TMessage msg(kMESS_ANY);
382 msg << TString(gSystem->HostName()) << fLogFilePath;
383 fSocket->Send(msg);
384
385 // Set working directory
386 fWorkDir = gSystem->WorkingDirectory();
387 if (strlen(fUrl.GetFile()) > 0) {
388 fWorkDir = fUrl.GetFile();
389 gSystem->ExpandPathName(fWorkDir);
390 }
391
392 // Go to working dir
393 if (gSystem->AccessPathName(fWorkDir)) {
394 gSystem->mkdir(fWorkDir, kTRUE);
395 if (!gSystem->ChangeDirectory(fWorkDir)) {
396 SysError("Setup", "can not change to directory %s",
397 fWorkDir.Data());
398 }
399 } else {
400 if (!gSystem->ChangeDirectory(fWorkDir)) {
401 gSystem->Unlink(fWorkDir);
402 gSystem->mkdir(fWorkDir, kTRUE);
403 if (!gSystem->ChangeDirectory(fWorkDir)) {
404 SysError("Setup", "can not change to directory %s",
405 fWorkDir.Data());
406 }
407 }
408 }
409
410#if 0 // G.Ganis May 11, 2007
411 // This needs to be fixed: we disable for the time being
412 // Socket options: incoming OOB should generate a SIGURG
413 if (fSocket->SetOption(kProcessGroup, (-1)*gSystem->GetPid()) != 0)
414 SysWarning("Setup", "failed to enable SIGURG generation on incoming OOB");
415#endif
416
417 // Send messages off immediately to reduce latency
418 if (fSocket->SetOption(kNoDelay, 1) != 0) {}
419 //SysWarning("Setup", "failed to set no-delay option on input socket");
420
421 // Check every two hours if client is still alive
422 if (fSocket->SetOption(kKeepAlive, 1) != 0) {}
423 //SysWarning("Setup", "failed to set keepalive option on input socket");
424
425 // Install SigPipe handler to handle kKeepAlive failure
426 gSystem->AddSignalHandler(new TASSigPipeHandler(this));
427
428 // Done
429 return 0;
430}
431
432////////////////////////////////////////////////////////////////////////////////
433/// Cleanup. Not really necessary since after this dtor there is no
434/// live anyway.
435
443
444////////////////////////////////////////////////////////////////////////////////
445/// Get and handle command line options. Fixed format:
446/// "protocol url"
447
448void TApplicationServer::GetOptions(Int_t *argc, char **argv)
449{
450 if (*argc < 4) {
451 Fatal("GetOptions", "must be started with 4 arguments");
452 gSystem->Exit(1);
453 }
454
455 // Protocol run by the client
456 fProtocol = TString(argv[1]).Atoi();
457
458 // Client URL
459 fUrl.SetUrl(argv[2]);
460
461 // Debug level
462 gDebug = 0;
463 TString argdbg(argv[3]);
464 if (argdbg.BeginsWith("-d=")) {
465 argdbg.ReplaceAll("-d=","");
466 gDebug = argdbg.Atoi();
467 }
468}
469
470////////////////////////////////////////////////////////////////////////////////
471/// Main server eventloop.
472
474{
475 // Setup the server
476 if (fIsValid) {
477 // Run the main event loop
478 TApplication::Run(retrn);
479 } else {
480 Error("Run", "invalid instance: cannot Run()");
481 gSystem->Exit(1);
482 }
483}
484
485////////////////////////////////////////////////////////////////////////////////
486/// Handle input coming from the client or from the master server.
487
489{
490 TMessage *mess;
491 char str[2048];
492 Int_t what;
493
494 if (fSocket->Recv(mess) <= 0) {
495 // Pending: do something more intelligent here
496 // but at least get a message in the log file
497 Error("HandleSocketInput", "retrieving message from input socket");
498 Terminate(0);
499 return;
500 }
501
502 what = mess->What();
503 if (gDebug > 0)
504 Info("HandleSocketInput", "got message of type %d", what);
505
506 switch (what) {
507
508 case kMESS_CINT:
510 mess->ReadString(str, sizeof(str));
511 if (gDebug > 1)
512 Info("HandleSocketInput:kMESS_CINT", "processing: %s...", str);
513 ProcessLine(str);
514 }
515 SendCanvases();
516 SendLogFile();
517 break;
518
519 case kMESS_STRING:
520 mess->ReadString(str, sizeof(str));
521 break;
522
523 case kMESS_OBJECT:
524 mess->ReadObject(mess->GetClass());
525 break;
526
527 case kMESS_ANY:
528 {
529 Int_t type;
530 (*mess) >> type;
531 switch (type) {
532 case kRRT_Reset:
533 mess->ReadString(str, sizeof(str));
534 Reset(str);
535 break;
536
537 case kRRT_CheckFile:
538 // Handle file checking request
539 HandleCheckFile(mess);
540 break;
541
542 case kRRT_File:
543 // A file follows
544 mess->ReadString(str, sizeof(str));
545 { char name[2048], i1[20], i2[40];
546 sscanf(str, "%2047s %19s %39s", name, i1, i2);
547 Int_t bin = atoi(i1);
548 Long_t size = atol(i2);
549 ReceiveFile(name, bin ? kTRUE : kFALSE, size);
550 }
551 break;
552
553 case kRRT_Terminate:
554 // Terminate the session (will not return from here)
555 Int_t status;
556 (*mess) >> status;
557 Terminate(status);
558 break;
559
560 default:
561 break;
562 }
563 }
564 SendLogFile();
565 break;
566 default:
567 Warning("HandleSocketInput","message type unknown (%d)", what);
568 SendLogFile();
569 break;
570 }
571
572 delete mess;
573}
574
575////////////////////////////////////////////////////////////////////////////////
576/// Handle Out-Of-Band data sent by the master or client.
577
579{
580 char oob_byte;
581 Int_t n, nch, wasted = 0;
582
583 const Int_t kBufSize = 1024;
584 char waste[kBufSize];
585
586 // Real-time notification of messages
588
589 Info("HandleUrgentData", "handling oob...");
590
591 // Receive the OOB byte
592 while ((n = fSocket->RecvRaw(&oob_byte, 1, kOob)) < 0) {
593 if (n == -2) { // EWOULDBLOCK
594 //
595 // The OOB data has not yet arrived: flush the input stream
596 //
597 // In some systems (Solaris) regular recv() does not return upon
598 // receipt of the oob byte, which makes the below call to recv()
599 // block indefinitely if there are no other data in the queue.
600 // FIONREAD ioctl can be used to check if there are actually any
601 // data to be flushed. If not, wait for a while for the oob byte
602 // to arrive and try to read it again.
603 //
604 fSocket->GetOption(kBytesToRead, nch);
605 if (nch == 0) {
606 gSystem->Sleep(1000);
607 continue;
608 }
609
610 if (nch > kBufSize) nch = kBufSize;
611 n = fSocket->RecvRaw(waste, nch);
612 if (n <= 0) {
613 Error("HandleUrgentData", "error receiving waste");
614 break;
615 }
616 wasted = 1;
617 } else {
618 Error("HandleUrgentData", "error receiving OOB (n = %d)",n);
619 return;
620 }
621 }
622
623 Info("HandleUrgentData", "got OOB byte: %d\n", oob_byte);
624
625 switch (oob_byte) {
626
627 case kRRI_Hard:
628 Info("HandleUrgentData", "*** Hard Interrupt");
629
630 // Flush input socket
631 while (1) {
632 Int_t atmark;
633
634 fSocket->GetOption(kAtMark, atmark);
635
636 if (atmark) {
637 // Send the OOB byte back so that the client knows where
638 // to stop flushing its input stream of obsolete messages
639 n = fSocket->SendRaw(&oob_byte, 1, kOob);
640 if (n <= 0)
641 Error("HandleUrgentData", "error sending OOB");
642 break;
643 }
644
645 // find out number of bytes to read before atmark
646 fSocket->GetOption(kBytesToRead, nch);
647 if (nch == 0) {
648 gSystem->Sleep(1000);
649 continue;
650 }
651
652 if (nch > kBufSize) nch = kBufSize;
653 n = fSocket->RecvRaw(waste, nch);
654 if (n <= 0) {
655 Error("HandleUrgentData", "error receiving waste (2)");
656 break;
657 }
658 }
659
660 SendLogFile();
661
662 break;
663
664 case kRRI_Soft:
665 Info("HandleUrgentData", "Soft Interrupt");
666
667 if (wasted) {
668 Error("HandleUrgentData", "soft interrupt flushed stream");
669 break;
670 }
671
672 Interrupt();
673
674 SendLogFile();
675
676 break;
677
678 case kRRI_Shutdown:
679 Info("HandleUrgentData", "Shutdown Interrupt");
680
681 Terminate(0);
682
683 break;
684
685 default:
686 Error("HandleUrgentData", "unexpected OOB byte");
687 break;
688 }
689}
690
691////////////////////////////////////////////////////////////////////////////////
692/// Called when the client is not alive anymore (i.e. when kKeepAlive
693/// has failed).
694
696{
697 // Real-time notification of messages
699
700 Info("HandleSigPipe", "client died");
701 Terminate(0); // will not return from here....
702}
703
704////////////////////////////////////////////////////////////////////////////////
705/// Reset environment to be ready for execution of next command.
706
707void TApplicationServer::Reset(const char *dir)
708{
709 // First go to new directory.
710 gDirectory->cd(dir);
711
712 // Clear interpreter environment.
713 gROOT->Reset();
714
715 // Make sure current directory is empty (don't delete anything when
716 // we happen to be in the ROOT memory only directory!?)
717 if (gDirectory != gROOT) {
718 gDirectory->Delete();
719 }
720}
721
722////////////////////////////////////////////////////////////////////////////////
723/// Receive a file, either sent by a client or a master server.
724/// If bin is true it is a binary file, other wise it is an ASCII
725/// file and we need to check for Windows \\r tokens. Returns -1 in
726/// case of error, 0 otherwise.
727
729{
730 if (size <= 0) return 0;
731
732 // open file, overwrite already existing file
733 Int_t fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600);
734 if (fd < 0) {
735 SysError("ReceiveFile", "error opening file %s", file);
736 return -1;
737 }
738
739 const Int_t kMAXBUF = 16384; //32768 //16384 //65536;
740 char buf[kMAXBUF];
741
742 Int_t left, r;
743 Long64_t filesize = 0;
744
745 while (filesize < size) {
746 left = Int_t(size - filesize);
747 if (left > kMAXBUF)
748 left = kMAXBUF;
749 r = fSocket->RecvRaw(&buf, left);
750 if (r > 0) {
751 char *p = buf;
752
753 filesize += r;
754 while (r) {
755 Int_t w;
756
757 if (!bin) {
758 Int_t j = 0;
759 for (Int_t i = 0; i < r; ++i) {
760 if (p[i] != '\r') {
761 p[j++] = p[i];
762 }
763 }
764 w = write(fd, p, j);
765 } else {
766 w = write(fd, p, r);
767 }
768
769 if (w < 0) {
770 SysError("ReceiveFile", "error writing to file %s", file);
771 close(fd);
772 return -1;
773 }
774 r -= w;
775 p += w;
776 }
777 } else if (r < 0) {
778 Error("ReceiveFile", "error during receiving file %s", file);
779 close(fd);
780 return -1;
781 }
782 }
783
784 close(fd);
785
786 chmod(file, 0666);
787
788 return 0;
789}
790
791////////////////////////////////////////////////////////////////////////////////
792/// Send log file to master.
793/// If start > -1 send only bytes in the range from start to end,
794/// if end <= start send everything from start.
795
797{
798 // Determine the number of bytes left to be read from the log file.
799 fflush(stdout);
800
801 off_t ltot=0, lnow=0;
802 Int_t left = -1;
803 Bool_t adhoc = kFALSE;
804
805 if (fLogFileDes > -1) {
806 ltot = lseek(fileno(stdout), (off_t) 0, SEEK_END);
807 lnow = lseek(fLogFileDes, (off_t) 0, SEEK_CUR);
808 if (lnow == -1) {
809 SysError("SendLogFile", "lseek failed");
810 lnow = 0;
811 }
812
813 if (start > -1) {
814 lseek(fLogFileDes, (off_t) start, SEEK_SET);
815 if (end <= start || end > ltot)
816 end = ltot;
817 left = (Int_t)(end - start);
818 if (end < ltot)
819 left++;
820 adhoc = kTRUE;
821 } else {
822 left = (Int_t)(ltot - lnow);
823 }
824 }
825
827
828 if (left > 0) {
829
830 m << (Int_t)kRRT_LogFile << left;
831 fSocket->Send(m);
832
833 const Int_t kMAXBUF = 32768; //16384 //65536;
834 char buf[kMAXBUF];
835 Int_t wanted = (left > kMAXBUF) ? kMAXBUF : left;
836 Int_t len;
837 do {
838 while ((len = read(fLogFileDes, buf, wanted)) < 0 &&
839 TSystem::GetErrno() == EINTR)
841
842 if (len < 0) {
843 SysError("SendLogFile", "error reading log file");
844 break;
845 }
846
847 if (end == ltot && len == wanted)
848 buf[len-1] = '\n';
849
850 if (fSocket->SendRaw(buf, len) < 0) {
851 SysError("SendLogFile", "error sending log file");
852 break;
853 }
854
855 // Update counters
856 left -= len;
857 wanted = (left > kMAXBUF) ? kMAXBUF : left;
858
859 } while (len > 0 && left > 0);
860 }
861
862 // Restore initial position if partial send
863 if (adhoc)
864 lseek(fLogFileDes, lnow, SEEK_SET);
865
866 m.Reset();
867 m << (Int_t)kRRT_LogDone << status;
868
869 fSocket->Send(m);
870}
871
872////////////////////////////////////////////////////////////////////////////////
873/// Send any created canvas to client
874
876{
877 Int_t nc = 0;
878
879 // Send back new canvases
881 TIter next(gROOT->GetListOfCanvases());
882 TObject *o = 0;
883 while ((o = next())) {
884 if (!fSentCanvases)
885 fSentCanvases = new TList;
886 Bool_t sentalready = kFALSE;
887 // We cannot use FindObject here because there may be invalid
888 // objects in the send list (i.e. deleted canvases)
889 TObjLink *lnk = fSentCanvases->FirstLink();
890 while (lnk) {
891 TObject *sc = lnk->GetObject();
892 lnk = lnk->Next();
893 if ((!ROOT::Detail::HasBeenDeleted(sc)) && sc == o)
894 sentalready = kTRUE;
895 }
896 if (!sentalready) {
897 if (gDebug > 0)
898 Info("SendCanvases","new canvas found: %p", o);
899 mess.Reset(kMESS_OBJECT);
900 mess.WriteObject(o);
901 fSocket->Send(mess);
902 nc++;
903 fSentCanvases->Add(o);
904 }
905 }
906 return nc;
907}
908
909////////////////////////////////////////////////////////////////////////////////
910/// Browse directory and send back its content to client.
911
913{
914 Int_t nc = 0;
915
917 if (!fWorkingDir || !dirname || !*dirname) {
918 if (!fWorkingDir)
919 fWorkingDir = new TRemoteObject(fWorkDir, fWorkDir, "TSystemDirectory");
920 TList *list = fWorkingDir->Browse();
921 mess.Reset(kMESS_OBJECT);
923 fSocket->Send(mess);
924 nc++;
925 list->Delete();
926 delete list;
927 }
928 else if (fWorkingDir) {
929 TRemoteObject dir(dirname, dirname, "TSystemDirectory");
930 TList *list = dir.Browse();
931 mess.Reset(kMESS_OBJECT);
932 mess.WriteObject(list);
933 fSocket->Send(mess);
934 nc++;
935 list->Delete();
936 delete list;
937 }
938 return nc;
939}
940
941////////////////////////////////////////////////////////////////////////////////
942/// Browse root file and send back its content;
943/// if fname is null, send the full list of files.
944
946{
947 Int_t nc = 0;
948
949 TList *list = new TList;
951 if (!fname || !*fname) {
952 // fname is null, so send the list of files.
953 TIter next(gROOT->GetListOfFiles());
954 TNamed *fh = 0;
955 TRemoteObject *robj;
956 while ((fh = (TNamed *)next())) {
957 robj = new TRemoteObject(fh->GetName(), fh->GetTitle(), "TFile");
958 list->Add(robj);
959 }
960 if (list->GetEntries() > 0) {
961 mess.Reset(kMESS_OBJECT);
962 mess.WriteObject(list);
963 fSocket->Send(mess);
964 nc++;
965 }
966 }
967 else {
968 // get Root file content and send the list of objects
969 TDirectory *fh = (TDirectory *)gROOT->GetListOfFiles()->FindObject(fname);
970 if (fh) {
971 fh->cd();
972 TRemoteObject dir(fh->GetName(), fh->GetTitle(), "TFile");
973 TList *keylist = (TList *)gROOT->ProcessLine(Form("((TFile *)0x%zx)->GetListOfKeys();", (size_t)fh));
974 TIter nextk(keylist);
975 TNamed *key = 0;
976 TRemoteObject *robj;
977 while ((key = (TNamed *)nextk())) {
978 robj = new TRemoteObject(key->GetName(), key->GetTitle(), "TKey");
979 const char *classname = (const char *)gROOT->ProcessLine(Form("((TKey *)0x%zx)->GetClassName();", (size_t)key));
980 robj->SetKeyClassName(classname);
981 Bool_t isFolder = (Bool_t)gROOT->ProcessLine(Form("((TKey *)0x%zx)->IsFolder();", (size_t)key));
982 robj->SetFolder(isFolder);
983 robj->SetRemoteAddress((Longptr_t) key);
984 list->Add(robj);
985 }
986 if (list->GetEntries() > 0) {
987 mess.Reset(kMESS_OBJECT);
988 mess.WriteObject(list);
989 fSocket->Send(mess);
990 nc++;
991 }
992 }
993 }
994
995 list->Delete();
996 delete list;
997
998 return nc;
999}
1000
1001////////////////////////////////////////////////////////////////////////////////
1002/// Read key object and send it back to client.
1003
1005{
1006 Int_t nc = 0;
1007
1008 TMessage mess(kMESS_OBJECT);
1009 TNamed *obj = (TNamed *)gROOT->ProcessLine(Form("gFile->GetKey(\"%s\")->ReadObj();", keyname));
1010 if (obj) {
1011 mess.Reset(kMESS_OBJECT);
1012 mess.WriteObject(obj);
1013 fSocket->Send(mess);
1014 nc++;
1015 }
1016 return nc;
1017}
1018
1019////////////////////////////////////////////////////////////////////////////////
1020/// Terminate the server.
1021
1023{
1024 // Close and remove the log file; remove the cleanup script
1025 if (fLogFile) {
1026 fclose(fLogFile);
1027 // Delete the log file unless we are in debug mode
1028 if (gDebug <= 0)
1029 gSystem->Unlink(fLogFilePath);
1030 TString cleanup = fLogFilePath;
1031 cleanup.ReplaceAll(".log", ".cleanup");
1032 gSystem->Unlink(cleanup);
1033 }
1034
1035 // Remove input handler to avoid spurious signals in socket
1036 // selection for closing activities executed upon exit()
1037 TIter next(gSystem->GetListOfFileHandlers());
1038 TObject *fh = 0;
1039 while ((fh = next())) {
1040 TASInputHandler *ih = dynamic_cast<TASInputHandler *>(fh);
1041 if (ih)
1042 gSystem->RemoveFileHandler(ih);
1043 }
1044
1045 // Stop processing events
1046 gSystem->Exit(status);
1047}
1048
1049////////////////////////////////////////////////////////////////////////////////
1050/// Handle file checking request.
1051
1053{
1054 TString filenam;
1055 TMD5 md5;
1057
1058 // Parse message
1059 (*mess) >> filenam >> md5;
1060
1061 // check file in working directory
1062 TMD5 *md5local = TMD5::FileChecksum(filenam);
1063 if (md5local && md5 == (*md5local)) {
1064 // We have an updated copy of the file
1065 m << (Int_t) kRRT_CheckFile << (Bool_t) kTRUE;
1066 fSocket->Send(m);
1067 if (gDebug > 0)
1068 Info("HandleCheckFile", "up-to-date version of %s available", filenam.Data());
1069 } else {
1071 fSocket->Send(m);
1072 if (gDebug > 0)
1073 Info("HandleCheckFile", "file %s needs to be uploaded", filenam.Data());
1074 }
1075 delete md5local;
1076}
1077
1078////////////////////////////////////////////////////////////////////////////////
1079/// The error handler function. It prints the message on stderr and
1080/// if abort is set it aborts the application.
1081
1082void TApplicationServer::ErrorHandler(Int_t level, Bool_t abort, const char *location,
1083 const char *msg)
1084{
1085 if (gErrorIgnoreLevel == kUnset) {
1087 if (gEnv) {
1088 TString slevel = gEnv->GetValue("Root.ErrorIgnoreLevel", "Print");
1089 if (!slevel.CompareTo("Print", TString::kIgnoreCase))
1091 else if (!slevel.CompareTo("Info", TString::kIgnoreCase))
1093 else if (!slevel.CompareTo("Warning", TString::kIgnoreCase))
1095 else if (!slevel.CompareTo("Error", TString::kIgnoreCase))
1097 else if (!slevel.CompareTo("Break", TString::kIgnoreCase))
1099 else if (!slevel.CompareTo("SysError", TString::kIgnoreCase))
1101 else if (!slevel.CompareTo("Fatal", TString::kIgnoreCase))
1103 }
1104 }
1105
1106 if (level < gErrorIgnoreLevel)
1107 return;
1108
1109 static TString syslogService;
1110
1111 if (syslogService.IsNull()) {
1112 syslogService = "server";
1113 gSystem->Openlog(syslogService, kLogPid | kLogCons, kLogLocal5);
1114 }
1115
1116 const char *type = 0;
1117 ELogLevel loglevel = kLogInfo;
1118
1119 if (level >= kPrint) {
1120 loglevel = kLogInfo;
1121 type = "Print";
1122 }
1123 if (level >= kInfo) {
1124 loglevel = kLogInfo;
1125 type = "Info";
1126 }
1127 if (level >= kWarning) {
1128 loglevel = kLogWarning;
1129 type = "Warning";
1130 }
1131 if (level >= kError) {
1132 loglevel = kLogErr;
1133 type = "Error";
1134 }
1135 if (level >= kBreak) {
1136 loglevel = kLogErr;
1137 type = "*** Break ***";
1138 }
1139 if (level >= kSysError) {
1140 loglevel = kLogErr;
1141 type = "SysError";
1142 }
1143 if (level >= kFatal) {
1144 loglevel = kLogErr;
1145 type = "Fatal";
1146 }
1147
1148 TString node = "server";
1149 TString buf;
1150
1151 if (!location || !location[0] ||
1152 (level >= kPrint && level < kInfo) ||
1153 (level >= kBreak && level < kSysError)) {
1154 fprintf(stderr, "%s on %s: %s\n", type, node.Data(), msg);
1155 buf.Form("%s:%s:%s", node.Data(), type, msg);
1156 } else {
1157 fprintf(stderr, "%s in <%s> on %s: %s\n", type, location, node.Data(), msg);
1158 buf.Form("%s:%s:<%s>:%s", node.Data(), type, location, msg);
1159 }
1160 fflush(stderr);
1161
1162 gSystem->Syslog(loglevel, buf);
1163
1164 if (abort) {
1165 fprintf(stderr, "aborting\n");
1166 fflush(stderr);
1167 gSystem->StackTrace();
1168 gSystem->Abort();
1169 }
1170}
1171
1172////////////////////////////////////////////////////////////////////////////////
1173/// Parse a command line received from the client, making sure that the files
1174/// needed for the execution, if any, are available. The line is either a C++
1175/// statement or an interpreter command starting with a ".".
1176/// Return the return value of the command casted to a long.
1177
1179{
1180 if (!line || !*line) return 0;
1181
1182 // If load or execute request we must make sure that we have the files.
1183 // If not we ask the client to send them, blocking until we have everything.
1184 if (!strncmp(line, ".L", 2) || !strncmp(line, ".U", 2) ||
1185 !strncmp(line, ".X", 2) || !strncmp(line, ".x", 2)) {
1186 TString aclicMode;
1187 TString arguments;
1188 TString io;
1189 TString fname = gSystem->SplitAclicMode(line+3, aclicMode, arguments, io);
1190
1191 char *imp = gSystem->Which(TROOT::GetMacroPath(), fname, kReadPermission);
1192 if (!imp) {
1193
1194 // Make sure that we can write in the directory where we are
1195 if (gSystem->AccessPathName(gSystem->WorkingDirectory(), kWritePermission)) {
1196 Error("ProcessLine","no write permission in %s", gSystem->WorkingDirectory());
1197 return 0;
1198 }
1199
1200 if (gDebug > 0)
1201 Info("ProcessLine", "macro %s not found in path %s: asking the client",
1202 fname.Data(), TROOT::GetMacroPath());
1204 m << (Int_t) kRRT_SendFile << TString(gSystem->BaseName(fname));
1205 fSocket->Send(m);
1206
1207 // Wait for the reply(ies)
1208 Int_t type;
1209 Bool_t filefollows = kTRUE;
1210
1211 while (filefollows) {
1212
1213 // Get a message
1214 TMessage *rm = 0;
1215 if (fSocket->Recv(rm) <= 0) {
1216 Error("ProcessLine","ask-file: received empty message from client");
1217 return 0;
1218 }
1219 if (rm->What() != kMESS_ANY) {
1220 Error("ProcessLine","ask-file: wrong message received (what: %d)", rm->What());
1221 return 0;
1222 }
1223 (*rm) >> type;
1224 if (type != kRRT_SendFile) {
1225 Error("ProcessLine","ask-file: wrong sub-type received (type: %d)", type);
1226 return 0;
1227 }
1228 (*rm) >> filefollows;
1229 if (filefollows) {
1230 // Read the file specifications
1231 if (fSocket->Recv(rm) <= 0) {
1232 Error("ProcessLine","file: received empty message from client");
1233 return 0;
1234 }
1235 if (rm->What() != kMESS_ANY) {
1236 Error("ProcessLine","file: wrong message received (what: %d)", rm->What());
1237 return 0;
1238 }
1239 (*rm) >> type;
1240 if (type != kRRT_File) {
1241 Error("ProcessLine","file: wrong sub-type received (type: %d)", type);
1242 return 0;
1243 }
1244 // A file follows
1245 char str[2048];
1246 rm->ReadString(str, sizeof(str));
1247 char name[2048], i1[20], i2[40];
1248 sscanf(str, "%2047s %19s %39s", name, i1, i2);
1249 Int_t bin = atoi(i1);
1250 Long_t size = atol(i2);
1251 ReceiveFile(name, bin ? kTRUE : kFALSE, size);
1252 }
1253 }
1254 }
1255 delete [] imp;
1256 }
1257
1258 // Process the line now
1260}
1261
1262////////////////////////////////////////////////////////////////////////////////
1263/// Execute logon macro's. There are three levels of logon macros that
1264/// will be executed: the system logon etc/system.rootlogon.C, the global
1265/// user logon ~/.rootlogon.C and the local ./.rootlogon.C. For backward
1266/// compatibility also the logon macro as specified by the Rint.Logon
1267/// environment setting, by default ./rootlogon.C, will be executed.
1268/// No logon macros will be executed when the system is started with
1269/// the -n option.
1270
1272{
1273 if (NoLogOpt()) return;
1274
1275 TString name = ".rootlogon.C";
1276 TString sname = "system";
1277 sname += name;
1278 TString temp_sname = sname;
1279 TString temp_name = name;
1280 const char *s = gSystem->PrependPathName(TROOT::GetEtcDir(), temp_sname);
1281 if (!gSystem->AccessPathName(s, kReadPermission)) {
1282 ProcessFile(s);
1283 }
1284 s = gSystem->PrependPathName(gSystem->HomeDirectory(), temp_name);
1285 if (!gSystem->AccessPathName(s, kReadPermission)) {
1286 ProcessFile(s);
1287 }
1288 // avoid executing ~/.rootlogon.C twice
1289 if (strcmp(gSystem->HomeDirectory(), gSystem->WorkingDirectory())) {
1290 if (!gSystem->AccessPathName(name, kReadPermission))
1292 }
1293
1294 // execute also the logon macro specified by "Rint.Logon"
1295 const char *logon = gEnv->GetValue("Rint.Logon", (char*)0);
1296 if (logon) {
1297 char *mac = gSystem->Which(TROOT::GetMacroPath(), logon, kReadPermission);
1298 if (mac)
1299 ProcessFile(logon);
1300 delete [] mac;
1301 }
1302}
@ kMESS_STRING
@ kMESS_ANY
@ kMESS_OBJECT
@ kROOTD_PROTOCOL
@ kMESS_CINT
ROOT::R::TRInterface & r
Definition Object.C:4
#define SafeDelete(p)
Definition RConfig.hxx:525
const Int_t kRRemote_Protocol
@ kRRT_Terminate
@ kRRT_LogFile
@ kRRT_CheckFile
@ kRRT_File
@ kRRT_LogDone
@ kRRT_Message
@ kRRT_SendFile
@ kRRT_Reset
@ kRRI_Shutdown
@ kRRI_Soft
@ kRRI_Hard
#define f(i)
Definition RSha256.hxx:104
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
start
Definition Rotated.cxx:223
int Int_t
Signed integer 4 bytes (int).
Definition RtypesCore.h:59
long Longptr_t
Integer large enough to hold a pointer (platform-dependent).
Definition RtypesCore.h:89
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:68
bool Bool_t
Boolean (0=false, 1=true) (bool).
Definition RtypesCore.h:77
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:83
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
externTBenchmark * gBenchmark
Definition TBenchmark.h:59
Error("WriteTObject","The current directory (%s) is not associated with a file. The object (%s) has not been written.", GetName(), objname)
#define gDirectory
Definition TDirectory.h:385
externTEnv * gEnv
Definition TEnv.h:170
constexpr Int_t kError
Definition TError.h:47
constexpr Int_t kFatal
Definition TError.h:50
constexpr Int_t kWarning
Definition TError.h:46
constexpr Int_t kBreak
Definition TError.h:48
externInt_t gErrorAbortLevel
non-ignored errors with level equal or above this value will call abort(). Default is kSysError+1.
Definition TError.h:141
constexpr Int_t kPrint
Definition TError.h:44
constexpr Int_t kInfo
Definition TError.h:45
constexpr Int_t kSysError
Definition TError.h:49
externInt_t gErrorIgnoreLevel
errors with level below this value will be ignored. Default is kUnset.
Definition TError.h:140
ErrorHandlerFunc_t SetErrorHandler(ErrorHandlerFunc_t newhandler)
Set an errorhandler function. Returns the old handler.
Definition TError.cxx:92
constexpr Int_t kUnset
Definition TError.h:43
R__EXTERN void Throw(int code)
If an exception context has been set (using the TRY and RETRY macros) jump back to where it was set.
char name[80]
Definition TGX11.cxx:148
#define gInterpreter
Int_t gDebug
Definition TROOT.cxx:777
#define gROOT
Definition TROOT.h:417
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2496
@ kSigPipe
@ kSigUrgent
@ kKeepAlive
Definition TSystem.h:233
@ kBytesToRead
Definition TSystem.h:239
@ kNoDelay
Definition TSystem.h:235
@ kProcessGroup
Definition TSystem.h:237
@ kAtMark
Definition TSystem.h:238
@ kOob
Definition TSystem.h:244
@ kReadPermission
Definition TSystem.h:55
@ kWritePermission
Definition TSystem.h:54
@ kLogLocal5
Definition TSystem.h:80
ELogLevel
Definition TSystem.h:63
@ kLogWarning
Definition TSystem.h:68
@ kLogInfo
Definition TSystem.h:70
@ kLogErr
Definition TSystem.h:67
externTSystem * gSystem
Definition TSystem.h:582
@ kLogPid
Definition TSystem.h:59
@ kLogCons
Definition TSystem.h:60
#define snprintf
Definition civetweb.c:1579
Bool_t Notify() override
Handle this input.
TASInputHandler(TApplicationServer *s, Int_t fd)
TApplicationServer * fServ
Bool_t ReadNotify() override
Notify when something can be read from the descriptor associated with this handler.
TApplicationServer * fServ
Bool_t Notify() override
Handle this interrupt.
TASInterruptHandler(TApplicationServer *s)
TASLogHandlerGuard(const char *cmd, TSocket *s, const char *pfx="", Bool_t on=kTRUE)
Init a guard for executing a command in a pipe.
virtual ~TASLogHandlerGuard()
Close a guard for executing a command in a pipe.
TASLogHandler * fExecHandler
virtual ~TASLogHandler()
Handle available message in the open file.
Bool_t Notify() override
Handle available message in the open file.
static TString fgPfx
TASLogHandler(const char *cmd, TSocket *s, const char *pfx="")
Execute 'cmd' in a pipe and handle output messages from the related file.
static void SetDefaultPrefix(const char *pfx)
Static method to set the default prefix.
Bool_t Notify() override
Handle this signal.
TApplicationServer * fServ
TASSigPipeHandler(TApplicationServer *s)
void Run(Bool_t retrn=kFALSE) override
Main server eventloop.
void ExecLogon()
Execute logon macro's.
Int_t SendCanvases()
Send any created canvas to client.
Longptr_t ProcessLine(const char *line, Bool_t=kFALSE, Int_t *err=nullptr) override
Parse a command line received from the client, making sure that the files needed for the execution,...
TApplicationServer(Int_t *argc, char **argv, FILE *flog, const char *logfile)
Main constructor.
void HandleUrgentData()
Handle Out-Of-Band data sent by the master or client.
void HandleSocketInput()
Handle input coming from the client or from the master server.
Int_t ReceiveFile(const char *file, Bool_t bin, Long64_t size)
Receive a file, either sent by a client or a master server.
const char * GetHost() const
void Reset(const char *dir)
Reset environment to be ready for execution of next command.
void GetOptions(Int_t *argc, char **argv) override
Get and handle command line options.
void HandleSigPipe()
Called when the client is not alive anymore (i.e.
TRemoteObject * fWorkingDir
static void ErrorHandler(Int_t level, Bool_t abort, const char *location, const char *msg)
The error handler function.
Int_t BrowseKey(const char *keyname)
Read key object and send it back to client.
Int_t Setup()
Print the Remote Server logo on standard output.
Int_t BrowseDirectory(const char *dirname)
Browse directory and send back its content to client.
void HandleCheckFile(TMessage *mess)
Handle file checking request.
virtual ~TApplicationServer()
Cleanup.
void Terminate(Int_t status) override
Terminate the server.
Int_t BrowseFile(const char *fname)
Browse root file and send back its content; if fname is null, send the full list of files.
void SendLogFile(Int_t status=0, Int_t start=-1, Int_t end=-1)
Send log file to master.
virtual Longptr_t ProcessLine(const char *line, Bool_t sync=kFALSE, Int_t *error=nullptr)
Process a single command line, either a C++ statement or an interpreter command starting with a "....
TApplication(const TApplication &)=delete
virtual void Run(Bool_t retrn=kFALSE)
Main application eventloop. Calls system dependent eventloop via gSystem.
virtual Longptr_t ProcessFile(const char *file, Int_t *error=nullptr, Bool_t keep=kFALSE)
Process a file containing a C++ macro.
Bool_t NoLogOpt() const
This class is a ROOT utility to help benchmarking applications.
Definition TBenchmark.h:29
TObject * ReadObject(const TClass *cl) override
Read object from 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.
Describe directory structure in memory.
Definition TDirectory.h:45
virtual Bool_t cd()
Change current directory to "this" directory.
void SetFd(int fd)
A doubly linked list.
Definition TList.h:38
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:473
UInt_t What() const
Definition TMessage.h:77
void Reset() override
Reset the message buffer so we can use (i.e. fill) it again.
Definition TMessage.cxx:183
TClass * GetClass() const
Definition TMessage.h:73
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
Bool_t TestBit(UInt_t f) const
Definition TObject.h:204
virtual void SysError(const char *method, const char *msgfmt,...) const
Issue system error message.
Definition TObject.cxx:1112
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1084
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:888
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1098
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1126
TObject()
TObject constructor.
Definition TObject.h:259
void ResetBit(UInt_t f)
Definition TObject.h:203
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1072
static const char * GetMacroPath()
Get macro search path. Static utility function.
Definition TROOT.cxx:2917
static Bool_t Initialized()
Return kTRUE if the TROOT object has been initialized.
Definition TROOT.cxx:3067
static const TString & GetEtcDir()
Get the sysconfig directory in the installation. Static utility function.
Definition TROOT.cxx:3381
The TRemoteObject class provides protocol for browsing ROOT objects from a remote ROOT session.
void SetKeyClassName(const char *name)
void Browse(TBrowser *b) override
Browse remote object.
void SetRemoteAddress(Longptr_t addr)
void SetFolder(Bool_t isFolder)
ESignals GetSignal() const
This class implements client sockets.
Definition TSocket.h:39
Basic string class.
Definition TString.h:138
int CompareTo(const char *cs, ECaseCompare cmp=kExact) const
Compare a string to char *cs2.
Definition TString.cxx:464
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1994
const char * Data() const
Definition TString.h:384
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:713
@ kIgnoreCase
Definition TString.h:285
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:632
Bool_t IsNull() const
Definition TString.h:422
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2363
static void ResetErrno()
Static function resetting system error number.
Definition TSystem.cxx:286
static Int_t GetErrno()
Static function returning system error number.
Definition TSystem.cxx:278
TLine * line
const Int_t n
Definition legend1.C:16
bool HasBeenDeleted(const TObject *obj)
Check if the TObject's memory has been deleted.
Definition TObject.h:409
static const char * what
Definition stlLoader.cc:5
TMarker m
Definition textangle.C:8