/******************************************************************************/ /* */ /* X r d C l i e n t A d m i n . c c */ /* */ /* Author: Fabrizio Furano (INFN Padova, 2004) */ /* Adapted from XTNetAdmin (root.cern.ch) originally done by */ /* Alvise Dorigo, Fabrizio Furano */ /* INFN Padova, 2003 */ /* */ /* 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. */ /******************************************************************************/ ////////////////////////////////////////////////////////////////////////// // // // A UNIX reference admin client for xrootd. // // // ////////////////////////////////////////////////////////////////////////// #include "XrdClient/XrdClientAdmin.hh" #include "XrdClient/XrdClientDebug.hh" #include "XrdClient/XrdClientUrlSet.hh" #include "XrdClient/XrdClientConn.hh" #include "XrdClient/XrdClientEnv.hh" #include "XrdClient/XrdClientConnMgr.hh" #include "XrdOuc/XrdOucTokenizer.hh" #include "XrdVersion.hh" #include #include #include #include #ifndef WIN32 #include #include #include #endif //_____________________________________________________________________________ void joinStrings(XrdOucString &buf, vecString &vs, int startidx, int endidx) { if (endidx < 0) endidx = vs.GetSize()-1; if (!vs.GetSize() || (vs.GetSize() <= startidx) || (endidx < startidx) ){ buf = ""; return; } int lastidx = xrdmin(vs.GetSize()-1, endidx); for(int j=startidx; j <= lastidx; j++) { buf += vs[j]; if (j < lastidx) buf += "\n"; } } //_____________________________________________________________________________ XrdClientAdmin::XrdClientAdmin(const char *url) { // Pick-up the latest setting of the debug level DebugSetLevel(EnvGetLong(NAME_DEBUG)); if (!ConnectionManager) Info(XrdClientDebug::kUSERDEBUG, "", "(C) 2004-2010 by the Xrootd group. XrdClientAdmin " << XrdVSTRING); fInitialUrl = url; fConnModule = new XrdClientConn(); if (!fConnModule) { Error("XrdClientAdmin", "Object creation failed."); abort(); } // Set this instance as a handler for handling the consequences of a redirection fConnModule->SetRedirHandler(this); } //_____________________________________________________________________________ XrdClientAdmin::~XrdClientAdmin() { delete fConnModule; } //_____________________________________________________________________________ bool XrdClientAdmin::Connect() { // Connect to the server // Nothing to do if already connected if (fConnModule && fConnModule->IsConnected()) { return TRUE; } // Now we try to set up the first connection // We cycle through the list of urls given in fInitialUrl // Max number of tries int connectMaxTry = EnvGetLong(NAME_FIRSTCONNECTMAXCNT); // Construction of the url set coming from the resolution of the hosts given XrdClientUrlSet urlArray(fInitialUrl); if (!urlArray.IsValid()) { Error("Connect", "The URL provided is incorrect."); return FALSE; } // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); // // Now start the connection phase, picking randomly from UrlArray // urlArray.Rewind(); int urlstried = 0; for (int connectTry = 0; (connectTry < connectMaxTry) && (!fConnModule->IsConnected()); connectTry++) { XrdClientUrlSet urlArray(fInitialUrl); urlArray.Rewind(); XrdClientUrlInfo *thisUrl = 0; urlstried = (urlstried == urlArray.Size()) ? 0 : urlstried; if ( fConnModule->IsOpTimeLimitElapsed(time(0)) ) { // We have been so unlucky and wasted too much time in connecting and being redirected fConnModule->Disconnect(TRUE); Error("Connect", "Access to server failed: Too much time elapsed without success."); break; } bool nogoodurl = TRUE; while (urlArray.Size() > 0) { // Get an url from the available set if ((thisUrl = urlArray.GetARandomUrl())) { if (fConnModule->CheckHostDomain(thisUrl->Host)) { nogoodurl = FALSE; Info(XrdClientDebug::kHIDEBUG, "Connect", "Trying to connect to " << thisUrl->Host << ":" << thisUrl->Port << ". Connect try " << connectTry+1); fConnModule->Connect(*thisUrl, this); // To find out if we have tried the whole URLs set urlstried++; break; } else { // Invalid domain: drop the url and move to next, if any urlArray.EraseUrl(thisUrl); continue; } } } if (nogoodurl) { Error("Connect", "Access denied to all URL domains requested"); break; } // We are connected to a host. Let's handshake with it. if (fConnModule->IsConnected()) { // Now the have the logical Connection ID, that we can use as streamid for // communications with the server Info(XrdClientDebug::kHIDEBUG, "Connect", "The logical connection id is " << fConnModule->GetLogConnID() << ". This will be the streamid for this client"); fConnModule->SetUrl(*thisUrl); Info(XrdClientDebug::kHIDEBUG, "Connect", "Working url is " << thisUrl->GetUrl()); // after connection deal with server if (!fConnModule->GetAccessToSrv()) { if (fConnModule->LastServerError.errnum == kXR_NotAuthorized) { if (urlstried == urlArray.Size()) { // Authentication error: we tried all the indicated URLs: // does not make much sense to retry fConnModule->Disconnect(TRUE); XrdOucString msg(fConnModule->LastServerError.errmsg); msg.erasefromend(1); Error("Connect", "Authentication failure: " << msg); connectTry = connectMaxTry; } else { XrdOucString msg(fConnModule->LastServerError.errmsg); msg.erasefromend(1); Info(XrdClientDebug::kHIDEBUG, "Connect", "Authentication failure: " << msg); } } else { Error("Connect", "Access to server failed: error: " << fConnModule->LastServerError.errnum << " (" << fConnModule->LastServerError.errmsg << ") - retrying."); } } else { Info(XrdClientDebug::kUSERDEBUG, "Connect", "Access to server granted."); break; } } // The server denied access. We have to disconnect. Info(XrdClientDebug::kHIDEBUG, "Connect", "Disconnecting."); fConnModule->Disconnect(FALSE); if (connectTry < connectMaxTry-1) { if (DebugLevel() >= XrdClientDebug::kUSERDEBUG) Info(XrdClientDebug::kUSERDEBUG, "Connect", "Connection attempt failed. Sleeping " << EnvGetLong(NAME_RECONNECTWAIT) << " seconds."); sleep(EnvGetLong(NAME_RECONNECTWAIT)); } } //for connect try if (!fConnModule->IsConnected()) { return FALSE; } // // Variable initialization // If the server is a new xrootd ( load balancer or data server) // if ((fConnModule->GetServerType() != kSTRootd) && (fConnModule->GetServerType() != kSTNone)) { // Now we are connected to a server that didn't redirect us after the // login/auth phase Info(XrdClientDebug::kUSERDEBUG, "Connect", "Connected."); } else { // We close the connection only if we do not know the server type. // In the rootd case the connection may be re-used later. if (fConnModule->GetServerType() == kSTNone) fConnModule->Disconnect(TRUE); return FALSE; } return TRUE; } //_____________________________________________________________________________ bool XrdClientAdmin::Stat(const char *fname, long &id, long long &size, long &flags, long &modtime) { // Return file stat information. The interface and return value is // identical to TSystem::GetPathInfo(). bool ok; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); // asks the server for stat file informations ClientRequest statFileRequest; memset( &statFileRequest, 0, sizeof(ClientRequest) ); fConnModule->SetSID(statFileRequest.header.streamid); statFileRequest.stat.requestid = kXR_stat; memset(statFileRequest.stat.reserved, 0, sizeof(statFileRequest.stat.reserved)); statFileRequest.header.dlen = strlen(fname); char fStats[2048]; id = 0; size = 0; flags = 0; modtime = 0; ok = fConnModule->SendGenCommand(&statFileRequest, (const char*)fname, NULL, fStats , FALSE, (char *)"Stat"); if (ok && (fConnModule->LastServerResp.status == 0)) { if (fConnModule->LastServerResp.dlen >= 0) fStats[fConnModule->LastServerResp.dlen] = 0; else fStats[0] = 0; Info(XrdClientDebug::kHIDEBUG, "Stat", "Returned stats=" << fStats); sscanf(fStats, "%ld %lld %ld %ld", &id, &size, &flags, &modtime); } return ok; } //_____________________________________________________________________________ bool XrdClientAdmin::Stat_vfs(const char *fname, int &rwservers, long long &rwfree, int &rwutil, int &stagingservers, long long &stagingfree, int &stagingutil) { // Return information for a virtual file system bool ok; // asks the server for stat file informations ClientRequest statFileRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &statFileRequest, 0, sizeof(ClientRequest) ); fConnModule->SetSID(statFileRequest.header.streamid); statFileRequest.stat.requestid = kXR_stat; memset(statFileRequest.stat.reserved, 0, sizeof(statFileRequest.stat.reserved)); statFileRequest.stat.options = kXR_vfs; statFileRequest.header.dlen = strlen(fname); char fStats[2048]; rwservers = 0; rwfree = 0; rwutil = 0; stagingservers = 0; stagingfree = 0; stagingutil = 0; ok = fConnModule->SendGenCommand(&statFileRequest, (const char*)fname, NULL, fStats , FALSE, (char *)"Stat_vfs"); if (ok && (fConnModule->LastServerResp.status == 0)) { if (fConnModule->LastServerResp.dlen >= 0) fStats[fConnModule->LastServerResp.dlen] = 0; else fStats[0] = 0; Info(XrdClientDebug::kHIDEBUG, "Stat_vfs", "Returned stats=" << fStats); sscanf(fStats, "%d %lld %d %d %lld %d", &rwservers, &rwfree, &rwutil, &stagingservers, &stagingfree, &stagingutil); } return ok; } //_____________________________________________________________________________ bool XrdClientAdmin::SysStatX(const char *paths_list, kXR_char *binInfo) { XrdOucString pl(paths_list); bool ret; // asks the server for stat file informations ClientRequest statXFileRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &statXFileRequest, 0, sizeof(ClientRequest) ); fConnModule->SetSID(statXFileRequest.header.streamid); statXFileRequest.header.requestid = kXR_statx; statXFileRequest.stat.dlen = pl.length(); ret = fConnModule->SendGenCommand(&statXFileRequest, pl.c_str(), NULL, binInfo , FALSE, (char *)"SysStatX"); return(ret); } //_____________________________________________________________________________ bool XrdClientAdmin::ExistFiles(vecString &vs, vecBool &vb) { bool ret; XrdOucString buf; joinStrings(buf, vs); kXR_char *Info; Info = (kXR_char*) malloc(vs.GetSize()+10); memset((void *)Info, 0, vs.GetSize()+10); ret = this->SysStatX(buf.c_str(), Info); if (ret) for(int j=0; j <= vs.GetSize()-1; j++) { bool tmp = TRUE; if ( (*(Info+j) & kXR_isDir) || (*(Info+j) & kXR_other) || (*(Info+j) & kXR_offline) ) tmp = FALSE; vb.Push_back(tmp); } free(Info); return ret; } //_____________________________________________________________________________ bool XrdClientAdmin::ExistDirs(vecString &vs, vecBool &vb) { bool ret; XrdOucString buf; joinStrings(buf, vs); kXR_char *Info; Info = (kXR_char*) malloc(vs.GetSize()+10); memset((void *)Info, 0, vs.GetSize()+10); ret = this->SysStatX(buf.c_str(), Info); if (ret) for(int j=0; j <= vs.GetSize()-1; j++) { bool tmp; if( (*(Info+j) & kXR_isDir) ) { tmp = TRUE; vb.Push_back(tmp); } else { tmp = FALSE; vb.Push_back(tmp); } } free(Info); return ret; } //_____________________________________________________________________________ bool XrdClientAdmin::IsFileOnline(vecString &vs, vecBool &vb) { bool ret; XrdOucString buf; joinStrings(buf, vs); kXR_char *Info; Info = (kXR_char*) malloc(vs.GetSize()+10); memset((void *)Info, 0, vs.GetSize()+10); ret = this->SysStatX(buf.c_str(), Info); if (ret) for(int j=0; j <= vs.GetSize()-1; j++) { bool tmp; if( !(*(Info+j) & kXR_offline) ) { tmp = TRUE; vb.Push_back(tmp); } else { tmp = FALSE; vb.Push_back(tmp); } } free(Info); return ret; } // Called by the conn module after a redirection has been succesfully handled //_____________________________________________________________________________ bool XrdClientAdmin::OpenFileWhenRedirected(char *newfhandle, bool &wasopen) { // We simply do nothing... wasopen = FALSE; return TRUE; } //_____________________________________________________________________________ bool XrdClientAdmin::Rmdir(const char *path) { // Remove an empty remote directory ClientRequest rmdirFileRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &rmdirFileRequest, 0, sizeof(rmdirFileRequest) ); fConnModule->SetSID(rmdirFileRequest.header.streamid); rmdirFileRequest.header.requestid = kXR_rmdir; rmdirFileRequest.header.dlen = strlen(path); return (fConnModule->SendGenCommand(&rmdirFileRequest, path, NULL, NULL, FALSE, (char *)"Rmdir")); } //_____________________________________________________________________________ bool XrdClientAdmin::Rm(const char *file) { // Remove a remote file ClientRequest rmFileRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &rmFileRequest, 0, sizeof(rmFileRequest) ); fConnModule->SetSID(rmFileRequest.header.streamid); rmFileRequest.header.requestid = kXR_rm; rmFileRequest.header.dlen = strlen(file); return (fConnModule->SendGenCommand(&rmFileRequest, file, NULL, NULL, FALSE, (char *)"Rm")); } //_____________________________________________________________________________ bool XrdClientAdmin::Chmod(const char *file, int user, int group, int other) { // Change the permission of a remote file ClientRequest chmodFileRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &chmodFileRequest, 0, sizeof(chmodFileRequest) ); fConnModule->SetSID(chmodFileRequest.header.streamid); chmodFileRequest.header.requestid = kXR_chmod; if(user & 4) chmodFileRequest.chmod.mode |= kXR_ur; if(user & 2) chmodFileRequest.chmod.mode |= kXR_uw; if(user & 1) chmodFileRequest.chmod.mode |= kXR_ux; if(group & 4) chmodFileRequest.chmod.mode |= kXR_gr; if(group & 2) chmodFileRequest.chmod.mode |= kXR_gw; if(group & 1) chmodFileRequest.chmod.mode |= kXR_gx; if(other & 4) chmodFileRequest.chmod.mode |= kXR_or; if(other & 2) chmodFileRequest.chmod.mode |= kXR_ow; if(other & 1) chmodFileRequest.chmod.mode |= kXR_ox; chmodFileRequest.header.dlen = strlen(file); return (fConnModule->SendGenCommand(&chmodFileRequest, file, NULL, NULL, FALSE, (char *)"Chmod")); } //_____________________________________________________________________________ bool XrdClientAdmin::Mkdir(const char *dir, int user, int group, int other) { // Create a remote directory ClientRequest mkdirRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &mkdirRequest, 0, sizeof(mkdirRequest) ); fConnModule->SetSID(mkdirRequest.header.streamid); mkdirRequest.header.requestid = kXR_mkdir; memset(mkdirRequest.mkdir.reserved, 0, sizeof(mkdirRequest.mkdir.reserved)); if(user & 4) mkdirRequest.mkdir.mode |= kXR_ur; if(user & 2) mkdirRequest.mkdir.mode |= kXR_uw; if(user & 1) mkdirRequest.mkdir.mode |= kXR_ux; if(group & 4) mkdirRequest.mkdir.mode |= kXR_gr; if(group & 2) mkdirRequest.mkdir.mode |= kXR_gw; if(group & 1) mkdirRequest.mkdir.mode |= kXR_gx; if(other & 4) mkdirRequest.mkdir.mode |= kXR_or; if(other & 2) mkdirRequest.mkdir.mode |= kXR_ow; if(other & 1) mkdirRequest.mkdir.mode |= kXR_ox; mkdirRequest.mkdir.options[0] = kXR_mkdirpath; mkdirRequest.header.dlen = strlen(dir); return (fConnModule->SendGenCommand(&mkdirRequest, dir, NULL, NULL, FALSE, (char *)"Mkdir")); } //_____________________________________________________________________________ bool XrdClientAdmin::Mv(const char *fileSrc, const char *fileDest) { bool ret; // Rename a remote file ClientRequest mvFileRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &mvFileRequest, 0, sizeof(mvFileRequest) ); fConnModule->SetSID(mvFileRequest.header.streamid); mvFileRequest.header.requestid = kXR_mv; mvFileRequest.header.dlen = strlen( fileDest ) + strlen( fileSrc ) + 1; // len + len + string terminator \0 char *data = new char[mvFileRequest.header.dlen+2]; // + 1 for space separator + 1 for \0 memset(data, 0, mvFileRequest.header.dlen+2); strcpy( data, fileSrc ); strcat( data, " " ); strcat( data, fileDest ); ret = fConnModule->SendGenCommand(&mvFileRequest, data, NULL, NULL, FALSE, (char *)"Mv"); delete [] data; return ret; } //_____________________________________________________________________________ UnsolRespProcResult XrdClientAdmin::ProcessUnsolicitedMsg(XrdClientUnsolMsgSender *sender, XrdClientMessage *unsolmsg) { // We are here if an unsolicited response comes from a logical conn // The response comes in the form of an TXMessage *, that must NOT be // destroyed after processing. It is destroyed by the first sender. // Remember that we are in a separate thread, since unsolicited // responses are asynchronous by nature. if (unsolmsg->GetStatusCode() != XrdClientMessage::kXrdMSC_ok) { Info(XrdClientDebug::kHIDEBUG, "ProcessUnsolicitedMsg", "Incoming unsolicited communication error message." ); } else { Info(XrdClientDebug::kHIDEBUG, "ProcessUnsolicitedMsg", "Incoming unsolicited response from streamid " << unsolmsg->HeaderSID() ); } // Local processing .... if (unsolmsg->IsAttn()) { struct ServerResponseBody_Attn *attnbody; attnbody = (struct ServerResponseBody_Attn *)unsolmsg->GetData(); int actnum = (attnbody) ? (attnbody->actnum) : 0; // "True" async resp is processed here switch (actnum) { case kXR_asyncdi: // Disconnection + delayed reconnection request struct ServerResponseBody_Attn_asyncdi *di; di = (struct ServerResponseBody_Attn_asyncdi *)unsolmsg->GetData(); // Explicit redirection request if (di) { Info(XrdClientDebug::kUSERDEBUG, "ProcessUnsolicitedMsg", "Requested Disconnection + Reconnect in " << ntohl(di->wsec) << " seconds."); fConnModule->SetRequestedDestHost((char *)(fConnModule->GetCurrentUrl().Host.c_str()), fConnModule->GetCurrentUrl().Port); fConnModule->SetREQDelayedConnectState(ntohl(di->wsec)); } // Other objects may be interested in this async resp return kUNSOL_CONTINUE; break; case kXR_asyncrd: // Redirection request struct ServerResponseBody_Attn_asyncrd *rd; rd = (struct ServerResponseBody_Attn_asyncrd *)unsolmsg->GetData(); // Explicit redirection request if (rd && (strlen(rd->host) > 0)) { Info(XrdClientDebug::kUSERDEBUG, "ProcessUnsolicitedMsg", "Requested redir to " << rd->host << ":" << ntohl(rd->port)); fConnModule->SetRequestedDestHost(rd->host, ntohl(rd->port)); } // Other objects may be interested in this async resp return kUNSOL_CONTINUE; break; case kXR_asyncwt: // Puts the client in wait state struct ServerResponseBody_Attn_asyncwt *wt; wt = (struct ServerResponseBody_Attn_asyncwt *)unsolmsg->GetData(); if (wt) { Info(XrdClientDebug::kUSERDEBUG, "ProcessUnsolicitedMsg", "Pausing client for " << ntohl(wt->wsec) << " seconds."); fConnModule->SetREQPauseState(ntohl(wt->wsec)); } // Other objects may be interested in this async resp return kUNSOL_CONTINUE; break; case kXR_asyncgo: // Resumes from pause state Info(XrdClientDebug::kUSERDEBUG, "ProcessUnsolicitedMsg", "Resuming from pause."); fConnModule->SetREQPauseState(0); // Other objects may be interested in this async resp return kUNSOL_CONTINUE; break; case kXR_asynresp: // A response to a request which got a kXR_waitresp as a response // We pass it direcly to the connmodule for processing // The processing will tell if the streamid matched or not, // in order to stop further processing return fConnModule->ProcessAsynResp(unsolmsg); break; default: Info(XrdClientDebug::kUSERDEBUG, "ProcessUnsolicitedMsg", "Empty message"); // Propagate the message return kUNSOL_CONTINUE; } // switch } else // Let's see if the message is a communication error message if (unsolmsg->GetStatusCode() != XrdClientMessage::kXrdMSC_ok) return fConnModule->ProcessAsynResp(unsolmsg); return kUNSOL_CONTINUE; } //_____________________________________________________________________________ bool XrdClientAdmin::Protocol(kXR_int32 &proto, kXR_int32 &kind) { ClientRequest protoRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &protoRequest, 0, sizeof(protoRequest) ); fConnModule->SetSID(protoRequest.header.streamid); protoRequest.header.requestid = kXR_protocol; char buf[8]; // For now 8 bytes are returned... in future could increase with more infos bool ret = fConnModule->SendGenCommand(&protoRequest, NULL, NULL, buf, FALSE, (char *)"Protocol"); memcpy(&proto, buf, sizeof(proto)); memcpy(&kind, buf + sizeof(proto), sizeof(kind)); proto = ntohl(proto); kind = ntohl(kind); return ret; } //_____________________________________________________________________________ bool XrdClientAdmin::Prepare(vecString vs, kXR_char option, kXR_char prty) { // Send a bulk prepare request for a vector of paths // Split a huge prepare list into smaller chunks XrdOucString buf; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); if (vs.GetSize() < 75) { joinStrings(buf, vs); return Prepare(buf.c_str(), option, prty); } for (int i = 0; i < vs.GetSize()+50; i+=50) { joinStrings(buf, vs, i, i+49); if (!Prepare(buf.c_str(), option, prty)) return false; buf = ""; } return true; } //_____________________________________________________________________________ bool XrdClientAdmin::Prepare(const char *buf, kXR_char option, kXR_char prty) { // Send a bulk prepare request for a '\n' separated list in buf ClientRequest prepareRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &prepareRequest, 0, sizeof(prepareRequest) ); fConnModule->SetSID(prepareRequest.header.streamid); prepareRequest.header.requestid = kXR_prepare; prepareRequest.prepare.options = option; prepareRequest.prepare.prty = prty; prepareRequest.header.dlen = strlen(buf); bool ret = fConnModule->SendGenCommand(&prepareRequest, buf, NULL, NULL , FALSE, (char *)"Prepare"); return ret; } //_____________________________________________________________________________ bool XrdClientAdmin::DirList(const char *dir, vecString &entries, bool askallservers) { // Get an ls-like output with respect to the specified dir // If this is a redirector, we will be given the list of the servers // which host this directory // If askallservers is true then we will just ask for the whole list of servers. // the query will always be the same, and this will likely skip the 5s delay after the first shot // The danger is to be forced to contact a huge number of servers in very big clusters // bool ret = true; XrdClientVector hosts; if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) { char str[1024]; strcpy(str, "*"); strncat(str, dir, 1023); if (!Locate((kXR_char *)str, hosts)) return false; } else { XrdClientLocate_Info nfo; memset(&nfo, 0, sizeof(nfo)); strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str()); hosts.Push_back(nfo); } // Then we cycle among them asking everyone bool foundsomething = false; for (int i = 0; i < hosts.GetSize(); i++) { fConnModule->Disconnect(false); XrdClientUrlInfo url((const char *)hosts[i].Location); url.Proto = "root"; if (fConnModule->GoToAnotherServer(url) != kOK) { ret = false; break; } fConnModule->ClearLastServerError(); if (!DirList_low(dir, entries)) { if (fConnModule->LastServerError.errnum != kXR_NotFound) { ret = false; break; } } else foundsomething = true; } // At the end we want to rewind to the main redirector in any case GoBackToRedirector(); if (!foundsomething) ret = false; return ret; } //_____________________________________________________________________________ bool XrdClientAdmin::DirList(const char *dir, XrdClientVector &dirlistinfo, bool askallservers) { // Get an ls-like output with respect to the specified dir // Here we are also interested in the stat information for each file // If this is a redirector, we will be given the list of the servers // which host this directory // If askallservers is true then we will just ask for the whole list of servers. // the query will always be the same, and this will likely skip the 5s delay after the first shot // The danger is to be forced to contact a huge number of servers in very big clusters // If this is a concern, one should set askallservers to false // bool ret = true; vecString entries; XrdClientVector hosts; XrdOucString fullpath; if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) { char str[1024]; strcpy(str, "*"); strncat(str, dir, 1023); if (!Locate((kXR_char *)str, hosts)) return false; } else { XrdClientLocate_Info nfo; memset(&nfo, 0, sizeof(nfo)); strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str()); hosts.Push_back(nfo); } // Then we cycle among them asking everyone bool foundsomething = false; for (int i = 0; i < hosts.GetSize(); i++) { fConnModule->Disconnect(false); XrdClientUrlInfo url((const char *)hosts[i].Location); url.Proto = "root"; if (fConnModule->GoToAnotherServer(url) != kOK) { ret = false; break; } fConnModule->ClearLastServerError(); int precentries = entries.GetSize(); if (!DirList_low(dir, entries)) { if ((fConnModule->LastServerError.errnum != kXR_NotFound) && (fConnModule->LastServerError.errnum != kXR_noErrorYet)) { ret = false; break; } } else foundsomething = true; int newentries = entries.GetSize(); DirListInfo info; dirlistinfo.Resize(newentries); // Here we have the entries. We want to accumulate the stat information for each of them // We are still connected to the same server which gave the last dirlist response info.host = GetCurrentUrl().HostWPort; for (int k = precentries; k < newentries; k++) { info.fullpath = dir; if (info.fullpath[info.fullpath.length()-1] != '/') info.fullpath += "/"; info.fullpath += entries[k]; info.size = 0; info.id = 0; info.flags = 0; info.modtime = 0; if (!Stat(info.fullpath.c_str(), info.id, info.size, info.flags, info.modtime)) { ret = false; //break; } dirlistinfo[k] = info; } } // At the end we want to rewind to the main redirector in any case GoBackToRedirector(); if (!foundsomething) ret = false; return ret; } //_____________________________________________________________________________ bool XrdClientAdmin::DirList_low(const char *dir, vecString &entries) { bool ret; // asks the server for the content of a directory ClientRequest DirListFileRequest; kXR_char *dl; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &DirListFileRequest, 0, sizeof(ClientRequest) ); fConnModule->SetSID(DirListFileRequest.header.streamid); DirListFileRequest.header.requestid = kXR_dirlist; DirListFileRequest.dirlist.dlen = strlen(dir); // Note that the connmodule has to dynamically alloc the space for the answer ret = fConnModule->SendGenCommand(&DirListFileRequest, dir, reinterpret_cast(&dl), 0, TRUE, (char *)"DirList"); // Now parse the answer building the entries vector if (ret) { kXR_char *startp = dl, *endp = dl; char entry[1024]; XrdOucString e; while (startp) { if ( (endp = (kXR_char *)strchr((const char*)startp, '\n')) ) { strncpy(entry, (char *)startp, endp-startp); entry[endp-startp] = 0; endp++; } else strcpy(entry, (char *)startp); if (strlen(entry) && strcmp((char *)entry, ".") && strcmp((char *)entry, "..")) { e = entry; entries.Push_back(e); } startp = endp; } } if (dl) free(dl); return(ret); } //_____________________________________________________________________________ long XrdClientAdmin::GetChecksum(kXR_char *path, kXR_char **chksum) { ClientRequest chksumRequest; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &chksumRequest, 0, sizeof(chksumRequest) ); fConnModule->SetSID(chksumRequest.header.streamid); chksumRequest.query.requestid = kXR_query; chksumRequest.query.infotype = kXR_Qcksum; chksumRequest.query.dlen = strlen((char *) path); bool ret = fConnModule->SendGenCommand(&chksumRequest, (const char*) path, (void **)chksum, NULL, TRUE, (char *)"GetChecksum"); if (ret) return (fConnModule->LastServerResp.dlen); else return 0; } int XrdClientAdmin::LocalLocate(kXR_char *path, XrdClientVector &res, bool writable, int opts, bool all) { // Fires a locate req towards the currently connected server, and pushes the // results into the res vector // // If 'all' is false, returns the position in the vector of the found info (-1 if // not found); else returns the number of non-data servers. ClientRequest locateRequest; char *resp = 0; int retval = (all) ? 0 : -1; memset( &locateRequest, 0, sizeof(locateRequest) ); fConnModule->SetSID(locateRequest.header.streamid); locateRequest.locate.requestid = kXR_locate; locateRequest.locate.options = opts; locateRequest.locate.dlen = strlen((char *) path); // Resp is allocated inside the call bool ret = fConnModule->SendGenCommand(&locateRequest, (const char*) path, (void **)&resp, 0, true, (char *)"LocalLocate"); if (!ret) return -2; if (!resp) return -1; if (!strlen(resp)) { free(resp); return -1; } // Parse the output XrdOucString rs(resp), s; free(resp); int from = 0; while ((from = rs.tokenize(s,from,' ')) != -1) { // If a token is bad, we keep the ones processed previously if (s.length() < 8 || (s[2] != '[') || (s[4] != ':')) { Error("LocalLocate", "Invalid server response. Resp: '" << s << "'"); continue; } XrdClientLocate_Info nfo; // Info type switch (s[0]) { case 'S': nfo.Infotype = XrdClientLocate_Info::kXrdcLocDataServer; break; case 's': nfo.Infotype = XrdClientLocate_Info::kXrdcLocDataServerPending; break; case 'M': nfo.Infotype = XrdClientLocate_Info::kXrdcLocManager; break; case 'm': nfo.Infotype = XrdClientLocate_Info::kXrdcLocManagerPending; break; default: Info(XrdClientDebug::kNODEBUG, "LocalLocate", "Unknown info type: '" << s << "'"); } // Write capabilities nfo.CanWrite = (s[1] == 'w') ? 1 : 0; // Endpoint url s.erase(0, s.find("[::")+3); s.replace("]",""); strcpy((char *)nfo.Location, s.c_str()); res.Push_back(nfo); if (nfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) { if (!all) { if (!writable || nfo.CanWrite) { retval = res.GetSize() - 1; break; } } } else { if (all) // Count non-dataservers retval++; } } return retval; } //_____________________________________________________________________________ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientLocate_Info &resp, bool writable) { // Find out any exact location of file 'path' and save the corresponding // URL in resp. // Returns true if found // If the retval is false and writable==true , resp contains a non writable url // if there is one bool found = false; memset(&resp, 0, sizeof(resp)); if (!fConnModule) return 0; if (!fConnModule->IsConnected()) return 0; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); // Old servers will use what's there if (fConnModule->GetServerProtocol() < 0x290) { long id, flags, modtime; long long size; bool ok = Stat((const char *)path, id, size, flags, modtime); if (ok && (fConnModule->LastServerResp.status == 0)) { resp.Infotype = XrdClientLocate_Info::kXrdcLocDataServer; resp.CanWrite = 1; strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str()); } GoBackToRedirector(); return ok; } XrdClientUrlInfo currurl(fConnModule->GetCurrentUrl().GetUrl()); if (!currurl.HostWPort.length()) return 0; // Set up the starting point in the vectored queue XrdClientVector hosts; XrdClientLocate_Info nfo; nfo.Infotype = XrdClientLocate_Info::kXrdcLocManager; nfo.CanWrite = true; strcpy((char *)nfo.Location, currurl.HostWPort.c_str()); hosts.Push_back(nfo); bool firsthost = true; XrdClientLocate_Info currnfo; int pos = 0; // Expand a level, i.e. ask to all the masters and remove items from the list while (pos < hosts.GetSize()) { // Take the first item to process currnfo = hosts[pos]; // If it's a master, we have to contact it, otherwise take the next if ((currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) || (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServerPending)) { pos++; continue; } // Here, currnfo is pointing to a master we have to contact currurl.TakeUrl((char *)currnfo.Location); if (currurl.HostAddr == "") currurl.HostAddr = currurl.Host; // Connect to the given host. At the beginning we are connected to the starting point // A failed connection is just ignored. Only one attempt is performed. Timeouts are // honored. if (!firsthost) { fConnModule->Disconnect(false); if (fConnModule->GoToAnotherServer(currurl) != kOK) { hosts.Erase(pos); continue; } } if (firsthost) firsthost = false; // We are connected, do the locate int posds = LocalLocate(path, hosts, writable, kXR_nowait); found = (posds > -1) ? 1 : 0; if (found) { resp = hosts[posds]; break; } // We did not finish, take the next hosts.Erase(pos); } if (!found && hosts.GetSize()) { // If not found, we check anyway in the remaining list // to pick a pending one if present for (int ii = 0; ii < hosts.GetSize(); ii++) { currnfo = hosts[ii]; if ( (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) || (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServerPending) ) { resp = currnfo; if (!writable || resp.CanWrite) { found = true; break; } } } } // At the end we want to rewind to the main redirector in any case GoBackToRedirector(); return found; } //_____________________________________________________________________________ bool XrdClientAdmin::Locate( kXR_char *path, XrdClientVector &hosts, int opts ) { // Find out any exact location of file 'path' and save the corresponding // URL in resp. // Returns true if found at least one hosts.Clear(); if (!fConnModule) return 0; if (!fConnModule->IsConnected()) return 0; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); // Old servers will use what's there if (fConnModule->GetServerProtocol() < 0x290) { long id, flags, modtime; long long size; XrdClientLocate_Info resp; bool ok = Stat((const char *)path, id, size, flags, modtime); if (ok && (fConnModule->LastServerResp.status == 0)) { resp.Infotype = XrdClientLocate_Info::kXrdcLocDataServer; resp.CanWrite = 1; strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str()); hosts.Push_back(resp); } GoBackToRedirector(); return ok; } XrdClientUrlInfo currurl(fConnModule->GetCurrentUrl().GetUrl()); if (!currurl.HostWPort.length()) return 0; // Set up the starting point in the vectored queue XrdClientLocate_Info nfo; nfo.Infotype = XrdClientLocate_Info::kXrdcLocManager; nfo.CanWrite = true; strcpy((char *)nfo.Location, currurl.HostWPort.c_str()); hosts.Push_back(nfo); bool firsthost = true; XrdClientLocate_Info currnfo; int pos = 0; // Expand a level, i.e. ask to all the masters and remove items from the list while (pos < hosts.GetSize()) { // Take the first item to process currnfo = hosts[pos]; // If it's a master, we have to contact it, otherwise take the next if ((currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) || (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServerPending)) { pos++; continue; } // Here, currnfo is pointing to a master we have to contact currurl.TakeUrl((char *)currnfo.Location); if (currurl.HostAddr == "") currurl.HostAddr = currurl.Host; // Connect to the given host. At the beginning we are connected to the starting point // A failed connection is just ignored. Only one attempt is performed. Timeouts are // honored. if (!firsthost) { fConnModule->Disconnect(false); if (fConnModule->GoToAnotherServer(currurl) != kOK) { hosts.Erase(pos); continue; } } if (firsthost) firsthost = false; // We are connected, do the locate LocalLocate(path, hosts, true, opts, true); // We did not finish, take the next hosts.Erase(pos); } // At the end we want to rewind to the main redirector in any case GoBackToRedirector(); return (hosts.GetSize() > 0); } bool XrdClientAdmin::Truncate(const char *path, long long newsize) { ClientRequest truncateRequest; int l = strlen(path); if (!l) return false; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &truncateRequest, 0, sizeof(truncateRequest) ); fConnModule->SetSID(truncateRequest.header.streamid); truncateRequest.header.requestid = kXR_truncate; truncateRequest.truncate.offset = newsize; truncateRequest.header.dlen = l; bool ret = fConnModule->SendGenCommand(&truncateRequest, path, NULL, NULL , FALSE, (char *)"Truncate"); return ret; } // Quickly jump to the former redirector. Useful after having been redirected. void XrdClientAdmin::GoBackToRedirector() { if (fConnModule) { fConnModule->GoBackToRedirector(); if (!fConnModule->IsConnected()) { XrdClientUrlInfo u(fInitialUrl); fConnModule->GoToAnotherServer(u); } } } // Compute an estimation of the available free space in the given cachefs partition // The estimation can be fooled if multiple servers mount the same network storage bool XrdClientAdmin::GetSpaceInfo(const char *logicalname, long long &totspace, long long &totfree, long long &totused, long long &largestchunk) { bool ret = true; XrdClientVector hosts; totspace = 0; totfree = 0; totused = 0; largestchunk = 0; if (fConnModule->GetServerProtocol() >= 0x291) { if (!Locate((kXR_char *)"*", hosts)) return false; } else { XrdClientLocate_Info nfo; memset(&nfo, 0, sizeof(nfo)); strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str()); hosts.Push_back(nfo); } // Then we cycle among them asking everyone for (int i = 0; i < hosts.GetSize(); i++) { fConnModule->Disconnect(false); XrdClientUrlInfo url((const char *)hosts[i].Location); url.Proto = "root"; if (fConnModule->GoToAnotherServer(url) != kOK) { ret = false; break; } // Fire the query request and update the results ClientRequest qspacereq; // Set the max transaction duration fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); memset( &qspacereq, 0, sizeof(qspacereq) ); fConnModule->SetSID(qspacereq.header.streamid); qspacereq.query.requestid = kXR_query; qspacereq.query.infotype = kXR_Qspace; qspacereq.query.dlen = ( logicalname ? strlen(logicalname) : 0); char *resp = 0; if (fConnModule->SendGenCommand(&qspacereq, logicalname, (void **)&resp, 0, TRUE, (char *)"GetSpaceInfo")) { XrdOucString rs(resp), s; free(resp); // Here we have the response relative to a server // Now we are going to have fun in parsing it int from = 0; while ((from = rs.tokenize(s,from,'&')) != -1) { if (s.length() < 4) continue; int pos = s.find("="); XrdOucString tk, val; if (pos != STR_NPOS) { tk.assign(s, 0, pos-1); val.assign(s, pos+1); #ifndef WIN32 if ( (tk == "oss.space") && (val.length() > 1) ) { totspace += atoll(val.c_str()); } else if ( (tk == "oss.free") && (val.length() > 1) ) { totfree += atoll(val.c_str()); } else if ( (tk == "oss.maxf") && (val.length() > 1) ) { largestchunk = xrdmax(largestchunk, atoll(val.c_str())); } else if ( (tk == "oss.used") && (val.length() > 1) ) { totused += atoll(val.c_str()); } #else if ( (tk == "oss.space") && (val.length() > 1) ) { totspace += _atoi64(val.c_str()); } else if ( (tk == "oss.free") && (val.length() > 1) ) { totfree += _atoi64(val.c_str()); } else if ( (tk == "oss.maxf") && (val.length() > 1) ) { largestchunk = xrdmax(largestchunk, _atoi64(val.c_str())); } else if ( (tk == "oss.used") && (val.length() > 1) ) { totused += _atoi64(val.c_str()); } #endif } } } } // At the end we want to rewind to the main redirector in any case GoBackToRedirector(); return ret; }