1 /***************************************************************************
2 $RCSfile$
3 -------------------
4 cvs : $Id$
5 begin : Sun Nov 23 2003
6 copyright : (C) 2003 by Martin Preuss
7 email : martin@libchipcard.de
8
9 ***************************************************************************
10 * *
11 * This library is free software; you can redistribute it and/or *
12 * modify it under the terms of the GNU Lesser General Public *
13 * License as published by the Free Software Foundation; either *
14 * version 2.1 of the License, or (at your option) any later version. *
15 * *
16 * This library is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
19 * Lesser General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU Lesser General Public *
22 * License along with this library; if not, write to the Free Software *
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
24 * MA 02111-1307 USA *
25 * *
26 ***************************************************************************/
27
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33
34 #include "fslock_p.h"
35 #include "i18n_l.h"
36 #include <gwenhywfar/debug.h>
37 #include <gwenhywfar/inetsocket.h> /* for select */
38 #include <gwenhywfar/gui.h>
39 #include <gwenhywfar/gwentime.h>
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44
45 #ifdef HAVE_UNISTD_H
46 # include <unistd.h>
47 #endif
48 #include <errno.h>
49 #include <string.h>
50
51 #include <windows.h>
52
53 #undef HAVE_LINK
54 /* Win32/Mingw does not have link(2), so we have to keep this
55 undefined for now. */
56
57
58
GWEN_LIST_FUNCTIONS(GWEN_FSLOCK,GWEN_FSLock)59 GWEN_LIST_FUNCTIONS(GWEN_FSLOCK, GWEN_FSLock)
60 GWEN_LIST2_FUNCTIONS(GWEN_FSLOCK, GWEN_FSLock)
61
62
63
64 GWEN_FSLOCK *GWEN_FSLock_new(const char *fname, GWEN_FSLOCK_TYPE t)
65 {
66 GWEN_FSLOCK *fl;
67 GWEN_BUFFER *nbuf;
68 const char *s;
69
70 assert(fname);
71 GWEN_NEW_OBJECT(GWEN_FSLOCK, fl);
72 GWEN_LIST_INIT(GWEN_FSLOCK, fl);
73 fl->usage=1;
74 fl->entryName=strdup(fname);
75
76 switch (t) {
77 case GWEN_FSLock_TypeFile:
78 s=".lck";
79 break;
80 case GWEN_FSLock_TypeDir:
81 s="/.dir.lck";
82 break;
83 default:
84 DBG_ERROR(GWEN_LOGDOMAIN, "Unknown log type %d", t);
85 abort();
86 } /* switch */
87
88 nbuf=GWEN_Buffer_new(0, 256, 0, 1);
89 GWEN_Buffer_AppendString(nbuf, fname);
90 GWEN_Buffer_AppendString(nbuf, s);
91 fl->baseLockFilename=strdup(GWEN_Buffer_GetStart(nbuf));
92
93 if (GWEN_FSLock__UnifyLockFileName(nbuf)) {
94 DBG_ERROR(GWEN_LOGDOMAIN, "Could not create unique lockfile name");
95 GWEN_Buffer_free(nbuf);
96 abort();
97 }
98 fl->uniqueLockFilename=strdup(GWEN_Buffer_GetStart(nbuf));
99 GWEN_Buffer_free(nbuf);
100
101 return fl;
102 }
103
104
105
GWEN_FSLock_free(GWEN_FSLOCK * fl)106 void GWEN_FSLock_free(GWEN_FSLOCK *fl)
107 {
108 if (fl) {
109 assert(fl->usage);
110 if (fl->usage==1) {
111 if (fl->lockCount) {
112 DBG_WARN(GWEN_LOGDOMAIN,
113 "File \"%s\" still locked", fl->entryName);
114 }
115 free(fl->entryName);
116 free(fl->baseLockFilename);
117 free(fl->uniqueLockFilename);
118 GWEN_LIST_FINI(GWEN_FSLOCK, fl);
119 fl->usage=0;
120 GWEN_FREE_OBJECT(fl);
121 }
122 else {
123 fl->usage--;
124 }
125 }
126 }
127
128
129
GWEN_FSLock_Attach(GWEN_FSLOCK * fl)130 void GWEN_FSLock_Attach(GWEN_FSLOCK *fl)
131 {
132 assert(fl);
133 assert(fl->usage);
134 fl->usage++;
135 }
136
137
138
GWEN_FSLock__Lock(GWEN_FSLOCK * fl)139 GWEN_FSLOCK_RESULT GWEN_FSLock__Lock(GWEN_FSLOCK *fl)
140 {
141 assert(fl);
142
143 if (fl->lockCount==0) {
144 int fd;
145 #ifdef HAVE_LINK
146 int linkCount;
147 #endif
148 struct stat st;
149
150 fd=open(fl->uniqueLockFilename, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
151 if (fd==-1) {
152 DBG_DEBUG(GWEN_LOGDOMAIN,
153 "open(%s): %s",
154 fl->uniqueLockFilename,
155 strerror(errno));
156 return GWEN_FSLock_ResultError;
157 }
158 close(fd);
159
160 /* get the link count of the new unique file for later comparison */
161 if (stat(fl->uniqueLockFilename, &st)) {
162 DBG_ERROR(GWEN_LOGDOMAIN, "stat(%s): %s",
163 fl->uniqueLockFilename, strerror(errno));
164 remove(fl->uniqueLockFilename);
165 return GWEN_FSLock_ResultError;
166 }
167
168 #ifdef HAVE_LINK
169 linkCount=(int)(st.st_nlink);
170
171 /* create a hard link to the new unique file with the name of the
172 * real lock file. This is guaranteed to be atomic even on NFS */
173 if (link(fl->uniqueLockFilename, fl->baseLockFilename)) {
174 /* Nonzero returned, i.e. some error occurred */
175 int lnerr;
176
177 lnerr=errno;
178
179 DBG_INFO(GWEN_LOGDOMAIN, "link(%s, %s): %s",
180 fl->uniqueLockFilename,
181 fl->baseLockFilename,
182 strerror(errno));
183 if (lnerr==EPERM)
184 #endif /* HAVE_LINK */
185 {
186 int fd;
187
188 /* link() is not supported on the destination filesystem, try it the
189 * traditional way. This should be ok, since the only FS which does
190 * not handle the O_EXCL flag properly is NFS, and NFS would not
191 * return EPERM (because it generally supports link()).
192 * So for NFS file systems we would not reach this point.
193 */
194 fd=open(fl->baseLockFilename,
195 O_CREAT | O_EXCL | O_TRUNC | O_RDWR,
196 S_IRUSR | S_IWUSR);
197 if (fd==-1) {
198 DBG_INFO(GWEN_LOGDOMAIN, "FS-Lock to %s already in use",
199 fl->entryName);
200 remove(fl->uniqueLockFilename);
201 return GWEN_FSLock_ResultBusy;
202 }
203 close(fd);
204 }
205 #ifdef HAVE_LINK
206 else {
207 /* link() generally is supported on the destination file system,
208 * check whether the link count of the unique file has been
209 * incremented */
210 if (stat(fl->uniqueLockFilename, &st)) {
211 DBG_ERROR(GWEN_LOGDOMAIN, "stat(%s): %s",
212 fl->uniqueLockFilename, strerror(errno));
213 remove(fl->uniqueLockFilename);
214 return GWEN_FSLock_ResultError;
215 }
216 if ((int)(st.st_nlink)!=linkCount+1) {
217 DBG_INFO(GWEN_LOGDOMAIN, "FS-Lock to %s already in use",
218 fl->entryName);
219 remove(fl->uniqueLockFilename);
220 return GWEN_FSLock_ResultBusy;
221 }
222 }
223 } /* if error on link */
224 #endif /* HAVE_LINK */
225
226 DBG_INFO(GWEN_LOGDOMAIN, "FS-Lock applied to %s", fl->entryName);
227 }
228 fl->lockCount++;
229 return GWEN_FSLock_ResultOk;
230 }
231
232
233
GWEN_FSLock_Unlock(GWEN_FSLOCK * fl)234 GWEN_FSLOCK_RESULT GWEN_FSLock_Unlock(GWEN_FSLOCK *fl)
235 {
236 assert(fl);
237
238 if (fl->lockCount<1) {
239 DBG_ERROR(GWEN_LOGDOMAIN,
240 "Entry \"%s\" not locked", fl->entryName);
241 return GWEN_FSLock_ResultNoLock;
242 }
243 fl->lockCount--;
244 if (fl->lockCount<1) {
245 remove(fl->baseLockFilename);
246 remove(fl->uniqueLockFilename);
247 DBG_INFO(GWEN_LOGDOMAIN, "FS-Lock released from %s", fl->entryName);
248 }
249 return GWEN_FSLock_ResultOk;
250 }
251
252
253
GWEN_FSLock_Lock(GWEN_FSLOCK * fl,int timeout,uint32_t gid)254 GWEN_FSLOCK_RESULT GWEN_FSLock_Lock(GWEN_FSLOCK *fl, int timeout, uint32_t gid)
255 {
256 GWEN_TIME *t0;
257 int distance;
258 int count;
259 GWEN_FSLOCK_RESULT rv;
260 uint32_t progressId;
261
262 t0=GWEN_CurrentTime();
263 assert(t0);
264
265 progressId=GWEN_Gui_ProgressStart(GWEN_GUI_PROGRESS_DELAY |
266 GWEN_GUI_PROGRESS_ALLOW_EMBED |
267 GWEN_GUI_PROGRESS_SHOW_PROGRESS |
268 GWEN_GUI_PROGRESS_SHOW_ABORT,
269 I18N("Accquiring lock"),
270 NULL,
271 (timeout==GWEN_TIMEOUT_FOREVER)
272 ?0:timeout, gid);
273
274 if (timeout==GWEN_TIMEOUT_NONE)
275 distance=GWEN_TIMEOUT_NONE;
276 else if (timeout==GWEN_TIMEOUT_FOREVER)
277 distance=GWEN_TIMEOUT_FOREVER;
278 else {
279 distance=GWEN_GUI_CHECK_PERIOD;
280 if (distance>timeout)
281 distance=timeout;
282 }
283
284 for (count=0;; count++) {
285 int err;
286
287 err=GWEN_Gui_ProgressAdvance(progressId, GWEN_GUI_PROGRESS_NONE);
288 if (err==GWEN_ERROR_USER_ABORTED) {
289 DBG_ERROR(GWEN_LOGDOMAIN, "User aborted.");
290 GWEN_Gui_ProgressEnd(progressId);
291 return GWEN_FSLock_ResultUserAbort;
292 }
293
294 rv=GWEN_FSLock__Lock(fl);
295 if (rv==GWEN_FSLock_ResultError) {
296 DBG_INFO(GWEN_LOGDOMAIN, "here");
297 GWEN_Time_free(t0);
298 GWEN_Gui_ProgressEnd(progressId);
299 return rv;
300 }
301 else if (rv==GWEN_FSLock_ResultOk) {
302 GWEN_Time_free(t0);
303 GWEN_Gui_ProgressEnd(progressId);
304 return rv;
305 }
306 else {
307 /* check timeout */
308 if (timeout!=GWEN_TIMEOUT_FOREVER) {
309 GWEN_TIME *t1;
310 double d;
311
312 if (timeout==GWEN_TIMEOUT_NONE) {
313 GWEN_Gui_ProgressEnd(progressId);
314 return GWEN_FSLock_ResultTimeout;
315 }
316 t1=GWEN_CurrentTime();
317 assert(t1);
318 d=GWEN_Time_Diff(t1, t0);
319 GWEN_Time_free(t1);
320
321 if (d>=timeout) {
322 DBG_DEBUG(GWEN_LOGDOMAIN,
323 "Could not lock within %d milliseconds, giving up",
324 timeout);
325 GWEN_Time_free(t0);
326 GWEN_Gui_ProgressEnd(progressId);
327 return GWEN_FSLock_ResultTimeout;
328 }
329 err=GWEN_Gui_ProgressAdvance(progressId, (uint64_t)d);
330 if (err) {
331 DBG_ERROR(GWEN_LOGDOMAIN, "User aborted.");
332 GWEN_Gui_ProgressEnd(progressId);
333 return GWEN_FSLock_ResultUserAbort;
334 }
335 }
336 /* sleep for the distance of the WaitCallback */
337 GWEN_Socket_Select(0, 0, 0, distance);
338 }
339 } /* for */
340 GWEN_Gui_ProgressEnd(progressId);
341
342 DBG_WARN(GWEN_LOGDOMAIN, "We should never reach this point");
343 GWEN_Time_free(t0);
344 return GWEN_FSLock_ResultError;
345
346
347
348
349
350 }
351
352
353
GWEN_FSLock__UnifyLockFileName(GWEN_BUFFER * nbuf)354 int GWEN_FSLock__UnifyLockFileName(GWEN_BUFFER *nbuf)
355 {
356 char buffer[256];
357
358 GWEN_Buffer_AppendString(nbuf, ".");
359
360 buffer[0]=0;
361 if (gethostname(buffer, sizeof(buffer)-1)) {
362 DBG_ERROR(GWEN_LOGDOMAIN, "gethostname: %s", strerror(errno));
363 return -1;
364 }
365 buffer[sizeof(buffer)-1]=0;
366 GWEN_Buffer_AppendString(nbuf, buffer);
367 GWEN_Buffer_AppendString(nbuf, "-");
368
369 buffer[0]=0;
370 snprintf(buffer, sizeof(buffer)-1, "%i", getpid());
371 buffer[sizeof(buffer)-1]=0;
372 GWEN_Buffer_AppendString(nbuf, buffer);
373
374 return 0;
375 }
376
377
378
GWEN_FSLock_GetName(const GWEN_FSLOCK * fl)379 const char *GWEN_FSLock_GetName(const GWEN_FSLOCK *fl)
380 {
381 assert(fl);
382 assert(fl->usage);
383 return fl->entryName;
384 }
385
386
387
388
389
390