blob: a2d686325d1f330f600810d227453c7469eccd04 [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 {
26 int intr;
27 pthread_t thr;
28 int cmd;
29 int fd;
30 struct flock lock;
31 int error;
32};
33
34struct fd_store {
35 struct fd_store *next;
36 int fd;
37 int origfd;
38 int finished;
39};
40
41struct owner {
42 struct fd_store *fds;
43 pthread_mutex_t lock;
44};
45
46struct req_data {
47 struct owner *o;
48 int cfd;
49 struct fd_store *f;
50 struct message msg;
51};
52
53#define MAX_SEND_FDS 2
54
55static int receive_message(int sock, void *buf, size_t buflen, int *fdp,
56 int numfds)
57{
58 struct msghdr msg;
59 struct iovec iov;
60 char ccmsg[CMSG_SPACE(sizeof(int)) * MAX_SEND_FDS];
61 struct cmsghdr *cmsg;
62 int res;
63
64 assert(numfds <= MAX_SEND_FDS);
65 iov.iov_base = buf;
66 iov.iov_len = buflen;
67
68 memset(&msg, 0, sizeof(msg));
69 msg.msg_iov = &iov;
70 msg.msg_iovlen = 1;
71 msg.msg_control = ccmsg;
72 msg.msg_controllen = sizeof(ccmsg);
73
74 res = recvmsg(sock, &msg, MSG_WAITALL);
75 if (!res)
76 return 0;
77 if (res == -1) {
78 perror("ulockmgr_server: recvmsg");
79 return -1;
80 }
81 if ((size_t) res != buflen) {
82 fprintf(stderr, "ulockmgr_server: short message received\n");
83 return -1;
84 }
85
86 cmsg = CMSG_FIRSTHDR(&msg);
87 if (cmsg) {
88 if (!cmsg->cmsg_type == SCM_RIGHTS) {
89 fprintf(stderr, "ulockmgr_server: unknown control message %d\n",
90 cmsg->cmsg_type);
91 return -1;
92 }
93 memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * numfds);
94 }
95 return res;
96}
97
98static int closefrom(int minfd)
99{
100 DIR *dir = opendir("/proc/self/fd");
101 if (dir) {
102 int dfd = dirfd(dir);
103 struct dirent *ent;
104 while ((ent = readdir(dir))) {
105 char *end;
106 int fd = strtol(ent->d_name, &end, 10);
107 if (ent->d_name[0] && !end[0] && fd >= minfd && fd != dfd)
108 close(fd);
109 }
110 closedir(dir);
111 }
112 return 0;
113}
114
115static void send_reply(int cfd, struct message *msg)
116{
117 int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
118 if (res == -1)
119 perror("ulockmgr_server: sending reply");
120#ifdef DEBUG
121 fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error);
122#endif
123}
124
125static void *process_request(void *d_)
126{
127 struct req_data *d = d_;
128 int res;
129
130 assert(d->msg.cmd == F_SETLKW);
131 res = fcntl(d->f->fd, F_SETLK, &d->msg.lock);
132 if (res == -1 && errno == EAGAIN) {
133 d->msg.error = EAGAIN;
134 d->msg.thr = pthread_self();
135 send_reply(d->cfd, &d->msg);
136 res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock);
137 }
138 d->msg.error = (res == -1) ? errno : 0;
139 pthread_mutex_lock(&d->o->lock);
140 d->f->finished = 1;
141 pthread_mutex_unlock(&d->o->lock);
142 send_reply(d->cfd, &d->msg);
143 close(d->cfd);
144 free(d);
145
146 return NULL;
147}
148
149static void process_message(struct owner *o, struct message *msg, int cfd,
150 int fd)
151{
152 struct fd_store *f;
153 struct req_data *d;
154 pthread_t tid;
155 int res;
156
157#ifdef DEBUG
158 fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n",
159 msg->cmd, msg->lock.l_type, msg->lock.l_whence, msg->lock.l_start,
160 msg->lock.l_len);
161#endif
162
163 if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
164 msg->lock.l_start == 0 && msg->lock.l_len == 0) {
165 struct fd_store **fp;
166
167 for (fp = &o->fds; *fp;) {
168 struct fd_store *f = *fp;
169 if (f->origfd == msg->fd && f->finished) {
170 close(f->fd);
171 *fp = f->next;
172 free(f);
173 } else
174 fp = &f->next;
175 }
176 close(fd);
177
178 msg->error = 0;
179 send_reply(cfd, msg);
180 close(cfd);
181 return;
182 }
183
184 f = malloc(sizeof(struct fd_store));
185 if (!f) {
186 msg->error = ENOLCK;
187 send_reply(cfd, msg);
188 close(cfd);
189 return;
190 }
191
192 f->fd = fd;
193 f->origfd = msg->fd;
194
195 if (msg->cmd == F_GETLK || msg->cmd == F_SETLK ||
196 msg->lock.l_type == F_UNLCK) {
197 res = fcntl(f->fd, msg->cmd, &msg->lock);
198 msg->error = (res == -1) ? errno : 0;
199 send_reply(cfd, msg);
200 close(cfd);
201 f->next = o->fds;
202 o->fds = f;
203 f->finished = 1;
204 return;
205 }
206
207 d = malloc(sizeof(struct req_data));
208 if (!d) {
209 msg->error = ENOLCK;
210 send_reply(cfd, msg);
211 close(cfd);
212 free(f);
213 return;
214 }
215
216 d->o = o;
217 d->cfd = cfd;
218 d->f = f;
219 d->msg = *msg;
220 res = pthread_create(&tid, NULL, process_request, d);
221 if (res) {
222 msg->error = ENOLCK;
223 send_reply(cfd, msg);
224 close(cfd);
225 free(d);
226 free(f);
227 return;
228 }
229
230 f->next = o->fds;
231 o->fds = f;
232 pthread_detach(tid);
233}
234
235static void sigusr1_handler(int sig)
236{
237 (void) sig;
238 /* Nothing to do */
239}
240
241static void process_owner(int cfd)
242{
243 struct owner o;
244 struct sigaction sa;
245
246 memset(&sa, 0, sizeof(struct sigaction));
247 sa.sa_handler = sigusr1_handler;
248 sigemptyset(&sa.sa_mask);
249
250 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
251 perror("ulockmgr_server: cannot set sigusr1 signal handler");
252 exit(1);
253 }
254
255 memset(&o, 0, sizeof(struct owner));
256 pthread_mutex_init(&o.lock, NULL);
257 while (1) {
258 struct message msg;
259 int rfds[2];
260 int res;
261
262 res = receive_message(cfd, &msg, sizeof(msg), rfds, 2);
263 if (!res)
264 break;
265 if (res == -1)
266 exit(1);
267
268 if (msg.intr)
269 pthread_kill(msg.thr, SIGUSR1);
270 else {
271 pthread_mutex_lock(&o.lock);
272 process_message(&o, &msg, rfds[0], rfds[1]);
273 pthread_mutex_unlock(&o.lock);
274 }
275 }
276 if (o.fds)
277 fprintf(stderr, "ulockmgr_server: open file descriptors on exit\n");
278}
279
280int main(int argc, char *argv[])
281{
282 int nullfd;
283 char *end;
284 int cfd;
285 sigset_t empty;
286
287 if (argc != 2 || !argv[1][0])
288 goto out_inval;
289
290 cfd = strtol(argv[1], &end, 10);
291 if (*end)
292 goto out_inval;
293
294 if (daemon(0, 1) == -1) {
295 perror("ulockmgr_server: daemon");
296 exit(1);
297 }
298
299 sigemptyset(&empty);
300 sigprocmask(SIG_SETMASK, &empty, NULL);
301
302 if (dup2(cfd, 4) == -1) {
303 perror("ulockmgr_server: dup2");
304 exit(1);
305 }
306 cfd = 4;
307 nullfd = open("/dev/null", O_RDWR);
308 dup2(nullfd, 0);
309 dup2(nullfd, 1);
310 close(3);
311 closefrom(5);
312 while (1) {
313 char c;
314 int sock;
315 int pid;
316 int res = receive_message(cfd, &c, sizeof(c), &sock, 1);
317 if (!res)
318 break;
319 if (res == -1)
320 exit(1);
321
322 pid = fork();
323 if (pid == -1) {
324 perror("ulockmgr_server: fork");
325 close(sock);
326 continue;
327 }
328 if (pid == 0) {
329 close(cfd);
330 pid = fork();
331 if (pid == -1) {
332 perror("ulockmgr_server: fork");
333 _exit(1);
334 }
335 if (pid == 0)
336 process_owner(sock);
337 _exit(0);
338 }
339 waitpid(pid, NULL, 0);
340 close(sock);
341 }
342 return 0;
343
344 out_inval:
345 fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]);
346 return 1;
347}