blob: 4a049d5b2c644e7d927455064d0946948275c607 [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/*
2 libulockmgr: Userspace Lock Manager Library
3 Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
7*/
8
9/* #define DEBUG 1 */
10
11#include "ulockmgr.h"
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16#include <pthread.h>
17#include <errno.h>
18#include <assert.h>
19#include <signal.h>
20#include <sys/stat.h>
21#include <sys/socket.h>
22#include <sys/wait.h>
23
24struct message {
25 unsigned intr : 1;
26 unsigned nofd : 1;
27 pthread_t thr;
28 int cmd;
29 int fd;
30 struct flock lock;
31 int error;
32};
33
34struct fd_store {
35 struct fd_store *next;
36 int fd;
37 int inuse;
38};
39
40struct owner {
41 struct owner *next;
42 struct owner *prev;
43 struct fd_store *fds;
44 void *id;
45 size_t id_len;
46 int cfd;
47};
48
49static pthread_mutex_t ulockmgr_lock;
50static int ulockmgr_cfd = -1;
51static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
52
53#define MAX_SEND_FDS 2
54
55static void list_del_owner(struct owner *owner)
56{
57 struct owner *prev = owner->prev;
58 struct owner *next = owner->next;
59 prev->next = next;
60 next->prev = prev;
61}
62
63static void list_add_owner(struct owner *owner, struct owner *next)
64{
65 struct owner *prev = next->prev;
66 owner->next = next;
67 owner->prev = prev;
68 prev->next = owner;
69 next->prev = owner;
70}
71
72/*
73 * There's a bug in the linux kernel (< 2.6.22) recv() implementation
74 * on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
75 * zero, even if data was available. Retrying the recv will return
76 * the data in this case.
77 */
78static int do_recv(int sock, void *buf, size_t len, int flags)
79{
80 int res = recv(sock, buf, len, flags);
81 if (res == 0)
82 res = recv(sock, buf, len, flags);
83
84 return res;
85}
86
87static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
88 int *fdp, int numfds)
89{
90 struct msghdr msg;
91 struct cmsghdr *p_cmsg;
92 struct iovec vec;
93 size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
94 int res;
95
96 assert(numfds <= MAX_SEND_FDS);
97 msg.msg_control = cmsgbuf;
98 msg.msg_controllen = sizeof(cmsgbuf);
99 p_cmsg = CMSG_FIRSTHDR(&msg);
100 p_cmsg->cmsg_level = SOL_SOCKET;
101 p_cmsg->cmsg_type = SCM_RIGHTS;
102 p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
103 memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
104 msg.msg_controllen = p_cmsg->cmsg_len;
105 msg.msg_name = NULL;
106 msg.msg_namelen = 0;
107 msg.msg_iov = &vec;
108 msg.msg_iovlen = 1;
109 msg.msg_flags = 0;
110 vec.iov_base = buf;
111 vec.iov_len = buflen;
112 res = sendmsg(sock, &msg, MSG_NOSIGNAL);
113 if (res == -1) {
114 perror("libulockmgr: sendmsg");
115 return -1;
116 }
117 if ((size_t) res != buflen) {
118 fprintf(stderr, "libulockmgr: sendmsg short\n");
119 return -1;
120 }
121 return 0;
122}
123
124static int ulockmgr_start_daemon(void)
125{
126 int sv[2];
127 int res;
128 char tmp[64];
129
130 res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
131 if (res == -1) {
132 perror("libulockmgr: socketpair");
133 return -1;
134 }
135 snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
136 res = system(tmp);
137 close(sv[0]);
138 if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
139 close(sv[1]);
140 return -1;
141 }
142 ulockmgr_cfd = sv[1];
143 return 0;
144}
145
146static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
147{
148 int sv[2];
149 int res;
150 char c = 'm';
151 struct owner *o;
152
153 if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
154 return NULL;
155
156 o = calloc(1, sizeof(struct owner) + id_len);
157 if (!o) {
158 fprintf(stderr, "libulockmgr: failed to allocate memory\n");
159 return NULL;
160 }
161 o->id = o + 1;
162 o->id_len = id_len;
163 res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
164 if (res == -1) {
165 perror("libulockmgr: socketpair");
166 goto out_free;
167 }
168 res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
169 close(sv[0]);
170 if (res == -1) {
171 close(ulockmgr_cfd);
172 ulockmgr_cfd = -1;
173 goto out_close;
174 }
175
176 o->cfd = sv[1];
177 memcpy(o->id, id, id_len);
178 list_add_owner(o, &owner_list);
179
180 return o;
181
182out_close:
183 close(sv[1]);
184out_free:
185 free(o);
186 return NULL;
187}
188
189static int ulockmgr_send_request(struct message *msg, const void *id,
190 size_t id_len)
191{
192 int sv[2];
193 int cfd;
194 struct owner *o;
195 struct fd_store *f = NULL;
196 struct fd_store *newf = NULL;
197 struct fd_store **fp;
198 int fd = msg->fd;
199 int cmd = msg->cmd;
200 int res;
201 int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
202 msg->lock.l_start == 0 && msg->lock.l_len == 0);
203
204 for (o = owner_list.next; o != &owner_list; o = o->next)
205 if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
206 break;
207
208 if (o == &owner_list)
209 o = NULL;
210
211 if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
212 o = ulockmgr_new_owner(id, id_len);
213
214 if (!o) {
215 if (cmd == F_GETLK) {
216 res = fcntl(msg->fd, F_GETLK, &msg->lock);
217 return (res == -1) ? -errno : 0;
218 } else if (msg->lock.l_type == F_UNLCK)
219 return 0;
220 else
221 return -ENOLCK;
222 }
223
224 if (unlockall)
225 msg->nofd = 1;
226 else {
227 for (fp = &o->fds; *fp; fp = &(*fp)->next) {
228 f = *fp;
229 if (f->fd == fd) {
230 msg->nofd = 1;
231 break;
232 }
233 }
234 }
235
236 if (!msg->nofd) {
237 newf = f = calloc(1, sizeof(struct fd_store));
238 if (!f) {
239 fprintf(stderr, "libulockmgr: failed to allocate memory\n");
240 return -ENOLCK;
241 }
242 }
243
244 res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
245 if (res == -1) {
246 perror("libulockmgr: socketpair");
247 free(newf);
248 return -ENOLCK;
249 }
250
251 cfd = sv[1];
252 sv[1] = msg->fd;
253 res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv,
254 msg->nofd ? 1 : 2);
255 close(sv[0]);
256 if (res == -1) {
257 free(newf);
258 close(cfd);
259 return -EIO;
260 }
261
262 if (newf) {
263 newf->fd = msg->fd;
264 newf->next = o->fds;
265 o->fds = newf;
266 }
267 if (f)
268 f->inuse++;
269
270 res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
271 if (res == -1) {
272 perror("libulockmgr: recv");
273 msg->error = EIO;
274 } else if (res != sizeof(struct message)) {
275 fprintf(stderr, "libulockmgr: recv short\n");
276 msg->error = EIO;
277 } else if (cmd == F_SETLKW && msg->error == EAGAIN) {
278 pthread_mutex_unlock(&ulockmgr_lock);
279 while (1) {
280 sigset_t old;
281 sigset_t unblock;
282 int errno_save;
283
284 sigemptyset(&unblock);
285 sigaddset(&unblock, SIGUSR1);
286 pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
287 res = do_recv(cfd, msg, sizeof(struct message),
288 MSG_WAITALL);
289 errno_save = errno;
290 pthread_sigmask(SIG_SETMASK, &old, NULL);
291 if (res == sizeof(struct message))
292 break;
293 else if (res >= 0) {
294 fprintf(stderr, "libulockmgr: recv short\n");
295 msg->error = EIO;
296 break;
297 } else if (errno_save != EINTR) {
298 errno = errno_save;
299 perror("libulockmgr: recv");
300 msg->error = EIO;
301 break;
302 }
303 msg->intr = 1;
304 res = send(o->cfd, msg, sizeof(struct message),
305 MSG_NOSIGNAL);
306 if (res == -1) {
307 perror("libulockmgr: send");
308 msg->error = EIO;
309 break;
310 }
311 if (res != sizeof(struct message)) {
312 fprintf(stderr, "libulockmgr: send short\n");
313 msg->error = EIO;
314 break;
315 }
316 }
317 pthread_mutex_lock(&ulockmgr_lock);
318
319 }
320 if (f)
321 f->inuse--;
322 close(cfd);
323 if (unlockall) {
324 for (fp = &o->fds; *fp;) {
325 f = *fp;
326 if (f->fd == fd && !f->inuse) {
327 *fp = f->next;
328 free(f);
329 } else
330 fp = &f->next;
331 }
332 if (!o->fds) {
333 list_del_owner(o);
334 close(o->cfd);
335 free(o);
336 }
337 /* Force OK on unlock-all, since it _will_ succeed once the
338 owner is deleted */
339 msg->error = 0;
340 }
341
342 return -msg->error;
343}
344
345#ifdef DEBUG
346static uint32_t owner_hash(const unsigned char *id, size_t id_len)
347{
348 uint32_t h = 0;
349 size_t i;
350 for (i = 0; i < id_len; i++)
351 h = ((h << 8) | (h >> 24)) ^ id[i];
352
353 return h;
354}
355#endif
356
357static int ulockmgr_canonicalize(int fd, struct flock *lock)
358{
359 off64_t offset;
360 if (lock->l_whence == SEEK_CUR) {
361 offset = lseek(fd, 0, SEEK_CUR);
362 if (offset == (off64_t) -1)
363 return -errno;
364 } else if (lock->l_whence == SEEK_END) {
365 struct stat stbuf;
366 int res = fstat(fd, &stbuf);
367 if (res == -1)
368 return -errno;
369
370 offset = stbuf.st_size;
371 } else
372 offset = 0;
373
374 lock->l_whence = SEEK_SET;
375 lock->l_start += offset;
376
377 if (lock->l_start < 0)
378 return -EINVAL;
379
380 if (lock->l_len < 0) {
381 lock->l_start += lock->l_len;
382 if (lock->l_start < 0)
383 return -EINVAL;
384 lock->l_len = -lock->l_len;
385 }
386 if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
387 return -EINVAL;
388
389 return 0;
390}
391
392int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
393 size_t owner_len)
394{
395 int err;
396 struct message msg;
397 sigset_t old;
398 sigset_t block;
399
400 if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
401 return -EINVAL;
402
403 if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
404 lock->l_whence != SEEK_END)
405 return -EINVAL;
406
407#ifdef DEBUG
408 fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
409 cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
410 owner_hash(owner, owner_len));
411#endif
412
413 /* Unlock should never block anyway */
414 if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
415 cmd = F_SETLK;
416
417 memset(&msg, 0, sizeof(struct message));
418 msg.cmd = cmd;
419 msg.fd = fd;
420 msg.lock = *lock;
421 err = ulockmgr_canonicalize(fd, &msg.lock);
422 if (err)
423 return err;
424
425 sigemptyset(&block);
426 sigaddset(&block, SIGUSR1);
427 pthread_sigmask(SIG_BLOCK, &block, &old);
428 pthread_mutex_lock(&ulockmgr_lock);
429 err = ulockmgr_send_request(&msg, owner, owner_len);
430 pthread_mutex_unlock(&ulockmgr_lock);
431 pthread_sigmask(SIG_SETMASK, &old, NULL);
432 if (!err && cmd == F_GETLK) {
433 if (msg.lock.l_type == F_UNLCK)
434 lock->l_type = F_UNLCK;
435 else
436 *lock = msg.lock;
437 }
438
439 return err;
440}