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