libfuse: remove "-D_FILE_OFFSET_BITS=64" from fuse.pc
[fuse.git] / util / ulockmgr_server.c
blob09972ce2f135db811521213f3c35980cc76d2f4c
1 /*
2 ulockmgr_server: Userspace Lock Manager Server
3 Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7 */
9 /* #define DEBUG 1 */
10 #include <config.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <dirent.h>
18 #include <pthread.h>
19 #include <stdint.h>
20 #include <errno.h>
21 #include <assert.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/wait.h>
26 struct message {
27 unsigned intr : 1;
28 unsigned nofd : 1;
29 pthread_t thr;
30 int cmd;
31 int fd;
32 struct flock lock;
33 int error;
36 struct fd_store {
37 struct fd_store *next;
38 int fd;
39 int origfd;
40 int inuse;
43 struct owner {
44 struct fd_store *fds;
45 pthread_mutex_t lock;
48 struct req_data {
49 struct owner *o;
50 int cfd;
51 struct fd_store *f;
52 struct message msg;
55 #define MAX_SEND_FDS 2
57 static int receive_message(int sock, void *buf, size_t buflen, int *fdp,
58 int *numfds)
60 struct msghdr msg;
61 struct iovec iov;
62 size_t ccmsg[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
63 struct cmsghdr *cmsg;
64 int res;
65 int i;
67 assert(*numfds <= MAX_SEND_FDS);
68 iov.iov_base = buf;
69 iov.iov_len = buflen;
71 memset(&msg, 0, sizeof(msg));
72 memset(ccmsg, -1, sizeof(ccmsg));
73 msg.msg_iov = &iov;
74 msg.msg_iovlen = 1;
75 msg.msg_control = ccmsg;
76 msg.msg_controllen = sizeof(ccmsg);
78 res = recvmsg(sock, &msg, MSG_WAITALL);
79 if (!res) {
80 /* retry on zero return, see do_recv() in ulockmgr.c */
81 res = recvmsg(sock, &msg, MSG_WAITALL);
82 if (!res)
83 return 0;
85 if (res == -1) {
86 perror("ulockmgr_server: recvmsg");
87 return -1;
89 if ((size_t) res != buflen) {
90 fprintf(stderr, "ulockmgr_server: short message received\n");
91 return -1;
94 cmsg = CMSG_FIRSTHDR(&msg);
95 if (cmsg) {
96 if (!cmsg->cmsg_type == SCM_RIGHTS) {
97 fprintf(stderr,
98 "ulockmgr_server: unknown control message %d\n",
99 cmsg->cmsg_type);
100 return -1;
102 memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
103 if (msg.msg_flags & MSG_CTRUNC) {
104 fprintf(stderr,
105 "ulockmgr_server: control message truncated\n");
106 for (i = 0; i < *numfds; i++)
107 close(fdp[i]);
108 *numfds = 0;
110 } else {
111 if (msg.msg_flags & MSG_CTRUNC) {
112 fprintf(stderr,
113 "ulockmgr_server: control message truncated(*)\n");
115 /* There's a bug in the Linux kernel, that if
116 not all file descriptors were allocated,
117 then the cmsg header is not filled in */
118 cmsg = (struct cmsghdr *) ccmsg;
119 memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
120 for (i = 0; i < *numfds; i++)
121 close(fdp[i]);
123 *numfds = 0;
125 return res;
128 static int closefrom(int minfd)
130 DIR *dir = opendir("/proc/self/fd");
131 if (dir) {
132 int dfd = dirfd(dir);
133 struct dirent *ent;
134 while ((ent = readdir(dir))) {
135 char *end;
136 int fd = strtol(ent->d_name, &end, 10);
137 if (ent->d_name[0] && !end[0] && fd >= minfd &&
138 fd != dfd)
139 close(fd);
141 closedir(dir);
143 return 0;
146 static void send_reply(int cfd, struct message *msg)
148 int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
149 if (res == -1)
150 perror("ulockmgr_server: sending reply");
151 #ifdef DEBUG
152 fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error);
153 #endif
156 static void *process_request(void *d_)
158 struct req_data *d = d_;
159 int res;
161 assert(d->msg.cmd == F_SETLKW);
162 res = fcntl(d->f->fd, F_SETLK, &d->msg.lock);
163 if (res == -1 && errno == EAGAIN) {
164 d->msg.error = EAGAIN;
165 d->msg.thr = pthread_self();
166 send_reply(d->cfd, &d->msg);
167 res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock);
169 d->msg.error = (res == -1) ? errno : 0;
170 pthread_mutex_lock(&d->o->lock);
171 d->f->inuse--;
172 pthread_mutex_unlock(&d->o->lock);
173 send_reply(d->cfd, &d->msg);
174 close(d->cfd);
175 free(d);
177 return NULL;
180 static void process_message(struct owner *o, struct message *msg, int cfd,
181 int fd)
183 struct fd_store *f = NULL;
184 struct fd_store *newf = NULL;
185 struct fd_store **fp;
186 struct req_data *d;
187 pthread_t tid;
188 int res;
190 #ifdef DEBUG
191 fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n",
192 msg->cmd, msg->lock.l_type, msg->lock.l_whence,
193 msg->lock.l_start, msg->lock.l_len);
194 #endif
196 if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
197 msg->lock.l_start == 0 && msg->lock.l_len == 0) {
198 for (fp = &o->fds; *fp;) {
199 f = *fp;
200 if (f->origfd == msg->fd && !f->inuse) {
201 close(f->fd);
202 *fp = f->next;
203 free(f);
204 } else
205 fp = &f->next;
207 if (!msg->nofd)
208 close(fd);
210 msg->error = 0;
211 send_reply(cfd, msg);
212 close(cfd);
213 return;
216 if (msg->nofd) {
217 for (fp = &o->fds; *fp; fp = &(*fp)->next) {
218 f = *fp;
219 if (f->origfd == msg->fd)
220 break;
222 if (!*fp) {
223 fprintf(stderr, "ulockmgr_server: fd %i not found\n",
224 msg->fd);
225 msg->error = EIO;
226 send_reply(cfd, msg);
227 close(cfd);
228 return;
230 } else {
231 newf = f = malloc(sizeof(struct fd_store));
232 if (!f) {
233 msg->error = ENOLCK;
234 send_reply(cfd, msg);
235 close(cfd);
236 return;
239 f->fd = fd;
240 f->origfd = msg->fd;
241 f->inuse = 0;
244 if (msg->cmd == F_GETLK || msg->cmd == F_SETLK ||
245 msg->lock.l_type == F_UNLCK) {
246 res = fcntl(f->fd, msg->cmd, &msg->lock);
247 msg->error = (res == -1) ? errno : 0;
248 send_reply(cfd, msg);
249 close(cfd);
250 if (newf) {
251 newf->next = o->fds;
252 o->fds = newf;
254 return;
257 d = malloc(sizeof(struct req_data));
258 if (!d) {
259 msg->error = ENOLCK;
260 send_reply(cfd, msg);
261 close(cfd);
262 free(newf);
263 return;
266 f->inuse++;
267 d->o = o;
268 d->cfd = cfd;
269 d->f = f;
270 d->msg = *msg;
271 res = pthread_create(&tid, NULL, process_request, d);
272 if (res) {
273 msg->error = ENOLCK;
274 send_reply(cfd, msg);
275 close(cfd);
276 free(d);
277 f->inuse--;
278 free(newf);
279 return;
282 if (newf) {
283 newf->next = o->fds;
284 o->fds = newf;
286 pthread_detach(tid);
289 static void sigusr1_handler(int sig)
291 (void) sig;
292 /* Nothing to do */
295 static void process_owner(int cfd)
297 struct owner o;
298 struct sigaction sa;
300 memset(&sa, 0, sizeof(struct sigaction));
301 sa.sa_handler = sigusr1_handler;
302 sigemptyset(&sa.sa_mask);
304 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
305 perror("ulockmgr_server: cannot set sigusr1 signal handler");
306 exit(1);
309 memset(&o, 0, sizeof(struct owner));
310 pthread_mutex_init(&o.lock, NULL);
311 while (1) {
312 struct message msg;
313 int rfds[2];
314 int res;
315 int numfds = 2;
317 res = receive_message(cfd, &msg, sizeof(msg), rfds, &numfds);
318 if (!res)
319 break;
320 if (res == -1)
321 exit(1);
323 if (msg.intr) {
324 if (numfds != 0)
325 fprintf(stderr,
326 "ulockmgr_server: too many fds for intr\n");
327 pthread_kill(msg.thr, SIGUSR1);
328 } else {
329 if (numfds != 2)
330 continue;
332 pthread_mutex_lock(&o.lock);
333 process_message(&o, &msg, rfds[0], rfds[1]);
334 pthread_mutex_unlock(&o.lock);
337 if (o.fds)
338 fprintf(stderr,
339 "ulockmgr_server: open file descriptors on exit\n");
342 int main(int argc, char *argv[])
344 int nullfd;
345 char *end;
346 int cfd;
347 sigset_t empty;
349 if (argc != 2 || !argv[1][0])
350 goto out_inval;
352 cfd = strtol(argv[1], &end, 10);
353 if (*end)
354 goto out_inval;
356 /* demonize current process */
357 switch(fork()) {
358 case -1:
359 perror("ulockmgr_server: fork");
360 exit(1);
361 case 0:
362 break;
363 default:
364 _exit(0);
367 if (setsid() == -1) {
368 perror("ulockmgr_server: setsid");
369 exit(1);
372 (void) chdir("/");
374 sigemptyset(&empty);
375 sigprocmask(SIG_SETMASK, &empty, NULL);
377 if (dup2(cfd, 4) == -1) {
378 perror("ulockmgr_server: dup2");
379 exit(1);
381 cfd = 4;
382 nullfd = open("/dev/null", O_RDWR);
383 if (nullfd >= 0) {
384 dup2(nullfd, 0);
385 dup2(nullfd, 1);
387 close(3);
388 closefrom(5);
389 while (1) {
390 char c;
391 int sock;
392 int pid;
393 int numfds = 1;
394 int res = receive_message(cfd, &c, sizeof(c), &sock, &numfds);
395 if (!res)
396 break;
397 if (res == -1)
398 exit(1);
399 assert(numfds == 1);
401 pid = fork();
402 if (pid == -1) {
403 perror("ulockmgr_server: fork");
404 close(sock);
405 continue;
407 if (pid == 0) {
408 close(cfd);
409 pid = fork();
410 if (pid == -1) {
411 perror("ulockmgr_server: fork");
412 _exit(1);
414 if (pid == 0)
415 process_owner(sock);
416 _exit(0);
418 waitpid(pid, NULL, 0);
419 close(sock);
421 return 0;
423 out_inval:
424 fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]);
425 return 1;