Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-lock.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Author: Michael Zucchi <notzed@ximian.com>
4  *
5  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19  * USA
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <sys/stat.h>
33
34 #ifdef USE_DOT
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #endif
39
40 #ifdef USE_FCNTL
41 #include <fcntl.h>
42 #include <unistd.h>
43 #endif
44
45 #ifdef USE_FLOCK
46 #include <sys/file.h>
47 #endif
48
49 #include <gio/gio.h>
50 #include <glib/gi18n-lib.h>
51 #include <glib/gstdio.h>
52
53 #ifdef G_OS_WIN32
54 #include <windows.h>
55 #endif
56
57 #include "camel-lock.h"
58
59 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
60
61 /**
62  * camel_lock_dot:
63  * @path:
64  * @error: return location for a #GError, or %NULL
65  *
66  * Create an exclusive lock using .lock semantics.
67  * All locks are equivalent to write locks (exclusive).
68  *
69  * Returns: -1 on error, sets @ex appropriately.
70  **/
71 gint
72 camel_lock_dot (const gchar *path,
73                 GError **error)
74 {
75 #ifdef USE_DOT
76         gchar *locktmp, *lock;
77         gint retry = 0;
78         gint fdtmp;
79         struct stat st;
80
81         /* TODO: Is there a reliable way to refresh the lock, if we're still busy with it?
82          * Does it matter?  We will normally also use fcntl too ... */
83
84         /* use alloca, save cleaning up afterwards */
85         lock = alloca (strlen (path) + strlen (".lock") + 1);
86         sprintf (lock, "%s.lock", path);
87         locktmp = alloca (strlen (path) + strlen ("XXXXXX") + 1);
88
89         while (retry < CAMEL_LOCK_DOT_RETRY) {
90
91                 d (printf ("trying to lock '%s', attempt %d\n", lock, retry));
92
93                 if (retry > 0)
94                         sleep (CAMEL_LOCK_DOT_DELAY);
95
96                 sprintf (locktmp, "%sXXXXXX", path);
97                 fdtmp = g_mkstemp (locktmp);
98                 if (fdtmp == -1) {
99                         g_set_error (
100                                 error, G_IO_ERROR,
101                                 g_io_error_from_errno (errno),
102                                 _("Could not create lock file for %s: %s"),
103                                 path, g_strerror (errno));
104                         return -1;
105                 }
106                 close (fdtmp);
107
108                 /* apparently return code from link can be unreliable for nfs (see link(2)), so we ignore it */
109                 link (locktmp, lock);
110
111                 /* but we check stat instead (again, see link(2)) */
112                 if (g_stat (locktmp, &st) == -1) {
113                         d (printf ("Our lock file %s vanished!?\n", locktmp));
114
115                         /* well that was unexpected, try cleanup/retry */
116                         unlink (locktmp);
117                         unlink (lock);
118                 } else {
119                         d (printf ("tmp lock created, link count is %d\n", st.st_nlink));
120
121                         unlink (locktmp);
122
123                         /* if we had 2 links, we have created the .lock, return ok, otherwise we need to keep trying */
124                         if (st.st_nlink == 2)
125                                 return 0;
126                 }
127
128                 /* check for stale lock, kill it */
129                 if (g_stat (lock, &st) == 0) {
130                         time_t now = time (NULL);
131                         (printf ("There is an existing lock %ld seconds old\n", now - st.st_ctime));
132                         if (st.st_ctime < now - CAMEL_LOCK_DOT_STALE) {
133                                 d (printf ("Removing it now\n"));
134                                 unlink (lock);
135                         }
136                 }
137
138                 retry++;
139         }
140
141         d (printf ("failed to get lock after %d retries\n", retry));
142
143         g_set_error (
144                 error, G_IO_ERROR, G_IO_ERROR_FAILED,
145                 _("Timed out trying to get lock file on %s.  "
146                 "Try again later."), path);
147         return -1;
148 #else /* !USE_DOT */
149         return 0;
150 #endif
151 }
152
153 /**
154  * camel_unlock_dot:
155  * @path:
156  *
157  * Attempt to unlock a .lock lock.
158  **/
159 void
160 camel_unlock_dot (const gchar *path)
161 {
162 #ifdef USE_DOT
163         gchar *lock;
164
165         lock = alloca (strlen (path) + strlen (".lock") + 1);
166         sprintf (lock, "%s.lock", path);
167         d (printf ("unlocking %s\n", lock));
168         (void) unlink (lock);
169 #endif
170 }
171
172 /**
173  * camel_lock_fcntl:
174  * @fd:
175  * @type:
176  * @error: return location for a #GError, or %NULL
177  *
178  * Create a lock using fcntl(2).
179  *
180  * @type is CAMEL_LOCK_WRITE or CAMEL_LOCK_READ,
181  * to create exclusive or shared read locks
182  *
183  * Returns: -1 on error.
184  **/
185 gint
186 camel_lock_fcntl (gint fd,
187                   CamelLockType type,
188                   GError **error)
189 {
190 #ifdef USE_FCNTL
191         struct flock lock;
192
193         d (printf ("fcntl locking %d\n", fd));
194
195         memset (&lock, 0, sizeof (lock));
196         lock.l_type = type == CAMEL_LOCK_READ ? F_RDLCK : F_WRLCK;
197         if (fcntl (fd, F_SETLK, &lock) == -1) {
198                 /* If we get a 'locking not vailable' type error,
199                  * we assume the filesystem doesn't support fcntl () locking */
200                 /* this is somewhat system-dependent */
201                 if (errno != EINVAL && errno != ENOLCK) {
202                         g_set_error (
203                                 error, G_IO_ERROR,
204                                 g_io_error_from_errno (errno),
205                                 _("Failed to get lock using fcntl(2): %s"),
206                                 g_strerror (errno));
207                         return -1;
208                 } else {
209                         static gint failed = 0;
210
211                         if (failed == 0)
212                                 fprintf (stderr, "fcntl(2) locking appears not to work on this filesystem");
213                         failed++;
214                 }
215         }
216 #endif
217         return 0;
218 }
219
220 /**
221  * camel_unlock_fcntl:
222  * @fd:
223  *
224  * Unlock an fcntl lock.
225  **/
226 void
227 camel_unlock_fcntl (gint fd)
228 {
229 #ifdef USE_FCNTL
230         struct flock lock;
231
232         d (printf ("fcntl unlocking %d\n", fd));
233
234         memset (&lock, 0, sizeof (lock));
235         lock.l_type = F_UNLCK;
236         fcntl (fd, F_SETLK, &lock);
237 #endif
238 }
239
240 /**
241  * camel_lock_flock:
242  * @fd:
243  * @type:
244  * @error: return location for a #GError, or %NULL
245  *
246  * Create a lock using flock(2).
247  *
248  * @type is CAMEL_LOCK_WRITE or CAMEL_LOCK_READ,
249  * to create exclusive or shared read locks
250  *
251  * Returns: -1 on error.
252  **/
253 gint
254 camel_lock_flock (gint fd,
255                   CamelLockType type,
256                   GError **error)
257 {
258 #ifdef USE_FLOCK
259         gint op;
260
261         d (printf ("flock locking %d\n", fd));
262
263         if (type == CAMEL_LOCK_READ)
264                 op = LOCK_SH | LOCK_NB;
265         else
266                 op = LOCK_EX | LOCK_NB;
267
268         if (flock (fd, op) == -1) {
269                 g_set_error (
270                         error, G_IO_ERROR,
271                         g_io_error_from_errno (errno),
272                         _("Failed to get lock using flock(2): %s"),
273                         g_strerror (errno));
274                 return -1;
275         }
276 #endif
277         return 0;
278 }
279
280 /**
281  * camel_unlock_flock:
282  * @fd:
283  *
284  * Unlock an flock lock.
285  **/
286 void
287 camel_unlock_flock (gint fd)
288 {
289 #ifdef USE_FLOCK
290         d (printf ("flock unlocking %d\n", fd));
291
292         (void) flock (fd, LOCK_UN);
293 #endif
294 }
295
296 /**
297  * camel_lock_folder:
298  * @path: Path to the file to lock (used for .locking only).
299  * @fd: Open file descriptor of the right type to lock.
300  * @type: Type of lock, CAMEL_LOCK_READ or CAMEL_LOCK_WRITE.
301  * @error: return location for a #GError, or %NULL
302  *
303  * Attempt to lock a folder, multiple attempts will be made using all
304  * locking strategies available.
305  *
306  * Returns: -1 on error, @ex will describe the locking system that failed.
307  **/
308 gint
309 camel_lock_folder (const gchar *path,
310                    gint fd,
311                    CamelLockType type,
312                    GError **error)
313 {
314         gint retry = 0;
315
316         while (retry < CAMEL_LOCK_RETRY) {
317                 if (retry > 0)
318                         g_usleep (CAMEL_LOCK_DELAY * 1000000);
319
320                 if (camel_lock_fcntl (fd, type, error) == 0) {
321                         if (camel_lock_flock (fd, type, error) == 0) {
322                                 if (camel_lock_dot (path, error) == 0)
323                                         return 0;
324                                 camel_unlock_flock (fd);
325                         }
326                         camel_unlock_fcntl (fd);
327                 }
328                 retry++;
329         }
330
331         return -1;
332 }
333
334 /**
335  * camel_unlock_folder:
336  * @path: Filename of folder.
337  * @fd: Open descrptor on which locks were placed.
338  *
339  * Free a lock on a folder.
340  **/
341 void
342 camel_unlock_folder (const gchar *path,
343                      gint fd)
344 {
345         camel_unlock_dot (path);
346         camel_unlock_flock (fd);
347         camel_unlock_fcntl (fd);
348 }
349
350 #if 0
351 gint
352 main (gint argc,
353       gchar **argv)
354 {
355         GError *error = NULL;
356         gint fd1, fd2;
357
358 #if 0
359         if (camel_lock_dot ("mylock", &error) == 0) {
360                 if (camel_lock_dot ("mylock", &error) == 0) {
361                         printf ("Got lock twice?\n");
362                 } else {
363                         printf ("failed to get lock 2: %s\n", error->message);
364                 }
365                 camel_unlock_dot ("mylock");
366         } else {
367                 printf ("failed to get lock 1: %s\n", error->message);
368         }
369
370         if (error != NULL)
371                 g_clear_error (&error);
372 #endif
373
374         fd1 = open ("mylock", O_RDWR);
375         if (fd1 == -1) {
376                 printf ("Could not open lock file (mylock): %s", g_strerror (errno));
377                 return 1;
378         }
379         fd2 = open ("mylock", O_RDWR);
380         if (fd2 == -1) {
381                 printf ("Could not open lock file (mylock): %s", g_strerror (errno));
382                 close (fd1);
383                 return 1;
384         }
385
386         if (camel_lock_fcntl (fd1, CAMEL_LOCK_WRITE, &error) == 0) {
387                 printf ("got fcntl write lock once\n");
388                 g_usleep (5000000);
389                 if (camel_lock_fcntl (fd2, CAMEL_LOCK_WRITE, &error) == 0) {
390                         printf ("got fcntl write lock twice!\n");
391                 } else {
392                         printf ("failed to get write lock: %s\n", error->message);
393                 }
394
395                 if (error != NULL)
396                         g_clear_error (&error);
397
398                 if (camel_lock_fcntl (fd2, CAMEL_LOCK_READ, &error) == 0) {
399                         printf ("got fcntl read lock as well?\n");
400                         camel_unlock_fcntl (fd2);
401                 } else {
402                         printf ("failed to get read lock: %s\n", error->message);
403                 }
404
405                 if (error != NULL)
406                         g_clear_error (&error);
407                 camel_unlock_fcntl (fd1);
408         } else {
409                 printf ("failed to get write lock at all: %s\n", error->message);
410         }
411
412         if (camel_lock_fcntl (fd1, CAMEL_LOCK_READ, &error) == 0) {
413                 printf ("got fcntl read lock once\n");
414                 g_usleep (5000000);
415                 if (camel_lock_fcntl (fd2, CAMEL_LOCK_WRITE, &error) == 0) {
416                         printf ("got fcntl write lock too?!\n");
417                 } else {
418                         printf ("failed to get write lock: %s\n", error->message);
419                 }
420
421                 if (error != NULL)
422                         g_clear_error (&error);
423
424                 if (camel_lock_fcntl (fd2, CAMEL_LOCK_READ, &error) == 0) {
425                         printf ("got fcntl read lock twice\n");
426                         camel_unlock_fcntl (fd2);
427                 } else {
428                         printf ("failed to get read lock: %s\n", error->message);
429                 }
430
431                 if (error != NULL)
432                         g_clear_error (&error);
433                 camel_unlock_fcntl (fd1);
434         }
435
436         close (fd1);
437         close (fd2);
438
439         return 0;
440 }
441 #endif