blob: 0d00975f44ca415a1e211d75f2ccaafc2e583d53 [file] [log] [blame]
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +00001/*
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
25struct message {
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +000026 unsigned intr : 1;
27 unsigned nofd : 1;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +000028 pthread_t thr;
29 int cmd;
30 int fd;
31 struct flock lock;
32 int error;
33};
34
35struct fd_store {
36 struct fd_store *next;
37 int fd;
38 int origfd;
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +000039 int inuse;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +000040};
41
42struct owner {
43 struct fd_store *fds;
44 pthread_mutex_t lock;
45};
46
47struct 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
56static int receive_message(int sock, void *buf, size_t buflen, int *fdp,
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +000057 int *numfds)
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +000058{
59 struct msghdr msg;
60 struct iovec iov;
61 char ccmsg[CMSG_SPACE(sizeof(int)) * MAX_SEND_FDS];
62 struct cmsghdr *cmsg;
63 int res;
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +000064 int i;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +000065
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +000066 assert(*numfds <= MAX_SEND_FDS);
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +000067 iov.iov_base = buf;
68 iov.iov_len = buflen;
69
70 memset(&msg, 0, sizeof(msg));
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +000071 memset(ccmsg, -1, sizeof(ccmsg));
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +000072 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 return 0;
80 if (res == -1) {
81 perror("ulockmgr_server: recvmsg");
82 return -1;
83 }
84 if ((size_t) res != buflen) {
85 fprintf(stderr, "ulockmgr_server: short message received\n");
86 return -1;
87 }
88
89 cmsg = CMSG_FIRSTHDR(&msg);
90 if (cmsg) {
91 if (!cmsg->cmsg_type == SCM_RIGHTS) {
92 fprintf(stderr, "ulockmgr_server: unknown control message %d\n",
93 cmsg->cmsg_type);
94 return -1;
95 }
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +000096 memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
97 if (msg.msg_flags & MSG_CTRUNC) {
98 fprintf(stderr, "ulockmgr_server: control message truncated\n");
99 for (i = 0; i < *numfds; i++)
100 close(fdp[i]);
101 *numfds = 0;
102 }
103 } else {
104 if (msg.msg_flags & MSG_CTRUNC) {
105 fprintf(stderr, "ulockmgr_server: control message truncated(*)\n");
106
107 /* There's a bug in the Linux kernel, that if not all file
108 descriptors were allocated, then the cmsg header is not
109 filled in */
110 cmsg = (struct cmsghdr *) ccmsg;
111 memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
112 for (i = 0; i < *numfds; i++)
113 close(fdp[i]);
114 }
115 *numfds = 0;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000116 }
117 return res;
118}
119
120static int closefrom(int minfd)
121{
122 DIR *dir = opendir("/proc/self/fd");
123 if (dir) {
124 int dfd = dirfd(dir);
125 struct dirent *ent;
126 while ((ent = readdir(dir))) {
127 char *end;
128 int fd = strtol(ent->d_name, &end, 10);
129 if (ent->d_name[0] && !end[0] && fd >= minfd && fd != dfd)
130 close(fd);
131 }
132 closedir(dir);
133 }
134 return 0;
135}
136
137static void send_reply(int cfd, struct message *msg)
138{
139 int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
140 if (res == -1)
141 perror("ulockmgr_server: sending reply");
142#ifdef DEBUG
143 fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error);
144#endif
145}
146
147static void *process_request(void *d_)
148{
149 struct req_data *d = d_;
150 int res;
151
152 assert(d->msg.cmd == F_SETLKW);
153 res = fcntl(d->f->fd, F_SETLK, &d->msg.lock);
154 if (res == -1 && errno == EAGAIN) {
155 d->msg.error = EAGAIN;
156 d->msg.thr = pthread_self();
157 send_reply(d->cfd, &d->msg);
158 res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock);
159 }
160 d->msg.error = (res == -1) ? errno : 0;
161 pthread_mutex_lock(&d->o->lock);
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000162 d->f->inuse--;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000163 pthread_mutex_unlock(&d->o->lock);
164 send_reply(d->cfd, &d->msg);
165 close(d->cfd);
166 free(d);
167
168 return NULL;
169}
170
171static void process_message(struct owner *o, struct message *msg, int cfd,
172 int fd)
173{
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000174 struct fd_store *f = NULL;
175 struct fd_store *newf = NULL;
176 struct fd_store **fp;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000177 struct req_data *d;
178 pthread_t tid;
179 int res;
180
181#ifdef DEBUG
182 fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n",
183 msg->cmd, msg->lock.l_type, msg->lock.l_whence, msg->lock.l_start,
184 msg->lock.l_len);
185#endif
186
187 if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
188 msg->lock.l_start == 0 && msg->lock.l_len == 0) {
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000189 for (fp = &o->fds; *fp;) {
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000190 f = *fp;
191 if (f->origfd == msg->fd && !f->inuse) {
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000192 close(f->fd);
193 *fp = f->next;
194 free(f);
195 } else
196 fp = &f->next;
197 }
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000198 if (!msg->nofd)
199 close(fd);
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000200
201 msg->error = 0;
202 send_reply(cfd, msg);
203 close(cfd);
204 return;
205 }
206
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000207 if (msg->nofd) {
208 for (fp = &o->fds; *fp; fp = &(*fp)->next) {
209 f = *fp;
210 if (f->origfd == msg->fd)
211 break;
212 }
213 if (!*fp) {
214 fprintf(stderr, "ulockmgr_server: fd %i not found\n", msg->fd);
215 msg->error = EIO;
216 send_reply(cfd, msg);
217 close(cfd);
218 return;
219 }
220 } else {
221 newf = f = malloc(sizeof(struct fd_store));
222 if (!f) {
223 msg->error = ENOLCK;
224 send_reply(cfd, msg);
225 close(cfd);
226 return;
227 }
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000228
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000229 f->fd = fd;
230 f->origfd = msg->fd;
231 f->inuse = 0;
232 }
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000233
234 if (msg->cmd == F_GETLK || msg->cmd == F_SETLK ||
235 msg->lock.l_type == F_UNLCK) {
236 res = fcntl(f->fd, msg->cmd, &msg->lock);
237 msg->error = (res == -1) ? errno : 0;
238 send_reply(cfd, msg);
239 close(cfd);
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000240 if (newf) {
241 newf->next = o->fds;
242 o->fds = newf;
243 }
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000244 return;
245 }
246
247 d = malloc(sizeof(struct req_data));
248 if (!d) {
249 msg->error = ENOLCK;
250 send_reply(cfd, msg);
251 close(cfd);
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000252 free(newf);
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000253 return;
254 }
255
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000256 f->inuse++;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000257 d->o = o;
258 d->cfd = cfd;
259 d->f = f;
260 d->msg = *msg;
261 res = pthread_create(&tid, NULL, process_request, d);
262 if (res) {
263 msg->error = ENOLCK;
264 send_reply(cfd, msg);
265 close(cfd);
266 free(d);
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000267 f->inuse--;
268 free(newf);
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000269 return;
270 }
271
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000272 if (newf) {
273 newf->next = o->fds;
274 o->fds = newf;
275 }
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000276 pthread_detach(tid);
277}
278
279static void sigusr1_handler(int sig)
280{
281 (void) sig;
282 /* Nothing to do */
283}
284
285static void process_owner(int cfd)
286{
287 struct owner o;
288 struct sigaction sa;
289
290 memset(&sa, 0, sizeof(struct sigaction));
291 sa.sa_handler = sigusr1_handler;
292 sigemptyset(&sa.sa_mask);
293
294 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
295 perror("ulockmgr_server: cannot set sigusr1 signal handler");
296 exit(1);
297 }
298
299 memset(&o, 0, sizeof(struct owner));
300 pthread_mutex_init(&o.lock, NULL);
301 while (1) {
302 struct message msg;
303 int rfds[2];
304 int res;
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000305 int numfds = 2;
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000306
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000307 res = receive_message(cfd, &msg, sizeof(msg), rfds, &numfds);
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000308 if (!res)
309 break;
310 if (res == -1)
311 exit(1);
312
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000313 if (msg.intr) {
314 if (numfds != 0)
315 fprintf(stderr, "ulockmgr_server: too many fds for intr\n");
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000316 pthread_kill(msg.thr, SIGUSR1);
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000317 } else {
318 if (numfds != 2)
319 continue;
320
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000321 pthread_mutex_lock(&o.lock);
322 process_message(&o, &msg, rfds[0], rfds[1]);
323 pthread_mutex_unlock(&o.lock);
324 }
325 }
326 if (o.fds)
327 fprintf(stderr, "ulockmgr_server: open file descriptors on exit\n");
328}
329
330int main(int argc, char *argv[])
331{
332 int nullfd;
333 char *end;
334 int cfd;
335 sigset_t empty;
336
337 if (argc != 2 || !argv[1][0])
338 goto out_inval;
339
340 cfd = strtol(argv[1], &end, 10);
341 if (*end)
342 goto out_inval;
343
344 if (daemon(0, 1) == -1) {
345 perror("ulockmgr_server: daemon");
346 exit(1);
347 }
348
349 sigemptyset(&empty);
350 sigprocmask(SIG_SETMASK, &empty, NULL);
351
352 if (dup2(cfd, 4) == -1) {
353 perror("ulockmgr_server: dup2");
354 exit(1);
355 }
356 cfd = 4;
357 nullfd = open("/dev/null", O_RDWR);
358 dup2(nullfd, 0);
359 dup2(nullfd, 1);
360 close(3);
361 closefrom(5);
362 while (1) {
363 char c;
364 int sock;
365 int pid;
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000366 int numfds = 1;
367 int res = receive_message(cfd, &c, sizeof(c), &sock, &numfds);
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000368 if (!res)
369 break;
370 if (res == -1)
371 exit(1);
Miklos Szeredi7e7fa1f2006-10-08 15:41:20 +0000372 assert(numfds == 1);
Miklos Szeredi0c59ebf2006-09-10 20:53:36 +0000373
374 pid = fork();
375 if (pid == -1) {
376 perror("ulockmgr_server: fork");
377 close(sock);
378 continue;
379 }
380 if (pid == 0) {
381 close(cfd);
382 pid = fork();
383 if (pid == -1) {
384 perror("ulockmgr_server: fork");
385 _exit(1);
386 }
387 if (pid == 0)
388 process_owner(sock);
389 _exit(0);
390 }
391 waitpid(pid, NULL, 0);
392 close(sock);
393 }
394 return 0;
395
396 out_inval:
397 fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]);
398 return 1;
399}