#include #include #include #include #include #include "disparm.h" #include "tofromstring.h" #include "token.h" #include "shellenv.h" #include #include #include const std::string disparm::keyname = "DISPARM_KEY"; const std::string disparm::committedname = "DISPARM_COMMITTED"; const std::string disparm::valuename = "DISPARM_VALUE"; const std::string disparm::rcname = "DISPARM_RC"; const std::string disparm::linename = "DISPARM_LINENO"; const std::string disparm::defpath_pool = "pool.disparm"; const std::string disparm::definputfilename = "-"; // //constructor disparm // disparm::disparm() { char defpoolname_c[this->defpath_pool.length()+1]; // default path_pool strcpy(defpoolname_c,this->defpath_pool.c_str()); // // A note about basename and dirname: they need as argument a char* // NOT a const char*. Both functions may modify the argument, // so, in general, do not use the same argument more than // once. // char defpath_lockdir_c[this->defpath_lockdir.length()+1]; // default directory for the lockfile strcpy(defpath_lockdir_c,this->defpath_lockdir.c_str()); this->setpath_pool(this->defpath_pool); this->defpath_lockdir = std::string(dirname(defpoolname_c)); this->setpath_link(""); this->setpath_lock(""); // the name of the lock folder this->setbaselinkfilename(""); this->setinputfilename(this->definputfilename); // default input file name this->setpath_lockdir(this->defpath_lockdir); // default lock directory this->setmaxiter(1); // default maximum times a string can be given out this->pmarker="pp"; // start of record with pointer to next string in pool this->pmarkerlen = this->pmarker.length(); this->lockname="lock.disparm"; std::string shell = getenv("SHELL"); if (shell.find("csh") != std::string::npos) this->shelltype = SHELL_CSH; else this->shelltype = SHELL_SH; // // following items are returned on stdout as in: // export DISPARM_KEY=1234 // See also above // this->key = -1; // key: pointer to next item in pool this->status = '0'; this->committed = 0; // the number of times the string is given out this->rc = "OK"; // return code this->value = ""; // the string to give out this->linenr = 0; // the line number in the pool this->debug = 0; } void disparm::removelock() { rmdir(this->path_lock.c_str()); unlink(this->path_link.c_str()); } void disparm::setpath_pool(std::string pool) { this->path_pool = pool; } void disparm::setpath_link(std::string path_link) { this->path_link = path_link; } void disparm::setpath_lock(std::string path_lock) { this->path_lock = path_lock; } void disparm::setbaselinkfilename(std::string baselinkfilename) { this->baselinkfilename = baselinkfilename; } int disparm::setpath_lockdir(std::string lockdir) { this->path_lockdir = lockdir; int rc = mkdir(path_lockdir.c_str(),0777); if (rc ==0) return 0; else { if (errno == EEXIST) { struct stat buf; stat(path_lockdir.c_str(),&buf); if (S_ISDIR(buf.st_mode)) return 0; } else return -1; } return 0; // never reached } // // we try to generate a name for a linkfile that is unique. // In the name is also the creation time. // We also create a suitable path_lockdir here. // void disparm::createlinkfilename() { if (this->debug) this->debugger("Before createlinkfilename:"); char hostname[1025]; gethostname(hostname,1024); char poolname_c[this->path_pool.length()+1]; strcpy(poolname_c,this->path_pool.c_str()); std::string poolnamebase = basename(poolname_c); std::string base = this->path_lockdir + "/" + poolnamebase + ".link."; this->setbaselinkfilename(base); this->setpath_link( base + to_string(time(0)) + "." + hostname + "." + to_string(getpid())); this->setpath_lock(this->path_lockdir + "/" + this->lockname); if (this->debug) this->debugger("After createlinkfilename:"); } void disparm::setinputfilename(std::string inputfilename) { this->inputfilename = inputfilename; } void disparm::setkey(std::streampos key) { this->key = key; } std::streampos disparm::getkey(void) { return this->key; } void disparm::setmaxiter(int maxiter) { this->maxiter = maxiter; } // // Create poolfile from input file // // On return, s wil contain the output of createrc // int disparm::create(std::string &s) { if (this->debug) this->debugger("Before create:"); std::ifstream inp; std::istream *pinp; s = ""; // // open inputfile, take care that stdin is also allowed // if (this->inputfilename == "-") pinp = &std::cin; else { inp.open(this->inputfilename.c_str()); if (!inp.is_open()) { std::cerr<<"Cannot open '"<rc = "FAIL"; s = this->createrc(); return 1; } pinp = &inp; } // // open poolfile // std::string line; std::fstream out; out.open(this->path_pool.c_str(),std::fstream::in | std::fstream::out | std::fstream::trunc); // // fill poolfile // token tok; tok.setfile(out); std::streampos p; unsigned int linenr = 0; while (getline(*pinp,line)) { tok.setlinenr(++linenr); tok.setcommitted(0); tok.clearstatus(); tok.setvalue(line); tok.writetoken(p); } tok.writelocpointer(0); // // close input file, only if it is not stdin // if (pinp == &inp) inp.close(); out.close(); this->key = 0; s = createrc(); if (this->debug) this->debugger("After create:"); return 0; } // // get next string from pool // int disparm::next(std::string& s) { if (this->debug) this->debugger("Before next:"); int rc = this->realnext(); s = this->createrc(); if (this->debug) this->debugger("After next:"); return rc; } // // create string to be eval'd by the shell // std::string disparm::createrc(void) { std::string s = ""; s = s + shellenv(to_string(this->key), this->keyname, this->shelltype); s = s + shellenv(to_string(this->committed),this->committedname,this->shelltype); s = s + shellenv(to_string(this->value), this->valuename, this->shelltype); s = s + shellenv(to_string(this->rc), this->rcname, this->shelltype); s = s + shellenv(to_string(this->linenr), this->linename, this->shelltype); return s; } // // get the next string from the pool // int disparm::realnext() { std::fstream file; bool good = 1; this->key = 0; this->status = '0'; this->committed = 0; this->rc = "OK"; this->linenr = 0; this->value = ""; // // get lock, return if that fails // if (this->getlock()!=0) { this->rc = "FAIL"; return 1; } // // read next string from pool // take care of ready records, maximum iterations // allowed and some kinds of mishaps // file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out); if (file.is_open()) { token tok; std::string line; tok.setfile(file); std::streampos locpointer, posused, posnext; // // get pointer to next string // locpointer = tok.readlocpointer(); if (locpointer < 0) { std::cerr << "Corrupt parameterfile: cannot find locpointer\n" << std::endl; this->rc = "FAIL"; good = 0; } // // read next string from pool // if (good) { int rc = tok.readnext(locpointer,posused,posnext); if (rc != 0) { std::cerr << __FILE__ << ":" << __LINE__ << std::endl; std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl; this->rc = "FAIL"; good = 0; } } // // keep reading until a valid record is found // take appropriate action if no such record exists // if (good) { std::streampos locpointerold = locpointer; bool allready = 1; // to learn if all records are // ready while (1) { if (!tok.getready()) { allready = 0; if (tok.getcommitted() >= this->maxiter) { good = 0; this->rc = "MAX_EXCEEDED"; break; } this->key = posused; this->status = tok.getstatus(); this->committed = tok.getcommitted(); this->value = tok.getvalue(); this->linenr = tok.getlinenr(); tok.setcommitted(tok.getcommitted()+1); tok.writetoken(posused); tok.writelocpointer(posused); break; } locpointer = posnext; if (locpointer == locpointerold) { // // so we are cycling, nothing to find, it seems // good = 0; if (allready) this->rc = "ALL_READY"; else this->rc = "MAX_EXCEEDED"; break; } int rc = tok.readnext(locpointer,posused,posnext); if (rc != 0) { std::cerr << __FILE__ << ":" << __LINE__ << std::endl; std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl; this->rc = "FAIL"; good = 0; break; } } } } else { std::cerr<<"Strange error: cannot open '"<removelock(); return !good; } // // get a lock on the poolfile // The normal methods don't work on an nfs filesystem. // Luckily, there is a method based on links to the // file to be locked, see also man(2) open under O_EXCL // // Creating a link is atomic, so it suffices to see if // the link count equals to two. If not, we try again. // // It seems that the file used for linking contains displays // an up-to-date content. // int disparm::getlock(void) { struct stat buf; int rc; int sleeptime=2; int iter = 0; int ntryremovelock = 0; while(1) { // // check poolfile's linkcount // if link count == 1, we try to create a link, else wait // rc = stat(this->path_pool.c_str(),&buf); std::string er=""; switch (errno) { case (EACCES): er="Search permission is denied for one of the directories in the path prefix of path. (See also path_resolution(7).)"; break; case(EBADF): er="fd is bad."; break; case(EFAULT): er="Bad address."; break; case(ELOOP): er="Too many symbolic links encountered while traversing the path."; break; case(ENAMETOOLONG):er="File name too long."; break; case(ENOENT): er="A component of path does not exist, or path is an empty string."; break; case(ENOMEM): er="Out of memory (i.e., kernel memory)."; break; case(ENOTDIR): er="A component of the path prefix of path is not a directory."; break; case(EOVERFLOW):er="(stat()) path refers to a file whose size cannot be represented in the type off_t. This can occur when an application compiled on a 32-bit platform without -D_FILE_OFFSET_BITS=64 calls stat() on a file whose size exceeds (2<<31)-1 bits"; break; } if (rc !=0 ) { std::cerr<path_pool.c_str()<<"'"<< std::endl; return 1; } this->createlinkfilename(); rc = mkdir(this->path_lock.c_str(), 0700); if (rc == 0) { // create link int rc = link(this->path_pool.c_str(),this->path_link.c_str()); if (rc < 0) { std::cerr<<"Cannot make link '"<path_link<<"' to '" <path_pool<<"'"< 9) { std::cerr << "Trying to remove locks" << std::endl; // // remove stale link files // this->remove_lock_and_links(); ntryremovelock ++; if (ntryremovelock > 10) { std::cerr << "Cannot get lock after many tries." << std::endl; return 1; } iter = 0; } } return 0; } // // mark string as ready // int disparm::ready() { if (this->getlock()!=0) { return 1; } // // if no key given as parameter, read it from environment // if (this->key < 0) { char * s = getenv(this->keyname.c_str()); if (s == NULL || *s == 0) { this->removelock(); std::cerr << "No key given" << std::endl; return 1; } else { this->key = from_string(std::string(s)); } } std::streampos locpointer = this->key; std::fstream file; file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out); if (file.is_open()) { token tok; std::string line; tok.setfile(file); std::streampos posused,posnext; // // read next record // int rc = tok.readnext(locpointer,posused,posnext); if (rc != 0) { std::cerr << __FILE__ << ":" << __LINE__ << std::endl; std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl; file.close(); this->removelock(); return 1; } // // mark record as ready // tok.ready(); // // write record back // tok.writetoken(locpointer); } file.close(); this->removelock(); return 0; } // // remove poolfile // int disparm::REMOVE() { if (this->getlock()!=0) { return 1; } this->removelock(); int rc = unlink(this->path_pool.c_str()); if (rc) { std::cerr << "Cannot remove " << this->path_pool << std::endl; return 1; } return 0; } // // return statistics about the pool // no wait for lock, so be careful interpretating the results // stats disparm::getstats() { stats statistics; token tok; std::fstream file; statistics.records = -1; statistics.committed = -1; statistics.ready = -1; statistics.commax = -1; file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out); if (file.is_open()) { tok.setfile(file); std::streampos pos = 0; std::streampos posused; std::streampos posnext; statistics.records = 0; statistics.committed = 0; statistics.ready = 0; statistics.commax = 0; while(1) { tok.readnext(pos, posused,posnext); if (pos != posused) break; if (tok.getready()) statistics.ready++; if (tok.getcommitted()>0) statistics.committed++; if (tok.getcommitted() > statistics.commax) statistics.commax = tok.getcommitted(); statistics.records++; pos = posnext; } } file.close(); return statistics; } int disparm::echounhandled(void) { token tok; std::fstream file; file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out); if (file.is_open()) { tok.setfile(file); std::streampos pos = 0; std::streampos posused; std::streampos posnext; std::cerr << "linenr:committed:value"<< std::endl; while(1) { tok.readnext(pos, posused,posnext); if (pos != posused) break; if (!tok.getready()&&tok.getcommitted()>0) { std::cerr << tok.getlinenr() << ":"<path_pool<<"'"< buf.st_mtime + timer) { std::cerr << "Trying to remove '"<d_name; if (name == ".") continue; if (name == "..") continue; rpathc = realpath((dirl+"/"+name).c_str(),rpathc); rpath1 = rpathc; std::cerr<<"TD: looking at '"< buf.st_mtime + timer) { int rc = unlink(rpath1.c_str()); if (rc == 0) std::cerr<<"Unlinked '"<debug) this->debugger("Before remove_lock_and_links:"); remove_lock_and_links(this->path_pool,this->path_lockdir,this->path_lock,60); if (this->debug) this->debugger("Before remove_lock_and_links:"); } void disparm::setdebug() { this->debug = 1; } void disparm::debugger(const std::string s) { std::cerr << s << std::endl; std::cerr << "baselinkfilename: '" << this->baselinkfilename << "'" << std::endl; std::cerr << "committed: '" << this->committed << "'" << std::endl; std::cerr << "committedname: '" << this->committedname << "'" << std::endl; std::cerr << "definputfilename: '" << this->definputfilename << "'" << std::endl; std::cerr << "defpath_pool: '" << this->defpath_pool << "'" << std::endl; std::cerr << "defpath_lockdir: '" << this->defpath_lockdir << "'" << std::endl; std::cerr << "inputfilename: '" << this->inputfilename << "'" << std::endl; std::cerr << "key: '" << this->key << "'" << std::endl; std::cerr << "keyname: '" << this->keyname << "'" << std::endl; std::cerr << "linename: '" << this->linename << "'" << std::endl; std::cerr << "linenr: '" << this->linenr << "'" << std::endl; std::cerr << "lockname: '" << this->lockname << "'" << std::endl; std::cerr << "maxiter: '" << this->maxiter << "'" << std::endl; std::cerr << "path_link: '" << this->path_link << "'" << std::endl; std::cerr << "path_lock: '" << this->path_lock << "'" << std::endl; std::cerr << "path_lockdir: '" << this->path_lockdir << "'" << std::endl; std::cerr << "path_pool: '" << this->path_pool << "'" << std::endl; std::cerr << "pmarker: '" << this->pmarker << "'" << std::endl; std::cerr << "pmarkerlen: '" << this->pmarkerlen << "'" << std::endl; std::cerr << "pointerlen: '" << this->pointerlen << "'" << std::endl; std::cerr << "rc: '" << this->rc << "'" << std::endl; std::cerr << "rcname: '" << this->rcname << "'" << std::endl; std::cerr << "shelltype: '" << this->shelltype << "'" << std::endl; std::cerr << "status: '" << this->status << "'" << std::endl; std::cerr << "value: '" << this->value << "'" << std::endl; std::cerr << "valuename: '" << this->valuename << "'" << std::endl; } // // Just to teach me again how to make an object // suitable for the << operator // std::ostream& operator <<(std::ostream &os,disparm &obj) { stats sta; sta = obj.getstats(); os << "Total: " << sta.records << "\n" << "Committed: " << sta.committed << "\n" << "Ready: " << sta.ready << "\n" << "Max: " << sta.commax; return os; }