/******************************************************************************/ /* */ /* X r d C m s C o n f i g . c c */ /* */ /* (c) 2011 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ /* The methods in this file handle cmsd() initialization. */ #include #include #include #include #include #include #include #include #include #include #include "XrdVersion.hh" #include "Xrd/XrdScheduler.hh" #include "Xrd/XrdSendQ.hh" #include "XrdCms/XrdCmsAdmin.hh" #include "XrdCms/XrdCmsBaseFS.hh" #include "XrdCms/XrdCmsBlackList.hh" #include "XrdCms/XrdCmsCache.hh" #include "XrdCms/XrdCmsCluster.hh" #include "XrdCms/XrdCmsConfig.hh" #include "XrdCms/XrdCmsManager.hh" #include "XrdCms/XrdCmsMeter.hh" #include "XrdCms/XrdCmsNode.hh" #include "XrdCms/XrdCmsPrepare.hh" #include "XrdCms/XrdCmsPrepArgs.hh" #include "XrdCms/XrdCmsProtocol.hh" #include "XrdCms/XrdCmsRole.hh" #include "XrdCms/XrdCmsRRQ.hh" #include "XrdCms/XrdCmsSecurity.hh" #include "XrdCms/XrdCmsState.hh" #include "XrdCms/XrdCmsSupervisor.hh" #include "XrdCms/XrdCmsTrace.hh" #include "XrdCms/XrdCmsUtils.hh" #include "XrdNet/XrdNetOpts.hh" #include "XrdNet/XrdNetUtils.hh" #include "XrdNet/XrdNetSecurity.hh" #include "XrdNet/XrdNetSocket.hh" #include "XrdOss/XrdOss.hh" #include "XrdOuc/XrdOuca2x.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucExport.hh" #include "XrdOuc/XrdOucN2NLoader.hh" #include "XrdOuc/XrdOucProg.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucUtils.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysPlatform.hh" #include "XrdSys/XrdSysPthread.hh" #include "XrdSys/XrdSysTimer.hh" using namespace XrdCms; /******************************************************************************/ /* G l o b a l O b j e c t s */ /******************************************************************************/ namespace XrdCms { XrdOucEnv theEnv; XrdCmsAdmin Admin; XrdCmsBaseFS baseFS(&XrdCmsNode::do_StateDFS); XrdCmsConfig Config; XrdSysError Say(0, ""); XrdOucTrace Trace(&Say); XrdScheduler *Sched = 0; }; /******************************************************************************/ /* S e c u r i t y S y m b o l T i e - I n */ /******************************************************************************/ // The following is a bit of a kludge. The client side will use the xrootd // security infrastructure if it exists. This is tipped off by the presence // of the following symbol being non-zero. On the server side, we have no // such symbol and need to provide one initialized to zero. // XrdSecProtocol *(*XrdXrootdSecGetProtocol) (const char *hostname, const struct sockaddr &netaddr, const XrdSecParameters &parms, XrdOucErrInfo *einfo)=0; /******************************************************************************/ /* E x t e r n a l T h r e a d I n t e r f a c e s */ /******************************************************************************/ void *XrdCmsStartMonPerf(void *carg) { return Cluster.MonPerf(); } void *XrdCmsStartMonRefs(void *carg) { return Cluster.MonRefs(); } void *XrdCmsStartMonStat(void *carg) { return CmsState.Monitor(); } void *XrdCmsStartAdmin(void *carg) {return XrdCms::Admin.Start((XrdNetSocket *)carg); } void *XrdCmsStartAnote(void *carg) {XrdCmsAdmin Anote; return Anote.Notes((XrdNetSocket *)carg); } void *XrdCmsStartPreparing(void *carg) {XrdCmsPrepArgs::Process(); return (void *)0; } void *XrdCmsStartSupervising(void *carg) {XrdCmsSupervisor::Start(); return (void *)0; } /******************************************************************************/ /* P i n g C l o c k H a n d l e r */ /******************************************************************************/ namespace XrdCms { class PingClock : XrdJob { public: void DoIt() {Config.PingTick++; Sched->Schedule((XrdJob *)this,time(0)+Config.AskPing); } static void Start() {static PingClock selfie;} PingClock() : XrdJob(".ping clock") {DoIt();} ~PingClock() {} private: }; }; /******************************************************************************/ /* d e f i n e s */ /******************************************************************************/ #define TS_String(x,m) if (!strcmp(x,var)) {free(m); m = strdup(val); return 0;} #define TS_Xeq(x,m) if (!strcmp(x,var)) return m(eDest, CFile); #define TS_Xer(x,m,v) if (!strcmp(x,var)) return m(eDest, CFile, v); #define TS_Set(x,v) if (!strcmp(x,var)) {v=1; CFile.Echo(); return 0;} #define TS_unSet(x,v) if (!strcmp(x,var)) {v=0; CFile.Echo(); return 0;} /******************************************************************************/ /* C o n f i g u r e 1 */ /******************************************************************************/ int XrdCmsConfig::Configure1(int argc, char **argv, char *cfn) { /* Function: Establish phase 1 configuration at start up time. Input: argc - argument count argv - argument vector cfn - optional configuration file name Output: 0 upon success or !0 otherwise. */ int NoGo = 0, immed = 0; char c, buff[512]; extern int opterr, optopt; // Prohibit this program from executing as superuser // if (geteuid() == 0) {Say.Emsg("Config", "Security reasons prohibit cmsd running as " "superuser; cmsd is terminating."); _exit(8); } // Process the options // opterr = 0; optind = 1; if (argc > 1 && '-' == *argv[1]) while ((c=getopt(argc,argv,"iw")) && ((unsigned char)c != 0xff)) { switch(c) { case 'i': immed = 1; break; case 'w': immed = -1; // Backward compatability only break; default: buff[0] = '-'; buff[1] = optopt; buff[2] = '\0'; Say.Say("Config warning: unrecognized option, ",buff,", ignored."); } } // Accept a single parameter defining the overiding major role // if (optind < argc) { if (!strcmp(argv[optind], "manager")) isManager = 1; else if (!strcmp(argv[optind], "server" )) isServer = 1; else if (!strcmp(argv[optind], "super" )) isServer = isManager = 1; else Say.Say("Config warning: unrecognized parameter, ", argv[optind],", ignored."); } // Bail if no configuration file specified // inArgv = argv; inArgc = argc; if ((!(ConfigFN = cfn) && !(ConfigFN = getenv("XrdCmsCONFIGFN"))) || !*ConfigFN) {Say.Emsg("Config", "Required config file not specified."); Usage(1); } // Establish my instance name // sprintf(buff, "%s@%s", XrdOucUtils::InstName(myInsName), myName); myInstance = strdup(buff); // This is somewhat poor but we need to establish the default non-blocking // message queue limit for the cms (this being 30) which can be overriden. // XrdSendQ::SetQM(30); // Print herald // Say.Say("++++++ ", myInstance, " phase 1 initialization started."); // If we don't know our role yet then we must find out before processing the // config file. This means a double scan, sigh. // if (!(isManager || isServer)) if (!(NoGo |= ConfigProc(1)) && !(isManager || isServer)) {Say.Say("Config warning: role not specified; manager role assumed."); isManager = -1; } // Process the configuration file // if (!NoGo) NoGo |= ConfigProc(); // Override the trace option // if (getenv("XRDDEBUG")) Trace.What = TRACE_ALL; // Override the wait/nowait from the command line // if (immed) doWait = (immed > 0 ? 0 : 1); // Determine the role // if (isManager < 0) isManager = 1; if (isPeer < 0) isPeer = 1; if (isProxy < 0) isProxy = 1; if (isServer < 0) isServer = 1; // Create a text description of our role for use in messages // if (!myRole) {XrdCmsRole::RoleID rid = XrdCmsRole::noRole; if (isMeta) rid = XrdCmsRole::MetaManager; else if (isPeer) rid = XrdCmsRole::Peer; else if (isProxy) {if (isManager) rid = (isServer ? XrdCmsRole::ProxySuper : XrdCmsRole::ProxyManager); else rid = XrdCmsRole::ProxyServer; } else if (isManager) {if (isManager) rid = (isServer ? XrdCmsRole::Supervisor : XrdCmsRole::Manager); } else rid = XrdCmsRole::Server; strcpy(myRType, XrdCmsRole::Type(rid)); myRole = strdup(XrdCmsRole::Name(rid)); myRoleID = static_cast(rid); } // Export the role IN basic form and expanded form // XrdOucEnv::Export("XRDROLE", myRole); XrdOucEnv::Export("XRDROLETYPE", myRType); // For managers, make sure that we have a well designated port. // For servers or supervisors, force an ephemeral port to be used. // if (!NoGo) {if ((isManager && !isServer) || isPeer) {if (PortTCP <= 0) {Say.Emsg("Config","port for this", myRole, "not specified."); NoGo = 1; } } else if ((isManager && isServer)) PortTCP = PortSUP; else PortTCP = 0; } // If we are configured in proxy mode then we are running a shared filesystem // if (isProxy) baseFS.Init(XrdCmsBaseFS::DFSys | XrdCmsBaseFS::Immed | (baseFS.Local() ? XrdCmsBaseFS::Cntrl : 0), 0, 0); // Determine how we ended and return status // sprintf(buff, " phase 1 %s initialization %s.", myRole, (NoGo ? "failed" : "completed")); Say.Say("------ ", myInstance, buff); return NoGo; } /******************************************************************************/ /* C o n f i g u r e 2 */ /******************************************************************************/ int XrdCmsConfig::Configure2() { /* Function: Establish phase 2 configuration at start up time. Input: None. Output: 0 upon success or !0 otherwise. */ int Who, NoGo = 0; char *p, buff[512]; std::string envData; // Print herald // sprintf(buff, " phase 2 %s initialization started.", myRole); Say.Say("++++++ ", myInstance, buff); // Fix up the QryMinum (we hard code 64 as the max) and P_gshr values. // The QryMinum only applies to a metamanager and is set as 1 minus the min. // if (!isMeta) QryMinum = 0; else if (QryMinum < 2) QryMinum = 0; else if (QryMinum > 64) QryMinum = 64; if (P_gshr < 0) P_gshr = 0; else if (P_gshr > 100) P_gshr = 100; // Determine who we are. If we are a manager or supervisor start the file // location cache scrubber. // if (QryDelay < 0) QryDelay = LUPDelay; if (isManager) NoGo = !Cache.Init(cachelife,LUPDelay,QryDelay,baseFS.isDFS(),emptylife); // Issue warning if the adminpath resides in /tmp // if (!strncmp(AdminPath, "/tmp/", 5)) Say.Say("Config warning: adminpath resides in /tmp and may be unstable!"); // Establish the path to be used for admin functions // p = XrdOucUtils::genPath(AdminPath,XrdOucUtils::InstName(myInsName,0),".olb"); free(AdminPath); AdminPath = p; // Setup the admin path (used in all roles) // if (!NoGo) NoGo = !(AdminSock = XrdNetSocket::Create(&Say, AdminPath, (isManager|isPeer ? "olbd.nimda":"olbd.admin"),AdminMode)); // Develop a stable unique identifier for this cmsd independent of the port // if (!NoGo) {if (!(mySID = setupSid())) NoGo = 1; else {if (QTRACE(Debug)) Say.Say("Config ", "Global System Identification: ", mySID); if (Config.mySite) {envData += "site="; envData += mySite; } } } // Create envCGI string for logins // envCGI = (envData.length() > 0 ? strdup(envData.c_str()) : 0); // If we need a name library, load it now // if ((LocalRoot || RemotRoot || N2N_Lib) && ConfigN2N()) NoGo = 1; // Configure the OSS, the base filesystem, and initialize the prep queue // if (!NoGo) NoGo = ConfigOSS(); if (!NoGo) baseFS.Start(); if (!NoGo) PrepQ.Init(); // Setup manager or server, as needed // if (!NoGo && isManager) NoGo = setupManager(); if (!NoGo && (isServer || ManList)) NoGo = setupServer(); // If we are a solo peer then we have no servers and a lot of space and // connections don't matter. Only one connection matters for a meta-manager. // Servers, supervisors, and managers who have a meta manager must wait for // for the local data server to connect so port mapping occurs. Otherwise, // we indicate that it doesn't matter as the local server won't connect. // if (isPeer && isSolo) {SUPCount = SUPLevel = 0; Meter.setVirtual(XrdCmsMeter::peerFS);} else if (isManager) {Meter.setVirtual(XrdCmsMeter::manFS); if (isMeta) {SUPCount = 1; SUPLevel = 0;} if (!ManList) CmsState.Update(XrdCmsState::FrontEnd, 1); } if (isManager) Who = (isServer ? -1 : 1); else Who = 0; CmsState.Set(SUPCount, Who, AdminPath); // Create the pid file // if (!NoGo) NoGo |= PidFile(); // All done, check for success or failure // sprintf(buff, " phase 2 %s initialization %s.", myRole, (NoGo ? "failed" : "completed")); Say.Say("------ ", myInstance, buff); // The remainder of the configuration needs to be run in a separate thread // if (!NoGo) Sched->Schedule((XrdJob *)this); // All done // return NoGo; } /******************************************************************************/ /* C o n f i g X e q */ /******************************************************************************/ int XrdCmsConfig::ConfigXeq(char *var, XrdOucStream &CFile, XrdSysError *eDest) { int dynamic; // Determine whether is is dynamic or not // if (eDest) dynamic = 1; else {dynamic = 0; eDest = &Say;} // Process items // TS_Xeq("delay", xdelay); // Manager, dynamic TS_Xeq("fxhold", xfxhld); // Manager, dynamic TS_Xeq("ping", xping); // Manager, dynamic TS_Xeq("sched", xsched); // Any, dynamic TS_Xeq("space", xspace); // Any, dynamic TS_Xeq("trace", xtrace); // Any, dynamic if (!dynamic) { TS_Xeq("adminpath", xapath); // Any, non-dynamic TS_Xeq("allow", xallow); // Manager, non-dynamic TS_Xeq("altds", xaltds); // Server, non-dynamic TS_Xeq("blacklist", xblk); // Manager, non-dynamic TS_Xeq("cidtag", xcid); // Any, non-dynamic TS_Xeq("defaults", xdefs); // Server, non-dynamic TS_Xeq("dfs", xdfs); // Any, non-dynamic TS_Xeq("export", xexpo); // Any, non-dynamic TS_Xeq("fsxeq", xfsxq); // Server, non-dynamic TS_Xeq("localroot", xlclrt); // Any, non-dynamic TS_Xeq("manager", xmang); // Server, non-dynamic TS_Xeq("namelib", xnml); // Server, non-dynamic TS_Xeq("vnid", xvnid); // Server, non-dynamic TS_Xeq("nbsendq", xnbsq); // Any non-dynamic TS_Xeq("osslib", xolib); // Any, non-dynamic TS_Xeq("perf", xperf); // Server, non-dynamic TS_Xeq("pidpath", xpidf); // Any, non-dynamic TS_Xeq("prep", xprep); // Any, non-dynamic TS_Xeq("prepmsg", xprepm); // Any, non-dynamic TS_Xeq("remoteroot", xrmtrt); // Any, non-dynamic TS_Xeq("repstats", xreps); // Any, non-dynamic TS_Xeq("role", xrole); // Server, non-dynamic TS_Xeq("seclib", xsecl); // Server, non-dynamic TS_Xeq("subcluster", xsubc); // Manager, non-dynamic TS_Xeq("superport", xsupp); // Super, non-dynamic TS_Set("wait", doWait); // Server, non-dynamic (backward compat) TS_unSet("nowait", doWait); // Server, non-dynamic TS_Xer("whitelist", xblk,true);//Manager, non-dynamic } // The following are client directives that we will ignore // if (!strcmp(var, "conwait") || !strcmp(var, "request")) return 0; // No match found, complain. // eDest->Say("Config warning: ignoring unknown directive '", var, "'."); CFile.Echo(); return 0; } /******************************************************************************/ /* D o I t */ /******************************************************************************/ void XrdCmsConfig::DoIt() { XrdSysSemaphore SyncUp(0); pthread_t tid; time_t eTime = time(0); int wTime; // Set doWait correctly. We only wait if we have to provide a data path. This // include server, supervisors, and managers who have a meta-manager, only. // Why? Because we never get a primary login if we are a mere manager. // if (isManager && !isServer && !ManList) doWait = 0; else if (isServer && adsMon) doWait = 1; // Start the notification thread if we need to // if (AnoteSock) if (XrdSysThread::Run(&tid, XrdCmsStartAnote, (void *)AnoteSock, 0, "Notification handler")) Say.Emsg("cmsd", errno, "start notification handler"); // Start the prepare handler // if (XrdSysThread::Run(&tid,XrdCmsStartPreparing, (void *)0, 0, "Prep handler")) Say.Emsg("cmsd", errno, "start prep handler"); // Start the supervisor subsystem // if (XrdCmsSupervisor::superOK) {if (XrdSysThread::Run(&tid,XrdCmsStartSupervising, (void *)0, 0, "supervisor")) {Say.Emsg("cmsd", errno, "start", myRole); return; } } // Start the ping clock if we are a manager of any kind // if (isManager) PingClock::Start(); // Start the admin thread if we need to, we will not continue until told // to do so by the admin interface. // if (AdminSock) {XrdCmsAdmin::setSync(&SyncUp); if (XrdSysThread::Run(&tid, XrdCmsStartAdmin, (void *)AdminSock, 0, "Admin traffic")) Say.Emsg("cmsd", errno, "start admin handler"); SyncUp.Wait(); } // Start the manager subsystem. // if (isManager || isServer || isPeer) XrdCmsManager::Start(ManList); // Start state monitoring thread // if (XrdSysThread::Run(&tid, XrdCmsStartMonStat, (void *)0, 0, "State monitor")) {Say.Emsg("Config", errno, "create state monitor thread"); return; } // If we are a manager then we must do a service enable after a service delay // if ((isManager || isPeer) && SRVDelay) {wTime = SRVDelay - static_cast((time(0) - eTime)); if (wTime > 0) XrdSysTimer::Wait(wTime*1000); } // All done // if (!SUPCount) CmsState.Update(XrdCmsState::Counts, 0, 0); CmsState.Enable(); Say.Emsg("Config", myRole, "service enabled."); } /******************************************************************************/ /* G e n L o c a l P a t h */ /******************************************************************************/ /* GenLocalPath() generates the path that a file will have in the local file system. The decision is made based on the user-given path (typically what the user thinks is the local file system path). The output buffer where the new path is placed must be at least XrdCmsMAX_PATH_LEN bytes long. */ int XrdCmsConfig::GenLocalPath(const char *oldp, char *newp) { if (lcl_N2N) return -(lcl_N2N->lfn2pfn(oldp, newp, XrdCmsMAX_PATH_LEN)); if (strlen(oldp) >= XrdCmsMAX_PATH_LEN) return -ENAMETOOLONG; strcpy(newp, oldp); return 0; } /******************************************************************************/ /* P r i v a t e F u n c t i o n s */ /******************************************************************************/ /******************************************************************************/ /* C o n f i g D e f a u l t s */ /******************************************************************************/ void XrdCmsConfig::ConfigDefaults(void) { static XrdVERSIONINFODEF(myVer, cmsd, XrdVNUMBER, XrdVERSION); int myTZ, isEast = 0; // Preset all variables with common defaults // myName = (char *)"localhost"; // Correctly set in Configure() myDomain = 0; LUPDelay = 5; QryDelay =-1; QryMinum = 0; LUPHold = 178; DELDelay = 960; // 15 minutes DRPDelay = 10*60; PSDelay = 0; RWDelay = 2; SRVDelay = 90; SUPCount = 1; SUPLevel = 80; SUPDelay = 15; SUSDelay = 30; MaxLoad = 0x7fffffff; MsgTTL = 7; PortTCP = 0; PortSUP = 0; P_cpu = 0; P_fuzz = 20; P_gsdf = 0; P_gshr = 0; P_io = 0; P_load = 0; P_mem = 0; P_pag = 0; AskPerf = 10; // Every 10 pings AskPing = 60; // Every 1 minute PingTick = 0; DoMWChk = 1; DoHnTry = 1; MaxDelay = -1; LogPerf = 10; // Every 10 usage requests DiskMin = 10240; // 10GB*1024 (Min partition space) in MB DiskHWM = 11264; // 11GB*1024 (High Water Mark SUO) in MB DiskMinP = 2; DiskHWMP = 5; DiskAsk = 12; // 15 Seconds between space calibrations. DiskWT = 0; // Do not defer when out of space DiskSS = 0; // Not a staging server DiskOK = 0; // Does not have any disk myPaths = (char *)""; // Default is 'r /' ConfigFN = 0; sched_RR = sched_Pack = sched_Level = 0; sched_Force = 1; isManager= 0; isMeta = 0; isPeer = 0; isSolo = 0; isProxy = 0; isServer = 0; VNID_Lib = 0; VNID_Parms=0; N2N_Lib = 0; N2N_Parms= 0; lcl_N2N = 0; xeq_N2N = 0; LocalRoot= 0; RemotRoot= 0; myInsName= 0; RepStats = 0; myRole =0; myRType[0]=0; myRoleID = XrdCmsRole::noRole; ManList =0; NanList =0; SanList =0; myVNID = 0; mySID = 0; mySite = 0; envCGI = 0; cidTag = 0; ifList =0; perfint = 3*60; perfpgm = 0; AdminPath= strdup("/tmp/"); AdminMode= 0700; AdminSock= 0; AnoteSock= 0; RedirSock= 0; pidPath = strdup("/tmp"); Police = 0; cachelife= 8*60*60; emptylife= 0; pendplife= 60*60*24*7; DiskLinger=0; ProgCH = 0; ProgMD = 0; ProgMV = 0; ProgRD = 0; ProgRM = 0; doWait = 1; RefReset = 60*60; RefTurn = 3*STMax*(DiskLinger+1); DirFlags = 0; blkList = 0; blkChk = 0; SecLib = 0; ossLib = 0; ossParms = 0; ossFS = 0; myVInfo = &myVer; adsPort = 0; adsMon = 0; adsProt = 0; nbSQ = 1; // Compute the time zone we are in // myTZ = XrdSysTimer::TimeZone(); if (myTZ <= 0) {isEast = 0x10; myTZ = -myTZ;} if (myTZ > 12) myTZ = 12; TimeZone = (myTZ | isEast); } /******************************************************************************/ /* C o n f i g N 2 N */ /******************************************************************************/ int XrdCmsConfig::ConfigN2N() { XrdOucN2NLoader n2nLoader(&Say, ConfigFN, N2N_Parms, LocalRoot, RemotRoot); // Get the plugin // if (!(xeq_N2N = n2nLoader.Load(N2N_Lib, *myVInfo, &theEnv))) return 1; // Optimize the local case // if (N2N_Lib || LocalRoot) lcl_N2N = xeq_N2N; // All done // PrepQ.setParms(lcl_N2N); return 0; } /******************************************************************************/ /* C o n f i g O S S */ /******************************************************************************/ int XrdCmsConfig::ConfigOSS() { extern XrdOss *XrdOssGetSS(XrdSysLogger *, const char *, const char *, const char *, XrdOucEnv *, XrdVersionInfo &); void *arFunc; // Set up environment for the OSS to keep it relevant for cmsd // XrdOucEnv::Export("XRDREDIRECT", "Q"); XrdOucEnv::Export("XRDOSSTYPE", "cms"); XrdOucEnv::Export("XRDOSSCSCAN", "off"); // If no osslib was specified but we are a proxy, then we must load the // the proxy osslib. // if (!ossLib && isProxy) ossLib = strdup("libXrdPss.so"); // Load and return result // ossFS=XrdOssGetSS(Say.logger(),ConfigFN,ossLib,ossParms,&theEnv,*myVInfo); if (!ossFS) return 1; // Check if we should elay add/remove events to the statinfo function // if (!isManager && isServer && (arFunc = theEnv.GetPtr("XrdOssStatInfo2*"))) return (XrdCmsAdmin::InitAREvents(arFunc) ? 0 : 1); return 0; } /******************************************************************************/ /* C o n f i g P r o c */ /******************************************************************************/ int XrdCmsConfig::ConfigProc(int getrole) { char *var; int cfgFD, retc, NoGo = 0; XrdOucEnv myEnv; XrdOucStream CFile(&Say, getenv("XRDINSTANCE"), &myEnv, "=====> "); // Try to open the configuration file. // if ( (cfgFD = open(ConfigFN, O_RDONLY, 0)) < 0) {Say.Emsg("Config", errno, "open config file", ConfigFN); return 1; } CFile.Attach(cfgFD); // Turn off echoing if we are doing a pre-scan // if (getrole) CFile.SetEroute(0); // Now start reading records until eof. // while((var = CFile.GetMyFirstWord())) if (getrole) {if (!strcmp("all.role", var) || !strcmp("olb.role", var)) if (xrole(&Say, CFile)) {CFile.SetEroute(&Say); CFile.Echo(); NoGo = 1; CFile.SetEroute(0); } } else if (!strncmp(var, "cms.", 4) || !strncmp(var, "olb.", 4) // Backward compatability || !strcmp(var, "ofs.osslib") || !strcmp(var, "oss.defaults") || !strcmp(var, "oss.localroot") || !strcmp(var, "oss.remoteroot") || !strcmp(var, "oss.namelib") || !strcmp(var, "all.adminpath") || !strcmp(var, "all.export") || !strcmp(var, "all.manager") || !strcmp(var, "all.pidpath") || !strcmp(var, "all.role") || !strcmp(var, "all.seclib") || !strcmp(var, "all.subcluster")) {if (ConfigXeq(var+4, CFile, 0)) {CFile.Echo(); NoGo = 1;}} else if (!strcmp(var, "oss.stagecmd")) DiskSS = 1; // Now check if any errors occured during file i/o // if ((retc = CFile.LastError())) NoGo = Say.Emsg("Config", retc, "read config file", ConfigFN); CFile.Close(); // Merge Paths as needed // if (!getrole && (ManList || SanList)) NoGo |= MergeP(); // Return final return code // return NoGo; } /******************************************************************************/ /* i s E x e c */ /******************************************************************************/ int XrdCmsConfig::isExec(XrdSysError *eDest, const char *ptype, char *prog) { char buff[512], pp, *mp = prog; // Isolate the program name // while(*mp && *mp != ' ') mp++; pp = *mp; *mp ='\0'; // Make sure the program is executable by us // if (access(prog, X_OK)) {sprintf(buff, "find %s execuatble", ptype); eDest->Emsg("Config", errno, buff, prog); *mp = pp; return 0; } // All is well // *mp = pp; return 1; } /******************************************************************************/ /* M e r g e P */ /******************************************************************************/ int XrdCmsConfig::MergeP() { static const unsigned long long stage4MM = XRDEXP_STAGEMM & ~XRDEXP_STAGE; XrdOucPList *plp = PexpList.First(); XrdCmsPList *pp; XrdCmsPInfo opinfo, npinfo; const char *ptype; char *pbP; unsigned long long Opts; int pbLen = 0, NoGo = 0, export2MM = isManager && !isServer; npinfo.rovec = 1; // For each path in the export list merge it into the path list // while(plp) {Opts = plp->Flag(); if (!(Opts & XRDEXP_LOCAL)) {npinfo.rwvec = (Opts & (XRDEXP_GLBLRO | XRDEXP_NOTRW) ? 0 : 1); if (export2MM) npinfo.ssvec = (Opts & stage4MM ? 1 : 0); else npinfo.ssvec = (Opts & XRDEXP_STAGE ? 1 : 0); if (!PathList.Add(plp->Path(), &npinfo)) Say.Emsg("Config","Ignoring duplicate export path",plp->Path()); else if (npinfo.ssvec) DiskSS = 1; } plp = plp->Next(); } // Document what we will be declaring as available // if (!NoGo) {const char *Who; if (isManager) {if (SanList) Who = "subcluster manager:"; else Who = (isServer ? "manager:" : "meta-manager:"); } else Who = "redirector:"; Say.Say("The following paths are available to the ", Who); if (!(pp = PathList.First())) Say.Say("r /"); else while(pp) {ptype = pp->PType(); Say.Say(ptype, (strlen(ptype) > 1 ? " " : " "), pp->Path()); pbLen += strlen(pp->Path())+8; pp = pp->Next(); } Say.Say(" "); } // Now allocate a buffer and place all of the paths into that buffer to be // sent during the login phase. // if (pbLen != 0 && (pp = PathList.First())) {pbP = myPaths = (char *)malloc(pbLen); while(pp) {pbP += sprintf(pbP, "\n%s %s", pp->PType(), pp->Path()); pp = pp->Next(); } myPaths++; } // All done update the staging status (it's nostage by default) // if (DiskSS) CmsState.Update(XrdCmsState::Counts, 0, 1); return NoGo; } /******************************************************************************/ /* P i d F i l e */ /******************************************************************************/ int XrdCmsConfig::PidFile() { int rc, xfd; const char *clID; char buff[1024]; char pidFN[1200], *ppath=XrdOucUtils::genPath(pidPath, XrdOucUtils::InstName(myInsName,0)); const char *xop = 0; if ((rc = XrdOucUtils::makePath(ppath, XrdOucUtils::pathMode))) {Say.Emsg("Config", rc, "create pid file path", ppath); free(ppath); return 1; } if ((clID = index(mySID, ' '))) clID++; else clID = mySID; if (isManager && isServer) snprintf(pidFN, sizeof(pidFN), "%s/cmsd.super.pid", ppath); else if (isServer) snprintf(pidFN, sizeof(pidFN), "%s/cmsd.pid", ppath); else snprintf(pidFN, sizeof(pidFN), "%s/cmsd.mangr.pid", ppath); if ((xfd = open(pidFN, O_WRONLY|O_CREAT|O_TRUNC,0644)) < 0) xop = "open"; else {if ((write(xfd,buff,snprintf(buff,sizeof(buff),"%d", static_cast(getpid()))) < 0) || (LocalRoot && (write(xfd,(void *)"\n&pfx=",6) < 0 || write(xfd,(void *)LocalRoot,strlen(LocalRoot)) < 0 ) ) || (AdminPath && (write(xfd,(void *)"\n&ap=", 5) < 0 || write(xfd,(void *)AdminPath,strlen(AdminPath)) < 0 ) ) || write(xfd,(void *)"\n&cn=", 5) < 0 || write(xfd,(void *)clID, strlen(clID)) < 0 ) xop = "write"; close(xfd); } if (xop) Say.Emsg("Config", errno, xop, pidFN); else XrdOucEnv::Export("XRDCMSPIDFN", pidFN); free(ppath); return xop != 0; } /******************************************************************************/ /* s e t u p M a n a g e r */ /******************************************************************************/ int XrdCmsConfig::setupManager() { pthread_t tid; int rc; // If we are a subcluster then we need to replace the manager list with the // one specified on the subcluster directive. // if (SanList) {XrdOucTList *nP, *tP = ManList; const char *urDom, *myDom = index(myName, '.'); bool isBad = false; while(tP) {nP = tP; tP = tP->next; delete nP;} ManList = tP = SanList; if (myDom) while(tP) {if ((urDom = index(tP->text, '.')) && strcmp(urDom, myDom)) {Say.Emsg("Config", "Subcluster's manager", tP->text, "is in a different domain."); isBad = true; } tP = tP->next; } if (isBad) {Say.Emsg("Config","Cross domain subclusters disallowed!"); return 1; } } // Setup supervisor mode if we are also a server // if (isServer && !XrdCmsSupervisor::Init(AdminPath, AdminMode)) return 1; // Compute the scheduling policy // sched_RR = (100 == P_fuzz) || !AskPerf || !(P_cpu || P_io || P_load || P_mem || P_pag); if (sched_RR) {Say.Say("Config round robin scheduling in effect."); sched_Level = 0; } // Create statistical monitoring thread // if ((rc = XrdSysThread::Run(&tid, XrdCmsStartMonPerf, (void *)0, 0, "Performance monitor"))) {Say.Emsg("Config", rc, "create perf monitor thread"); return 1; } // Create reference monitoring thread // RefTurn = 3*STMax*(DiskLinger+1); if (RefReset) {if ((rc = XrdSysThread::Run(&tid, XrdCmsStartMonRefs, (void *)0, 0, "Refcount monitor"))) {Say.Emsg("Config", rc, "create refcount monitor thread"); return 1; } } // Initialize the fast redirect queue // RRQ.Init(LUPHold, LUPDelay); // Initialize the security interface // if (SecLib && !XrdCmsSecurity::Configure(SecLib, ConfigFN)) return 1; // Initialize the black list // if (!isServer && blkChk) XrdCmsBlackList::Init(Sched, &Cluster, blkList, blkChk); // All done // return 0; } /******************************************************************************/ /* s e t u p S e r v e r */ /******************************************************************************/ int XrdCmsConfig::setupServer() { XrdOucTList *tp; int n = 0; // Make sure we have enough info to be a server // if (!ManList) {Say.Emsg("Config", "Manager node not specified for", myRole, "role"); return 1; } // Count the number of managers. Make sure there are not too many. // tp = ManList; while(tp) {n++; tp = tp->next;} if (n > XrdCmsManager::MTMax) {Say.Emsg("Config", "Too many managers have been specified"); return 1;} // Calculate overload delay time // if (MaxDelay < 0) MaxDelay = AskPerf*AskPing+30; if (DiskWT < 0) DiskWT = AskPerf*AskPing+30; // Setup notification path // if (!(AnoteSock = XrdNetSocket::Create(&Say, AdminPath, (isManager|isPeer ? "olbd.seton":"olbd.notes"), AdminMode, XRDNET_UDPSOCKET))) return 1; // We have data only if we are a pure data server (the default is noData) // If we have no data, then we are done (the rest is for pure servers) // if (isManager || isPeer) return 0; SUPCount = 0; SUPLevel = 0; if (isProxy) return 0; DiskOK = 1; // If this is a staging server then set up the Prepq object // if (DiskSS) PrepQ.Reset(myInsName, AdminPath, AdminMode); // Setup file system metering (skip it for peers) // Meter.Init(); if (perfpgm && Meter.Monitor(perfpgm, perfint)) Say.Say("Config warning: load based scheduling disabled."); // All done // return 0; } /******************************************************************************/ /* s e t u p S i d */ /******************************************************************************/ char *XrdCmsConfig::setupSid() { XrdOucTList *tp = (NanList ? NanList : ManList); char *sidVal, sfx; // Grab the interfaces. This is normally set as an envar. If present then // we will copy it because we must use it permanently. // if (getenv("XRDIFADDRS")) ifList = strdup(getenv("XRDIFADDRS")); // Grab the site name // if ((mySite = getenv("XRDSITE")) && *mySite) mySite = strdup(mySite); else mySite = 0; // Determine what type of role we are playing // if (isManager && isServer) sfx = 'u'; else sfx = (isManager ? 'm' : 's'); if (isProxy) sfx = toupper(sfx); // Get the node ID if we need to // if (VNID_Lib) {myVNID = XrdCmsSecurity::getVnId(Say,ConfigFN,VNID_Lib,VNID_Parms,sfx); if (!myVNID) return 0; } // Generate the system ID and set the cluster ID // sidVal = XrdCmsSecurity::setSystemID(tp, myVNID, cidTag, sfx); if (!sidVal || *sidVal == '!') {const char *msg; if (!sidVal) msg = "too many managers."; else msg = sidVal+1; Say.Emsg("cmsd","Unable to generate system ID; ", msg); return 0; } return sidVal; } /******************************************************************************/ /* U s a g e */ /******************************************************************************/ void XrdCmsConfig::Usage(int rc) { cerr <<"\nUsage: cmsd [xrdopts] [-i] [-m] [-s] -c " < The dns name of the host that is allowed to connect or the netgroup name the host must be a member of. For DNS names, a single asterisk may be specified anywhere in the name. Type: Manager only, non-dynamic. Output: 0 upon success or !0 upon failure. */ int XrdCmsConfig::xallow(XrdSysError *eDest, XrdOucStream &CFile) { char *val; int ishost; if (!isManager) return CFile.noEcho(); if (!(val = CFile.GetWord())) {eDest->Emsg("Config", "allow type not specified"); return 1;} if (!strcmp(val, "host")) ishost = 1; else if (!strcmp(val, "netgroup")) ishost = 0; else {eDest->Emsg("Config", "invalid allow type -", val); return 1; } if (!(val = CFile.GetWord())) {eDest->Emsg("Config", "allow target name not specified"); return 1;} if (!Police) Police = new XrdNetSecurity(); if (ishost) Police->AddHost(val); else Police->AddNetGroup(val); return 0; } /******************************************************************************/ /* x a l t d s */ /******************************************************************************/ /* Function: xaltds Purpose: To parse the directive: altds xroot [[no]monitor] xroot The protocol used by the alternate data server. The port being used by the alternate data server. mon Actively monitor alternate data server by connecting to it. This is the default. nomon Do not monitor the alternate data server. it and if is greater than zero, send "ping" requests every seconds. Zero merely connects. Type: Manager only, non-dynamic. Output: 0 upon success or !0 upon failure. */ int XrdCmsConfig::xaltds(XrdSysError *eDest, XrdOucStream &CFile) { char *val; if (isManager) return CFile.noEcho(); if (!(val = CFile.GetWord())) {eDest->Emsg("Config", "protocol not specified"); return 1;} if (strcmp(val, "xroot")) {eDest->Emsg("Config", "unsupported protocol, '", val, "'."); return 1;} if (adsProt) free(adsProt); adsProt = strdup(val); if (!(val = CFile.GetWord())) {eDest->Emsg("Config", "data server port not specified"); return 1;} if (isdigit(*val)) {if (XrdOuca2x::a2i(*eDest,"data server port",val,&adsPort,1,65535)) return 1; } else if (!(adsPort = XrdNetUtils::ServPort(val, "tcp"))) {eDest->Emsg("Config", "Unable to find tcp service '",val,"'."); return 1; } if (!(val = CFile.GetWord()) || !strcmp(val, "monitor")) adsMon = 1; else if (!strcmp(val, "nomonitor")) adsMon = 0; else {eDest->Emsg("Config", "invalid option, '", val, "'."); return 1; } return 0; } /******************************************************************************/ /* x a p a t h */ /******************************************************************************/ /* Function: xapath Purpose: To parse the directive: adminpath the path of the named socket to use for admin requests. Type: Manager and Server, non-dynamic. Output: 0 upon success or !0 upon failure. */ int XrdCmsConfig::xapath(XrdSysError *eDest, XrdOucStream &CFile) { char *pval, *val; mode_t mode = S_IRWXU; // Get the path // pval = CFile.GetWord(); if (!pval || !pval[0]) {eDest->Emsg("Config", "adminpath not specified"); return 1;} // Make sure it's an absolute path // if (*pval != '/') {eDest->Emsg("Config", "adminpath not absolute"); return 1;} pval = strdup(pval); // Get the optional access rights // if ((val = CFile.GetWord()) && val[0]) {if (!strcmp("group", val)) mode |= S_IRWXG; else {eDest->Emsg("Config", "invalid admin path modifier -", val); free(pval); return 1; } } // Record the path // if (AdminPath) free(AdminPath); AdminPath = pval; AdminMode = mode; return 0; } /******************************************************************************/ /* x b l k */ /******************************************************************************/ /* Function: xblk Purpose: To parse the directive: blacklist [check