/******************************************************************************/ /* */ /* X r d S s i S f s C o n f i g . c c */ /* */ /* (c) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Deprtment 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. */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "XrdCms/XrdCmsRole.hh" #include "XrdOuc/XrdOuca2x.hh" #include "XrdOuc/XrdOucBuffer.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucUtils.hh" #include "XrdSsi/XrdSsiSfs.hh" #include "XrdSsi/XrdSsiFileReq.hh" #include "XrdSsi/XrdSsiFileSess.hh" #include "XrdSsi/XrdSsiLogger.hh" #include "XrdSsi/XrdSsiProvider.hh" #include "XrdSsi/XrdSsiSfsConfig.hh" #include "XrdSsi/XrdSsiTrace.hh" #include "XrdSsi/XrdSsiCms.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysLogger.hh" #include "XrdSys/XrdSysPlatform.hh" #include "XrdSys/XrdSysPlugin.hh" #include "XrdSys/XrdSysPthread.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucERoute.hh" #include "XrdOuc/XrdOucPList.hh" #include "XrdNet/XrdNetIF.hh" #include "XrdVersion.hh" #ifdef AIX #include #endif /******************************************************************************/ /* E x t e r n s */ /******************************************************************************/ class XrdScheduler; namespace XrdSsi { XrdSsiCms *SsiCms = 0; XrdScheduler *Sched = 0; XrdOucBuffPool *BuffPool = 0; XrdOucPListAnchor FSPath; XrdNetIF *myIF = 0; extern XrdSsiProvider *Provider; XrdSsiService *Service = 0; XrdSsiLogger SsiLogger; int respWT = 0x7fffffff; bool fsChk = false; bool detReqOK = false; extern XrdSfsFileSystem *theFS; extern XrdSysError Log; }; using namespace XrdSsi; /******************************************************************************/ /* C o n s t r u c t o r */ /******************************************************************************/ XrdSsiSfsConfig::XrdSsiSfsConfig(bool iscms) { static XrdVERSIONINFODEF(myVer, ssi, XrdVNUMBER, XrdVERSION); char *bp; // Establish defaults // ConfigFN = 0; CmsLib = 0; CmsParms = 0; SsiCms = 0; SvcLib = 0; SvcParms = 0; myRole = 0; maxRSZ = 2097152; respWT = 0x7fffffff; isServer = true; isCms = iscms; myHost = getenv("XRDHOST"); myProg = getenv("XRDPROG"); myInsName = XrdOucUtils::InstName(1); myVersion = &myVer; myPort = (bp = getenv("XRDPORT")) ? strtol(bp, (char **)NULL, 10) : 0; } /******************************************************************************/ /* D e s t r u c t o r */ /******************************************************************************/ XrdSsiSfsConfig::~XrdSsiSfsConfig() { if (ConfigFN) free(ConfigFN); if (CmsLib) free(CmsLib); if (CmsParms) free(CmsParms); if (SvcLib) free(SvcLib); if (SvcParms) free(SvcParms); } /******************************************************************************/ /* C o n f i g u r e */ /******************************************************************************/ bool XrdSsiSfsConfig::Configure(const char *cFN) { char *var; const char *tmp; int cfgFD, retc, NoGo = 0; XrdOucEnv myEnv; XrdOucStream cStrm(&Log, getenv("XRDINSTANCE"), &myEnv, "=====> "); // Print warm-up message // Log.Say("++++++ ssi phase 1 initialization started."); // Preset all variables with common defaults // if (getenv("XRDDEBUG")) Trace.What = TRACESSI_ALL | TRACESSI_Debug; // If there is no config file, return with an error. // if( !cFN || !*cFN) {Log.Emsg("Config", "Configuration file not specified."); return false; } // Try to open the configuration file. // ConfigFN = strdup(cFN); if ( (cfgFD = open(cFN, O_RDONLY, 0)) < 0) {Log.Emsg("Config", errno, "open config file", cFN); return false; } cStrm.Attach(cfgFD); // Now start reading records until eof. // cFile = &cStrm; while((var = cFile->GetMyFirstWord())) {if (!strncmp(var, "ssi.", 4) || !strcmp(var, "all.role")) {if (ConfigXeq(var+4)) {cFile->Echo(); NoGo=1;}} } // Now check if any errors occured during file i/o // if ((retc = cStrm.LastError())) NoGo = Log.Emsg("Config", -retc, "read config file", cFN); cStrm.Close(); // Make sure we are configured as a server // if (!isServer) {Log.Emsg("Config", "ssi only supports server roles but role is not " "defined as 'server'."); return false; } // Configure filesystem callout as needed // fsChk = FSPath.NotEmpty(); if (isServer && !theFS) fsChk = false; // All done // tmp = (NoGo ? " failed." : " completed."); Log.Say("------ ssi phase 1 initialization", tmp); return !NoGo; } /******************************************************************************/ bool XrdSsiSfsConfig::Configure(XrdOucEnv *envP) { static char theSSI[] = {'s', 's', 'i', 0}; static char **myArgv = 0, *dfltArgv[] = {0, 0}; XrdOucEnv *xrdEnvP; const char *tmp; int myArgc = 0, NoGo; // Print warm-up message // Log.Say("++++++ ssi phase 2 initialization started."); // Now find the scheduler // if (envP && !(Sched = (XrdScheduler *)envP->GetPtr("XrdScheduler*"))) {Log.Emsg("Config", "Scheduler pointer is undefined!"); NoGo = 1; } else NoGo = 0; // Find our arguments, if any // if ((xrdEnvP = (XrdOucEnv *)envP->GetPtr("xrdEnv*")) && (myArgv = (char **)xrdEnvP->GetPtr("xrdssi.argv**"))) myArgc = xrdEnvP->GetInt("xrdssi.argc"); // Verify that we have some and substitute if not // if (!myArgv || myArgc < 1) {if (!(dfltArgv[0] = (char *)xrdEnvP->GetPtr("argv[0]"))) dfltArgv[0] = theSSI; myArgv = dfltArgv; myArgc = 1; } // Establish the network interface that the caller must provide // if (!isCms && (!envP || !(myIF = (XrdNetIF *)envP->GetPtr("XrdNetIF*")))) {Log.Emsg("Finder", "Network i/f undefined; unable to self-locate."); NoGo = 1; } // Now configure management functions and the cms if we are not the cms // if (!NoGo && !isCms && envP) {if (ConfigObj() || ConfigCms(envP)) NoGo = 1;} // Now configure the server // if (!NoGo && ConfigSvc(myArgv, myArgc)) NoGo = 1; // All done // tmp = (NoGo ? " failed." : " completed."); Log.Say("------ ssi phase 2 initialization", tmp); return !NoGo; } /******************************************************************************/ /* C o n f i g C m s */ /******************************************************************************/ class XrdOss; int XrdSsiSfsConfig::ConfigCms(XrdOucEnv *envP) { static const int cmsOpt = XrdCms::IsTarget; XrdCmsClient *cmsP, *(*CmsGC)(XrdSysLogger *, int, int, XrdOss *); XrdSysLogger *myLogger = Log.logger(); // Check if we are configuring a simple standalone server // if (!myRole) {myRole = strdup("standalone"); Log.Say("Config Configuring standalone server."); SsiCms = new XrdSsiCms; return 0; } // If a cmslib was specified then create a plugin object and get the client. // Otherwise, simply get the default client. // if (CmsLib) {XrdSysPlugin myLib(&Log, CmsLib, "cmslib", myVersion); CmsGC = (XrdCmsClient *(*)(XrdSysLogger *, int, int, XrdOss *)) (myLib.getPlugin("XrdCmsGetClient")); if (!CmsGC) return 1; myLib.Persist(); cmsP = CmsGC(myLogger, cmsOpt, myPort, 0); } else cmsP = XrdCms::GetDefaultClient(myLogger, cmsOpt, myPort); // If we have a client object onfigure it // if (!cmsP || !cmsP->Configure(ConfigFN, CmsParms, envP)) {delete cmsP; Log.Emsg("Config", "Unable to create cluster object."); return 1; } // Create the cluster onject and return // SsiCms = new XrdSsiCms(cmsP); return 0; } /******************************************************************************/ /* C o n f i g O b j */ /******************************************************************************/ int XrdSsiSfsConfig::ConfigObj() { static const int minRSZ = 8192; // Allocate a buffer pool // if (maxRSZ < minRSZ) maxRSZ = minRSZ; BuffPool = new XrdOucBuffPool(minRSZ, maxRSZ); XrdSsiFileSess::SetMaxSz(maxRSZ); return 0; } /******************************************************************************/ /* C o n f i g S v c */ /******************************************************************************/ int XrdSsiSfsConfig::ConfigSvc(char **myArgv, int myArgc) { XrdSsiErrInfo eInfo; XrdSysPlugin *myLib; XrdSsiProvider **theProvider; const char *pName = (isCms ? "XrdSsiProviderLookup" : "XrdSsiProviderServer"); // Make sure a library was specified // if (!SvcLib) {Log.Emsg("Config", "svclib not specified; provider cannot be loaded."); return 1; } // Create a plugin object // if (!(myLib = new XrdSysPlugin(&Log, SvcLib, "svclib", myVersion))) return 1; // Now get the entry point of the object creator // theProvider = (XrdSsiProvider **)(myLib->getPlugin(pName)); if (!theProvider) return 1; Provider = *theProvider; // Persist the library // myLib->Persist(); delete myLib; // Initialize the provider // if (!(Provider->Init(&SsiLogger, (XrdSsiCluster *)SsiCms, std::string(ConfigFN), std::string(SvcParms ? SvcParms : ""), myArgc, myArgv))) {Log.Emsg("Config", "Provider initialization failed."); return 1; } // If we are the cms then we are done. // if (isCms) return 0; // Otherwise we need to get the service object (we get only one) // if (!(Service = Provider->GetService(eInfo, ""))) {const char *eText = eInfo.Get().c_str(); Log.Emsg("Config", "Unable to obtain server-side service object;", (eText ? eText : "reason unknown.")); } return Service == 0; } /******************************************************************************/ /* C o n f i g X e q */ /******************************************************************************/ int XrdSsiSfsConfig::ConfigXeq(char *var) { // Now assign the appropriate global variable // if (!strcmp("cmslib", var)) return Xlib("cmslib", &CmsLib, &CmsParms); if (!strcmp("svclib", var)) return Xlib("svclib", &SvcLib, &SvcParms); if (!strcmp("fspath", var)) return Xfsp(); if (!strcmp("loglib", var)){char *theLib=0, *theParms=0; int rc=Xlib("loglib", &theLib, &theParms); if (theLib) free(theLib); if (theParms) free(theParms); return rc; } if (!strcmp("opts", var)) return Xopts(); if (!strcmp("role", var)) return Xrole(); if (!strcmp("trace", var)) return Xtrace(); // No match found, complain. // Log.Say("Config warning: ignoring unknown directive '",var,"'."); cFile->Echo(); return 0; } /******************************************************************************/ /* x L i b */ /******************************************************************************/ /* Function: Xlib Purpose: To parse the directive: xxxlib [] the path of the library to be used. optional parms to be passed Output: 0 upon success or !0 upon failure. */ int XrdSsiSfsConfig::Xlib(const char *lName, char **lPath, char **lParm) { char *val, parms[2048]; // Get the path and parms // if (!(val = cFile->GetWord()) || !val[0]) {Log.Emsg("Config", lName, "not specified"); return 1;} // Set the CmsLib pointer // if (*lPath) free(*lPath); *lPath = strdup(val); // Combine the path and parameters // *parms = 0; if (!cFile->GetRest(parms, sizeof(parms))) {Log.Emsg("Config", lName, "parameters too long"); return 1;} // Record the parameters, if any // if (*lParm) free(*lParm); *lParm = (*parms ? strdup(parms) : 0); return 0; } /******************************************************************************/ /* x f s p */ /******************************************************************************/ /* Function: xfsp Purpose: To parse the directive: fspath the path that is a file system path. Output: 0 upon success or !0 upon failure. */ int XrdSsiSfsConfig::Xfsp() { XrdOucPList *plp; char *val, pbuff[1024]; // Get the path // val = cFile->GetWord(); if (!val || !val[0]) {Log.Emsg("Config", "fspath path not specified"); return 1;} strlcpy(pbuff, val, sizeof(pbuff)); // Add path to configuration // if (!(plp = FSPath.Match(pbuff))) {plp = new XrdOucPList(pbuff,1); FSPath.Insert(plp); } return 0; } /******************************************************************************/ /* X o p t s */ /******************************************************************************/ /* Function: Xopts Purpose: To parse directive: opts [files ] [requests ] [respwt ] [maxrsz ] [authdns] [detreqok] authdns always supply client's resolved host name. detreqok allow detached requests. files the maximum number of file objects to hold in reserve. maxrsz the maximum size of a request. requests the maximum number of requests objects to hold in reserve. respwait the number of seconds to place client in response wait. Output: 0 upon success or 1 upon failure. */ int XrdSsiSfsConfig::Xopts() { static const int noArg = 1; static const int isNum = 2; static const int isSz = 3; static const int isTM = 4; char *val, oBuff[256]; long long ppp, rMax = -1, rObj = -1, fAut = -1, fDet = -1, fRwt = -1; int i, xtm; struct optsopts {const char *opname; long long *oploc; int maxv; int aOpt;} opopts[] = { {"authinfo", &fAut, 2, noArg}, {"detreqok", &fDet, 2, noArg}, {"maxrsz", &rMax, 16*1024*1024, isSz}, {"requests", &rObj, 64*1024, isNum}, {"respwt", &fRwt, 0x7fffffffLL, isTM} }; int numopts = sizeof(opopts)/sizeof(struct optsopts); if (!(val = cFile->GetWord())) {Log.Emsg("Config", "opts option not specified"); return 1;} while (val) {for (i = 0; i < numopts; i++) if (!strcmp(val, opopts[i].opname)) {if (opopts[i].aOpt == noArg) {*opopts[i].oploc = 1; break; } if (!(val = cFile->GetWord())) {Log.Emsg("Config", "opts ", opopts[i].opname, "argument not specified."); return 1; } snprintf(oBuff,sizeof(oBuff),"%s opts value",opopts[i].opname); if (opopts[i].aOpt == isSz) {if (XrdOuca2x::a2sz(Log, oBuff, val, &ppp, 0, opopts[i].maxv)) return 1; } else if (opopts[i].aOpt == isTM) {if (XrdOuca2x::a2tm(Log, oBuff, val, &xtm, 0, opopts[i].maxv)) return 1; ppp = xtm; } else if (XrdOuca2x::a2ll(Log, oBuff, val, &ppp, 0, opopts[i].maxv)) return 1; *opopts[i].oploc = ppp; break; } if (i >= numopts) Log.Say("Config warning: ignoring invalid opts option '",val,"'."); val = cFile->GetWord(); } // Set the values that were specified // if (fAut >= 0) XrdSsiFileSess::SetAuthDNS(); if (fAut >= 0) detReqOK = true; if (rMax >= 0) maxRSZ = static_cast(rMax); if (rObj >= 0) XrdSsiFileReq::SetMax(static_cast(rObj)); if (fRwt >= 0) respWT = fRwt; return 0; } /******************************************************************************/ /* x r o l e */ /******************************************************************************/ /* Function: Xrole Purpose: Parse: role { {[meta] | [proxy]} manager | proxy | [proxy] server | [proxy] supervisor } [if ...] manager xrootd: act as a manager (redirecting server). Prefixes: meta - connect only to manager meta's proxy - ignored cmsd: accept server subscribes and redirectors. Prefix modifiers do the following: meta - No other managers apply proxy - manage a cluster of proxy servers proxy xrootd: act as a server but supply data from another server. No local cmsd is present or required. cmsd: Generates an error as this makes no sense. server xrootd: act as a server (supply local data). Prefix modifications do the following: proxy - server is part of a cluster. A local cmsd is required. cmsd: subscribe to a manager, possibly as a proxy. supervisor xrootd: equivalent to manager. cmsd: equivalent to manager but also subscribe to a manager. When proxy is specified, subscribe as a proxy and only accept proxy servers. if Apply the manager directive if "if" is true. See XrdOucUtils:doIf() for "if" syntax. Output: 0 upon success or !0 upon failure. */ int XrdSsiSfsConfig::Xrole() { XrdCmsRole::RoleID roleID; char *val, *Tok1, *Tok2; int rc; // Get the first token // if (!(val = cFile->GetWord()) || !strcmp(val, "if")) {Log.Emsg("Config", "role not specified"); return 1;} Tok1 = strdup(val); // Get second token which might be an "if" // if ((val = cFile->GetWord()) && strcmp(val, "if")) {Tok2 = strdup(val); val = cFile->GetWord(); } else Tok2 = 0; // Process the if at this point // if (val && !strcmp("if", val)) if ((rc = XrdOucUtils::doIf(&Log,*cFile,"role directive", myHost,myInsName,myProg)) <= 0) {free(Tok1); if (Tok2) free(Tok2); if (!rc) cFile->noEcho(); return (rc < 0); } // Convert the role names to a role ID, if possible // roleID = XrdCmsRole::Convert(Tok1, Tok2); // Validate the role // rc = 0; if (roleID == XrdCmsRole::noRole) {Log.Emsg("Config", "invalid role -", Tok1, Tok2); rc = 1;} // Release storage and return if an error occured // free(Tok1); if (Tok2) free(Tok2); if (rc) return rc; // Fill out information // if (myRole) free(myRole); myRole = strdup(XrdCmsRole::Name(roleID)); isServer = (roleID == XrdCmsRole::Server); return 0; } /******************************************************************************/ /* x t r a c e */ /******************************************************************************/ /* Function: Xtrace Purpose: To parse the directive: trace the blank separated list of events to trace. Trace directives are cummalative. Output: 0 upon success or !0 upon failure. */ int XrdSsiSfsConfig::Xtrace() { static struct traceopts {const char *opname; int opval;} tropts[] = { {"all", TRACESSI_ALL}, {"debug", TRACESSI_Debug} }; int i, neg, trval = 0, numopts = sizeof(tropts)/sizeof(struct traceopts); char *val; if (!(val = cFile->GetWord())) {Log.Emsg("Config", "trace option not specified"); return 1;} while (val) {if (!strcmp(val, "off")) trval = 0; else {if ((neg = (val[0] == '-' && val[1]))) val++; for (i = 0; i < numopts; i++) {if (!strcmp(val, tropts[i].opname)) {if (neg) trval &= ~tropts[i].opval; else trval |= tropts[i].opval; break; } } if (i >= numopts) Log.Say("Config warning: ignoring invalid trace option '",val,"'."); } val = cFile->GetWord(); } Trace.What = trval; // All done // return 0; }