X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fcnid_dbd%2Fcnid_metad.c;h=b05d72db61cf4d1d3cd76711f129ba50079d9358;hb=5eb3b5ac51c8221009041928a5a08c101d2be743;hp=a51d15c4196d911f2f132cc42207cc6f87fddcb2;hpb=4054f4b3c85ecab060dafd46c0d3632cadbb5803;p=netatalk.git diff --git a/etc/cnid_dbd/cnid_metad.c b/etc/cnid_dbd/cnid_metad.c index a51d15c4..b05d72db 100644 --- a/etc/cnid_dbd/cnid_metad.c +++ b/etc/cnid_dbd/cnid_metad.c @@ -40,10 +40,11 @@ #include #include #include +#include #include #include #include -#define _XPG4_2 1 +// #define _XPG4_2 1 #include #include #include @@ -86,9 +87,14 @@ #include #include -#include +#include #include -#include +#include +#include +#include +#include +#include +#include #include "usockfd.h" @@ -98,39 +104,38 @@ static int srvfd; static int rqstfd; static volatile sig_atomic_t sigchild = 0; +static uint maxvol; #define MAXSPAWN 3 /* Max times respawned in.. */ -#define TESTTIME 42 /* this much seconds apfd client tries to * +#define TESTTIME 10 /* this much seconds apfd client tries to * * to reconnect every 5 secondes, catch it */ -#define MAXVOLS 512 +#define MAXVOLS 4096 #define DEFAULTHOST "localhost" #define DEFAULTPORT "4700" struct server { - struct volinfo *volinfo; + char *v_path; pid_t pid; time_t tm; /* When respawned last */ - int count; /* Times respawned in the last TESTTIME secondes */ + unsigned int count; /* Times respawned in the last TESTTIME secondes */ int control_fd; /* file descriptor to child cnid_dbd process */ }; static struct server srv[MAXVOLS]; -/* Default logging config: log to syslog with level log_note */ -static char logconfig[MAXPATHLEN + 21 + 1] = "default log_note"; - static void daemon_exit(int i) { - server_unlock(_PATH_CNID_METAD_LOCK); exit(i); } /* ------------------ */ -static void sigterm_handler(int sig) +static void sig_handler(int sig) { switch( sig ) { - case SIGTERM : - LOG(log_info, logtype_afpd, "shutting down on signal %d", sig ); + case SIGTERM: + case SIGQUIT: + LOG(log_note, logtype_afpd, "shutting down on %s", + sig == SIGTERM ? "SIGTERM" : "SIGQUIT"); break; default : LOG(log_error, logtype_afpd, "unexpected signal: %d", sig); @@ -138,19 +143,29 @@ static void sigterm_handler(int sig) daemon_exit(0); } -static struct server *test_usockfn(struct volinfo *volinfo) +static struct server *test_usockfn(const char *path) { int i; - for (i = 0; i < MAXVOLS; i++) { - if ((srv[i].volinfo) && (strcmp(srv[i].volinfo->v_path, volinfo->v_path) == 0)) { + + for (i = 0; i < maxvol; i++) { + if (srv[i].v_path && STRCMP(path, ==, srv[i].v_path)) return &srv[i]; - } } + return NULL; } -/* -------------------- */ -static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo) +/** + * Pass connection request to existing cnid_dbd process or start a new one + * + * @param[in] obj handle + * @param[in] dbdpn Path to cnid_dbd binary + * @param[in] volpath Path of AFP volume + * @param[in] username Optional username, may be NULL + * + * @return 0 on success, -1 on error + **/ + int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath, const char *username) { pid_t pid; struct server *up; @@ -159,13 +174,13 @@ static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo) time_t t; char buf1[8]; char buf2[8]; - char *volpath = volinfo->v_path; - LOG(log_debug, logtype_cnid, "maybe_start_dbd: Volume: \"%s\"", volpath); + LOG(log_debug, logtype_cnid, "maybe_start_dbd(\"%s\"): BEGIN", volpath); - up = test_usockfn(volinfo); + up = test_usockfn(volpath); if (up && up->pid) { /* we already have a process, send our fd */ + LOG(log_debug, logtype_cnid, "maybe_start_dbd: cnid_dbd[%d] already serving", up->pid); if (send_fd(up->control_fd, rqstfd) < 0) { /* FIXME */ return -1; @@ -173,18 +188,20 @@ static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo) return 0; } - LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: no cnid_dbd for that volume yet"); + LOG(log_debug, logtype_cnid, "maybe_start_dbd: no cnid_dbd serving yet"); time(&t); if (!up) { - /* find an empty slot */ - for (i = 0; i < MAXVOLS; i++) { - if (srv[i].volinfo == NULL) { + /* find an empty slot (i < maxvol) or the first free slot (i == maxvol)*/ + for (i = 0; i <= maxvol && i < MAXVOLS; i++) { + if (srv[i].v_path == NULL) { up = &srv[i]; - up->volinfo = volinfo; - retainvolinfo(volinfo); + if ((up->v_path = strdup(volpath)) == NULL) + return -1; up->tm = t; up->count = 0; + if (i == maxvol) + maxvol++; break; } } @@ -193,25 +210,30 @@ static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo) return -1; } } else { - /* we have a slot but no process, check for respawn too fast */ - if ( (t < (up->tm + TESTTIME)) /* We're in the respawn time window */ - && - (up->count > MAXSPAWN) ) { /* ...and already tried to fork too often */ - LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawn too fast just exiting"); - return -1; /* just exit, dont sleep, because we might have work to do for another client */ - } - if ( t >= (up->tm + TESTTIME) ) { /* out of respawn too fast windows reset the count */ - LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawn window ended"); - up->tm = t; - up->count = 0; + /* we have a slot but no process */ + if (up->count > 0) { + /* check for respawn too fast */ + if (t < (up->tm + TESTTIME)) { + /* We're in the respawn time window */ + if (up->count > MAXSPAWN) { + /* ...and already tried to fork too often */ + LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawning too fast"); + return -1; /* just exit, dont sleep, because we might have work to do for another client */ + } + } else { + /* out of respawn too fast windows reset the count */ + LOG(log_info, logtype_cnid, "maybe_start_dbd: respawn window ended"); + up->count = 0; + } } up->count++; - LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawn count now is: %u", up->count); + up->tm = t; + LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: respawn count: %u", up->count); if (up->count > MAXSPAWN) { /* We spawned too fast. From now until the first time we tried + TESTTIME seconds we will just return -1 above */ - LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: reached MAXSPAWN threshhold"); - } + LOG(log_info, logtype_cnid, "maybe_start_dbd: reached MAXSPAWN threshhold"); + } } /* @@ -249,14 +271,25 @@ static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo) sprintf(buf2, "%i", rqstfd); if (up->count == MAXSPAWN) { - /* there's a pb with the db inform child - * it will run recover, delete the db whatever - */ - LOG(log_error, logtype_cnid, "try with -d %s", up->volinfo->v_path); - ret = execlp(dbdpn, dbdpn, "-d", volpath, buf1, buf2, logconfig, NULL); - } - else { - ret = execlp(dbdpn, dbdpn, volpath, buf1, buf2, logconfig, NULL); + /* there's a pb with the db inform child, it will delete the db */ + LOG(log_warning, logtype_cnid, + "Multiple attempts to start CNID db daemon for \"%s\" failed, wiping the slate clean...", + up->v_path); + ret = execlp(dbdpn, dbdpn, + "-F", obj->options.configfile, + "-p", volpath, + "-t", buf1, + "-l", buf2, + "-u", username, + NULL); + } else { + ret = execlp(dbdpn, dbdpn, + "-F", obj->options.configfile, + "-p", volpath, + "-t", buf1, + "-l", buf2, + "-u", username, + NULL); } /* Yikes! We're still here, so exec failed... */ LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno)); @@ -272,66 +305,46 @@ static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo) } /* ------------------ */ -static int set_dbdir(char *dbdir) +static int set_dbdir(const char *dbdir, const char *vpath) { - int len; + EC_INIT; struct stat st; + bstring oldpath, newpath; + char *cmd_argv[4]; - len = strlen(dbdir); + LOG(log_debug, logtype_cnid, "set_dbdir: volume: %s, db path: %s", vpath, dbdir); - if (stat(dbdir, &st) < 0 && mkdir(dbdir, 0755) < 0) { - LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", dbdir); - return -1; - } + EC_NULL_LOG( oldpath = bformat("%s/%s/", vpath, DBHOME) ); + EC_NULL_LOG( newpath = bformat("%s/%s/", dbdir, DBHOME) ); - if (dbdir[len - 1] != '/') { - strcat(dbdir, "/"); - len++; - } - strcpy(dbdir + len, DBHOME); - if (stat(dbdir, &st) < 0 && mkdir(dbdir, 0755 ) < 0) { + if (lstat(dbdir, &st) < 0 && mkdir(dbdir, 0755) < 0) { LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", dbdir); - return -1; + EC_FAIL; } - return 0; -} - -/* ------------------ */ -static uid_t user_to_uid (char *username) -{ - struct passwd *this_passwd; - - /* check for anything */ - if ( !username || strlen ( username ) < 1 ) return 0; - - /* grab the /etc/passwd record relating to username */ - this_passwd = getpwnam ( username ); - - /* return false if there is no structure returned */ - if (this_passwd == NULL) return 0; - - /* return proper uid */ - return this_passwd->pw_uid; - -} - -/* ------------------ */ -static gid_t group_to_gid ( char *group) -{ - struct group *this_group; - /* check for anything */ - if ( !group || strlen ( group ) < 1 ) return 0; - - /* grab the /etc/groups record relating to group */ - this_group = getgrnam ( group ); + if (lstat(cfrombstr(oldpath), &st) == 0 && lstat(cfrombstr(newpath), &st) != 0 && errno == ENOENT) { + /* There's an .AppleDB in the volume root, we move it */ + cmd_argv[0] = "mv"; + cmd_argv[1] = bdata(oldpath); + cmd_argv[2] = (char *)dbdir; + cmd_argv[3] = NULL; + if (run_cmd("mv", cmd_argv) != 0) { + LOG(log_error, logtype_cnid, "set_dbdir: moving CNID db from \"%s\" to \"%s\" failed", + bdata(oldpath), dbdir); + EC_FAIL; + } - /* return false if there is no structure returned */ - if (this_group == NULL) return 0; + } - /* return proper gid */ - return this_group->gr_gid; + if (lstat(cfrombstr(newpath), &st) < 0 && mkdir(cfrombstr(newpath), 0755 ) < 0) { + LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", bdata(newpath)); + EC_FAIL; + } +EC_CLEANUP: + bdestroy(oldpath); + bdestroy(newpath); + EC_EXIT; } /* ------------------ */ @@ -357,13 +370,17 @@ static void set_signal(void) daemon_exit(EXITERR_SYS); } - /* Catch SIGTERM */ - sv.sa_handler = sigterm_handler; + /* Catch SIGTERM and SIGQUIT */ + sv.sa_handler = sig_handler; sigfillset(&sv.sa_mask ); if (sigaction(SIGTERM, &sv, NULL ) < 0 ) { LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) ); daemon_exit(EXITERR_SYS); } + if (sigaction(SIGQUIT, &sv, NULL ) < 0 ) { + LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) ); + daemon_exit(EXITERR_SYS); + } /* Ignore the rest */ sv.sa_handler = SIG_IGN; @@ -400,102 +417,97 @@ static void set_signal(void) /* block everywhere but in pselect */ sigemptyset(&set); sigaddset(&set, SIGCHLD); - sigprocmask(SIG_BLOCK, &set, NULL); + sigprocmask(SIG_SETMASK, &set, NULL); +} + +static int setlimits(void) +{ + struct rlimit rlim; + + if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { + LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno)); + exit(1); + } + if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < 65535) { + rlim.rlim_cur = 65535; + if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < 65535) + rlim.rlim_max = 65535; + if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { + LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno)); + exit(1); + } + } + return 0; +} + +static uid_t uid_from_name(const char *name) +{ + struct passwd *pwd; + + pwd = getpwnam(name); + if (pwd == NULL) + return 0; + return pwd->pw_uid; } /* ------------------ */ int main(int argc, char *argv[]) { - char volpath[MAXPATHLEN + 1]; - int len, actual_len; + char *volname = NULL; + char *volpath = NULL; + char *username = NULL; + int len[DBD_NUM_OPEN_ARGS], actual_len; pid_t pid; int status; char *dbdpn = _PATH_CNID_DBD; - char *host = DEFAULTHOST; - char *port = DEFAULTPORT; + char *host; + char *port; int i; int cc; uid_t uid = 0; gid_t gid = 0; - int err = 0; int debug = 0; int ret; - char *loglevel = NULL; - char *logfile = NULL; sigset_t set; - struct volinfo *volinfo; - - set_processname("cnid_metad"); + AFPObj obj = { 0 }; + struct vol *vol; - while (( cc = getopt( argc, argv, "ds:p:h:u:g:l:f:")) != -1 ) { + while (( cc = getopt( argc, argv, "dF:vV")) != -1 ) { switch (cc) { case 'd': debug = 1; break; - case 'h': - host = strdup(optarg); - break; - case 'u': - uid = user_to_uid (optarg); - if (!uid) { - LOG(log_error, logtype_cnid, "main: bad user %s", optarg); - err++; - } - break; - case 'g': - gid =group_to_gid (optarg); - if (!gid) { - LOG(log_error, logtype_cnid, "main: bad group %s", optarg); - err++; - } - break; - case 'p': - port = strdup(optarg); - break; - case 's': - dbdpn = strdup(optarg); - break; - case 'l': - loglevel = strdup(optarg); - break; - case 'f': - logfile = strdup(optarg); + case 'F': + obj.cmdlineconfigfile = strdup(optarg); break; + case 'v': + case 'V': + printf("cnid_metad (Netatalk %s)\n", VERSION); + return -1; default: - err++; - break; + printf("cnid_metad [-dvV] [-F alternate configfile ]\n"); + return -1; } } - if (loglevel) { - strlcpy(logconfig + 8, loglevel, 13); - free(loglevel); - strcat(logconfig, " "); - } - if (logfile) { - strlcat(logconfig, logfile, MAXPATHLEN); - free(logfile); - } - setuplog(logconfig); + if (!debug && daemonize(0, 0) != 0) + exit(EXITERR_SYS); - if (err) { - LOG(log_error, logtype_cnid, "main: bad arguments"); + if (afp_config_parse(&obj, "cnid_metad") != 0) daemon_exit(1); - } - /* Check PID lockfile and become a daemon */ - switch(server_lock("cnid_metad", _PATH_CNID_METAD_LOCK, debug)) { - case -1: /* error */ - daemon_exit(EXITERR_SYS); - case 0: /* child */ - break; - default: /* server */ - exit(0); - } + (void)setlimits(); + host = atalk_iniparser_getstrdup(obj.iniconfig, INISEC_GLOBAL, "cnid listen", "localhost:4700"); + if ((port = strrchr(host, ':'))) + *port++ = 0; + else + port = DEFAULTPORT; if ((srvfd = tsockfd_create(host, port, 10)) < 0) daemon_exit(1); + LOG(log_note, logtype_afpd, "CNID Server listening on %s:%s", host, port); + /* switch uid/gid */ if (uid || gid) { LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", uid, gid); @@ -523,7 +535,7 @@ int main(int argc, char *argv[]) rqstfd = usockfd_check(srvfd, &set); /* Collect zombie processes and log what happened to them */ if (sigchild) while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - for (i = 0; i < MAXVOLS; i++) { + for (i = 0; i < maxvol; i++) { if (srv[i].pid == pid) { srv[i].pid = 0; close(srv[i].control_fd); @@ -531,11 +543,15 @@ int main(int argc, char *argv[]) } } if (WIFEXITED(status)) { - LOG(log_info, logtype_cnid, "cnid_dbd pid %i exited with exit code %i", + LOG(log_info, logtype_cnid, "cnid_dbd[%i] exited with exit code %i", pid, WEXITSTATUS(status)); + } else { + /* cnid_dbd did a clean exit probably on idle timeout, reset bookkeeping */ + srv[i].tm = 0; + srv[i].count = 0; } - else if (WIFSIGNALED(status)) { - LOG(log_info, logtype_cnid, "cnid_dbd pid %i exited with signal %i", + if (WIFSIGNALED(status)) { + LOG(log_info, logtype_cnid, "cnid_dbd[%i] got signal %i", pid, WTERMSIG(status)); } sigchild = 0; @@ -543,7 +559,7 @@ int main(int argc, char *argv[]) if (rqstfd <= 0) continue; - ret = readt(rqstfd, &len, sizeof(int), 1, 4); + ret = readt(rqstfd, &len[0], sizeof(int) * DBD_NUM_OPEN_ARGS, 1, 4); if (!ret) { /* already close */ @@ -553,46 +569,82 @@ int main(int argc, char *argv[]) LOG(log_severe, logtype_cnid, "error read: %s", strerror(errno)); goto loop_end; } - else if (ret != sizeof(int)) { + else if (ret != DBD_NUM_OPEN_ARGS * sizeof(int)) { LOG(log_error, logtype_cnid, "short read: got %d", ret); goto loop_end; } + /* * checks for buffer overruns. The client libatalk side does it too * before handing the dir path over but who trusts clients? */ - if (!len || len +DBHOMELEN +2 > MAXPATHLEN) { - LOG(log_error, logtype_cnid, "wrong len parameter: %d", len); + if (!len[0] || !len[1]) { + LOG(log_error, logtype_cnid, "wrong len parameter: len[0]: %d, len[1]: %d", len[0], len[1]); goto loop_end; } - actual_len = readt(rqstfd, volpath, len, 1, 5); - if (actual_len < 0) { - LOG(log_severe, logtype_cnid, "Read(2) error : %s", strerror(errno)); + volname = malloc(len[0]); + volpath = malloc(len[1]); + if (len[2]) { + username = malloc(len[2]); + } + if (!volname || !volpath || (len[2] && !username)) { + LOG(log_severe, logtype_cnid, "malloc: %s", strerror(errno)); goto loop_end; } - if (actual_len != len) { - LOG(log_error, logtype_cnid, "error/short read (dir): %s", strerror(errno)); + + actual_len = readt(rqstfd, volname, len[0], 1, 5); + if (actual_len != len[0]) { + LOG(log_severe, logtype_cnid, "readt: %s", strerror(errno)); goto loop_end; } - volpath[len] = '\0'; - /* Load .volinfo file */ - if ((volinfo = allocvolinfo(volpath)) == NULL) { - LOG(log_severe, logtype_cnid, "allocvolinfo(\"%s\"): %s", - volpath, strerror(errno)); + actual_len = readt(rqstfd, volpath, len[1], 1, 5); + if (actual_len != len[1]) { + LOG(log_severe, logtype_cnid, "readt: %s", strerror(errno)); goto loop_end; } - if (set_dbdir(volinfo->v_dbpath) < 0) { + if (len[2]) { + actual_len = readt(rqstfd, username, len[2], 1, 5); + if (actual_len != len[2]) { + LOG(log_severe, logtype_cnid, "readt: %s", strerror(errno)); + goto loop_end; + } + strlcpy(obj.username, username, MAXUSERLEN); + obj.uid = uid_from_name(username); + if (!obj.uid) + goto loop_end; + } else { + obj.username[0] = 0; + } + + LOG(log_debug, logtype_cnid, "user: %s, volume %s, path %s", + username ? username : "-", volname, volpath); + + if (load_volumes(&obj, lv_all) != 0) { + LOG(log_severe, logtype_cnid, "main: error reloading config"); + goto loop_end; + } + + if ((vol = getvolbypath(&obj, volpath)) == NULL) { + LOG(log_severe, logtype_cnid, "main: no volume for path \"%s\"", volpath); goto loop_end; } - maybe_start_dbd(dbdpn, volinfo); + LOG(log_maxdebug, logtype_cnid, "main: dbpath: %s", vol->v_dbpath); + + if (set_dbdir(vol->v_dbpath, vol->v_path) < 0) { + goto loop_end; + } - (void)closevolinfo(volinfo); + maybe_start_dbd(&obj, dbdpn, vol->v_path, username); loop_end: close(rqstfd); + unload_volumes(&obj); + SAFE_FREE(volname); + SAFE_FREE(volpath); + SAFE_FREE(username); } }