XrdProofdSandbox.cxx

Go to the documentation of this file.
00001 // @(#)root/proofd:$Id: XrdProofdSandbox.cxx 28810 2009-06-04 14:22:37Z ganis $
00002 // Author: G. Ganis  Jan 2008
00003 
00004 /*************************************************************************
00005  * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers.               *
00006  * All rights reserved.                                                  *
00007  *                                                                       *
00008  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010  *************************************************************************/
00011 
00012 //////////////////////////////////////////////////////////////////////////
00013 //                                                                      //
00014 // XrdProofdSandbox                                                     //
00015 //                                                                      //
00016 // Authors: G. Ganis, CERN, 2008                                        //
00017 //                                                                      //
00018 // Create and manage a Unix sandbox.                                    //
00019 //                                                                      //
00020 //////////////////////////////////////////////////////////////////////////
00021 #include "XrdProofdPlatform.h"
00022 
00023 #include "XrdProofdSandbox.h"
00024 #include "XrdSys/XrdSysPriv.hh"
00025 
00026 // Tracing utilities
00027 #include "XrdProofdTrace.h"
00028 
00029 // Modified via config directives by the manager
00030 int          XrdProofdSandbox::fgMaxOldSessions = 10;
00031 XrdOucString XrdProofdSandbox::fgWorkdir = "";
00032 XrdProofUI   XrdProofdSandbox::fgUI;
00033 
00034 //_________________________________________________________________________
00035 XrdProofdSandbox::XrdProofdSandbox(XrdProofUI ui, bool full, bool changeown)
00036                 : fChangeOwn(changeown), fUI(ui)
00037 {
00038    // Assert existence on the sandbox for the user defined by 'ui'.
00039    // The sandbox is created under fgWorkdir or $HOME/proof; the boolean
00040    // 'full' controls the set of directories to be asserted: the sub-set
00041    // {cache, packages, .creds} is always asserted; if full is true also
00042    // the sub-dirs {queries, datasets} are asserted.
00043    // If 'changeown' is true the sandbox ownership is set to 'ui'; this
00044    // requires su-privileges.
00045    // The constructor also builds the list of sessions directories in the
00046    // sandbox; directories corresponding to terminated sessions are
00047    // removed if the total number of session directories is larger than
00048    // fgMaxOldSessions .
00049    XPDLOC(CMGR, "XrdProofdSandbox")
00050 
00051    fValid = 0;
00052 
00053    // The first time fill the info about the owner of this process
00054    if (fgUI.fUid < 0)
00055       XrdProofdAux::GetUserInfo(getuid(), fgUI);
00056 
00057    // Assert the workdir directory ...
00058    if (fgWorkdir.length() > 0) {
00059       // The user directory path will be <workdir>/<user>
00060       fDir = fgWorkdir;
00061       if (!fDir.endswith('/'))
00062          fDir += "/";
00063       fDir += ui.fUser;
00064    } else {
00065       if (changeown || ui.fUser == fgUI.fUser) {
00066          // Default: $HOME/proof
00067          fDir = ui.fHomeDir;
00068          if (!fDir.endswith('/'))
00069             fDir += "/";
00070          fDir += ".proof";
00071       } else {
00072          // ~daemon_owner/proof/<user>
00073          fDir = fgUI.fHomeDir;
00074          if (!fDir.endswith('/'))
00075             fDir += "/";
00076          fDir += ".proof/";
00077          fDir += ui.fUser;
00078       }
00079    }
00080    TRACE(REQ, "work dir = " << fDir);
00081 
00082    // Make sure the directory exists
00083    if (XrdProofdAux::AssertDir(fDir.c_str(), ui, changeown) == -1) {
00084       fErrMsg += "unable to create work dir: ";
00085       fErrMsg += fDir;
00086       TRACE(XERR, fErrMsg);
00087       return;
00088    }
00089 
00090    // Dirs to be asserted
00091    const char *basicdirs[4] = { "/cache", "/packages", "/.creds", "/queries" };
00092    int i = 0;
00093    int n = (full) ? 4 : 3;
00094    for (i = 0; i < n; i++) {
00095       XrdOucString dir = fDir;
00096       dir += basicdirs[i];
00097       if (XrdProofdAux::AssertDir(dir.c_str(), ui, changeown) == -1) {
00098          fErrMsg += "unable to create dir: ";
00099          fErrMsg += dir;
00100          TRACE(XERR, fErrMsg);
00101          return;
00102       }
00103    }
00104 
00105    // Set validity
00106    fValid = 1;
00107 
00108    // Trim old terminated sessions
00109    TrimSessionDirs();
00110 }
00111 
00112 //__________________________________________________________________________
00113 bool XpdSessionTagComp(XrdOucString *&lhs, XrdOucString *&rhs)
00114 {
00115    // Compare times from session tag strings
00116 
00117    if (!lhs || !rhs)
00118       return 1;
00119 
00120    // Left hand side
00121    XrdOucString ll(*lhs);
00122    ll.erase(ll.rfind('-'));
00123    ll.erase(0, ll.rfind('-')+1);
00124    int tl = strtol(ll.c_str(), 0, 10);
00125 
00126    // Right hand side
00127    XrdOucString rr(*rhs);
00128    rr.erase(rr.rfind('-'));
00129    rr.erase(0, rr.rfind('-')+1);
00130    int tr = strtol(rr.c_str(), 0, 10);
00131 
00132    // Done
00133    return ((tl < tr) ? 0 : 1);
00134 }
00135 
00136 #if defined(__sun)
00137 
00138 //__________________________________________________________________________
00139 static void Sort(std::list<XrdOucString *> *lst)
00140 {
00141    // Sort ascendingly the list.
00142    // Function used on Solaris where std::list::sort() does not support an
00143    // alternative comparison algorithm.
00144 
00145    // Check argument
00146    if (!lst)
00147       return;
00148 
00149    // If empty or just one element, nothing to do
00150    if (lst->size() < 2)
00151       return;
00152 
00153    // Fill a temp array with the current status
00154    XrdOucString **ta = new XrdOucString *[lst->size()];
00155    std::list<XrdOucString *>::iterator i;
00156    int n = 0;
00157    for (i = lst->begin(); i != lst->end(); ++i)
00158       ta[n++] = *i;
00159 
00160    // Now start the loops
00161    XrdOucString *tmp = 0;
00162    bool notyet = 1;
00163    int jold = 0;
00164    while (notyet) {
00165       int j = jold;
00166       while (j < n - 1) {
00167          if (XpdSessionTagComp(ta[j], ta[j+1]))
00168             break;
00169          j++;
00170       }
00171       if (j >= n - 1) {
00172          notyet = 0;
00173       } else {
00174          jold = j + 1;
00175          XPDSWAP(ta[j], ta[j+1], tmp);
00176          int k = j;
00177          while (k > 0) {
00178             if (!XpdSessionTagComp(ta[k], ta[k-1])) {
00179                XPDSWAP(ta[k], ta[k-1], tmp);
00180             } else {
00181                break;
00182             }
00183             k--;
00184          }
00185       }
00186    }
00187 
00188    // Empty the original list
00189    lst->clear();
00190 
00191    // Fill it again
00192    while (n--)
00193       lst->push_back(ta[n]);
00194 
00195    // Clean up
00196    delete[] ta;
00197 }
00198 #endif
00199 
00200 //______________________________________________________________________________
00201 int XrdProofdSandbox::GetSessionDirs(int opt, std::list<XrdOucString *> *sdirs,
00202                                      XrdOucString *tag)
00203 {
00204    // Scan the sandbox for sessions working dirs and return their
00205    // sorted (according to creation time, first is the newest) list
00206    // in 'sdirs'.
00207    // The option 'opt' may have 3 values:
00208    //    0        all working dirs are kept
00209    //    1        active sessions only
00210    //    2        terminated sessions only
00211    //    3        search entry containing 'tag' and fill tag with
00212    //             the full entry name; if defined, sdirs is filled
00213    // Returns -1 otherwise in case of failure.
00214    // In case of success returns 0 for opt < 3, 1 if found or 0 if not
00215    // found for opt == 3.
00216    XPDLOC(CMGR, "Sandbox::GetSessionDirs")
00217 
00218    // If unknown take all
00219    opt = (opt >= 0 && opt <= 3) ? opt : 0;
00220 
00221    // Check inputs
00222    if ((opt < 3 && !sdirs) || (opt == 3 && !tag)) {
00223       TRACE(XERR, "invalid inputs");
00224       return -1;
00225    }
00226 
00227    TRACE(DBG, "opt: "<<opt<<", dir: "<<fDir);
00228 
00229    // Open dir
00230    DIR *dir = opendir(fDir.c_str());
00231    if (!dir) {
00232       TRACE(XERR, "cannot open dir "<<fDir<< " (errno: "<<errno<<")");
00233       return -1;
00234    }
00235 
00236    // Scan the directory, and save the paths for terminated sessions
00237    // into a list
00238    bool found = 0;
00239    struct dirent *ent = 0;
00240    while ((ent = (struct dirent *)readdir(dir))) {
00241       if (!strncmp(ent->d_name, "session-", 8)) {
00242          bool keep = 1;
00243          if (opt == 3 && tag->length() > 0) {
00244             if (strstr(ent->d_name, tag->c_str())) {
00245                *tag = ent->d_name;
00246                found = 1;
00247             }
00248          } else {
00249             if (opt > 0) {
00250                XrdOucString fterm(fDir.c_str());
00251                fterm += '/';
00252                fterm += ent->d_name;
00253                fterm += "/.terminated";
00254                int rc = access(fterm.c_str(), F_OK);
00255                if ((opt == 1 && rc == 0) || (opt == 2 && rc != 0))
00256                   keep = 0;
00257             }
00258          }
00259          TRACE(HDBG, "found entry "<<ent->d_name<<", keep: "<<keep);
00260          if (sdirs && keep)
00261             sdirs->push_back(new XrdOucString(ent->d_name));
00262       }
00263    }
00264 
00265    // Close the directory
00266    closedir(dir);
00267 
00268    // Sort the list
00269    if (sdirs)
00270 #if !defined(__sun)
00271       sdirs->sort(&XpdSessionTagComp);
00272 #else
00273       Sort(sdirs);
00274 #endif
00275 
00276    // Done
00277    return ((opt == 3 && found) ? 1 : 0);
00278 }
00279 
00280 //______________________________________________________________________________
00281 int XrdProofdSandbox::AddSession(const char *tag)
00282 {
00283    // Record entry for new proofserv session tagged 'tag' in the active
00284    // sessions file (<SandBox>/.sessions). The file is created if needed.
00285    // Return 0 on success, -1 on error.
00286    XPDLOC(CMGR, "Sandbox::AddSession")
00287 
00288    // Check inputs
00289    if (!tag) {
00290       XPDPRT("invalid input");
00291       return -1;
00292    }
00293    TRACE(DBG, "tag:"<<tag);
00294 
00295    XrdSysPrivGuard pGuard((uid_t)0, (gid_t)0);
00296    if (XpdBadPGuard(pGuard, fUI.fUid) && fChangeOwn) {
00297       TRACE(XERR, "could not get privileges");
00298       return -1;
00299    }
00300 
00301    // File name
00302    XrdOucString fn = fDir;
00303    fn += "/.sessions";
00304 
00305    // Open the file for appending
00306    FILE *fact = fopen(fn.c_str(), "a+");
00307    if (!fact) {
00308       TRACE(XERR, "cannot open file "<<fn<<" for appending (errno: "<<errno<<")");
00309       return -1;
00310    }
00311 
00312    // Lock the file
00313    lseek(fileno(fact), 0, SEEK_SET);
00314    if (lockf(fileno(fact), F_LOCK, 0) == -1) {
00315       TRACE(XERR, "cannot lock file "<<fn<<" (errno: "<<errno<<")");
00316       fclose(fact);
00317       return -1;
00318    }
00319 
00320    bool writeout = 1;
00321 
00322    // Check if already there
00323    std::list<XrdOucString *> actln;
00324    char ln[1024];
00325    while (fgets(ln, sizeof(ln), fact)) {
00326       // Get rid of '\n'
00327       if (ln[strlen(ln)-1] == '\n')
00328          ln[strlen(ln)-1] = '\0';
00329       // Skip empty or comment lines
00330       if (strlen(ln) <= 0 || ln[0] == '#')
00331          continue;
00332       // Count if not the one we want to remove
00333       if (strstr(ln, tag))
00334          writeout = 0;
00335    }
00336 
00337    // Append the session unique tag
00338    if (writeout) {
00339       lseek(fileno(fact), 0, SEEK_END);
00340       fprintf(fact, "%s\n", tag);
00341    }
00342 
00343    // Unlock the file
00344    lseek(fileno(fact), 0, SEEK_SET);
00345    if (lockf(fileno(fact), F_ULOCK, 0) == -1)
00346       TRACE(XERR, "cannot unlock file "<<fn<<" (errno: "<<errno<<")");
00347 
00348    // Close the file
00349    fclose(fact);
00350 
00351    // We are done
00352    return 0;
00353 }
00354 
00355 //______________________________________________________________________________
00356 int XrdProofdSandbox::GuessTag(XrdOucString &tag, int ridx)
00357 {
00358    // Guess session tag completing 'tag' (typically "-<pid>") by scanning the
00359    // active session file or the session dir.
00360    // In case of success, tag is filled with the full tag and 0 is returned.
00361    // In case of failure, -1 is returned.
00362    XPDLOC(CMGR, "Sandbox::GuessTag")
00363 
00364    TRACE(DBG, "tag: "<<tag);
00365 
00366    bool found = 0;
00367    bool last = (tag == "last") ? 1 : 0;
00368 
00369    if (!last && tag.length() > 0) {
00370       // Scan the sessions file
00371       XrdOucString fn = fDir;
00372       fn += "/.sessions";
00373 
00374       // Open the file for reading
00375       FILE *fact = fopen(fn.c_str(), "a+");
00376       if (fact) {
00377          // Lock the file
00378          if (lockf(fileno(fact), F_LOCK, 0) == 0) {
00379             // Read content, if already existing
00380             char ln[1024];
00381             while (fgets(ln, sizeof(ln), fact)) {
00382                // Get rid of '\n'
00383                if (ln[strlen(ln)-1] == '\n')
00384                   ln[strlen(ln)-1] = '\0';
00385                // Skip empty or comment lines
00386                if (strlen(ln) <= 0 || ln[0] == '#')
00387                   continue;
00388                // Count if not the one we want to remove
00389                if (!strstr(ln, tag.c_str())) {
00390                   tag = ln;
00391                   found = 1;
00392                   break;
00393                }
00394             }
00395             // Unlock the file
00396             lseek(fileno(fact), 0, SEEK_SET);
00397             if (lockf(fileno(fact), F_ULOCK, 0) == -1)
00398                TRACE(DBG, "cannot unlock file "<<fn<<" ; fact: "<<fact<<
00399                           ", fd: "<< fileno(fact) << " (errno: "<<errno<<")");
00400 
00401          } else {
00402             TRACE(DBG, "cannot lock file: "<<fn<<" ; fact: "<<fact<<
00403                        ", fd: "<< fileno(fact) << " (errno: "<<errno<<")");
00404          }
00405          // Close the file
00406          fclose(fact);
00407 
00408       } else {
00409          TRACE(DBG, "cannot open file "<<fn<<
00410                     " for reading (errno: "<<errno<<")");
00411       }
00412    }
00413 
00414    if (!found) {
00415 
00416       // Search the tag in the dirs
00417       std::list<XrdOucString *> staglst;
00418       staglst.clear();
00419       int rc = GetSessionDirs(3, &staglst, &tag);
00420       if (rc < 0) {
00421          TRACE(XERR, "cannot scan dir "<<fDir);
00422          return -1;
00423       }
00424       found = (rc == 1) ? 1 : 0;
00425 
00426       if (!found && staglst.size() > 0) {
00427          // Take last one, if required
00428          if (last) {
00429             tag = staglst.front()->c_str();
00430             found = 1;
00431          } else {
00432             if (ridx < 0) {
00433                int itag = ridx;
00434                // Reiterate back
00435                std::list<XrdOucString *>::iterator i;
00436                for (i = staglst.begin(); i != staglst.end(); i++) {
00437                   if (itag == 0) {
00438                      tag = (*i)->c_str();
00439                      found = 1;
00440                      break;
00441                   }
00442                   itag++;
00443                }
00444             }
00445          }
00446       }
00447       // Cleanup
00448       staglst.clear();
00449       // Correct the tag
00450       if (found) {
00451          tag.replace("session-", "");
00452       } else {
00453          TRACE(DBG, "tag "<<tag<<" not found in dir");
00454       }
00455    }
00456 
00457    // We are done
00458    return ((found) ? 0 : -1);
00459 }
00460 
00461 //______________________________________________________________________________
00462 int XrdProofdSandbox::RemoveSession(const char *tag)
00463 {
00464    // Move record for tag from the active sessions file to the old 
00465    // sessions file (<SandBox>/.sessions). The active file is removed if
00466    // empty after the operation. The old sessions file is created if needed.
00467    // Return 0 on success, -1 on error.
00468    XPDLOC(CMGR, "Sandbox::RemoveSession")
00469 
00470    char ln[1024];
00471 
00472    // Check inputs
00473    if (!tag) {
00474       TRACE(XERR, "invalid input");
00475       return -1;
00476    }
00477    TRACE(DBG, "tag:"<<tag);
00478 
00479    XrdSysPrivGuard pGuard((uid_t)0, (gid_t)0);
00480    if (XpdBadPGuard(pGuard, fUI.fUid) && fChangeOwn) {
00481       TRACE(XERR, "could not get privileges");
00482       return -1;
00483    }
00484 
00485    // Update of the active file
00486    XrdOucString fna = fDir;
00487    fna += "/.sessions";
00488 
00489    // Open the file
00490    FILE *fact = fopen(fna.c_str(), "a+");
00491    if (!fact) {
00492       TRACE(XERR, "cannot open file "<<fna<<" (errno: "<<errno<<")");
00493       return -1;
00494    }
00495 
00496    // Lock the file
00497    if (lockf(fileno(fact), F_LOCK, 0) == -1) {
00498       TRACE(XERR, "cannot lock file "<<fna<<" (errno: "<<errno<<")");
00499       fclose(fact);
00500       return -1;
00501    }
00502 
00503    // Read content, if already existing
00504    std::list<XrdOucString *> actln;
00505    while (fgets(ln, sizeof(ln), fact)) {
00506       // Get rid of '\n'
00507       if (ln[strlen(ln)-1] == '\n')
00508          ln[strlen(ln)-1] = '\0';
00509       // Skip empty or comment lines
00510       if (strlen(ln) <= 0 || ln[0] == '#')
00511          continue;
00512       // Count if not the one we want to remove
00513       if (!strstr(ln, tag))
00514          actln.push_back(new XrdOucString(ln));
00515    }
00516 
00517    // Truncate the file
00518    if (ftruncate(fileno(fact), 0) == -1) {
00519       TRACE(XERR, "cannot truncate file "<<fna<<" (errno: "<<errno<<")");
00520       lseek(fileno(fact), 0, SEEK_SET);
00521       if (lockf(fileno(fact), F_ULOCK, 0) != 0)
00522          TRACE(XERR, "cannot lockf file "<<fna<<" (errno: "<<errno<<")");
00523       fclose(fact);
00524       return -1;
00525    }
00526 
00527    // If active sessions still exist, write out new composition
00528    bool unlk = 1;
00529    if (actln.size() > 0) {
00530       unlk = 0;
00531       std::list<XrdOucString *>::iterator i;
00532       for (i = actln.begin(); i != actln.end(); ++i) {
00533          fprintf(fact, "%s\n", (*i)->c_str());
00534          delete (*i);
00535       }
00536    }
00537 
00538    // Unlock the file
00539    lseek(fileno(fact), 0, SEEK_SET);
00540    if (lockf(fileno(fact), F_ULOCK, 0) == -1)
00541       TRACE(DBG, "cannot unlock file "<<fna<<" (errno: "<<errno<<")");
00542 
00543    // Close the file
00544    fclose(fact);
00545 
00546    // Unlink the file if empty
00547    if (unlk)
00548       if (unlink(fna.c_str()) == -1) 
00549          TRACE(DBG, "cannot unlink file "<<fna<<" (errno: "<<errno<<")");
00550 
00551    // Flag the session as closed
00552    XrdOucString fterm = fDir;
00553    fterm += (strstr(tag,"session-")) ? "/" : "/session-";
00554    fterm += tag;
00555    fterm += "/.terminated";
00556    // Create the file
00557    FILE *ft = fopen(fterm.c_str(), "w");
00558    if (!ft) {
00559       TRACE(XERR, "cannot open file "<<fterm<<" (errno: "<<errno<<")");
00560       return -1;
00561    }
00562    fclose(ft);
00563 
00564    // Done
00565    return 0;
00566 }
00567 
00568 //______________________________________________________________________________
00569 int XrdProofdSandbox::TrimSessionDirs()
00570 {
00571    // If the static fgMaxOldLogs > 0, logs for a fgMaxOldLogs number of sessions
00572    // are kept in the sandbox; working dirs for sessions in excess are removed.
00573    // By default logs for the last 10 sessions are kept; the limit can be changed
00574    // via the static method XrdProofdClient::SetMaxOldLogs.
00575    // Return 0 on success, -1 on error.
00576    XPDLOC(CMGR, "Sandbox::TrimSessionDirs")
00577 
00578    TRACE(DBG, "maxold:"<<fgMaxOldSessions);
00579 
00580    // To avoid dead locks we must close the file and do the mv actions after
00581    XrdOucString tobemv, fnact = fDir;
00582    fnact += "/.sessions";
00583    FILE *f = fopen(fnact.c_str(), "r");
00584    if (f) {
00585       char ln[1024];
00586       while (fgets(ln, sizeof(ln), f)) {
00587          if (ln[strlen(ln)-1] == '\n')
00588             ln[strlen(ln)-1] = 0;
00589          char *p = strrchr(ln, '-');
00590          if (p) {
00591             int pid = strtol(p+1, 0, 10);
00592             if (!XrdProofdAux::VerifyProcessByID(pid)) {
00593                tobemv += ln;
00594                tobemv += '|';
00595             }
00596          }
00597       }
00598       fclose(f);
00599    }
00600 
00601    XrdSysPrivGuard pGuard((uid_t)0, (gid_t)0);
00602    if (XpdBadPGuard(pGuard, fUI.fUid) && fChangeOwn) {
00603       TRACE(XERR, "could not get privileges to trim directories");
00604       return -1;
00605    }
00606 
00607    // Mv inactive sessions, if needed
00608    if (tobemv.length() > 0) {
00609       char del = '|';
00610       XrdOucString tag;
00611       int from = 0;
00612       while ((from = tobemv.tokenize(tag, from, del)) != -1) {
00613          if (RemoveSession(tag.c_str()) == -1)
00614             TRACE(XERR, "problems tagging session as old in sandbox");
00615       }
00616    }
00617 
00618    // If a limit on the number of sessions dirs is set, apply it
00619    if (fgMaxOldSessions > 0) {
00620 
00621       // Get list of terminated session working dirs
00622       std::list<XrdOucString *> staglst;
00623       staglst.clear();
00624       if (GetSessionDirs(2, &staglst) != 0) {
00625          TRACE(XERR, "cannot get list of dirs ");
00626          return -1;
00627       }
00628       TRACE(DBG, "number of working dirs: "<<staglst.size());
00629 
00630       if (TRACING(HDBG)) {
00631          std::list<XrdOucString *>::iterator i;
00632          for (i = staglst.begin(); i != staglst.end(); ++i) {
00633             TRACE(HDBG, "found "<<(*i)->c_str());
00634          }
00635       }
00636 
00637       // Remove the oldest, if needed
00638       while ((int)staglst.size() > fgMaxOldSessions) {
00639          XrdOucString *s = staglst.back();
00640          if (s) {
00641             TRACE(HDBG, "removing "<<s->c_str());
00642             // Remove associated workdir
00643             XrdOucString rmcmd = "/bin/rm -rf ";
00644             rmcmd += fDir;
00645             rmcmd += '/';
00646             rmcmd += s->c_str();
00647             if (system(rmcmd.c_str()) == -1)
00648                TRACE(XERR, "cannot invoke system("<<rmcmd<<") (errno: "<<errno<<")");
00649             // Delete the string
00650             delete s;
00651          }
00652          // Remove the last element
00653          staglst.pop_back();
00654       }
00655 
00656       // Clear the list
00657       staglst.clear();
00658    }
00659 
00660    // Done
00661    return 0;
00662 }
00663 
00664 

Generated on Tue Jul 5 14:51:52 2011 for ROOT_528-00b_version by  doxygen 1.5.1