00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 const char *XrdClientAdminCVSID = "$Id: XrdClientAdmin.cc 38011 2011-02-08 18:35:57Z ganis $";
00017
00018 #include "XrdClient/XrdClientAdmin.hh"
00019 #include "XrdClient/XrdClientDebug.hh"
00020 #include "XrdClient/XrdClientUrlSet.hh"
00021 #include "XrdClient/XrdClientConn.hh"
00022 #include "XrdClient/XrdClientEnv.hh"
00023 #include "XrdClient/XrdClientConnMgr.hh"
00024 #include "XrdOuc/XrdOucTokenizer.hh"
00025
00026
00027 #include <stdio.h>
00028 #include <sys/types.h>
00029 #include <sys/stat.h>
00030 #include <fcntl.h>
00031 #ifndef WIN32
00032 #include <unistd.h>
00033 #include <strings.h>
00034 #include <netinet/in.h>
00035 #endif
00036
00037
00038 void joinStrings(XrdOucString &buf, vecString &vs,
00039 int startidx, int endidx)
00040 {
00041
00042 if (endidx < 0) endidx = vs.GetSize()-1;
00043
00044 if (!vs.GetSize() || (vs.GetSize() <= startidx) ||
00045 (endidx < startidx) ){
00046 buf = "";
00047 return;
00048 }
00049
00050 int lastidx = xrdmin(vs.GetSize()-1, endidx);
00051
00052 for(int j=startidx; j <= lastidx; j++) {
00053 buf += vs[j];
00054 if (j < lastidx) buf += "\n";
00055 }
00056
00057 }
00058
00059
00060 XrdClientAdmin::XrdClientAdmin(const char *url) {
00061
00062
00063
00064 DebugSetLevel(EnvGetLong(NAME_DEBUG));
00065
00066 if (!ConnectionManager)
00067 Info(XrdClientDebug::kUSERDEBUG,
00068 "",
00069 "(C) 2004-2010 by the Xrootd group. XrdClientAdmin " << XRD_CLIENT_VERSION);
00070
00071 fInitialUrl = url;
00072
00073 fConnModule = new XrdClientConn();
00074
00075 if (!fConnModule) {
00076 Error("XrdClientAdmin",
00077 "Object creation failed.");
00078 abort();
00079 }
00080
00081
00082 fConnModule->SetRedirHandler(this);
00083
00084 }
00085
00086
00087 XrdClientAdmin::~XrdClientAdmin()
00088 {
00089 delete fConnModule;
00090 }
00091
00092
00093
00094
00095 bool XrdClientAdmin::Connect()
00096 {
00097
00098
00099
00100 if (fConnModule && fConnModule->IsConnected()) {
00101 return TRUE;
00102 }
00103
00104 short locallogid;
00105
00106
00107
00108
00109
00110 int connectMaxTry = EnvGetLong(NAME_FIRSTCONNECTMAXCNT);
00111
00112
00113 XrdClientUrlSet urlArray(fInitialUrl);
00114 if (!urlArray.IsValid()) {
00115 Error("Connect", "The URL provided is incorrect.");
00116 return FALSE;
00117 }
00118
00119
00120 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00121
00122
00123
00124
00125 urlArray.Rewind();
00126 locallogid = -1;
00127 int urlstried = 0;
00128 for (int connectTry = 0;
00129 (connectTry < connectMaxTry) && (!fConnModule->IsConnected());
00130 connectTry++) {
00131
00132 XrdClientUrlInfo *thisUrl = 0;
00133 urlstried = (urlstried == urlArray.Size()) ? 0 : urlstried;
00134
00135 if ( fConnModule->IsOpTimeLimitElapsed(time(0)) ) {
00136
00137 fConnModule->Disconnect(TRUE);
00138 Error("Connect", "Access to server failed: Too much time elapsed without success.");
00139 break;
00140 }
00141
00142 bool nogoodurl = TRUE;
00143 while (urlArray.Size() > 0) {
00144
00145
00146 if ((thisUrl = urlArray.GetARandomUrl())) {
00147
00148 if (fConnModule->CheckHostDomain(thisUrl->Host)) {
00149 nogoodurl = FALSE;
00150 Info(XrdClientDebug::kHIDEBUG, "Connect", "Trying to connect to " <<
00151 thisUrl->Host << ":" << thisUrl->Port <<
00152 ". Connect try " << connectTry+1);
00153 locallogid = fConnModule->Connect(*thisUrl, this);
00154
00155 urlstried++;
00156 break;
00157 } else {
00158
00159 urlArray.EraseUrl(thisUrl);
00160 continue;
00161 }
00162
00163 }
00164 }
00165 if (nogoodurl) {
00166 Error("Connect", "Access denied to all URL domains requested");
00167 break;
00168 }
00169
00170
00171 if (fConnModule->IsConnected()) {
00172
00173
00174
00175
00176 Info(XrdClientDebug::kHIDEBUG, "Connect",
00177 "The logical connection id is " << fConnModule->GetLogConnID() <<
00178 ". This will be the streamid for this client");
00179
00180 fConnModule->SetUrl(*thisUrl);
00181
00182 Info(XrdClientDebug::kHIDEBUG, "Connect",
00183 "Working url is " << thisUrl->GetUrl());
00184
00185
00186 if (!fConnModule->GetAccessToSrv()) {
00187 if (fConnModule->LastServerError.errnum == kXR_NotAuthorized) {
00188 if (urlstried == urlArray.Size()) {
00189
00190
00191 fConnModule->Disconnect(TRUE);
00192 XrdOucString msg(fConnModule->LastServerError.errmsg);
00193 msg.erasefromend(1);
00194 Error("Connect", "Authentication failure: " << msg);
00195 connectTry = connectMaxTry;
00196 } else {
00197 XrdOucString msg(fConnModule->LastServerError.errmsg);
00198 msg.erasefromend(1);
00199 Info(XrdClientDebug::kHIDEBUG, "Connect",
00200 "Authentication failure: " << msg);
00201 }
00202 } else {
00203 Error("Connect", "Access to server failed: error: " <<
00204 fConnModule->LastServerError.errnum << " (" <<
00205 fConnModule->LastServerError.errmsg << ") - retrying.");
00206 }
00207 } else {
00208 Info(XrdClientDebug::kUSERDEBUG, "Connect", "Access to server granted.");
00209 break;
00210 }
00211 }
00212
00213
00214 Info(XrdClientDebug::kHIDEBUG, "Connect", "Disconnecting.");
00215
00216 fConnModule->Disconnect(FALSE);
00217
00218 if (connectTry < connectMaxTry-1) {
00219
00220 if (DebugLevel() >= XrdClientDebug::kUSERDEBUG)
00221 Info(XrdClientDebug::kUSERDEBUG, "Connect",
00222 "Connection attempt failed. Sleeping " <<
00223 EnvGetLong(NAME_RECONNECTWAIT) << " seconds.");
00224
00225 sleep(EnvGetLong(NAME_RECONNECTWAIT));
00226
00227 }
00228
00229 }
00230
00231
00232 if (!fConnModule->IsConnected()) {
00233 return FALSE;
00234 }
00235
00236
00237
00238
00239
00240
00241 if ((fConnModule->GetServerType() != kSTRootd) &&
00242 (fConnModule->GetServerType() != kSTNone)) {
00243
00244
00245
00246 Info(XrdClientDebug::kUSERDEBUG, "Connect", "Connected.");
00247
00248
00249 } else {
00250
00251
00252 if (fConnModule->GetServerType() == kSTNone)
00253 fConnModule->Disconnect(TRUE);
00254
00255 return FALSE;
00256 }
00257
00258 return TRUE;
00259
00260 }
00261
00262
00263
00264
00265 bool XrdClientAdmin::Stat(const char *fname, long &id, long long &size, long &flags, long &modtime)
00266 {
00267
00268
00269
00270 bool ok;
00271
00272
00273 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00274
00275
00276 ClientRequest statFileRequest;
00277
00278 memset( &statFileRequest, 0, sizeof(ClientRequest) );
00279
00280 fConnModule->SetSID(statFileRequest.header.streamid);
00281
00282 statFileRequest.stat.requestid = kXR_stat;
00283
00284 memset(statFileRequest.stat.reserved, 0,
00285 sizeof(statFileRequest.stat.reserved));
00286
00287 statFileRequest.header.dlen = strlen(fname);
00288
00289 char fStats[2048];
00290 id = 0;
00291 size = 0;
00292 flags = 0;
00293 modtime = 0;
00294
00295 ok = fConnModule->SendGenCommand(&statFileRequest, (const char*)fname,
00296 NULL, fStats , FALSE, (char *)"Stat");
00297
00298
00299 if (ok && (fConnModule->LastServerResp.status == 0)) {
00300 if (fConnModule->LastServerResp.dlen >= 0)
00301 fStats[fConnModule->LastServerResp.dlen] = 0;
00302 else
00303 fStats[0] = 0;
00304 Info(XrdClientDebug::kHIDEBUG,
00305 "Stat", "Returned stats=" << fStats);
00306 sscanf(fStats, "%ld %lld %ld %ld", &id, &size, &flags, &modtime);
00307 }
00308
00309 return ok;
00310 }
00311
00312
00313
00314
00315 bool XrdClientAdmin::Stat_vfs(const char *fname,
00316 int &rwservers,
00317 long long &rwfree,
00318 int &rwutil,
00319 int &stagingservers,
00320 long long &stagingfree,
00321 int &stagingutil)
00322 {
00323
00324
00325 bool ok;
00326
00327
00328 ClientRequest statFileRequest;
00329
00330
00331 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00332
00333 memset( &statFileRequest, 0, sizeof(ClientRequest) );
00334
00335 fConnModule->SetSID(statFileRequest.header.streamid);
00336
00337 statFileRequest.stat.requestid = kXR_stat;
00338
00339 memset(statFileRequest.stat.reserved, 0,
00340 sizeof(statFileRequest.stat.reserved));
00341
00342 statFileRequest.stat.options = kXR_vfs;
00343
00344 statFileRequest.header.dlen = strlen(fname);
00345
00346 char fStats[2048];
00347 rwservers = 0;
00348 rwfree = 0;
00349 rwutil = 0;
00350 stagingservers = 0;
00351 stagingfree = 0;
00352 stagingutil = 0;
00353
00354
00355 ok = fConnModule->SendGenCommand(&statFileRequest, (const char*)fname,
00356 NULL, fStats , FALSE, (char *)"Stat_vfs");
00357
00358
00359 if (ok && (fConnModule->LastServerResp.status == 0)) {
00360 if (fConnModule->LastServerResp.dlen >= 0)
00361 fStats[fConnModule->LastServerResp.dlen] = 0;
00362 else
00363 fStats[0] = 0;
00364 Info(XrdClientDebug::kHIDEBUG,
00365 "Stat_vfs", "Returned stats=" << fStats);
00366
00367 sscanf(fStats, "%d %lld %d %d %lld %d", &rwservers, &rwfree, &rwutil,
00368 &stagingservers, &stagingfree, &stagingutil);
00369
00370 }
00371
00372 return ok;
00373 }
00374
00375
00376
00377 bool XrdClientAdmin::SysStatX(const char *paths_list, kXR_char *binInfo)
00378 {
00379 XrdOucString pl(paths_list);
00380 bool ret;
00381
00382 ClientRequest statXFileRequest;
00383
00384
00385 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00386
00387 memset( &statXFileRequest, 0, sizeof(ClientRequest) );
00388 fConnModule->SetSID(statXFileRequest.header.streamid);
00389 statXFileRequest.header.requestid = kXR_statx;
00390
00391 statXFileRequest.stat.dlen = pl.length();
00392
00393 ret = fConnModule->SendGenCommand(&statXFileRequest, pl.c_str(),
00394 NULL, binInfo , FALSE, (char *)"SysStatX");
00395
00396 return(ret);
00397 }
00398
00399
00400 bool XrdClientAdmin::ExistFiles(vecString &vs, vecBool &vb)
00401 {
00402 bool ret;
00403 XrdOucString buf;
00404 joinStrings(buf, vs);
00405
00406 kXR_char *Info;
00407 Info = (kXR_char*) malloc(vs.GetSize()+10);
00408 memset((void *)Info, 0, vs.GetSize()+10);
00409
00410 ret = this->SysStatX(buf.c_str(), Info);
00411
00412 if (ret) for(int j=0; j <= vs.GetSize()-1; j++) {
00413 bool tmp = TRUE;
00414
00415 if ( (*(Info+j) & kXR_isDir) || (*(Info+j) & kXR_other) ||
00416 (*(Info+j) & kXR_offline) )
00417 tmp = FALSE;
00418
00419 vb.Push_back(tmp);
00420 }
00421
00422
00423 free(Info);
00424
00425 return ret;
00426 }
00427
00428
00429 bool XrdClientAdmin::ExistDirs(vecString &vs, vecBool &vb)
00430 {
00431 bool ret;
00432 XrdOucString buf;
00433 joinStrings(buf, vs);
00434
00435 kXR_char *Info;
00436 Info = (kXR_char*) malloc(vs.GetSize()+10);
00437 memset((void *)Info, 0, vs.GetSize()+10);
00438
00439 ret = this->SysStatX(buf.c_str(), Info);
00440
00441 if (ret) for(int j=0; j <= vs.GetSize()-1; j++) {
00442 bool tmp;
00443
00444 if( (*(Info+j) & kXR_isDir) ) {
00445 tmp = TRUE;
00446 vb.Push_back(tmp);
00447 } else {
00448 tmp = FALSE;
00449 vb.Push_back(tmp);
00450 }
00451
00452 }
00453
00454 free(Info);
00455
00456 return ret;
00457 }
00458
00459
00460 bool XrdClientAdmin::IsFileOnline(vecString &vs, vecBool &vb)
00461 {
00462 bool ret;
00463 XrdOucString buf;
00464 joinStrings(buf, vs);
00465
00466 kXR_char *Info;
00467 Info = (kXR_char*) malloc(vs.GetSize()+10);
00468 memset((void *)Info, 0, vs.GetSize()+10);
00469
00470 ret = this->SysStatX(buf.c_str(), Info);
00471
00472 if (ret) for(int j=0; j <= vs.GetSize()-1; j++) {
00473 bool tmp;
00474
00475 if( !(*(Info+j) & kXR_offline) ) {
00476 tmp = TRUE;
00477 vb.Push_back(tmp);
00478 } else {
00479 tmp = FALSE;
00480 vb.Push_back(tmp);
00481 }
00482
00483 }
00484
00485 free(Info);
00486
00487 return ret;
00488 }
00489
00490
00491
00492
00493 bool XrdClientAdmin::OpenFileWhenRedirected(char *newfhandle, bool &wasopen) {
00494
00495 wasopen = FALSE;
00496 return TRUE;
00497 }
00498
00499
00500 bool XrdClientAdmin::Rmdir(const char *path)
00501 {
00502
00503 ClientRequest rmdirFileRequest;
00504
00505
00506 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00507
00508 memset( &rmdirFileRequest, 0, sizeof(rmdirFileRequest) );
00509 fConnModule->SetSID(rmdirFileRequest.header.streamid);
00510 rmdirFileRequest.header.requestid = kXR_rmdir;
00511 rmdirFileRequest.header.dlen = strlen(path);
00512
00513 return (fConnModule->SendGenCommand(&rmdirFileRequest, path,
00514 NULL, NULL, FALSE, (char *)"Rmdir"));
00515
00516 }
00517
00518
00519 bool XrdClientAdmin::Rm(const char *file)
00520 {
00521
00522 ClientRequest rmFileRequest;
00523
00524
00525 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00526
00527 memset( &rmFileRequest, 0, sizeof(rmFileRequest) );
00528 fConnModule->SetSID(rmFileRequest.header.streamid);
00529 rmFileRequest.header.requestid = kXR_rm;
00530 rmFileRequest.header.dlen = strlen(file);
00531
00532 return (fConnModule->SendGenCommand(&rmFileRequest, file,
00533 NULL, NULL, FALSE, (char *)"Rm"));
00534 }
00535
00536
00537 bool XrdClientAdmin::Chmod(const char *file, int user, int group, int other)
00538 {
00539
00540 ClientRequest chmodFileRequest;
00541
00542
00543 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00544
00545 memset( &chmodFileRequest, 0, sizeof(chmodFileRequest) );
00546
00547 fConnModule->SetSID(chmodFileRequest.header.streamid);
00548 chmodFileRequest.header.requestid = kXR_chmod;
00549
00550 if(user & 4)
00551 chmodFileRequest.chmod.mode |= kXR_ur;
00552 if(user & 2)
00553 chmodFileRequest.chmod.mode |= kXR_uw;
00554 if(user & 1)
00555 chmodFileRequest.chmod.mode |= kXR_ux;
00556
00557 if(group & 4)
00558 chmodFileRequest.chmod.mode |= kXR_gr;
00559 if(group & 2)
00560 chmodFileRequest.chmod.mode |= kXR_gw;
00561 if(group & 1)
00562 chmodFileRequest.chmod.mode |= kXR_gx;
00563
00564 if(other & 4)
00565 chmodFileRequest.chmod.mode |= kXR_or;
00566 if(other & 2)
00567 chmodFileRequest.chmod.mode |= kXR_ow;
00568 if(other & 1)
00569 chmodFileRequest.chmod.mode |= kXR_ox;
00570
00571 chmodFileRequest.header.dlen = strlen(file);
00572
00573
00574 return (fConnModule->SendGenCommand(&chmodFileRequest, file,
00575 NULL, NULL, FALSE, (char *)"Chmod"));
00576
00577 }
00578
00579
00580 bool XrdClientAdmin::Mkdir(const char *dir, int user, int group, int other)
00581 {
00582
00583 ClientRequest mkdirRequest;
00584
00585
00586 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00587
00588 memset( &mkdirRequest, 0, sizeof(mkdirRequest) );
00589
00590 fConnModule->SetSID(mkdirRequest.header.streamid);
00591
00592 mkdirRequest.header.requestid = kXR_mkdir;
00593
00594 memset(mkdirRequest.mkdir.reserved, 0,
00595 sizeof(mkdirRequest.mkdir.reserved));
00596
00597 if(user & 4)
00598 mkdirRequest.mkdir.mode |= kXR_ur;
00599 if(user & 2)
00600 mkdirRequest.mkdir.mode |= kXR_uw;
00601 if(user & 1)
00602 mkdirRequest.mkdir.mode |= kXR_ux;
00603
00604 if(group & 4)
00605 mkdirRequest.mkdir.mode |= kXR_gr;
00606 if(group & 2)
00607 mkdirRequest.mkdir.mode |= kXR_gw;
00608 if(group & 1)
00609 mkdirRequest.mkdir.mode |= kXR_gx;
00610
00611 if(other & 4)
00612 mkdirRequest.mkdir.mode |= kXR_or;
00613 if(other & 2)
00614 mkdirRequest.mkdir.mode |= kXR_ow;
00615 if(other & 1)
00616 mkdirRequest.mkdir.mode |= kXR_ox;
00617
00618 mkdirRequest.mkdir.options[0] = kXR_mkdirpath;
00619
00620 mkdirRequest.header.dlen = strlen(dir);
00621
00622 return (fConnModule->SendGenCommand(&mkdirRequest, dir,
00623 NULL, NULL, FALSE, (char *)"Mkdir"));
00624
00625 }
00626
00627
00628 bool XrdClientAdmin::Mv(const char *fileSrc, const char *fileDest)
00629 {
00630 bool ret;
00631
00632
00633 ClientRequest mvFileRequest;
00634
00635
00636 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00637
00638
00639 memset( &mvFileRequest, 0, sizeof(mvFileRequest) );
00640
00641 fConnModule->SetSID(mvFileRequest.header.streamid);
00642 mvFileRequest.header.requestid = kXR_mv;
00643
00644 mvFileRequest.header.dlen = strlen( fileDest ) + strlen( fileSrc ) + 1;
00645
00646 char *data = new char[mvFileRequest.header.dlen+2];
00647 memset(data, 0, mvFileRequest.header.dlen+2);
00648 strcpy( data, fileSrc );
00649 strcat( data, " " );
00650 strcat( data, fileDest );
00651
00652 ret = fConnModule->SendGenCommand(&mvFileRequest, data,
00653 NULL, NULL, FALSE, (char *)"Mv");
00654
00655 delete(data);
00656
00657 return ret;
00658 }
00659
00660
00661 UnsolRespProcResult XrdClientAdmin::ProcessUnsolicitedMsg(XrdClientUnsolMsgSender *sender,
00662 XrdClientMessage *unsolmsg)
00663 {
00664
00665
00666
00667
00668
00669
00670 if (unsolmsg->GetStatusCode() != XrdClientMessage::kXrdMSC_ok) {
00671 Info(XrdClientDebug::kHIDEBUG,
00672 "ProcessUnsolicitedMsg", "Incoming unsolicited communication error message." );
00673 }
00674 else {
00675 Info(XrdClientDebug::kHIDEBUG,
00676 "ProcessUnsolicitedMsg", "Incoming unsolicited response from streamid " <<
00677 unsolmsg->HeaderSID() );
00678 }
00679
00680
00681 if (unsolmsg->IsAttn()) {
00682 struct ServerResponseBody_Attn *attnbody;
00683
00684 attnbody = (struct ServerResponseBody_Attn *)unsolmsg->GetData();
00685
00686 int actnum = (attnbody) ? (attnbody->actnum) : 0;
00687
00688
00689 switch (actnum) {
00690
00691 case kXR_asyncdi:
00692
00693
00694 struct ServerResponseBody_Attn_asyncdi *di;
00695 di = (struct ServerResponseBody_Attn_asyncdi *)unsolmsg->GetData();
00696
00697
00698 if (di) {
00699 Info(XrdClientDebug::kUSERDEBUG,
00700 "ProcessUnsolicitedMsg", "Requested Disconnection + Reconnect in " <<
00701 ntohl(di->wsec) << " seconds.");
00702
00703 fConnModule->SetRequestedDestHost((char *)(fConnModule->GetCurrentUrl().Host.c_str()),
00704 fConnModule->GetCurrentUrl().Port);
00705 fConnModule->SetREQDelayedConnectState(ntohl(di->wsec));
00706 }
00707
00708
00709 return kUNSOL_CONTINUE;
00710 break;
00711
00712 case kXR_asyncrd:
00713
00714
00715 struct ServerResponseBody_Attn_asyncrd *rd;
00716 rd = (struct ServerResponseBody_Attn_asyncrd *)unsolmsg->GetData();
00717
00718
00719 if (rd && (strlen(rd->host) > 0)) {
00720 Info(XrdClientDebug::kUSERDEBUG,
00721 "ProcessUnsolicitedMsg", "Requested redir to " << rd->host <<
00722 ":" << ntohl(rd->port));
00723
00724 fConnModule->SetRequestedDestHost(rd->host, ntohl(rd->port));
00725 }
00726
00727
00728 return kUNSOL_CONTINUE;
00729 break;
00730
00731 case kXR_asyncwt:
00732
00733
00734 struct ServerResponseBody_Attn_asyncwt *wt;
00735 wt = (struct ServerResponseBody_Attn_asyncwt *)unsolmsg->GetData();
00736
00737 if (wt) {
00738 Info(XrdClientDebug::kUSERDEBUG,
00739 "ProcessUnsolicitedMsg", "Pausing client for " << ntohl(wt->wsec) <<
00740 " seconds.");
00741
00742 fConnModule->SetREQPauseState(ntohl(wt->wsec));
00743 }
00744
00745
00746 return kUNSOL_CONTINUE;
00747 break;
00748
00749 case kXR_asyncgo:
00750
00751
00752 Info(XrdClientDebug::kUSERDEBUG,
00753 "ProcessUnsolicitedMsg", "Resuming from pause.");
00754
00755 fConnModule->SetREQPauseState(0);
00756
00757
00758 return kUNSOL_CONTINUE;
00759 break;
00760
00761 case kXR_asynresp:
00762
00763
00764
00765
00766
00767 return fConnModule->ProcessAsynResp(unsolmsg);
00768 break;
00769
00770 default:
00771
00772 Info(XrdClientDebug::kUSERDEBUG,
00773 "ProcessUnsolicitedMsg", "Empty message");
00774
00775
00776 return kUNSOL_CONTINUE;
00777
00778 }
00779
00780
00781 }
00782 else
00783
00784 if (unsolmsg->GetStatusCode() != XrdClientMessage::kXrdMSC_ok)
00785 return fConnModule->ProcessAsynResp(unsolmsg);
00786
00787
00788 return kUNSOL_CONTINUE;
00789 }
00790
00791
00792
00793
00794 bool XrdClientAdmin::Protocol(kXR_int32 &proto, kXR_int32 &kind)
00795 {
00796 ClientRequest protoRequest;
00797
00798
00799 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00800
00801 memset( &protoRequest, 0, sizeof(protoRequest) );
00802
00803 fConnModule->SetSID(protoRequest.header.streamid);
00804
00805 protoRequest.header.requestid = kXR_protocol;
00806
00807 char buf[8];
00808 bool ret = fConnModule->SendGenCommand(&protoRequest, NULL,
00809 NULL, buf, FALSE, (char *)"Protocol");
00810
00811 memcpy(&proto, buf, sizeof(proto));
00812 memcpy(&kind, buf + sizeof(proto), sizeof(kind));
00813
00814 proto = ntohl(proto);
00815 kind = ntohl(kind);
00816
00817 return ret;
00818 }
00819
00820
00821 bool XrdClientAdmin::Prepare(vecString vs, kXR_char option, kXR_char prty)
00822 {
00823
00824
00825
00826 XrdOucString buf;
00827
00828
00829 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00830
00831 if (vs.GetSize() < 75) {
00832 joinStrings(buf, vs);
00833 return Prepare(buf.c_str(), option, prty);
00834 }
00835
00836
00837 for (int i = 0; i < vs.GetSize()+50; i+=50) {
00838 joinStrings(buf, vs, i, i+49);
00839
00840 if (!Prepare(buf.c_str(), option, prty)) return false;
00841 buf = "";
00842 }
00843
00844 return true;
00845 }
00846
00847
00848 bool XrdClientAdmin::Prepare(const char *buf, kXR_char option, kXR_char prty)
00849 {
00850
00851
00852 ClientRequest prepareRequest;
00853
00854
00855 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
00856
00857 memset( &prepareRequest, 0, sizeof(prepareRequest) );
00858
00859 fConnModule->SetSID(prepareRequest.header.streamid);
00860
00861 prepareRequest.header.requestid = kXR_prepare;
00862 prepareRequest.prepare.options = option;
00863 prepareRequest.prepare.prty = prty;
00864
00865 prepareRequest.header.dlen = strlen(buf);
00866
00867 bool ret = fConnModule->SendGenCommand(&prepareRequest, buf,
00868 NULL, NULL , FALSE, (char *)"Prepare");
00869
00870 return ret;
00871 }
00872
00873
00874 bool XrdClientAdmin::DirList(const char *dir, vecString &entries, bool askallservers) {
00875
00876
00877
00878
00879
00880
00881
00882
00883 bool ret = true;
00884 XrdClientVector<XrdClientLocate_Info> hosts;
00885 if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) {
00886 char str[1024];
00887 strcpy(str, "*");
00888 strncat(str, dir, 1023);
00889 if (!Locate((kXR_char *)str, hosts)) return false;
00890 }
00891 else {
00892 XrdClientLocate_Info nfo;
00893 memset(&nfo, 0, sizeof(nfo));
00894 strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str());
00895 hosts.Push_back(nfo);
00896 }
00897
00898
00899
00900 bool foundsomething = false;
00901 for (int i = 0; i < hosts.GetSize(); i++) {
00902
00903 fConnModule->Disconnect(false);
00904 XrdClientUrlInfo url((const char *)hosts[i].Location);
00905
00906 url.Proto = "root";
00907
00908 if (fConnModule->GoToAnotherServer(url) != kOK) {
00909 ret = false;
00910 break;
00911 }
00912
00913 fConnModule->ClearLastServerError();
00914 if (!DirList_low(dir, entries)) {
00915 if (fConnModule->LastServerError.errnum != kXR_NotFound) {
00916 ret = false;
00917 break;
00918 }
00919 }
00920 else foundsomething = true;
00921
00922
00923 }
00924
00925
00926 GoBackToRedirector();
00927
00928 if (!foundsomething) ret = false;
00929 return ret;
00930 }
00931
00932
00933 bool XrdClientAdmin::DirList(const char *dir,
00934 XrdClientVector<XrdClientAdmin::DirListInfo> &dirlistinfo,
00935 bool askallservers) {
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946 bool ret = true;
00947 vecString entries;
00948 XrdClientVector<XrdClientLocate_Info> hosts;
00949 XrdOucString fullpath;
00950
00951 if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) {
00952 char str[1024];
00953 strcpy(str, "*");
00954 strncat(str, dir, 1023);
00955 if (!Locate((kXR_char *)str, hosts)) return false;
00956 }
00957 else {
00958 XrdClientLocate_Info nfo;
00959 memset(&nfo, 0, sizeof(nfo));
00960 strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str());
00961 hosts.Push_back(nfo);
00962 }
00963
00964
00965
00966 bool foundsomething = false;
00967 for (int i = 0; i < hosts.GetSize(); i++) {
00968
00969 fConnModule->Disconnect(false);
00970 XrdClientUrlInfo url((const char *)hosts[i].Location);
00971 url.Proto = "root";
00972
00973 if (fConnModule->GoToAnotherServer(url) != kOK) {
00974 ret = false;
00975 break;
00976 }
00977
00978 fConnModule->ClearLastServerError();
00979
00980 int precentries = entries.GetSize();
00981 if (!DirList_low(dir, entries)) {
00982 if ((fConnModule->LastServerError.errnum != kXR_NotFound) && (fConnModule->LastServerError.errnum != kXR_noErrorYet)) {
00983 ret = false;
00984 break;
00985 }
00986 }
00987 else foundsomething = true;
00988
00989 int newentries = entries.GetSize();
00990
00991 DirListInfo info;
00992 dirlistinfo.Resize(newentries);
00993
00994
00995
00996 info.host = GetCurrentUrl().HostWPort;
00997 for (int k = precentries; k < newentries; k++) {
00998 info.fullpath = dir;
00999 if (info.fullpath[info.fullpath.length()-1] != '/') info.fullpath += "/";
01000 info.fullpath += entries[k];
01001 info.size = 0;
01002 info.id = 0;
01003 info.flags = 0;
01004 info.modtime = 0;
01005
01006 if (!Stat(info.fullpath.c_str(),
01007 info.id,
01008 info.size,
01009 info.flags,
01010 info.modtime)) {
01011 ret = false;
01012
01013 }
01014
01015 dirlistinfo[k] = info;
01016
01017 }
01018
01019 }
01020
01021
01022 GoBackToRedirector();
01023
01024 if (!foundsomething) ret = false;
01025 return ret;
01026 }
01027
01028
01029
01030
01031
01032
01033 bool XrdClientAdmin::DirList_low(const char *dir, vecString &entries) {
01034 bool ret;
01035
01036 ClientRequest DirListFileRequest;
01037 kXR_char *dl;
01038
01039
01040 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
01041
01042 memset( &DirListFileRequest, 0, sizeof(ClientRequest) );
01043 fConnModule->SetSID(DirListFileRequest.header.streamid);
01044 DirListFileRequest.header.requestid = kXR_dirlist;
01045
01046 DirListFileRequest.dirlist.dlen = strlen(dir);
01047
01048
01049 ret = fConnModule->SendGenCommand(&DirListFileRequest, dir,
01050 reinterpret_cast<void **>(&dl), 0, TRUE, (char *)"DirList");
01051
01052
01053 if (ret) {
01054
01055 kXR_char *startp = dl, *endp = dl;
01056 char entry[1024];
01057 XrdOucString e;
01058
01059 while (startp) {
01060
01061 if ( (endp = (kXR_char *)strchr((const char*)startp, '\n')) ) {
01062 strncpy(entry, (char *)startp, endp-startp);
01063 entry[endp-startp] = 0;
01064 endp++;
01065 }
01066 else
01067 strcpy(entry, (char *)startp);
01068
01069
01070 if (strlen(entry) && strcmp((char *)entry, ".") && strcmp((char *)entry, "..")) {
01071 e = entry;
01072 entries.Push_back(e);
01073 }
01074
01075
01076 startp = endp;
01077 }
01078
01079
01080
01081 }
01082
01083 if (dl) free(dl);
01084 return(ret);
01085
01086 }
01087
01088
01089 long XrdClientAdmin::GetChecksum(kXR_char *path, kXR_char **chksum)
01090 {
01091 ClientRequest chksumRequest;
01092
01093
01094 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
01095
01096
01097 memset( &chksumRequest, 0, sizeof(chksumRequest) );
01098
01099 fConnModule->SetSID(chksumRequest.header.streamid);
01100
01101 chksumRequest.query.requestid = kXR_query;
01102 chksumRequest.query.infotype = kXR_Qcksum;
01103 chksumRequest.query.dlen = strlen((char *) path);
01104
01105 bool ret = fConnModule->SendGenCommand(&chksumRequest, (const char*) path,
01106 (void **)chksum, NULL, TRUE,
01107 (char *)"GetChecksum");
01108
01109 if (ret) return (fConnModule->LastServerResp.dlen);
01110 else return 0;
01111 }
01112
01113 int XrdClientAdmin::LocalLocate(kXR_char *path, XrdClientVector<XrdClientLocate_Info> &res,
01114 bool writable, int opts, bool all) {
01115
01116
01117
01118
01119
01120
01121 ClientRequest locateRequest;
01122
01123 char *resp = 0;
01124 int retval = (all) ? 0 : -1;
01125
01126 memset( &locateRequest, 0, sizeof(locateRequest) );
01127
01128 fConnModule->SetSID(locateRequest.header.streamid);
01129
01130 locateRequest.locate.requestid = kXR_locate;
01131 locateRequest.locate.options = opts;
01132 locateRequest.locate.dlen = strlen((char *) path);
01133
01134
01135 bool ret = fConnModule->SendGenCommand(&locateRequest, (const char*) path,
01136 (void **)&resp, 0, true,
01137 (char *)"LocalLocate");
01138
01139 if (!ret) return -2;
01140 if (!resp) return -1;
01141 if (!strlen(resp)) {
01142 free(resp);
01143 return -1;
01144 }
01145
01146
01147
01148
01149 XrdOucString rs(resp), s;
01150 free(resp);
01151 int from = 0;
01152 while ((from = rs.tokenize(s,from,' ')) != -1) {
01153
01154
01155 if (s.length() < 8 || (s[2] != '[') || (s[4] != ':')) {
01156 Error("LocalLocate", "Invalid server response. Resp: '" << s << "'");
01157 continue;
01158 }
01159
01160 XrdClientLocate_Info nfo;
01161
01162
01163 switch (s[0]) {
01164 case 'S':
01165 nfo.Infotype = XrdClientLocate_Info::kXrdcLocDataServer;
01166 break;
01167 case 's':
01168 nfo.Infotype = XrdClientLocate_Info::kXrdcLocDataServerPending;
01169 break;
01170 case 'M':
01171 nfo.Infotype = XrdClientLocate_Info::kXrdcLocManager;
01172 break;
01173 case 'm':
01174 nfo.Infotype = XrdClientLocate_Info::kXrdcLocManagerPending;
01175 break;
01176 default:
01177 Info(XrdClientDebug::kNODEBUG, "LocalLocate",
01178 "Unknown info type: '" << s << "'");
01179 }
01180
01181
01182 nfo.CanWrite = (s[1] == 'w') ? 1 : 0;
01183
01184
01185 s.erase(0, s.find("[::")+3);
01186 s.replace("]","");
01187 strcpy((char *)nfo.Location, s.c_str());
01188
01189 res.Push_back(nfo);
01190
01191 if (nfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) {
01192 if (!all) {
01193 if (!writable || nfo.CanWrite) {
01194 retval = res.GetSize() - 1;
01195 break;
01196 }
01197 }
01198 } else {
01199 if (all)
01200
01201 retval++;
01202 }
01203 }
01204
01205 return retval;
01206 }
01207
01208
01209
01210 bool XrdClientAdmin::Locate(kXR_char *path, XrdClientLocate_Info &resp, bool writable)
01211 {
01212
01213
01214
01215
01216
01217
01218 bool found = false;
01219 memset(&resp, 0, sizeof(resp));
01220
01221 if (!fConnModule) return 0;
01222 if (!fConnModule->IsConnected()) return 0;
01223
01224
01225 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
01226
01227
01228
01229
01230 if (fConnModule->GetServerProtocol() < 0x290) {
01231 long id, flags, modtime;
01232 long long size;
01233
01234 bool ok = Stat((const char *)path, id, size, flags, modtime);
01235 if (ok && (fConnModule->LastServerResp.status == 0)) {
01236 resp.Infotype = XrdClientLocate_Info::kXrdcLocDataServer;
01237 resp.CanWrite = 1;
01238 strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str());
01239 }
01240 GoBackToRedirector();
01241 return ok;
01242 }
01243
01244
01245 XrdClientUrlInfo currurl(fConnModule->GetCurrentUrl().GetUrl());
01246 if (!currurl.HostWPort.length()) return 0;
01247
01248
01249 XrdClientVector<XrdClientLocate_Info> hosts;
01250 XrdClientLocate_Info nfo;
01251 nfo.Infotype = XrdClientLocate_Info::kXrdcLocManager;
01252 nfo.CanWrite = true;
01253 strcpy((char *)nfo.Location, currurl.HostWPort.c_str());
01254 hosts.Push_back(nfo);
01255 bool firsthost = true;
01256 XrdClientLocate_Info currnfo;
01257
01258 int pos = 0;
01259
01260
01261 while (pos < hosts.GetSize()) {
01262
01263
01264 currnfo = hosts[pos];
01265
01266
01267 if ((currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) ||
01268 (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServerPending)) {
01269 pos++;
01270 continue;
01271 }
01272
01273
01274 currurl.TakeUrl((char *)currnfo.Location);
01275 if (currurl.HostAddr == "") currurl.HostAddr = currurl.Host;
01276
01277
01278
01279
01280 if (!firsthost) {
01281 fConnModule->Disconnect(false);
01282 if (fConnModule->GoToAnotherServer(currurl) != kOK) {
01283 hosts.Erase(pos);
01284 continue;
01285 }
01286 }
01287
01288 if (firsthost) firsthost = false;
01289
01290
01291 int posds = LocalLocate(path, hosts, writable, kXR_nowait);
01292
01293 found = (posds > -1) ? 1 : 0;
01294
01295 if (found) {
01296 resp = hosts[posds];
01297 break;
01298 }
01299
01300
01301 hosts.Erase(pos);
01302 }
01303
01304 if (!found && hosts.GetSize()) {
01305
01306
01307 for (int ii = 0; ii < hosts.GetSize(); ii++) {
01308 currnfo = hosts[ii];
01309 if ( (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) ||
01310 (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServerPending) ) {
01311 resp = currnfo;
01312
01313 if (!writable || resp.CanWrite) {
01314 found = true;
01315 break;
01316 }
01317
01318 }
01319
01320
01321 }
01322
01323 }
01324
01325
01326 GoBackToRedirector();
01327
01328 return found;
01329 }
01330
01331
01332
01333 bool XrdClientAdmin::Locate( kXR_char *path,
01334 XrdClientVector<XrdClientLocate_Info> &hosts,
01335 int opts )
01336 {
01337
01338
01339
01340
01341 hosts.Clear();
01342
01343 if (!fConnModule) return 0;
01344 if (!fConnModule->IsConnected()) return 0;
01345
01346
01347
01348 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
01349
01350
01351
01352
01353 if (fConnModule->GetServerProtocol() < 0x290) {
01354 long id, flags, modtime;
01355 long long size;
01356 XrdClientLocate_Info resp;
01357
01358 bool ok = Stat((const char *)path, id, size, flags, modtime);
01359 if (ok && (fConnModule->LastServerResp.status == 0)) {
01360 resp.Infotype = XrdClientLocate_Info::kXrdcLocDataServer;
01361 resp.CanWrite = 1;
01362 strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str());
01363 hosts.Push_back(resp);
01364 }
01365 GoBackToRedirector();
01366
01367 return ok;
01368 }
01369
01370 XrdClientUrlInfo currurl(fConnModule->GetCurrentUrl().GetUrl());
01371 if (!currurl.HostWPort.length()) return 0;
01372
01373
01374 XrdClientLocate_Info nfo;
01375 nfo.Infotype = XrdClientLocate_Info::kXrdcLocManager;
01376 nfo.CanWrite = true;
01377 strcpy((char *)nfo.Location, currurl.HostWPort.c_str());
01378 hosts.Push_back(nfo);
01379 bool firsthost = true;
01380 XrdClientLocate_Info currnfo;
01381
01382 int pos = 0;
01383
01384
01385 while (pos < hosts.GetSize()) {
01386
01387
01388 currnfo = hosts[pos];
01389
01390
01391 if ((currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServer) ||
01392 (currnfo.Infotype == XrdClientLocate_Info::kXrdcLocDataServerPending)) {
01393 pos++;
01394 continue;
01395 }
01396
01397
01398 currurl.TakeUrl((char *)currnfo.Location);
01399 if (currurl.HostAddr == "") currurl.HostAddr = currurl.Host;
01400
01401
01402
01403
01404 if (!firsthost) {
01405
01406 fConnModule->Disconnect(false);
01407 if (fConnModule->GoToAnotherServer(currurl) != kOK) {
01408 hosts.Erase(pos);
01409 continue;
01410 }
01411 }
01412
01413 if (firsthost) firsthost = false;
01414
01415
01416 LocalLocate(path, hosts, true, opts, true);
01417
01418
01419 hosts.Erase(pos);
01420 }
01421
01422
01423 GoBackToRedirector();
01424
01425 return (hosts.GetSize() > 0);
01426 }
01427
01428 bool XrdClientAdmin::Truncate(const char *path, long long newsize) {
01429
01430 ClientRequest truncateRequest;
01431 int l = strlen(path);
01432 if (!l) return false;
01433
01434
01435 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
01436
01437
01438 memset( &truncateRequest, 0, sizeof(truncateRequest) );
01439
01440 fConnModule->SetSID(truncateRequest.header.streamid);
01441
01442 truncateRequest.header.requestid = kXR_truncate;
01443 truncateRequest.truncate.offset = newsize;
01444
01445 truncateRequest.header.dlen = l;
01446
01447 bool ret = fConnModule->SendGenCommand(&truncateRequest, path,
01448 NULL, NULL , FALSE, (char *)"Truncate");
01449
01450 return ret;
01451
01452
01453 }
01454
01455
01456
01457
01458 void XrdClientAdmin::GoBackToRedirector() {
01459
01460 if (fConnModule) {
01461 fConnModule->GoBackToRedirector();
01462
01463 if (!fConnModule->IsConnected()) {
01464 XrdClientUrlInfo u(fInitialUrl);
01465 fConnModule->GoToAnotherServer(u);
01466 }
01467
01468 }
01469
01470
01471
01472 }
01473
01474
01475
01476
01477
01478 bool XrdClientAdmin::GetSpaceInfo(const char *logicalname,
01479 long long &totspace,
01480 long long &totfree,
01481 long long &totused,
01482 long long &largestchunk) {
01483
01484 bool ret = true;
01485 XrdClientVector<XrdClientLocate_Info> hosts;
01486
01487 totspace = 0;
01488 totfree = 0;
01489 totused = 0;
01490 largestchunk = 0;
01491
01492 if (fConnModule->GetServerProtocol() >= 0x291) {
01493 if (!Locate((kXR_char *)"*", hosts)) return false;
01494 }
01495 else {
01496 XrdClientLocate_Info nfo;
01497 memset(&nfo, 0, sizeof(nfo));
01498 strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str());
01499 hosts.Push_back(nfo);
01500 }
01501
01502
01503
01504 for (int i = 0; i < hosts.GetSize(); i++) {
01505
01506 fConnModule->Disconnect(false);
01507 XrdClientUrlInfo url((const char *)hosts[i].Location);
01508
01509 url.Proto = "root";
01510
01511 if (fConnModule->GoToAnotherServer(url) != kOK) {
01512 ret = false;
01513 break;
01514 }
01515
01516
01517
01518
01519 ClientRequest qspacereq;
01520
01521
01522 fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT));
01523
01524
01525 memset( &qspacereq, 0, sizeof(qspacereq) );
01526
01527 fConnModule->SetSID(qspacereq.header.streamid);
01528
01529 qspacereq.query.requestid = kXR_query;
01530 qspacereq.query.infotype = kXR_Qspace;
01531 qspacereq.query.dlen = ( logicalname ? strlen(logicalname) : 0);
01532
01533 char *resp = 0;
01534 if (fConnModule->SendGenCommand(&qspacereq, logicalname,
01535 (void **)&resp, 0, TRUE,
01536 (char *)"GetSpaceInfo")) {
01537
01538 XrdOucString rs(resp), s;
01539 free(resp);
01540
01541
01542
01543
01544 int from = 0;
01545 while ((from = rs.tokenize(s,from,'&')) != -1) {
01546 if (s.length() < 4) continue;
01547
01548 int pos = s.find("=");
01549 XrdOucString tk, val;
01550 if (pos != STR_NPOS) {
01551 tk.assign(s, 0, pos-1);
01552 val.assign(s, pos+1);
01553 #ifndef WIN32
01554 if ( (tk == "oss.space") && (val.length() > 1) ) {
01555 totspace += atoll(val.c_str());
01556 } else
01557 if ( (tk == "oss.free") && (val.length() > 1) ) {
01558 totfree += atoll(val.c_str());
01559 } else
01560 if ( (tk == "oss.maxf") && (val.length() > 1) ) {
01561 largestchunk = xrdmax(largestchunk, atoll(val.c_str()));
01562 } else
01563 if ( (tk == "oss.used") && (val.length() > 1) ) {
01564 totused += atoll(val.c_str());
01565 }
01566 #else
01567 if ( (tk == "oss.space") && (val.length() > 1) ) {
01568 totspace += _atoi64(val.c_str());
01569 } else
01570 if ( (tk == "oss.free") && (val.length() > 1) ) {
01571 totfree += _atoi64(val.c_str());
01572 } else
01573 if ( (tk == "oss.maxf") && (val.length() > 1) ) {
01574 largestchunk = xrdmax(largestchunk, _atoi64(val.c_str()));
01575 } else
01576 if ( (tk == "oss.used") && (val.length() > 1) ) {
01577 totused += _atoi64(val.c_str());
01578 }
01579 #endif
01580 }
01581 }
01582
01583
01584 }
01585
01586 }
01587
01588
01589 GoBackToRedirector();
01590 return ret;
01591
01592
01593 }