-rw-r--r-- | lib/sitecing_util.cc | 278 |
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... + } + +} |