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=817bce4a4569b0d983e335e7ff2fdd7fbda808fa;hpb=50f402a0be7019ce71e8cba9ee70e1f88613a4ec;p=netatalk.git diff --git a/etc/cnid_dbd/cnid_metad.c b/etc/cnid_dbd/cnid_metad.c index 817bce4a..b05d72db 100644 --- a/etc/cnid_dbd/cnid_metad.c +++ b/etc/cnid_dbd/cnid_metad.c @@ -87,11 +87,12 @@ #include #include -#include +#include #include #include #include #include +#include #include #include @@ -106,17 +107,17 @@ 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 4096 #define DEFAULTHOST "localhost" #define DEFAULTPORT "4700" struct server { - struct vol *vol; + 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 */ }; @@ -124,16 +125,17 @@ static struct server srv[MAXVOLS]; 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); @@ -141,23 +143,29 @@ static void sigterm_handler(int sig) daemon_exit(0); } -static struct server *test_usockfn(const struct vol *vol) +static struct server *test_usockfn(const char *path) { int i; - if (!(vol->v_flags & AFPVOL_OPEN)) - return NULL; - for (i = 0; i < maxvol; i++) { - if (vol->v_vid == srv[i].vol->v_vid) + if (srv[i].v_path && STRCMP(path, ==, srv[i].v_path)) return &srv[i]; } return NULL; } -/* -------------------- */ -static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol) +/** + * 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; @@ -166,13 +174,13 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol) time_t t; char buf1[8]; char buf2[8]; - char *volpath = vol->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(vol); + 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; @@ -180,16 +188,16 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol) 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 (i < maxvol) or the first free slot (i == maxvol)*/ - for (i = 0; i <= maxvol; i++) { - if (srv[i].vol == NULL && i < MAXVOLS) { + for (i = 0; i <= maxvol && i < MAXVOLS; i++) { + if (srv[i].v_path == NULL) { up = &srv[i]; - up->vol = vol; - vol->v_flags |= AFPVOL_OPEN; + if ((up->v_path = strdup(volpath)) == NULL) + return -1; up->tm = t; up->count = 0; if (i == maxvol) @@ -202,25 +210,30 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol) 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"); + } } /* @@ -261,10 +274,22 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol) /* 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->vol->v_path); - ret = execlp(dbdpn, dbdpn, "-F", obj->options.configfile, "-p", volpath, "-t", buf1, "-l", buf2, "-d", NULL); + 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, NULL); + 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)); @@ -280,66 +305,46 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol) } /* ------------------ */ -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; } /* ------------------ */ @@ -365,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; @@ -408,7 +417,7 @@ 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) @@ -431,11 +440,23 @@ static int setlimits(void) 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; @@ -445,7 +466,6 @@ int main(int argc, char *argv[]) int cc; uid_t uid = 0; gid_t gid = 0; - int err = 0; int debug = 0; int ret; sigset_t set; @@ -470,30 +490,16 @@ int main(int argc, char *argv[]) } } - /* Check for PID lockfile */ - if (check_lockfile("cnid_metad", _PATH_CNID_METAD_LOCK)) - return -1; - if (!debug && daemonize(0, 0) != 0) exit(EXITERR_SYS); - /* Create PID lockfile */ - if (create_lockfile("cnid_metad", _PATH_CNID_METAD_LOCK)) - return -1; - - if (afp_config_parse(&obj) != 0) - daemon_exit(1); - - set_processname("cnid_metad"); - setuplog(obj.options.logconfig, obj.options.logfile); - - if (load_volumes(&obj, NULL) != 0) + if (afp_config_parse(&obj, "cnid_metad") != 0) daemon_exit(1); (void)setlimits(); - host = iniparser_getstrdup(obj.iniconfig, INISEC_CNID, "listen", "localhost:4700"); - if (port = strrchr(host, ':')) + host = atalk_iniparser_getstrdup(obj.iniconfig, INISEC_GLOBAL, "cnid listen", "localhost:4700"); + if ((port = strrchr(host, ':'))) *port++ = 0; else port = DEFAULTPORT; @@ -533,15 +539,18 @@ int main(int argc, char *argv[]) if (srv[i].pid == pid) { srv[i].pid = 0; close(srv[i].control_fd); - srv[i].vol->v_flags &= ~AFPVOL_OPEN; break; } } if (WIFEXITED(status)) { 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)) { + if (WIFSIGNALED(status)) { LOG(log_info, logtype_cnid, "cnid_dbd[%i] got signal %i", pid, WTERMSIG(status)); } @@ -550,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 */ @@ -560,47 +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; + } + + 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; } - actual_len = readt(rqstfd, volpath, len, 1, 5); - if (actual_len < 0) { - LOG(log_severe, logtype_cnid, "Read(2) error : %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; } - if (actual_len != len) { - LOG(log_error, logtype_cnid, "error/short read (dir): %s", 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; } - volpath[len] = '\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; + } - if (load_volumes(&obj, NULL) != 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(volpath)) == NULL) { + if ((vol = getvolbypath(&obj, volpath)) == NULL) { LOG(log_severe, logtype_cnid, "main: no volume for path \"%s\"", volpath); goto loop_end; } - if (set_dbdir(vol->v_dbpath) < 0) { + LOG(log_maxdebug, logtype_cnid, "main: dbpath: %s", vol->v_dbpath); + + if (set_dbdir(vol->v_dbpath, vol->v_path) < 0) { goto loop_end; } - maybe_start_dbd(&obj, dbdpn, vol); + 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); } }