Back to index

nordugrid-arc-nox  1.1.0~rc6
SimpleMap.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #include <string>
00006 #include <iostream>
00007 #include <fstream>
00008 #include <list>
00009 
00010 #include <cstdlib>
00011 #include <cstring>
00012 // NOTE: On Solaris errno is not working properly if cerrno is included first
00013 #include <cerrno>
00014 
00015 #include <utime.h>
00016 #include <sys/file.h>
00017 #include <sys/types.h>
00018 #include <sys/stat.h>
00019 #include <fcntl.h>
00020 #include <unistd.h>
00021 
00022 #include <glibmm.h>
00023 
00024 #define odlog(LEVEL) std::cerr
00025 
00026 #include "SimpleMap.h"
00027 
00028 namespace ArcSec {
00029 
00030 #ifndef WIN32
00031 class FileLock {
00032  private:
00033   int h_;
00034   struct flock l_;
00035  public:
00036   FileLock(int h):h_(h) {
00037     if(h_ == -1) return;
00038     l_.l_type=F_WRLCK;
00039     l_.l_whence=SEEK_SET;
00040     l_.l_start=0;
00041     l_.l_len=0;
00042     for(;;) {
00043       if(fcntl(h_,F_SETLKW,&l_) == 0) break;
00044       if(errno != EINTR) { h_=-1; return; };
00045     };
00046   };
00047   ~FileLock(void) {
00048     if(h_ == -1) return;
00049     l_.l_type=F_UNLCK;
00050     fcntl(h_,F_SETLKW,&l_);
00051   };
00052   operator bool(void) { return (h_ != -1); };
00053   bool operator!(void) { return (h_ == -1); };
00054 };
00055 #else
00056 // TODO: implement
00057 class FileLock {
00058  public:
00059   FileLock(int) { };
00060   operator bool(void) { return false; };
00061   bool operator!(void) { return true; };
00062 };
00063 #endif
00064 
00065 SimpleMap::SimpleMap(const std::string& dir):dir_(dir) {
00066   if((dir_.length() == 0) || (dir_[dir_.length()-1] != '/')) dir_+="/";
00067   if(dir_[0] != '/') {
00068     char buf[PATH_MAX];
00069     if(getcwd(buf,sizeof(buf))) dir_=std::string(buf)+"/"+dir_;
00070   };
00071   pool_handle_=open((dir_+"pool").c_str(),O_RDWR);
00072 }
00073 
00074 SimpleMap::~SimpleMap(void) {
00075   if(pool_handle_ != -1) close(pool_handle_);
00076   pool_handle_=-1;
00077 }
00078 
00079 #define failure(S) { \
00080   odlog(ERROR)<<"SimpleMap: "<<(S)<<std::endl; \
00081   return ""; \
00082 }
00083 
00084 #define info(S) { \
00085   odlog(INFO)<<"SimpleMap: "<<(S)<<std::endl; \
00086 }
00087 
00088 std::string SimpleMap::map(const std::string& subject) {
00089   if(pool_handle_ == -1) failure("not initialized");
00090   if(subject.empty()) failure("missing subject");
00091   std::string filename(subject);
00092   for(std::string::size_type i = filename.find('/');i!=std::string::npos;
00093       i=filename.find('/',i+1)) filename[i]='_';
00094   filename=dir_+filename;
00095   FileLock lock(pool_handle_);
00096   if(!lock) failure("failed to lock pool file");
00097   // Check for existing mapping
00098   struct stat st;
00099   if(stat(filename.c_str(),&st) == 0) {
00100     if(!S_ISREG(st.st_mode)) failure("mapping is not a regular file");
00101     std::ifstream f(filename.c_str());
00102     if(!f.is_open()) failure("can't open mapping file");
00103     std::string name;
00104     getline(f,name);
00105     utime(filename.c_str(),NULL);
00106     return name;
00107   };
00108   // Look for unused names
00109   // Get full list first.
00110   std::list<std::string> names;
00111   {
00112     std::ifstream f((dir_+"pool").c_str());
00113     if(!f.is_open()) failure("can't open pool file")
00114     std::string name;
00115     while(!f.eof()) {
00116       std::getline(f,name);
00117       if(!f.fail()) break;
00118       if(name.empty()) continue;
00119       names.push_back(name);
00120     };
00121   };
00122   if(!names.size()) failure("pool is empty");
00123   // Remove all used names from list. Also find oldest maping.
00124   time_t oldmap_time = 0;
00125   std::string oldmap_name;
00126   std::string oldmap_subject;
00127   try {
00128     std::string file;
00129     Glib::Dir dir(dir_);
00130     for(;;) {
00131       file=dir.read_name();
00132       if(file.empty()) break;
00133       if(file == ".") continue;
00134       if(file == "..") continue;
00135       if(file == "pool") continue;
00136       std::string filename = dir_+file;
00137       struct stat st;
00138       if(stat(filename.c_str(),&st) != 0) continue;
00139       if(!S_ISREG(st.st_mode)) continue;
00140       std::ifstream f(filename.c_str());
00141       if(!f.is_open()) { // trash in directory
00142         failure("can't open one of mapping files"); 
00143       };
00144       std::string name;
00145       std::getline(f,name);
00146       // find this name in list
00147       std::list<std::string>::iterator i = names.begin();
00148       for(;i!=names.end();++i) if(*i == name) break;
00149       if(i == names.end()) {
00150         // Always try to destroy old mappings without corresponding 
00151         // entry in the pool file
00152         if(((unsigned int)(time(NULL) - st.st_mtime)) >= SELFUNMAP_TIME) {
00153           unlink(filename.c_str());
00154         };
00155       } else {
00156         names.erase(i);
00157         if( (oldmap_name.length() == 0) ||
00158             (((int)(oldmap_time - st.st_mtime)) > 0) ) {
00159           oldmap_name=name;
00160           oldmap_subject=file;
00161           oldmap_time=st.st_mtime;
00162         };
00163       };
00164     };
00165   } catch(Glib::FileError& e) {
00166     failure("can't list pool directory");
00167   };
00168   if(names.size()) {
00169     // Claim one of unused names
00170     std::ofstream f(filename.c_str());
00171     if(!f.is_open()) failure("can't create mapping file");
00172     f<<*(names.begin())<<std::endl;
00173     info(std::string("Mapped ")+subject+" to "+(*(names.begin())));
00174     return *(names.begin());
00175   };
00176   // Try to release one of old names
00177   if(oldmap_name.length() == 0) failure("no old mappings found");
00178   if(((unsigned int)(time(NULL) - oldmap_time)) < SELFUNMAP_TIME) failure("no old enough mappings found");
00179   // releasing the old entry
00180   info(std::string("Releasing expired mapping of ")+oldmap_subject+
00181        " to "+oldmap_name+" back to pool");
00182   if(unlink((dir_+oldmap_subject).c_str()) != 0) failure("failed to remove mapping file");
00183   // writing the new mapping to the file
00184   std::ofstream f(filename.c_str());
00185   if(!f.is_open()) failure("can't create mapping file");
00186   f<<oldmap_name<<std::endl;
00187   info(std::string("Mapped ")+subject+" to "+oldmap_name);
00188   return oldmap_name;
00189 }
00190 
00191 bool SimpleMap::unmap(const std::string& subject) {
00192   if(pool_handle_ == -1) return false;
00193   FileLock lock(pool_handle_);
00194   if(!lock) return false;
00195   if(unlink((dir_+subject).c_str()) == 0) return true;
00196   if(errno == ENOENT) return true;
00197   return false;
00198 }
00199 
00200 }