Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-lock-client.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * Authors: Michael Zucchi <notzed@ximian.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 <fcntl.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35
36 #include "camel-lock-client.h"
37 #include "camel-lock-helper.h"
38 #include "camel-object.h"
39
40 #define d(x)
41
42 /* dunno where this thing is got from */
43 /* see also camel-lock.c */
44 #define _(x) (x)
45
46 static GMutex lock_lock;
47 #define LOCK() g_mutex_lock(&lock_lock)
48 #define UNLOCK() g_mutex_unlock(&lock_lock)
49
50 static gint lock_sequence;
51 static gint lock_helper_pid = -1;
52 static gint lock_stdin_pipe[2], lock_stdout_pipe[2];
53
54 static gint read_n (gint fd, gpointer buffer, gint inlen)
55 {
56         gchar *p = buffer;
57         gint len, left = inlen;
58
59         do {
60                 len = read (fd, p, left);
61                 if (len == -1) {
62                         if (errno != EINTR)
63                                 return -1;
64                 } else {
65                         left -= len;
66                         p += len;
67                 }
68         } while (left > 0 && len != 0);
69
70         return inlen - left;
71 }
72
73 static gint write_n (gint fd, gpointer buffer, gint inlen)
74 {
75         gchar *p = buffer;
76         gint len, left = inlen;
77
78         do {
79                 len = write (fd, p, left);
80                 if (len == -1) {
81                         if (errno != EINTR)
82                                 return -1;
83                 } else {
84                         left -= len;
85                         p += len;
86                 }
87         } while (left > 0);
88
89         return inlen;
90 }
91
92 static gint
93 lock_helper_init (GError **error)
94 {
95         gint i;
96
97         lock_stdin_pipe[0] = -1;
98         lock_stdin_pipe[1] = -1;
99         lock_stdout_pipe[0] = -1;
100         lock_stdout_pipe[1] = -1;
101         if (pipe (lock_stdin_pipe) == -1
102             || pipe (lock_stdout_pipe) == -1) {
103                 g_set_error (
104                         error, G_IO_ERROR,
105                         g_io_error_from_errno (errno),
106                         _("Cannot build locking helper pipe: %s"),
107                         g_strerror (errno));
108                 if (lock_stdin_pipe[0] != -1)
109                         close (lock_stdin_pipe[0]);
110                 if (lock_stdin_pipe[1] != -1)
111                         close (lock_stdin_pipe[1]);
112                 if (lock_stdout_pipe[0] != -1)
113                         close (lock_stdout_pipe[0]);
114                 if (lock_stdout_pipe[1] != -1)
115                         close (lock_stdout_pipe[1]);
116
117                 return -1;
118         }
119
120         lock_helper_pid = fork ();
121         switch (lock_helper_pid) {
122         case -1:
123                 close (lock_stdin_pipe[0]);
124                 close (lock_stdin_pipe[1]);
125                 close (lock_stdout_pipe[0]);
126                 close (lock_stdout_pipe[1]);
127                 g_set_error (
128                         error, G_IO_ERROR,
129                         g_io_error_from_errno (errno),
130                         _("Cannot fork locking helper: %s"),
131                         g_strerror (errno));
132                 return -1;
133         case 0:
134                 close (STDIN_FILENO);
135                 dup (lock_stdin_pipe[0]);
136                 close (STDOUT_FILENO);
137                 dup (lock_stdout_pipe[1]);
138                 close (lock_stdin_pipe[0]);
139                 close (lock_stdin_pipe[1]);
140                 close (lock_stdout_pipe[0]);
141                 close (lock_stdout_pipe[1]);
142                 for (i = 3; i < 255; i++)
143                              close (i);
144                 execl (CAMEL_LIBEXECDIR "/camel-lock-helper-" API_VERSION, "camel-lock-helper", NULL);
145                 /* it'll pick this up when it tries to use us */
146                 exit (255);
147         default:
148                 close (lock_stdin_pipe[0]);
149                 close (lock_stdout_pipe[1]);
150
151                 /* so the child knows when we vanish */
152                 fcntl (lock_stdin_pipe[1], F_SETFD, FD_CLOEXEC);
153                 fcntl (lock_stdout_pipe[0], F_SETFD, FD_CLOEXEC);
154         }
155
156         return 0;
157 }
158
159 gint
160 camel_lock_helper_lock (const gchar *path,
161                         GError **error)
162 {
163         struct _CamelLockHelperMsg *msg;
164         gint len = strlen (path);
165         gint res = -1;
166         gint retry = 3;
167
168         LOCK ();
169
170         if (lock_helper_pid == -1) {
171                 if (lock_helper_init (error) == -1) {
172                         UNLOCK ();
173                         return -1;
174                 }
175         }
176
177         msg = alloca (len + sizeof (*msg));
178 again:
179         msg->magic = CAMEL_LOCK_HELPER_MAGIC;
180         msg->seq = lock_sequence;
181         msg->id = CAMEL_LOCK_HELPER_LOCK;
182         msg->data = len;
183         memcpy (msg + 1, path, len);
184
185         write_n (lock_stdin_pipe[1], msg, len + sizeof (*msg));
186
187         do {
188                 /* should also have a timeout here?  cancellation? */
189                 len = read_n (lock_stdout_pipe[0], msg, sizeof (*msg));
190                 if (len == 0) {
191                         /* child quit, do we try ressurect it? */
192                         res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL;
193                         /* if the child exited, this should get it, waidpid returns 0 if the child hasn't */
194                         if (waitpid (lock_helper_pid, NULL, WNOHANG) > 0) {
195                                 lock_helper_pid = -1;
196                                 close (lock_stdout_pipe[0]);
197                                 close (lock_stdin_pipe[1]);
198                                 lock_stdout_pipe[0] = -1;
199                                 lock_stdin_pipe[1] = -1;
200                         }
201                         goto fail;
202                 }
203
204                 if (msg->magic != CAMEL_LOCK_HELPER_RETURN_MAGIC
205                     || msg->seq > lock_sequence) {
206                         res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL;
207                         d (printf ("lock child protocol error\n"));
208                         g_set_error (
209                                 error, CAMEL_ERROR,
210                                 CAMEL_ERROR_GENERIC,
211                                 _("Could not lock '%s': protocol "
212                                 "error with lock-helper"), path);
213                         goto fail;
214                 }
215         } while (msg->seq < lock_sequence);
216
217         if (msg->seq == lock_sequence) {
218                 switch (msg->id) {
219                 case CAMEL_LOCK_HELPER_STATUS_OK:
220                         d (printf ("lock child locked ok, id is %d\n", msg->data));
221                         res = msg->data;
222                         break;
223                 default:
224                         g_set_error (
225                                 error, CAMEL_ERROR,
226                                 CAMEL_ERROR_GENERIC,
227                                 _("Could not lock '%s'"), path);
228                         d (printf ("locking failed ! status = %d\n", msg->id));
229                         break;
230                 }
231         } else if (retry > 0) {
232                 d (printf ("sequence failure, lost message? retry?\n"));
233                 retry--;
234                 goto again;
235         } else {
236                 g_set_error (
237                         error, CAMEL_ERROR,
238                         CAMEL_ERROR_GENERIC,
239                         _("Could not lock '%s': protocol "
240                         "error with lock-helper"), path);
241         }
242
243 fail:
244         lock_sequence++;
245
246         UNLOCK ();
247
248         return res;
249 }
250
251 gint camel_lock_helper_unlock (gint lockid)
252 {
253         struct _CamelLockHelperMsg *msg;
254         gint res = -1;
255         gint retry = 3;
256         gint len;
257
258         d (printf ("unlocking lock id %d\n", lockid));
259
260         LOCK ();
261
262         /* impossible to unlock if we haven't locked yet */
263         if (lock_helper_pid == -1) {
264                 UNLOCK ();
265                 return -1;
266         }
267
268         msg = alloca (sizeof (*msg));
269 again:
270         msg->magic = CAMEL_LOCK_HELPER_MAGIC;
271         msg->seq = lock_sequence;
272         msg->id = CAMEL_LOCK_HELPER_UNLOCK;
273         msg->data = lockid;
274
275         write_n (lock_stdin_pipe[1], msg, sizeof (*msg));
276
277         do {
278                 /* should also have a timeout here?  cancellation? */
279                 len = read_n (lock_stdout_pipe[0], msg, sizeof (*msg));
280                 if (len == 0) {
281                         /* child quit, do we try ressurect it? */
282                         res = CAMEL_LOCK_HELPER_STATUS_PROTOCOL;
283                         if (waitpid (lock_helper_pid, NULL, WNOHANG) > 0) {
284                                 lock_helper_pid = -1;
285                                 close (lock_stdout_pipe[0]);
286                                 close (lock_stdin_pipe[1]);
287                                 lock_stdout_pipe[0] = -1;
288                                 lock_stdin_pipe[1] = -1;
289                         }
290                         goto fail;
291                 }
292
293                 if (msg->magic != CAMEL_LOCK_HELPER_RETURN_MAGIC
294                     || msg->seq > lock_sequence) {
295                         goto fail;
296                 }
297         } while (msg->seq < lock_sequence);
298
299         if (msg->seq == lock_sequence) {
300                 switch (msg->id) {
301                 case CAMEL_LOCK_HELPER_STATUS_OK:
302                         d (printf ("lock child unlocked ok\n"));
303                         res = 0;
304                         break;
305                 default:
306                         d (printf ("locking failed !\n"));
307                         break;
308                 }
309         } else if (retry > 0) {
310                 d (printf ("sequence failure, lost message? retry?\n"));
311                 lock_sequence++;
312                 retry--;
313                 goto again;
314         }
315
316 fail:
317         lock_sequence++;
318
319         UNLOCK ();
320
321         return res;
322 }
323
324 #if 0
325 gint main (gint argc, gchar **argv)
326 {
327         gint id1, id2;
328
329         d (printf ("locking started\n"));
330         lock_helper_init ();
331
332         id1 = camel_lock_helper_lock ("1 path 1");
333         if (id1 != -1) {
334                 d (printf ("lock ok, unlock\n"));
335                 camel_lock_helper_unlock (id1);
336         }
337
338         id1 = camel_lock_helper_lock ("2 path 1");
339         id2 = camel_lock_helper_lock ("2 path 2");
340         camel_lock_helper_unlock (id2);
341         camel_lock_helper_unlock (id1);
342
343         id1 = camel_lock_helper_lock ("3 path 1");
344         id2 = camel_lock_helper_lock ("3 path 2");
345         camel_lock_helper_unlock (id1);
346 }
347 #endif