1 /*
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 
24 struct 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 
34 struct fd_store {
35 	struct fd_store *next;
36 	int fd;
37 	int inuse;
38 };
39 
40 struct 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 
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 };
52 
53 #define MAX_SEND_FDS 2
54 
list_del_owner(struct owner * owner)55 static 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 
list_add_owner(struct owner * owner,struct owner * next)63 static 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  */
do_recv(int sock,void * buf,size_t len,int flags)78 static 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 
ulockmgr_send_message(int sock,void * buf,size_t buflen,int * fdp,int numfds)87 static 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 
ulockmgr_start_daemon(void)124 static 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 
ulockmgr_new_owner(const void * id,size_t id_len)146 static 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 
182 out_close:
183 	close(sv[1]);
184 out_free:
185 	free(o);
186 	return NULL;
187 }
188 
ulockmgr_send_request(struct message * msg,const void * id,size_t id_len)189 static 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
owner_hash(const unsigned char * id,size_t id_len)346 static 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 
ulockmgr_canonicalize(int fd,struct flock * lock)357 static int ulockmgr_canonicalize(int fd, struct flock *lock)
358 {
359 	off_t offset;
360 	if (lock->l_whence == SEEK_CUR) {
361 		offset = lseek(fd, 0, SEEK_CUR);
362 		if (offset == (off_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 
ulockmgr_op(int fd,int cmd,struct flock * lock,const void * owner,size_t owner_len)392 int 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_type != F_RDLCK && lock->l_type != F_WRLCK &&
404 	    lock->l_type != F_UNLCK)
405 		return -EINVAL;
406 
407 	if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
408 	    lock->l_whence != SEEK_END)
409 		return -EINVAL;
410 
411 #ifdef DEBUG
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));
415 #endif
416 
417 	/* Unlock should never block anyway */
418 	if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
419 		cmd = F_SETLK;
420 
421 	memset(&msg, 0, sizeof(struct message));
422 	msg.cmd = cmd;
423 	msg.fd = fd;
424 	msg.lock = *lock;
425 	err = ulockmgr_canonicalize(fd, &msg.lock);
426 	if (err)
427 		return err;
428 
429 	sigemptyset(&block);
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;
439 		else
440 			*lock = msg.lock;
441 	}
442 
443 	return err;
444 }
445