2 libulockmgr: Userspace Lock Manager Library
3 Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
22 #include <sys/socket.h>
36 struct fd_store
*next
;
50 static pthread_mutex_t ulockmgr_lock
;
51 static int ulockmgr_cfd
= -1;
52 static struct owner owner_list
= { .next
= &owner_list
, .prev
= &owner_list
};
54 #define MAX_SEND_FDS 2
56 static void list_del_owner(struct owner
*owner
)
58 struct owner
*prev
= owner
->prev
;
59 struct owner
*next
= owner
->next
;
64 static void list_add_owner(struct owner
*owner
, struct owner
*next
)
66 struct owner
*prev
= next
->prev
;
74 * There's a bug in the linux kernel (< 2.6.22) recv() implementation
75 * on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
76 * zero, even if data was available. Retrying the recv will return
77 * the data in this case.
79 static int do_recv(int sock
, void *buf
, size_t len
, int flags
)
81 int res
= recv(sock
, buf
, len
, flags
);
83 res
= recv(sock
, buf
, len
, flags
);
88 static int ulockmgr_send_message(int sock
, void *buf
, size_t buflen
,
92 struct cmsghdr
*p_cmsg
;
94 size_t cmsgbuf
[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS
) / sizeof(size_t)];
97 assert(numfds
<= MAX_SEND_FDS
);
98 msg
.msg_control
= cmsgbuf
;
99 msg
.msg_controllen
= sizeof(cmsgbuf
);
100 p_cmsg
= CMSG_FIRSTHDR(&msg
);
101 p_cmsg
->cmsg_level
= SOL_SOCKET
;
102 p_cmsg
->cmsg_type
= SCM_RIGHTS
;
103 p_cmsg
->cmsg_len
= CMSG_LEN(sizeof(int) * numfds
);
104 memcpy(CMSG_DATA(p_cmsg
), fdp
, sizeof(int) * numfds
);
105 msg
.msg_controllen
= p_cmsg
->cmsg_len
;
112 vec
.iov_len
= buflen
;
113 res
= sendmsg(sock
, &msg
, MSG_NOSIGNAL
);
115 perror("libulockmgr: sendmsg");
118 if ((size_t) res
!= buflen
) {
119 fprintf(stderr
, "libulockmgr: sendmsg short\n");
125 static int ulockmgr_start_daemon(void)
131 res
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sv
);
133 perror("libulockmgr: socketpair");
136 snprintf(tmp
, sizeof(tmp
), "exec ulockmgr_server %i", sv
[0]);
139 if (res
== -1 || !WIFEXITED(res
) || WEXITSTATUS(res
) != 0) {
143 ulockmgr_cfd
= sv
[1];
147 static struct owner
*ulockmgr_new_owner(const void *id
, size_t id_len
)
154 if (ulockmgr_cfd
== -1 && ulockmgr_start_daemon() == -1)
157 o
= calloc(1, sizeof(struct owner
) + id_len
);
159 fprintf(stderr
, "libulockmgr: failed to allocate memory\n");
164 res
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sv
);
166 perror("libulockmgr: socketpair");
169 res
= ulockmgr_send_message(ulockmgr_cfd
, &c
, sizeof(c
), &sv
[0], 1);
178 memcpy(o
->id
, id
, id_len
);
179 list_add_owner(o
, &owner_list
);
190 static int ulockmgr_send_request(struct message
*msg
, const void *id
,
196 struct fd_store
*f
= NULL
;
197 struct fd_store
*newf
= NULL
;
198 struct fd_store
**fp
;
202 int unlockall
= (cmd
== F_SETLK
&& msg
->lock
.l_type
== F_UNLCK
&&
203 msg
->lock
.l_start
== 0 && msg
->lock
.l_len
== 0);
205 for (o
= owner_list
.next
; o
!= &owner_list
; o
= o
->next
)
206 if (o
->id_len
== id_len
&& memcmp(o
->id
, id
, id_len
) == 0)
209 if (o
== &owner_list
)
212 if (!o
&& cmd
!= F_GETLK
&& msg
->lock
.l_type
!= F_UNLCK
)
213 o
= ulockmgr_new_owner(id
, id_len
);
216 if (cmd
== F_GETLK
) {
217 res
= fcntl(msg
->fd
, F_GETLK
, &msg
->lock
);
218 return (res
== -1) ? -errno
: 0;
219 } else if (msg
->lock
.l_type
== F_UNLCK
)
228 for (fp
= &o
->fds
; *fp
; fp
= &(*fp
)->next
) {
238 newf
= f
= calloc(1, sizeof(struct fd_store
));
240 fprintf(stderr
, "libulockmgr: failed to allocate memory\n");
245 res
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sv
);
247 perror("libulockmgr: socketpair");
254 res
= ulockmgr_send_message(o
->cfd
, msg
, sizeof(struct message
), sv
,
271 res
= do_recv(cfd
, msg
, sizeof(struct message
), MSG_WAITALL
);
273 perror("libulockmgr: recv");
275 } else if (res
!= sizeof(struct message
)) {
276 fprintf(stderr
, "libulockmgr: recv short\n");
278 } else if (cmd
== F_SETLKW
&& msg
->error
== EAGAIN
) {
279 pthread_mutex_unlock(&ulockmgr_lock
);
285 sigemptyset(&unblock
);
286 sigaddset(&unblock
, SIGUSR1
);
287 pthread_sigmask(SIG_UNBLOCK
, &unblock
, &old
);
288 res
= do_recv(cfd
, msg
, sizeof(struct message
),
291 pthread_sigmask(SIG_SETMASK
, &old
, NULL
);
292 if (res
== sizeof(struct message
))
295 fprintf(stderr
, "libulockmgr: recv short\n");
298 } else if (errno_save
!= EINTR
) {
300 perror("libulockmgr: recv");
305 res
= send(o
->cfd
, msg
, sizeof(struct message
),
308 perror("libulockmgr: send");
312 if (res
!= sizeof(struct message
)) {
313 fprintf(stderr
, "libulockmgr: send short\n");
318 pthread_mutex_lock(&ulockmgr_lock
);
325 for (fp
= &o
->fds
; *fp
;) {
327 if (f
->fd
== fd
&& !f
->inuse
) {
338 /* Force OK on unlock-all, since it _will_ succeed once the
347 static uint32_t owner_hash(const unsigned char *id
, size_t id_len
)
351 for (i
= 0; i
< id_len
; i
++)
352 h
= ((h
<< 8) | (h
>> 24)) ^ id
[i
];
358 static int ulockmgr_canonicalize(int fd
, struct flock
*lock
)
361 if (lock
->l_whence
== SEEK_CUR
) {
362 offset
= lseek(fd
, 0, SEEK_CUR
);
363 if (offset
== (off_t
) -1)
365 } else if (lock
->l_whence
== SEEK_END
) {
367 int res
= fstat(fd
, &stbuf
);
371 offset
= stbuf
.st_size
;
375 lock
->l_whence
= SEEK_SET
;
376 lock
->l_start
+= offset
;
378 if (lock
->l_start
< 0)
381 if (lock
->l_len
< 0) {
382 lock
->l_start
+= lock
->l_len
;
383 if (lock
->l_start
< 0)
385 lock
->l_len
= -lock
->l_len
;
387 if (lock
->l_len
&& lock
->l_start
+ lock
->l_len
- 1 < 0)
393 int ulockmgr_op(int fd
, int cmd
, struct flock
*lock
, const void *owner
,
401 if (cmd
!= F_GETLK
&& cmd
!= F_SETLK
&& cmd
!= F_SETLKW
)
404 if (lock
->l_type
!= F_RDLCK
&& lock
->l_type
!= F_WRLCK
&&
405 lock
->l_type
!= F_UNLCK
)
408 if (lock
->l_whence
!= SEEK_SET
&& lock
->l_whence
!= SEEK_CUR
&&
409 lock
->l_whence
!= SEEK_END
)
413 fprintf(stderr
, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
414 cmd
, lock
->l_type
, lock
->l_whence
, lock
->l_start
, lock
->l_len
,
415 owner_hash(owner
, owner_len
));
418 /* Unlock should never block anyway */
419 if (cmd
== F_SETLKW
&& lock
->l_type
== F_UNLCK
)
422 memset(&msg
, 0, sizeof(struct message
));
426 err
= ulockmgr_canonicalize(fd
, &msg
.lock
);
431 sigaddset(&block
, SIGUSR1
);
432 pthread_sigmask(SIG_BLOCK
, &block
, &old
);
433 pthread_mutex_lock(&ulockmgr_lock
);
434 err
= ulockmgr_send_request(&msg
, owner
, owner_len
);
435 pthread_mutex_unlock(&ulockmgr_lock
);
436 pthread_sigmask(SIG_SETMASK
, &old
, NULL
);
437 if (!err
&& cmd
== F_GETLK
) {
438 if (msg
.lock
.l_type
== F_UNLCK
)
439 lock
->l_type
= F_UNLCK
;