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
21 #include <sys/socket.h>
35 struct fd_store
*next
;
49 static pthread_mutex_t ulockmgr_lock
;
50 static int ulockmgr_cfd
= -1;
51 static struct owner owner_list
= { .next
= &owner_list
, .prev
= &owner_list
};
53 #define MAX_SEND_FDS 2
55 static void list_del_owner(struct owner
*owner
)
57 struct owner
*prev
= owner
->prev
;
58 struct owner
*next
= owner
->next
;
63 static void list_add_owner(struct owner
*owner
, struct owner
*next
)
65 struct owner
*prev
= next
->prev
;
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.
78 static int do_recv(int sock
, void *buf
, size_t len
, int flags
)
80 int res
= recv(sock
, buf
, len
, flags
);
82 res
= recv(sock
, buf
, len
, flags
);
87 static int ulockmgr_send_message(int sock
, void *buf
, size_t buflen
,
91 struct cmsghdr
*p_cmsg
;
93 size_t cmsgbuf
[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS
) / sizeof(size_t)];
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
;
111 vec
.iov_len
= buflen
;
112 res
= sendmsg(sock
, &msg
, MSG_NOSIGNAL
);
114 perror("libulockmgr: sendmsg");
117 if ((size_t) res
!= buflen
) {
118 fprintf(stderr
, "libulockmgr: sendmsg short\n");
124 static int ulockmgr_start_daemon(void)
130 res
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sv
);
132 perror("libulockmgr: socketpair");
135 snprintf(tmp
, sizeof(tmp
), "exec ulockmgr_server %i", sv
[0]);
138 if (res
== -1 || !WIFEXITED(res
) || WEXITSTATUS(res
) != 0) {
142 ulockmgr_cfd
= sv
[1];
146 static struct owner
*ulockmgr_new_owner(const void *id
, size_t id_len
)
153 if (ulockmgr_cfd
== -1 && ulockmgr_start_daemon() == -1)
156 o
= calloc(1, sizeof(struct owner
) + id_len
);
158 fprintf(stderr
, "libulockmgr: failed to allocate memory\n");
163 res
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sv
);
165 perror("libulockmgr: socketpair");
168 res
= ulockmgr_send_message(ulockmgr_cfd
, &c
, sizeof(c
), &sv
[0], 1);
177 memcpy(o
->id
, id
, id_len
);
178 list_add_owner(o
, &owner_list
);
189 static int ulockmgr_send_request(struct message
*msg
, const void *id
,
195 struct fd_store
*f
= NULL
;
196 struct fd_store
*newf
= NULL
;
197 struct fd_store
**fp
;
201 int unlockall
= (cmd
== F_SETLK
&& msg
->lock
.l_type
== F_UNLCK
&&
202 msg
->lock
.l_start
== 0 && msg
->lock
.l_len
== 0);
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)
208 if (o
== &owner_list
)
211 if (!o
&& cmd
!= F_GETLK
&& msg
->lock
.l_type
!= F_UNLCK
)
212 o
= ulockmgr_new_owner(id
, id_len
);
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
)
227 for (fp
= &o
->fds
; *fp
; fp
= &(*fp
)->next
) {
237 newf
= f
= calloc(1, sizeof(struct fd_store
));
239 fprintf(stderr
, "libulockmgr: failed to allocate memory\n");
244 res
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sv
);
246 perror("libulockmgr: socketpair");
253 res
= ulockmgr_send_message(o
->cfd
, msg
, sizeof(struct message
), sv
,
270 res
= do_recv(cfd
, msg
, sizeof(struct message
), MSG_WAITALL
);
272 perror("libulockmgr: recv");
274 } else if (res
!= sizeof(struct message
)) {
275 fprintf(stderr
, "libulockmgr: recv short\n");
277 } else if (cmd
== F_SETLKW
&& msg
->error
== EAGAIN
) {
278 pthread_mutex_unlock(&ulockmgr_lock
);
284 sigemptyset(&unblock
);
285 sigaddset(&unblock
, SIGUSR1
);
286 pthread_sigmask(SIG_UNBLOCK
, &unblock
, &old
);
287 res
= do_recv(cfd
, msg
, sizeof(struct message
),
290 pthread_sigmask(SIG_SETMASK
, &old
, NULL
);
291 if (res
== sizeof(struct message
))
294 fprintf(stderr
, "libulockmgr: recv short\n");
297 } else if (errno_save
!= EINTR
) {
299 perror("libulockmgr: recv");
304 res
= send(o
->cfd
, msg
, sizeof(struct message
),
307 perror("libulockmgr: send");
311 if (res
!= sizeof(struct message
)) {
312 fprintf(stderr
, "libulockmgr: send short\n");
317 pthread_mutex_lock(&ulockmgr_lock
);
324 for (fp
= &o
->fds
; *fp
;) {
326 if (f
->fd
== fd
&& !f
->inuse
) {
337 /* Force OK on unlock-all, since it _will_ succeed once the
346 static uint32_t owner_hash(const unsigned char *id
, size_t id_len
)
350 for (i
= 0; i
< id_len
; i
++)
351 h
= ((h
<< 8) | (h
>> 24)) ^ id
[i
];
357 static int ulockmgr_canonicalize(int fd
, struct flock
*lock
)
360 if (lock
->l_whence
== SEEK_CUR
) {
361 offset
= lseek(fd
, 0, SEEK_CUR
);
362 if (offset
== (off_t
) -1)
364 } else if (lock
->l_whence
== SEEK_END
) {
366 int res
= fstat(fd
, &stbuf
);
370 offset
= stbuf
.st_size
;
374 lock
->l_whence
= SEEK_SET
;
375 lock
->l_start
+= offset
;
377 if (lock
->l_start
< 0)
380 if (lock
->l_len
< 0) {
381 lock
->l_start
+= lock
->l_len
;
382 if (lock
->l_start
< 0)
384 lock
->l_len
= -lock
->l_len
;
386 if (lock
->l_len
&& lock
->l_start
+ lock
->l_len
- 1 < 0)
392 int ulockmgr_op(int fd
, int cmd
, struct flock
*lock
, const void *owner
,
400 if (cmd
!= F_GETLK
&& cmd
!= F_SETLK
&& cmd
!= F_SETLKW
)
403 if (lock
->l_type
!= F_RDLCK
&& lock
->l_type
!= F_WRLCK
&&
404 lock
->l_type
!= F_UNLCK
)
407 if (lock
->l_whence
!= SEEK_SET
&& lock
->l_whence
!= SEEK_CUR
&&
408 lock
->l_whence
!= SEEK_END
)
412 fprintf(stderr
, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
413 cmd
, lock
->l_type
, lock
->l_whence
, lock
->l_start
, lock
->l_len
,
414 owner_hash(owner
, owner_len
));
417 /* Unlock should never block anyway */
418 if (cmd
== F_SETLKW
&& lock
->l_type
== F_UNLCK
)
421 memset(&msg
, 0, sizeof(struct message
));
425 err
= ulockmgr_canonicalize(fd
, &msg
.lock
);
430 sigaddset(&block
, SIGUSR1
);
431 pthread_sigmask(SIG_BLOCK
, &block
, &old
);
432 pthread_mutex_lock(&ulockmgr_lock
);
433 err
= ulockmgr_send_request(&msg
, owner
, owner_len
);
434 pthread_mutex_unlock(&ulockmgr_lock
);
435 pthread_sigmask(SIG_SETMASK
, &old
, NULL
);
436 if (!err
&& cmd
== F_GETLK
) {
437 if (msg
.lock
.l_type
== F_UNLCK
)
438 lock
->l_type
= F_UNLCK
;