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