]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/cnid_metad.c
With glibc we always need GNU_SOURCE
[netatalk.git] / etc / cnid_dbd / cnid_metad.c
1 /*
2  * $Id: cnid_metad.c,v 1.22 2009-11-16 02:04:47 didg Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * All Rights Reserved.  See COPYING.
6  *
7  */
8
9 /* 
10    cnid_dbd metadaemon to start up cnid_dbd upon request from afpd.
11    Here is how it works:
12    
13                        via TCP socket
14    1.       afpd          ------->        cnid_metad
15
16                    via UNIX domain socket
17    2.   cnid_metad        ------->         cnid_dbd
18
19                     passes afpd client fd
20    3.   cnid_metad        ------->         cnid_dbd
21
22    Result:
23                        via TCP socket
24    4.       afpd          ------->         cnid_dbd
25  */
26
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif /* HAVE_CONFIG_H */
31
32 #include <unistd.h>
33 #undef __USE_GNU
34
35 #include <stdlib.h>
36 #include <sys/param.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <signal.h>
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #ifdef HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 #ifdef HAVE_SYS_WAIT_H
47 #include <sys/wait.h>
48 #endif
49 #ifdef HAVE_SYS_UIO_H
50 #include <sys/uio.h>
51 #endif
52 #include <sys/un.h>
53 #define _XPG4_2 1
54 #include <sys/socket.h>
55 #include <stdio.h>
56 #include <time.h>
57 #include <sys/ioctl.h>
58
59 #ifndef WEXITSTATUS
60 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
61 #endif /* ! WEXITSTATUS */
62 #ifndef WIFEXITED
63 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
64 #endif /* ! WIFEXITED */
65 #ifndef WIFSTOPPED
66 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
67 #endif
68
69 #ifndef WIFSIGNALED
70 #define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
71 #endif
72 #ifndef WTERMSIG
73 #define WTERMSIG(status)      ((status) & 0x7f)
74 #endif
75
76 /* functions for username and group */
77 #include <pwd.h>
78 #include <grp.h>
79
80 /* FIXME */
81 #ifdef linux
82 #ifndef USE_SETRESUID
83 #define USE_SETRESUID 1
84 #define SWITCH_TO_GID(gid)  ((setresgid(gid,gid,gid) < 0 || setgid(gid) < 0) ? -1 : 0)
85 #define SWITCH_TO_UID(uid)  ((setresuid(uid,uid,uid) < 0 || setuid(uid) < 0) ? -1 : 0)
86 #endif  /* USE_SETRESUID */
87 #else   /* ! linux */
88 #ifndef USE_SETEUID
89 #define USE_SETEUID 1
90 #define SWITCH_TO_GID(gid)  ((setegid(gid) < 0 || setgid(gid) < 0) ? -1 : 0)
91 #define SWITCH_TO_UID(uid)  ((setuid(uid) < 0 || seteuid(uid) < 0 || setuid(uid) < 0) ? -1 : 0)
92 #endif  /* USE_SETEUID */
93 #endif  /* linux */
94
95 #include <atalk/util.h>
96 #include <atalk/logger.h>
97 #include <atalk/cnid_dbd_private.h>
98
99 #include "db_param.h"
100 #include "usockfd.h"
101
102 #define DBHOME        ".AppleDB"
103 #define DBHOMELEN    8
104
105 static int srvfd;
106 static int rqstfd;
107 static volatile sig_atomic_t sigchild = 0;
108
109 #define MAXSPAWN   3                   /* Max times respawned in.. */
110 #define TESTTIME   42                  /* this much seconds apfd client tries to  *
111                                         * to reconnect every 5 secondes, catch it */
112 #define MAXVOLS    512
113 #define DEFAULTHOST  "localhost"
114 #define DEFAULTPORT  "4700"
115
116 struct server {
117     char  *name;
118     pid_t pid;
119     time_t tm;                    /* When respawned last */
120     int count;                    /* Times respawned in the last TESTTIME secondes */
121     int control_fd;               /* file descriptor to child cnid_dbd process */
122 };
123
124 static struct server srv[MAXVOLS];
125
126 /* Default logging config: log to syslog with level log_note */
127 static char logconfig[MAXPATHLEN + 21 + 1] = "default log_note";
128
129 static struct server *test_usockfn(char *dir)
130 {
131     int i;
132     for (i = 0; i < MAXVOLS; i++) {
133         if (srv[i].name && !strcmp(srv[i].name, dir)) {
134             return &srv[i];
135         }
136     }
137     return NULL;
138 }
139
140 /* -------------------- */
141 static int send_cred(int socket, int fd)
142 {
143     int ret;
144     struct msghdr msgh;
145     struct iovec iov[1];
146     struct cmsghdr *cmsgp = NULL;
147     char *buf;
148     size_t size;
149     int er=0;
150
151     size = CMSG_SPACE(sizeof fd);
152     buf = malloc(size);
153     if (!buf) {
154         LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
155         return -1;
156     }
157
158     memset(&msgh,0,sizeof (msgh));
159     memset(buf,0, size);
160
161     msgh.msg_name = NULL;
162     msgh.msg_namelen = 0;
163
164     msgh.msg_iov = iov;
165     msgh.msg_iovlen = 1;
166
167     iov[0].iov_base = &er;
168     iov[0].iov_len = sizeof(er);
169
170     msgh.msg_control = buf;
171     msgh.msg_controllen = size;
172
173     cmsgp = CMSG_FIRSTHDR(&msgh);
174     cmsgp->cmsg_level = SOL_SOCKET;
175     cmsgp->cmsg_type = SCM_RIGHTS;
176     cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
177
178     *((int *)CMSG_DATA(cmsgp)) = fd;
179     msgh.msg_controllen = cmsgp->cmsg_len;
180
181     do  {
182         ret = sendmsg(socket,&msgh, 0);
183     } while ( ret == -1 && errno == EINTR );
184     if (ret == -1) {
185         LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
186         free(buf);
187         return -1;
188     }
189     free(buf);
190     return 0;
191 }
192
193 /* -------------------- */
194 static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn)
195 {
196     pid_t pid;
197     struct server *up;
198     int sv[2];
199     int i;
200     time_t t;
201     char buf1[8];
202     char buf2[8];
203
204     LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: dbdir: '%s', UNIX socket file: '%s'", 
205         dbdir, usockfn);
206
207     up = test_usockfn(dbdir);
208     if (up && up->pid) {
209         /* we already have a process, send our fd */
210         if (send_cred(up->control_fd, rqstfd) < 0) {
211             /* FIXME */
212             return -1;
213         }
214         return 0;
215     }
216
217     LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: no cnid_dbd for that volume yet. Starting one ...");
218
219     time(&t);
220     if (!up) {
221         /* find an empty slot */
222         for (i = 0; i < MAXVOLS; i++) {
223             if ( !srv[i].name ) {
224                 up = &srv[i];
225                 up->tm = t;
226                 up->count = 0;
227                 up->name = strdup(dbdir);
228                 break;
229             }
230         }
231         if (!up) {
232             LOG(log_error, logtype_cnid, "no free slot for cnid_dbd child. Configured maximum: %d. Do you have so many volumes?", MAXVOLS);
233             return -1;
234         }
235     }
236     else {
237         /* we have a slot but no process, check for respawn too fast */
238         if ( (t < (up->tm + TESTTIME)) /* We're in the respawn time window */
239              &&
240              (up->count > MAXSPAWN) ) { /* ...and already tried to fork too often */
241             LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawn too fast just exiting");
242             return -1; /* just exit, dont sleep, because we might have work to do for another client  */
243         }
244         if ( t >= (up->tm + TESTTIME) ) { /* out of respawn too fast windows reset the count */
245             LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawn window ended");
246             up->tm = t;
247             up->count = 0;
248         }
249         up->count++;
250         LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawn count now is: %u", up->count);
251         if (up->count > MAXSPAWN) {
252             /* We spawned too fast. From now until the first time we tried + TESTTIME seconds
253                we will just return -1 above */
254             LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: reached MAXSPAWN threshhold");
255         }
256     }
257
258     /* 
259        Create socketpair for comm between parent and child.
260        We use it to pass fds from connecting afpd processes to our
261        cnid_dbd child via fd passing.
262     */
263     if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) {
264         LOG(log_error, logtype_cnid, "error in socketpair: %s", strerror(errno));
265         return -1;
266     }
267
268     if ((pid = fork()) < 0) {
269         LOG(log_error, logtype_cnid, "error in fork: %s", strerror(errno));
270         return -1;
271     }
272     if (pid == 0) {
273         int ret;
274         /*
275          *  Child. Close descriptors and start the daemon. If it fails
276          *  just log it. The client process will fail connecting
277          *  afterwards anyway.
278          */
279
280         close(srvfd);
281         close(sv[0]);
282
283         for (i = 0; i < MAXVOLS; i++) {
284             if (srv[i].pid && up != &srv[i]) {
285                 close(srv[i].control_fd);
286             }
287         }
288
289         sprintf(buf1, "%i", sv[1]);
290         sprintf(buf2, "%i", rqstfd);
291
292         if (up->count == MAXSPAWN) {
293             /* there's a pb with the db inform child
294              * it will run recover, delete the db whatever
295              */
296             LOG(log_error, logtype_cnid, "try with -d %s", up->name);
297             ret = execlp(dbdpn, dbdpn, "-d", dbdir, buf1, buf2, logconfig, NULL);
298         }
299         else {
300             ret = execlp(dbdpn, dbdpn, dbdir, buf1, buf2, logconfig, NULL);
301         }
302         if (ret < 0) {
303             LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
304             exit(0);
305         }
306     }
307     /*
308      *  Parent.
309      */
310     up->pid = pid;
311     close(sv[1]);
312     up->control_fd = sv[0];
313     return 0;
314 }
315
316 /* ------------------ */
317 static int set_dbdir(char *dbdir, int len)
318 {
319     struct stat st;
320
321     if (!len)
322         return -1;
323
324     if (stat(dbdir, &st) < 0 && mkdir(dbdir, 0755) < 0) {
325         LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", dbdir);
326         return -1;
327     }
328
329     if (dbdir[len - 1] != '/') {
330         strcat(dbdir, "/");
331         len++;
332     }
333     strcpy(dbdir + len, DBHOME);
334     if (stat(dbdir, &st) < 0 && mkdir(dbdir, 0755 ) < 0) {
335         LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", dbdir);
336         return -1;
337     }
338     return 0;
339 }
340
341 /* ------------------ */
342 static uid_t user_to_uid (char *username)
343 {
344     struct passwd *this_passwd;
345
346     /* check for anything */
347     if ( !username || strlen ( username ) < 1 ) return 0;
348
349     /* grab the /etc/passwd record relating to username */
350     this_passwd = getpwnam ( username );
351
352     /* return false if there is no structure returned */
353     if (this_passwd == NULL) return 0;
354
355     /* return proper uid */
356     return this_passwd->pw_uid;
357
358 }
359
360 /* ------------------ */
361 static gid_t group_to_gid ( char *group)
362 {
363     struct group *this_group;
364
365     /* check for anything */
366     if ( !group || strlen ( group ) < 1 ) return 0;
367
368     /* grab the /etc/groups record relating to group */
369     this_group = getgrnam ( group );
370
371     /* return false if there is no structure returned */
372     if (this_group == NULL) return 0;
373
374     /* return proper gid */
375     return this_group->gr_gid;
376
377 }
378
379 /* ------------------ */
380 static void catch_child(int sig _U_) 
381 {
382     sigchild = 1;
383 }
384
385 /* ----------------------- */
386 static void set_signal(void)
387 {
388     struct sigaction sv;
389     sigset_t set;
390
391     signal(SIGPIPE, SIG_IGN);
392
393     sv.sa_handler = catch_child;
394     sv.sa_flags = SA_NOCLDSTOP;
395     sigemptyset(&sv.sa_mask);
396     if (sigaction(SIGCHLD, &sv, NULL) < 0) {
397         LOG(log_error, logtype_cnid, "cnid_metad: sigaction: %s", strerror(errno));
398         exit(1);
399     }
400     /* block everywhere but in pselect */
401     sigemptyset(&set);
402     sigaddset(&set, SIGCHLD);
403     sigprocmask(SIG_BLOCK, &set, NULL);
404 }
405
406 /* ------------------ */
407 int main(int argc, char *argv[])
408 {
409     char  dbdir[MAXPATHLEN + 1];
410     int   len, actual_len;
411     pid_t pid;
412     int   status;
413     char  *dbdpn = _PATH_CNID_DBD;
414     char  *host = DEFAULTHOST;
415     char  *port = DEFAULTPORT;
416     struct db_param *dbp;
417     int    i;
418     int    cc;
419     uid_t  uid = 0;
420     gid_t  gid = 0;
421     int    err = 0;
422     int    debug = 0;
423     int    ret;
424     char   *loglevel = NULL;
425     char   *logfile  = NULL;
426     sigset_t set;
427
428     set_processname("cnid_metad");
429
430     while (( cc = getopt( argc, argv, "ds:p:h:u:g:l:f:")) != -1 ) {
431         switch (cc) {
432         case 'd':
433             debug = 1;
434             break;
435         case 'h':
436             host = strdup(optarg);
437             break;
438         case 'u':
439             uid = user_to_uid (optarg);
440             if (!uid) {
441                 LOG(log_error, logtype_cnid, "main: bad user %s", optarg);
442                 err++;
443             }
444             break;
445         case 'g':
446             gid =group_to_gid (optarg);
447             if (!gid) {
448                 LOG(log_error, logtype_cnid, "main: bad group %s", optarg);
449                 err++;
450             }
451             break;
452         case 'p':
453             port = strdup(optarg);
454             break;
455         case 's':
456             dbdpn = strdup(optarg);
457             break;
458         case 'l':
459             loglevel = strdup(optarg);
460             break;
461         case 'f':
462             logfile = strdup(optarg);
463             break;
464         default:
465             err++;
466             break;
467         }
468     }
469
470     if (loglevel) {
471         strlcpy(logconfig + 8, loglevel, 13);
472         free(loglevel);
473         strcat(logconfig, " ");
474     }
475     if (logfile) {
476         strlcat(logconfig, logfile, MAXPATHLEN);
477         free(logfile);
478     }
479     setuplog(logconfig);
480
481     if (err) {
482         LOG(log_error, logtype_cnid, "main: bad arguments");
483         exit(1);
484     }
485
486     if (!debug) {
487
488         switch (fork()) {
489         case 0 :
490             fclose(stdin);
491             fclose(stdout);
492             fclose(stderr);
493
494 #ifdef TIOCNOTTY
495             {
496                 int i;
497                 if (( i = open( "/dev/tty", O_RDWR )) >= 0 ) {
498                     (void)ioctl( i, TIOCNOTTY, 0 );
499                     setpgid( 0, getpid());
500                     (void) close(i);
501                 }
502             }
503 #else
504             setpgid( 0, getpid());
505 #endif
506             break;
507         case -1 :  /* error */
508             LOG(log_error, logtype_cnid, "detach from terminal: %s", strerror(errno));
509             exit(1);
510         default :  /* server */
511             exit(0);
512         }
513     }
514
515     if ((srvfd = tsockfd_create(host, port, 10)) < 0)
516         exit(1);
517
518     /* switch uid/gid */
519     if (uid || gid) {
520         LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", uid, gid);
521         if (gid) {
522             if (SWITCH_TO_GID(gid) < 0) {
523                 LOG(log_info, logtype_cnid, "unable to switch to group %d", gid);
524                 exit(1);
525             }
526         }
527         if (uid) {
528             if (SWITCH_TO_UID(uid) < 0) {
529                 LOG(log_info, logtype_cnid, "unable to switch to user %d", uid);
530                 exit(1);
531             }
532         }
533     }
534
535     set_signal();
536
537     sigemptyset(&set);
538     sigprocmask(SIG_SETMASK, NULL, &set);
539     sigdelset(&set, SIGCHLD);
540
541     while (1) {
542         rqstfd = usockfd_check(srvfd, &set);
543         /* Collect zombie processes and log what happened to them */
544         if (sigchild) while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
545             for (i = 0; i < MAXVOLS; i++) {
546                 if (srv[i].pid == pid) {
547                     srv[i].pid = 0;
548                     close(srv[i].control_fd);
549                     break;
550                 }
551             }
552             if (WIFEXITED(status)) {
553                 LOG(log_info, logtype_cnid, "cnid_dbd pid %i exited with exit code %i",
554                     pid, WEXITSTATUS(status));
555             }
556             else if (WIFSIGNALED(status)) {
557                 LOG(log_info, logtype_cnid, "cnid_dbd pid %i exited with signal %i",
558                     pid, WTERMSIG(status));
559             }
560             sigchild = 0;
561         }
562         if (rqstfd <= 0)
563             continue;
564
565         /* TODO: Check out read errors, broken pipe etc. in libatalk. Is
566            SIGIPE ignored there? Answer: Ignored for dsi, but not for asp ... */
567         ret = read(rqstfd, &len, sizeof(int));
568         if (!ret) {
569             /* already close */
570             goto loop_end;
571         }
572         else if (ret < 0) {
573             LOG(log_severe, logtype_cnid, "error read: %s", strerror(errno));
574             goto loop_end;
575         }
576         else if (ret != sizeof(int)) {
577             LOG(log_error, logtype_cnid, "short read: got %d", ret);
578             goto loop_end;
579         }
580         /*
581          *  checks for buffer overruns. The client libatalk side does it too
582          *  before handing the dir path over but who trusts clients?
583          */
584         if (!len || len +DBHOMELEN +2 > MAXPATHLEN) {
585             LOG(log_error, logtype_cnid, "wrong len parameter: %d", len);
586             goto loop_end;
587         }
588
589         actual_len = read(rqstfd, dbdir, len);
590         if (actual_len < 0) {
591             LOG(log_severe, logtype_cnid, "Read(2) error : %s", strerror(errno));
592             goto loop_end;
593         }
594         if (actual_len != len) {
595             LOG(log_error, logtype_cnid, "error/short read (dir): %s", strerror(errno));
596             goto loop_end;
597         }
598         dbdir[len] = '\0';
599
600         if (set_dbdir(dbdir, len) < 0) {
601             goto loop_end;
602         }
603
604         if ((dbp = db_param_read(dbdir, METAD)) == NULL) {
605             LOG(log_error, logtype_cnid, "Error reading config file");
606             goto loop_end;
607         }
608         maybe_start_dbd(dbdpn, dbdir, dbp->usock_file);
609
610     loop_end:
611         close(rqstfd);
612     }
613 }