Miklos Szeredi | 0c59ebf | 2006-09-10 20:53:36 +0000 | [diff] [blame^] | 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 | int intr; |
| 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 origfd; |
| 38 | int finished; |
| 39 | }; |
| 40 | |
| 41 | struct owner { |
| 42 | struct fd_store *fds; |
| 43 | pthread_mutex_t lock; |
| 44 | }; |
| 45 | |
| 46 | struct 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 | |
| 55 | static 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 | |
| 98 | static 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 | |
| 115 | static 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 | |
| 125 | static 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 | |
| 149 | static 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 | |
| 235 | static void sigusr1_handler(int sig) |
| 236 | { |
| 237 | (void) sig; |
| 238 | /* Nothing to do */ |
| 239 | } |
| 240 | |
| 241 | static 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 | |
| 280 | int 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 | } |