-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 @@ | |||
1 | #ifdef USE_PCH | ||
2 | #include "pch.h" | ||
3 | #else | ||
4 | #include <sys/stat.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <unistd.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <sys/ipc.h> | ||
9 | #include <sys/sem.h> | ||
10 | #include <errno.h> | ||
11 | #include <iostream> | ||
12 | #include <fstream> | ||
13 | #include <cassert> | ||
14 | #include "sitecing/sitecing_util.h" | ||
15 | #endif | ||
16 | |||
17 | namespace sitecing { | ||
18 | |||
19 | /* | ||
20 | * XXX: all of these utilities could be sheerly optimized. | ||
21 | */ | ||
22 | |||
23 | string normalize_path(const string& path,int opts) { | ||
24 | const char *s = path.c_str(); | ||
25 | string rv; | ||
26 | string::size_type notslash = 0; | ||
27 | if( (*s)=='.' && s[1]=='/' ) | ||
28 | s+=2; | ||
29 | if(opts&strip_leading_slash) | ||
30 | for(;(*s) && (*s)=='/';s++); | ||
31 | for(;*s;s++) { | ||
32 | if( (*s)=='/' ) { | ||
33 | if(s[1]=='/') | ||
34 | continue; | ||
35 | if(s[1]=='.' && s[2]=='/') { | ||
36 | s+=2; | ||
37 | continue; | ||
38 | } | ||
39 | } | ||
40 | if(opts&restrict_dotdot) { | ||
41 | if( | ||
42 | ( rv.empty() && s[0]=='.' && s[1]=='.' && s[2]=='/' )// "^../" | ||
43 | || ( s[0]=='/' && s[1]=='.' && s[2]=='.' && (s[3]==0 || s[3]=='/') ) // "/..(/|$)" | ||
44 | ) | ||
45 | throw utility_restricted_sequence(CODEPOINT,"restricted updir sequence encountered"); | ||
46 | } | ||
47 | rv += *s; | ||
48 | if( (*s) != '/' ) | ||
49 | notslash=rv.length(); | ||
50 | } | ||
51 | if(!(opts&strip_trailing_slash)) | ||
52 | notslash++; | ||
53 | if(notslash<rv.length()) | ||
54 | rv.erase(notslash); // XXX: check the logic of stripping/not strippling trailing slash | ||
55 | return rv; | ||
56 | } | ||
57 | |||
58 | string strip_prefix(const string& str,const string& prefix) { | ||
59 | if(str.compare(0,prefix.length(),prefix)) | ||
60 | throw utility_no_prefix(CODEPOINT,"no such prefix"); | ||
61 | return str.substr(prefix.length()); | ||
62 | } | ||
63 | |||
64 | string strip_suffix(const string& str,const string& suffix) { | ||
65 | if(str.compare(str.length()-suffix.length(),suffix.length(),suffix)) | ||
66 | throw utility_no_suffix(CODEPOINT,"no such suffix"); | ||
67 | return str.substr(0,str.length()-suffix.length()); | ||
68 | } | ||
69 | |||
70 | string dir_name(const string& filename) { | ||
71 | string::size_type sl = filename.find_last_of('/'); | ||
72 | if(sl==string::npos) | ||
73 | return ""; // no slashes -- no dir. | ||
74 | string::size_type nosl = filename.find_last_not_of('/',sl); | ||
75 | if(nosl==string::npos) | ||
76 | return ""; // only slashes -- no dir. XXX: only slashes after the last slash... does it mean no dir? | ||
77 | return filename.substr(0,nosl+1); | ||
78 | } | ||
79 | |||
80 | void make_path(const string& path,mode_t mode) { | ||
81 | struct stat st; | ||
82 | for(string::size_type sl=0;sl!=string::npos;sl=path.find('/',sl+1)) { | ||
83 | if(!sl) | ||
84 | continue; | ||
85 | string p = path.substr(0,sl); | ||
86 | if(stat(p.c_str(),&st) || !S_ISDIR(st.st_mode)) { | ||
87 | if(mkdir(p.c_str(),mode)) | ||
88 | throw konforka::exception(CODEPOINT,"failed to mkdir()"); | ||
89 | } | ||
90 | } | ||
91 | if(stat(path.c_str(),&st) || !S_ISDIR(st.st_mode)) { | ||
92 | if(mkdir(path.c_str(),mode)) | ||
93 | throw konforka::exception(CODEPOINT,"failed to mkdir()"); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | void file_lock::lock(const string& f) { | ||
98 | unlock(); | ||
99 | fd = open(f.c_str(),O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); | ||
100 | if(fd<0) | ||
101 | throw konforka::exception(CODEPOINT,"failed to open/create lockfile"); | ||
102 | try { | ||
103 | lock(); | ||
104 | }catch(konforka::exception& ke) { | ||
105 | ke.see(CODEPOINT); | ||
106 | close(fd); fd=-1; | ||
107 | throw; | ||
108 | }catch(...) { | ||
109 | close(fd); fd=-1; | ||
110 | throw; | ||
111 | } | ||
112 | } | ||
113 | void file_lock::lock() { | ||
114 | assert(fd>=0); | ||
115 | struct flock fl; | ||
116 | fl.l_type = F_WRLCK; | ||
117 | fl.l_whence=SEEK_SET; | ||
118 | fl.l_start=fl.l_len=0; | ||
119 | for(int tries=3;tries;tries--) { | ||
120 | if(!fcntl(fd,F_SETLK,&fl)) | ||
121 | return; | ||
122 | sleep(8); | ||
123 | } | ||
124 | throw konforka::exception(CODEPOINT,"failed to obtain file lock"); | ||
125 | } | ||
126 | void file_lock::unlock() { | ||
127 | if(fd<0) | ||
128 | return; | ||
129 | struct flock fl; | ||
130 | fl.l_type = F_UNLCK; | ||
131 | fl.l_whence=SEEK_SET; | ||
132 | fl.l_start=fl.l_len=0; | ||
133 | int rv = fcntl(fd,F_SETLK,&fl); | ||
134 | close(fd); | ||
135 | fd=-1; | ||
136 | if(rv) | ||
137 | throw konforka::exception(CODEPOINT,"failed to release file lock"); | ||
138 | } | ||
139 | |||
140 | void pid_file::set(const string& f,bool u) { | ||
141 | ofstream of(f.c_str(),ios::trunc); | ||
142 | if(!of) | ||
143 | throw konforka::exception(CODEPOINT,"failed to open file for writing pid"); | ||
144 | of << getpid() << endl; | ||
145 | of.close(); | ||
146 | file_name = f; | ||
147 | unlink_pid = u; | ||
148 | } | ||
149 | void pid_file::unlink() { | ||
150 | if(!unlink_pid) | ||
151 | return; | ||
152 | ::unlink(file_name.c_str()); | ||
153 | } | ||
154 | |||
155 | void semaphore::init() { | ||
156 | deinit(); | ||
157 | semid = semget(IPC_PRIVATE,1,IPC_CREAT|0600); | ||
158 | if(semid<0) | ||
159 | throw konforka::exception(CODEPOINT,"failed to semget()"); | ||
160 | if(semctl(semid,0,SETVAL,1)) | ||
161 | throw konforka::exception(CODEPOINT,"failed to semctl()"); | ||
162 | } | ||
163 | void semaphore::deinit() { | ||
164 | if(semid<0) | ||
165 | return; | ||
166 | semctl(semid,0,IPC_RMID,0); | ||
167 | } | ||
168 | void semaphore::on() { | ||
169 | assert(semid>=0); | ||
170 | struct sembuf sb; | ||
171 | sb.sem_num=0; | ||
172 | sb.sem_op=-1; | ||
173 | sb.sem_flg = SEM_UNDO; | ||
174 | while(semop(semid,&sb,1)<0) { | ||
175 | if(errno!=EINTR) | ||
176 | throw konforka::exception(CODEPOINT,"failed to semop()"); | ||
177 | } | ||
178 | } | ||
179 | void semaphore::off() { | ||
180 | assert(semid>=0); | ||
181 | struct sembuf sb; | ||
182 | sb.sem_num=0; | ||
183 | sb.sem_op=1; | ||
184 | sb.sem_flg = SEM_UNDO; | ||
185 | while(semop(semid,&sb,1)<0) { | ||
186 | if(errno!=EINTR) | ||
187 | throw konforka::exception(CODEPOINT,"failed to semop()"); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | void semaphore_lock::lock() { | ||
192 | assert(sem); | ||
193 | if(locked) | ||
194 | return; | ||
195 | sem->on(); | ||
196 | locked = true; | ||
197 | } | ||
198 | void semaphore_lock::unlock() { | ||
199 | if(!sem) | ||
200 | return; | ||
201 | if(!locked) | ||
202 | return; | ||
203 | sem->off(); | ||
204 | locked=false; | ||
205 | } | ||
206 | |||
207 | string combine_path(const string& origin,const string& relative,int opts) { | ||
208 | string r = normalize_path(relative,0); | ||
209 | string rv; | ||
210 | // XXX: what to do if relative is empty is a question, really. | ||
211 | if(r.empty()) { | ||
212 | return normalize_path( (opts&origin_is_file)?dir_name(origin):origin ,strip_leading_slash|restrict_dotdot|strip_trailing_slash); | ||
213 | }else{ | ||
214 | if(r[0]=='/') { | ||
215 | r.erase(0,1); | ||
216 | }else{ | ||
217 | rv = normalize_path((opts&origin_is_file)?dir_name(origin):origin,restrict_dotdot|strip_trailing_slash); | ||
218 | } | ||
219 | } | ||
220 | string::size_type lsl = rv.rfind('/'); | ||
221 | for(string::size_type sl=r.find('/');sl!=string::npos;sl=r.find('/')) { | ||
222 | assert(sl!=0); | ||
223 | if(sl==1 && r[0]=='.') { | ||
224 | // it's a "./" | ||
225 | r.erase(0,2); | ||
226 | }else if(sl==2 && r[0]=='.' && r[1]=='.') { | ||
227 | // we have a "../" | ||
228 | if(lsl==string::npos) { | ||
229 | if(rv.empty() && (opts&fail_beyond_root)) | ||
230 | throw utility_beyond_root(CODEPOINT,"went beyond root while combining path"); | ||
231 | rv.clear(); | ||
232 | }else{ | ||
233 | rv.erase(lsl); | ||
234 | lsl = rv.rfind('/'); | ||
235 | } | ||
236 | r.erase(0,3); | ||
237 | }else{ | ||
238 | // we have a "something/" | ||
239 | lsl = rv.length(); | ||
240 | rv += '/'; | ||
241 | rv += r.substr(0,sl); | ||
242 | r.erase(0,sl+1); | ||
243 | } | ||
244 | } | ||
245 | if(r.empty()) | ||
246 | return rv+'/'; | ||
247 | if(r.length()==2 && r[0]=='.' && r[0]=='.') { | ||
248 | if(lsl==string::npos) { | ||
249 | if(rv.empty() & (opts&fail_beyond_root)) | ||
250 | throw utility_beyond_root(CODEPOINT,"went beyond root while combining path"); | ||
251 | return "/"; | ||
252 | }else{ | ||
253 | rv.erase(lsl+1); | ||
254 | return rv; | ||
255 | } | ||
256 | } | ||
257 | rv += '/'; | ||
258 | rv += r; | ||
259 | return rv; | ||
260 | } | ||
261 | |||
262 | void auto_chdir::pushdir(const string& td,bool ap) { | ||
263 | char *tmp = get_current_dir_name(); | ||
264 | assert(tmp); | ||
265 | saved_pwd = tmp; | ||
266 | free(tmp); | ||
267 | autopop=ap; | ||
268 | if(chdir(td.c_str())) | ||
269 | throw konforka::exception(CODEPOINT,"failed to chdir()"); | ||
270 | } | ||
271 | void auto_chdir::popdir() { | ||
272 | autopop=false; | ||
273 | if(chdir(saved_pwd.c_str())) | ||
274 | throw konforka::exception(CODEPOINT,"failed to chdir()"); | ||
275 | // XXX: or should it be thrown? after all we call it from destructor... | ||
276 | } | ||
277 | |||
278 | } | ||