summaryrefslogtreecommitdiffabout
path: root/lib/sitecing_util.cc
Side-by-side diff
Diffstat (limited to 'lib/sitecing_util.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/sitecing_util.cc278
1 files changed, 278 insertions, 0 deletions
diff --git a/lib/sitecing_util.cc b/lib/sitecing_util.cc
new file mode 100644
index 0000000..9b6c54e
--- a/dev/null
+++ b/lib/sitecing_util.cc
@@ -0,0 +1,278 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/ipc.h>
+ #include <sys/sem.h>
+ #include <errno.h>
+ #include <iostream>
+ #include <fstream>
+ #include <cassert>
+ #include "sitecing/sitecing_util.h"
+#endif
+
+namespace sitecing {
+
+ /*
+ * XXX: all of these utilities could be sheerly optimized.
+ */
+
+ string normalize_path(const string& path,int opts) {
+ const char *s = path.c_str();
+ string rv;
+ string::size_type notslash = 0;
+ if( (*s)=='.' && s[1]=='/' )
+ s+=2;
+ if(opts&strip_leading_slash)
+ for(;(*s) && (*s)=='/';s++);
+ for(;*s;s++) {
+ if( (*s)=='/' ) {
+ if(s[1]=='/')
+ continue;
+ if(s[1]=='.' && s[2]=='/') {
+ s+=2;
+ continue;
+ }
+ }
+ if(opts&restrict_dotdot) {
+ if(
+ ( rv.empty() && s[0]=='.' && s[1]=='.' && s[2]=='/' ) // "^../"
+ || ( s[0]=='/' && s[1]=='.' && s[2]=='.' && (s[3]==0 || s[3]=='/') ) // "/..(/|$)"
+ )
+ throw utility_restricted_sequence(CODEPOINT,"restricted updir sequence encountered");
+ }
+ rv += *s;
+ if( (*s) != '/' )
+ notslash=rv.length();
+ }
+ if(!(opts&strip_trailing_slash))
+ notslash++;
+ if(notslash<rv.length())
+ rv.erase(notslash); // XXX: check the logic of stripping/not strippling trailing slash
+ return rv;
+ }
+
+ string strip_prefix(const string& str,const string& prefix) {
+ if(str.compare(0,prefix.length(),prefix))
+ throw utility_no_prefix(CODEPOINT,"no such prefix");
+ return str.substr(prefix.length());
+ }
+
+ string strip_suffix(const string& str,const string& suffix) {
+ if(str.compare(str.length()-suffix.length(),suffix.length(),suffix))
+ throw utility_no_suffix(CODEPOINT,"no such suffix");
+ return str.substr(0,str.length()-suffix.length());
+ }
+
+ string dir_name(const string& filename) {
+ string::size_type sl = filename.find_last_of('/');
+ if(sl==string::npos)
+ return ""; // no slashes -- no dir.
+ string::size_type nosl = filename.find_last_not_of('/',sl);
+ if(nosl==string::npos)
+ return ""; // only slashes -- no dir. XXX: only slashes after the last slash... does it mean no dir?
+ return filename.substr(0,nosl+1);
+ }
+
+ void make_path(const string& path,mode_t mode) {
+ struct stat st;
+ for(string::size_type sl=0;sl!=string::npos;sl=path.find('/',sl+1)) {
+ if(!sl)
+ continue;
+ string p = path.substr(0,sl);
+ if(stat(p.c_str(),&st) || !S_ISDIR(st.st_mode)) {
+ if(mkdir(p.c_str(),mode))
+ throw konforka::exception(CODEPOINT,"failed to mkdir()");
+ }
+ }
+ if(stat(path.c_str(),&st) || !S_ISDIR(st.st_mode)) {
+ if(mkdir(path.c_str(),mode))
+ throw konforka::exception(CODEPOINT,"failed to mkdir()");
+ }
+ }
+
+ void file_lock::lock(const string& f) {
+ unlock();
+ fd = open(f.c_str(),O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
+ if(fd<0)
+ throw konforka::exception(CODEPOINT,"failed to open/create lockfile");
+ try {
+ lock();
+ }catch(konforka::exception& ke) {
+ ke.see(CODEPOINT);
+ close(fd); fd=-1;
+ throw;
+ }catch(...) {
+ close(fd); fd=-1;
+ throw;
+ }
+ }
+ void file_lock::lock() {
+ assert(fd>=0);
+ struct flock fl;
+ fl.l_type = F_WRLCK;
+ fl.l_whence=SEEK_SET;
+ fl.l_start=fl.l_len=0;
+ for(int tries=3;tries;tries--) {
+ if(!fcntl(fd,F_SETLK,&fl))
+ return;
+ sleep(8);
+ }
+ throw konforka::exception(CODEPOINT,"failed to obtain file lock");
+ }
+ void file_lock::unlock() {
+ if(fd<0)
+ return;
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence=SEEK_SET;
+ fl.l_start=fl.l_len=0;
+ int rv = fcntl(fd,F_SETLK,&fl);
+ close(fd);
+ fd=-1;
+ if(rv)
+ throw konforka::exception(CODEPOINT,"failed to release file lock");
+ }
+
+ void pid_file::set(const string& f,bool u) {
+ ofstream of(f.c_str(),ios::trunc);
+ if(!of)
+ throw konforka::exception(CODEPOINT,"failed to open file for writing pid");
+ of << getpid() << endl;
+ of.close();
+ file_name = f;
+ unlink_pid = u;
+ }
+ void pid_file::unlink() {
+ if(!unlink_pid)
+ return;
+ ::unlink(file_name.c_str());
+ }
+
+ void semaphore::init() {
+ deinit();
+ semid = semget(IPC_PRIVATE,1,IPC_CREAT|0600);
+ if(semid<0)
+ throw konforka::exception(CODEPOINT,"failed to semget()");
+ if(semctl(semid,0,SETVAL,1))
+ throw konforka::exception(CODEPOINT,"failed to semctl()");
+ }
+ void semaphore::deinit() {
+ if(semid<0)
+ return;
+ semctl(semid,0,IPC_RMID,0);
+ }
+ void semaphore::on() {
+ assert(semid>=0);
+ struct sembuf sb;
+ sb.sem_num=0;
+ sb.sem_op=-1;
+ sb.sem_flg = SEM_UNDO;
+ while(semop(semid,&sb,1)<0) {
+ if(errno!=EINTR)
+ throw konforka::exception(CODEPOINT,"failed to semop()");
+ }
+ }
+ void semaphore::off() {
+ assert(semid>=0);
+ struct sembuf sb;
+ sb.sem_num=0;
+ sb.sem_op=1;
+ sb.sem_flg = SEM_UNDO;
+ while(semop(semid,&sb,1)<0) {
+ if(errno!=EINTR)
+ throw konforka::exception(CODEPOINT,"failed to semop()");
+ }
+ }
+
+ void semaphore_lock::lock() {
+ assert(sem);
+ if(locked)
+ return;
+ sem->on();
+ locked = true;
+ }
+ void semaphore_lock::unlock() {
+ if(!sem)
+ return;
+ if(!locked)
+ return;
+ sem->off();
+ locked=false;
+ }
+
+ string combine_path(const string& origin,const string& relative,int opts) {
+ string r = normalize_path(relative,0);
+ string rv;
+ // XXX: what to do if relative is empty is a question, really.
+ if(r.empty()) {
+ return normalize_path( (opts&origin_is_file)?dir_name(origin):origin ,strip_leading_slash|restrict_dotdot|strip_trailing_slash);
+ }else{
+ if(r[0]=='/') {
+ r.erase(0,1);
+ }else{
+ rv = normalize_path((opts&origin_is_file)?dir_name(origin):origin,restrict_dotdot|strip_trailing_slash);
+ }
+ }
+ string::size_type lsl = rv.rfind('/');
+ for(string::size_type sl=r.find('/');sl!=string::npos;sl=r.find('/')) {
+ assert(sl!=0);
+ if(sl==1 && r[0]=='.') {
+ // it's a "./"
+ r.erase(0,2);
+ }else if(sl==2 && r[0]=='.' && r[1]=='.') {
+ // we have a "../"
+ if(lsl==string::npos) {
+ if(rv.empty() && (opts&fail_beyond_root))
+ throw utility_beyond_root(CODEPOINT,"went beyond root while combining path");
+ rv.clear();
+ }else{
+ rv.erase(lsl);
+ lsl = rv.rfind('/');
+ }
+ r.erase(0,3);
+ }else{
+ // we have a "something/"
+ lsl = rv.length();
+ rv += '/';
+ rv += r.substr(0,sl);
+ r.erase(0,sl+1);
+ }
+ }
+ if(r.empty())
+ return rv+'/';
+ if(r.length()==2 && r[0]=='.' && r[0]=='.') {
+ if(lsl==string::npos) {
+ if(rv.empty() & (opts&fail_beyond_root))
+ throw utility_beyond_root(CODEPOINT,"went beyond root while combining path");
+ return "/";
+ }else{
+ rv.erase(lsl+1);
+ return rv;
+ }
+ }
+ rv += '/';
+ rv += r;
+ return rv;
+ }
+
+ void auto_chdir::pushdir(const string& td,bool ap) {
+ char *tmp = get_current_dir_name();
+ assert(tmp);
+ saved_pwd = tmp;
+ free(tmp);
+ autopop=ap;
+ if(chdir(td.c_str()))
+ throw konforka::exception(CODEPOINT,"failed to chdir()");
+ }
+ void auto_chdir::popdir() {
+ autopop=false;
+ if(chdir(saved_pwd.c_str()))
+ throw konforka::exception(CODEPOINT,"failed to chdir()");
+ // XXX: or should it be thrown? after all we call it from destructor...
+ }
+
+}