Пример #1
0
int main(int argc, char **argv)
{
    int reti;
    uint16_t vid;
    struct vol *vol;
    struct dir *retdir;
    struct path *path;

    /* initialize */
    printf("Initializing\n============\n");
    TEST(setuplog("default:note","/dev/tty"));

    TEST( afp_options_parse_cmdline(&obj, 3, &args[0]) );

    TEST_int( afp_config_parse(&obj, NULL), 0);
    TEST_int( configinit(&obj), 0);
    TEST( cnid_init() );
    TEST( load_volumes(&obj, NULL) );
    TEST_int( dircache_init(8192), 0);
    obj.afp_version = 32;

    printf("\n");

    /* now run tests */
    printf("Running tests\n=============\n");

    TEST_expr(vid = openvol(&obj, "test"), vid != 0);
    TEST_expr(vol = getvolbyvid(vid), vol != NULL);

    /* test directory.c stuff */
    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT_PARENT), retdir != NULL);
    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
    TEST_expr(path = cname(vol, retdir, cnamewrap("Network Trash Folder")), path != NULL);

    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
    TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT_PARENT, "test"), 0);
    TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT, ""), 0);

    TEST_expr(reti = createdir(&obj, vid, DIRDID_ROOT, "dir1"),
              reti == 0 || reti == AFPERR_EXIST);

    TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT, "dir1"), 0);
/*
  FIXME: this doesn't work although it should. "//" get translated to \000 \000 at means ".."
  ie this should getfiledirparms for DIRDID_ROOT_PARENT -- at least afair!
    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "//"), 0);
*/
    TEST_int(createfile(&obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
    TEST_int(delete(&obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
    TEST_int(delete(&obj, vid, DIRDID_ROOT, "dir1"), 0);

    TEST_int(createfile(&obj, vid, DIRDID_ROOT, "file1"), 0);
    TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT, "file1"), 0);
    TEST_int(delete(&obj, vid, DIRDID_ROOT, "file1"), 0);


    /* test enumerate.c stuff */
    TEST_int(enumerate(&obj, vid, DIRDID_ROOT), 0);
}
Пример #2
0
int ad_mv(int argc, char *argv[], AFPObj *obj)
{
    size_t baselen, len;
    int rval;
    char *p, *endp;
    struct stat sb;
    int ch;
    char path[MAXPATHLEN];


    pdid = htonl(1);
    did = htonl(2);

    argc--;
    argv++;

    while ((ch = getopt(argc, argv, "finv")) != -1)
        switch (ch) {
        case 'i':
            iflg = 1;
            fflg = nflg = 0;
            break;
        case 'f':
            fflg = 1;
            iflg = nflg = 0;
            break;
        case 'n':
            nflg = 1;
            fflg = iflg = 0;
            break;
        case 'v':
            vflg = 1;
            break;
        default:
            usage_mv();
        }

    argc -= optind;
    argv += optind;

    if (argc < 2)
        usage_mv();

    set_signal();
    cnid_init();
    if (openvol(obj, argv[argc - 1], &dvolume) != 0) {
        SLOG("Error opening CNID database for source \"%s\": ", argv[argc - 1]);
        return 1;
    }

    /*
     * If the stat on the target fails or the target isn't a directory,
     * try the move.  More than 2 arguments is an error in this case.
     */
    if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
        if (argc > 2)
            usage_mv();
        if (openvol(obj, argv[0], &svolume) != 0) {
            SLOG("Error opening CNID database for destination \"%s\": ", argv[0]);
            return 1;
        }
        rval = do_move(argv[0], argv[1]);
        closevol(&svolume);
        closevol(&dvolume);
        return 1;
    }

    /* It's a directory, move each file into it. */
    if (strlen(argv[argc - 1]) > sizeof(path) - 1)
        ERROR("%s: destination pathname too long", *argv);

    (void)strcpy(path, argv[argc - 1]);
    baselen = strlen(path);
    endp = &path[baselen];
    if (!baselen || *(endp - 1) != '/') {
        *endp++ = '/';
        ++baselen;
    }

    for (rval = 0; --argc; ++argv) {
        /*
         * Find the last component of the source pathname.  It
         * may have trailing slashes.
         */
        p = *argv + strlen(*argv);
        while (p != *argv && p[-1] == '/')
            --p;
        while (p != *argv && p[-1] != '/')
            --p;

        if ((baselen + (len = strlen(p))) >= PATH_MAX) {
            SLOG("%s: destination pathname too long", *argv);
            rval = 1;
        } else {
            memmove(endp, p, (size_t)len + 1);
            openvol(obj, *argv, &svolume);

            if (do_move(*argv, path))
                rval = 1;
            closevol(&svolume);
        }
    }

    closevol(&dvolume);
    return rval;
}
Пример #3
0
int ad_set(int argc, char **argv, AFPObj *obj)
{
    int c, firstarg;
    afpvol_t vol;
    struct stat st;
    int adflags = 0;
    struct adouble ad;

    while ((c = getopt(argc, argv, ":l:t:c:f:a:")) != -1) {
        switch(c) {
        case 'l':
            new_label = strdup(optarg);
            break;
        case 't':
            new_type = strdup(optarg);
            break;
        case 'c':
            new_creator = strdup(optarg);
            break;
        case 'f':
            new_flags = strdup(optarg);
            break;
        case 'a':
            new_attributes = strdup(optarg);
            break;
        case ':':
        case '?':
            usage_set();
            return -1;
            break;
        }

    }

    if (argc <= optind)
        exit(1);

    cnid_init();

    openvol(obj, argv[optind], &vol);
    if (vol.vol->v_path == NULL)
        exit(1);

    if (stat(argv[optind], &st) != 0) {
        perror("stat");
        exit(1);
    }

    if (S_ISDIR(st.st_mode))
        adflags = ADFLAGS_DIR;

    ad_init(&ad, vol.vol);

    if (ad_open(&ad, argv[optind], adflags | ADFLAGS_HF | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666) < 0)
        goto exit;

    if (new_label)
        change_label(argv[optind], &vol, &st, &ad, new_label);
    if (new_type)
        change_type(argv[optind], &vol, &st, &ad, new_type);
    if (new_creator)
        change_creator(argv[optind], &vol, &st, &ad, new_creator);
    if (new_flags)
        change_flags(argv[optind], &vol, &st, &ad, new_flags);
    if (new_attributes)
        change_attributes(argv[optind], &vol, &st, &ad, new_attributes);

    ad_flush(&ad);
    ad_close(&ad, ADFLAGS_HF);

exit:
    closevol(&vol);

    return 0;
}
Пример #4
0
int main(int ac, char **av)
{
    AFPConfig           *config;
    fd_set              rfds;
    void                *ipc;
    struct sigaction	sv;
    sigset_t            sigs;
    int                 ret;

#ifdef TRU64
    argc = ac;
    argv = av;
    set_auth_parameters( ac, av );
#endif /* TRU64 */

    /* Log SIGBUS/SIGSEGV SBT */
    fault_setup(NULL);

    /* Default log setup: log to syslog */
    setuplog("default log_note");

    afp_options_init(&default_options);
    if (!afp_options_parse(ac, av, &default_options))
        exit(EXITERR_CONF);

    /* Save the user's current umask for use with CNID (and maybe some 
     * other things, too). */
    default_options.save_mask = umask( default_options.umask );

    switch(server_lock("afpd", default_options.pidfile,
                       default_options.flags & OPTION_DEBUG)) {
    case -1: /* error */
        exit(EXITERR_SYS);
    case 0: /* child */
        break;
    default: /* server */
        exit(0);
    }
    atexit(afp_exit);

    /* install child handler for asp and dsi. we do this before afp_goaway
     * as afp_goaway references stuff from here. 
     * XXX: this should really be setup after the initial connections. */
    if (!(server_children = server_child_alloc(default_options.connections,
                            CHILD_NFORKS))) {
        LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
        exit(EXITERR_SYS);
    }

    memset(&sv, 0, sizeof(sv));    
    /* linux at least up to 2.4.22 send a SIGXFZ for vfat fs,
       even if the file is open with O_LARGEFILE ! */
#ifdef SIGXFSZ
    sv.sa_handler = SIG_IGN;
    sigemptyset( &sv.sa_mask );
    if (sigaction(SIGXFSZ, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        exit(EXITERR_SYS);
    }
#endif
    
    sv.sa_handler = child_handler;
    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGTERM);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGQUIT);    
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        exit(EXITERR_SYS);
    }

    sv.sa_handler = afp_goaway;
    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGTERM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGQUIT);
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        exit(EXITERR_SYS);
    }

    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGTERM);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGQUIT);
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        exit(EXITERR_SYS);
    }


    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGQUIT);
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        exit(EXITERR_SYS);
    }

    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGTERM);
    sv.sa_flags = SA_RESTART;
    if (sigaction(SIGQUIT, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        exit(EXITERR_SYS);
    }

    /* afpd.conf: not in config file: lockfile, connections, configfile
     *            preference: command-line provides defaults.
     *                        config file over-writes defaults.
     *
     * we also need to make sure that killing afpd during startup
     * won't leave any lingering registered names around.
     */

    sigemptyset(&sigs);
    sigaddset(&sigs, SIGALRM);
    sigaddset(&sigs, SIGHUP);
    sigaddset(&sigs, SIGUSR1);
#if 0
    /* don't block SIGTERM */
    sigaddset(&sigs, SIGTERM);
#endif
    sigaddset(&sigs, SIGCHLD);

    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
    if (!(configs = configinit(&default_options))) {
        LOG(log_error, logtype_afpd, "main: no servers configured");
        exit(EXITERR_CONF);
    }
    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);

    /* Register CNID  */
    cnid_init();

    /* watch atp, dsi sockets and ipc parent/child file descriptor. */
    disasociated_ipc_fd = ipc_server_uds(_PATH_AFP_IPC);
    fd_set_listening_sockets();

    /* set limits */
    (void)setlimits();

    afp_child_t *child;
    int fd[2];  /* we only use one, but server_child_add expects [2] */
    pid_t pid;

    /* wait for an appleshare connection. parent remains in the loop
     * while the children get handled by afp_over_{asp,dsi}.  this is
     * currently vulnerable to a denial-of-service attack if a
     * connection is made without an actual login attempt being made
     * afterwards. establishing timeouts for logins is a possible 
     * solution. */
    while (1) {
        LOG(log_maxdebug, logtype_afpd, "main: polling %i fds", fdset_used);
        pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
        ret = poll(fdset, fdset_used, -1);
        pthread_sigmask(SIG_BLOCK, &sigs, NULL);
        int saveerrno = errno;

        if (reloadconfig) {
            nologin++;
            auth_unload();
            fd_reset_listening_sockets();

            LOG(log_info, logtype_afpd, "re-reading configuration file");
            for (config = configs; config; config = config->next)
                if (config->server_cleanup)
                    config->server_cleanup(config);

            /* configfree close atp socket used for DDP tickle, there's an issue
             * with atp tid. */
            configfree(configs, NULL);
            if (!(configs = configinit(&default_options))) {
                LOG(log_error, logtype_afpd, "config re-read: no servers configured");
                exit(EXITERR_CONF);
            }

            fd_set_listening_sockets();

            nologin = 0;
            reloadconfig = 0;
            errno = saveerrno;
            continue;
        }

        if (ret == 0)
            continue;
        
        if (ret < 0) {
            if (errno == EINTR)
                continue;
            LOG(log_error, logtype_afpd, "main: can't wait for input: %s", strerror(errno));
            break;
        }

        for (int i = 0; i < fdset_used; i++) {
            if (fdset[i].revents & (POLLIN | POLLERR | POLLHUP)) {
                switch (polldata[i].fdtype) {

                case LISTEN_FD:
                    config = (AFPConfig *)polldata[i].data;
                    /* config->server_start is afp_config.c:dsi_start() for DSI */
                    if (child = config->server_start(config, configs, server_children)) {
                        /* Add IPC fd to select fd set */
                        fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0], IPC_FD, child);
                    }
                    break;

                case IPC_FD:
                    child = (afp_child_t *)polldata[i].data;
                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);

                    if (ipc_server_read(server_children, child->ipc_fds[0]) != 0) {
                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0]);
                        close(child->ipc_fds[0]);
                        child->ipc_fds[0] = -1;
                        if (child->disasociated) {
                            LOG(log_note, logtype_afpd, "main: removing reattached child[%u]", child->pid);
                            server_child_remove(server_children, CHILD_DSIFORK, child->pid);
                        }
                    }
                    break;

                case DISASOCIATED_IPC_FD:
                    LOG(log_debug, logtype_afpd, "main: IPC reconnect request");
                    if ((fd[0] = accept(disasociated_ipc_fd, NULL, NULL)) == -1) {
                        LOG(log_error, logtype_afpd, "main: accept: %s", strerror(errno));
                        break;
                    }
                    if (readt(fd[0], &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
                        LOG(log_error, logtype_afpd, "main: readt: %s", strerror(errno));
                        close(fd[0]);
                        break;
                    }
                    LOG(log_note, logtype_afpd, "main: IPC reconnect from pid [%u]", pid);
                    if ((child = server_child_add(server_children, CHILD_DSIFORK, pid, fd)) == NULL) {
                        LOG(log_error, logtype_afpd, "main: server_child_add");
                        close(fd[0]);
                        break;
                    }
                    child->disasociated = 1;
                    fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd[0], IPC_FD, child);
                    break;

                default:
                    LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
                    break;
                } /* switch */
            }  /* if */
        } /* for (i)*/
    } /* while (1) */

    return 0;
}
Пример #5
0
int ad_cp(int argc, char *argv[])
{
    struct stat to_stat, tmp_stat;
    int r, ch, have_trailing_slash;
    char *target;
#if 0
    afpvol_t srcvol;
    afpvol_t dstvol;
#endif

    ppdid = pdid = htonl(1);
    did = htonl(2);

    while ((ch = getopt(argc, argv, "afinpRvx")) != -1)
        switch (ch) {
        case 'a':
            pflag = 1;
            Rflag = 1;
            break;
        case 'f':
            fflag = 1;
            iflag = nflag = 0;
            break;
        case 'i':
            iflag = 1;
            fflag = nflag = 0;
            break;
        case 'n':
            nflag = 1;
            fflag = iflag = 0;
            break;
        case 'p':
            pflag = 1;
            break;
        case 'R':
            Rflag = 1;
            break;
        case 'v':
            vflag = 1;
            break;
        case 'x':
            ftw_options |= FTW_MOUNT;
            break;
        default:
            usage_cp();
            break;
        }
    argc -= optind;
    argv += optind;

    if (argc < 2)
        usage_cp();

    set_signal();
    cnid_init();

    /* Save the target base in "to". */
    target = argv[--argc];
    if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX)
        ERROR("%s: name too long", target);

    to.p_end = to.p_path + strlen(to.p_path);
    if (to.p_path == to.p_end) {
        *to.p_end++ = '.';
        *to.p_end = 0;
    }
    have_trailing_slash = (to.p_end[-1] == '/');
    if (have_trailing_slash)
        STRIP_TRAILING_SLASH(to);
    to.target_end = to.p_end;

    /* Set end of argument list */
    argv[argc] = NULL;

    /*
     * Cp has two distinct cases:
     *
     * cp [-R] source target
     * cp [-R] source1 ... sourceN directory
     *
     * In both cases, source can be either a file or a directory.
     *
     * In (1), the target becomes a copy of the source. That is, if the
     * source is a file, the target will be a file, and likewise for
     * directories.
     *
     * In (2), the real target is not directory, but "directory/source".
     */
    r = stat(to.p_path, &to_stat);
    if (r == -1 && errno != ENOENT)
        ERROR("%s", to.p_path);
    if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
        /*
         * Case (1).  Target is not a directory.
         */
        if (argc > 1)
            ERROR("%s is not a directory", to.p_path);

        /*
         * Need to detect the case:
         *cp -R dir foo
         * Where dir is a directory and foo does not exist, where
         * we want pathname concatenations turned on but not for
         * the initial mkdir().
         */
        if (r == -1) {
            lstat(*argv, &tmp_stat);

            if (S_ISDIR(tmp_stat.st_mode) && Rflag)
                type = DIR_TO_DNE;
            else
                type = FILE_TO_FILE;
        } else
            type = FILE_TO_FILE;

        if (have_trailing_slash && type == FILE_TO_FILE) {
            if (r == -1)
                ERROR("directory %s does not exist", to.p_path);
            else
                ERROR("%s is not a directory", to.p_path);
        }
    } else
        /*
         * Case (2).  Target is a directory.
         */
        type = FILE_TO_DIR;

    /*
     * Keep an inverted copy of the umask, for use in correcting
     * permissions on created directories when not using -p.
     */
    mask = ~umask(0777);
    umask(~mask);

#if 0
    /* Inhereting perms in ad_mkdir etc requires this */
    ad_setfuid(0);
#endif

    /* Load .volinfo file for destination*/
    openvol(to.p_path, &dvolume);

    for (int i = 0; argv[i] != NULL; i++) {
        /* Load .volinfo file for source */
        openvol(argv[i], &svolume);

        if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
            if (alarmed) {
                SLOG("...break");
            } else {
                SLOG("Error: %s: %s", argv[i], strerror(errno));
            }
            closevol(&svolume);
            closevol(&dvolume);
        }
    }
    return rval;
}
Пример #6
0
int main(int ac, char **av)
{
    fd_set              rfds;
    void                *ipc;
    struct sigaction	sv;
    sigset_t            sigs;
    int                 ret;

    /* Parse argv args and initialize default options */
    afp_options_parse_cmdline(&obj, ac, av);

    if (!(obj.cmdlineflags & OPTION_DEBUG) && (daemonize(0, 0) != 0))
        exit(EXITERR_SYS);

    /* Log SIGBUS/SIGSEGV SBT */
    fault_setup(NULL);

    if (afp_config_parse(&obj, "afpd") != 0)
        afp_exit(EXITERR_CONF);

    /* Save the user's current umask */
    obj.options.save_mask = umask(obj.options.umask);

    /* install child handler for asp and dsi. we do this before afp_goaway
     * as afp_goaway references stuff from here. 
     * XXX: this should really be setup after the initial connections. */
    if (!(server_children = server_child_alloc(obj.options.connections, CHILD_NFORKS))) {
        LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
        afp_exit(EXITERR_SYS);
    }
    
    sigemptyset(&sigs);
    pthread_sigmask(SIG_SETMASK, &sigs, NULL);

    memset(&sv, 0, sizeof(sv));    
    /* linux at least up to 2.4.22 send a SIGXFZ for vfat fs,
       even if the file is open with O_LARGEFILE ! */
#ifdef SIGXFSZ
    sv.sa_handler = SIG_IGN;
    sigemptyset( &sv.sa_mask );
    if (sigaction(SIGXFSZ, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        afp_exit(EXITERR_SYS);
    }
#endif
    
    sv.sa_handler = afp_goaway; /* handler for all sigs */

    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGTERM);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGQUIT);    
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        afp_exit(EXITERR_SYS);
    }

    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGTERM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGQUIT);
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        afp_exit(EXITERR_SYS);
    }

    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGTERM);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGQUIT);
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        afp_exit(EXITERR_SYS);
    }

    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGQUIT);
    sv.sa_flags = SA_RESTART;
    if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        afp_exit(EXITERR_SYS);
    }

    sigemptyset( &sv.sa_mask );
    sigaddset(&sv.sa_mask, SIGALRM);
    sigaddset(&sv.sa_mask, SIGHUP);
    sigaddset(&sv.sa_mask, SIGUSR1);
    sigaddset(&sv.sa_mask, SIGCHLD);
    sigaddset(&sv.sa_mask, SIGTERM);
    sv.sa_flags = SA_RESTART;
    if (sigaction(SIGQUIT, &sv, NULL ) < 0 ) {
        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
        afp_exit(EXITERR_SYS);
    }

    /* afp.conf:  not in config file: lockfile, configfile
     *            preference: command-line provides defaults.
     *                        config file over-writes defaults.
     *
     * we also need to make sure that killing afpd during startup
     * won't leave any lingering registered names around.
     */

    sigemptyset(&sigs);
    sigaddset(&sigs, SIGALRM);
    sigaddset(&sigs, SIGHUP);
    sigaddset(&sigs, SIGUSR1);
#if 0
    /* don't block SIGTERM */
    sigaddset(&sigs, SIGTERM);
#endif
    sigaddset(&sigs, SIGCHLD);

    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
    if (configinit(&obj) != 0) {
        LOG(log_error, logtype_afpd, "main: no servers configured");
        afp_exit(EXITERR_CONF);
    }
    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);

    /* Initialize */
    cnid_init();
    
    /* watch atp, dsi sockets and ipc parent/child file descriptor. */

    if (obj.options.flags & OPTION_KEEPSESSIONS) {
        LOG(log_note, logtype_afpd, "Activating continous service");
        disasociated_ipc_fd = ipc_server_uds(_PATH_AFP_IPC);
    }

    fd_set_listening_sockets(&obj);

    /* set limits */
    (void)setlimits();

    afp_child_t *child;
    int recon_ipc_fd;
    pid_t pid;
    int saveerrno;

    /* wait for an appleshare connection. parent remains in the loop
     * while the children get handled by afp_over_{asp,dsi}.  this is
     * currently vulnerable to a denial-of-service attack if a
     * connection is made without an actual login attempt being made
     * afterwards. establishing timeouts for logins is a possible 
     * solution. */
    while (1) {
        LOG(log_maxdebug, logtype_afpd, "main: polling %i fds", fdset_used);
        pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
        ret = poll(fdset, fdset_used, -1);
        pthread_sigmask(SIG_BLOCK, &sigs, NULL);
        saveerrno = errno;

        if (gotsigchld) {
            gotsigchld = 0;
            child_handler();
            continue;
        }

        if (reloadconfig) {
            nologin++;
            auth_unload();
            fd_reset_listening_sockets(&obj);

            LOG(log_info, logtype_afpd, "re-reading configuration file");

            configfree(&obj, NULL);
            if (configinit(&obj) != 0) {
                LOG(log_error, logtype_afpd, "config re-read: no servers configured");
                afp_exit(EXITERR_CONF);
            }

            fd_set_listening_sockets(&obj);

            nologin = 0;
            reloadconfig = 0;
            errno = saveerrno;
            continue;
        }

        if (ret == 0)
            continue;
        
        if (ret < 0) {
            if (errno == EINTR)
                continue;
            LOG(log_error, logtype_afpd, "main: can't wait for input: %s", strerror(errno));
            break;
        }

        for (int i = 0; i < fdset_used; i++) {
            if (fdset[i].revents & (POLLIN | POLLERR | POLLHUP | POLLNVAL)) {
                switch (polldata[i].fdtype) {

                case LISTEN_FD:
                    if (child = dsi_start(&obj, (DSI *)polldata[i].data, server_children)) {
                        /* Add IPC fd to select fd set */
                        fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY,
                                     &fdset,
                                     &polldata,
                                     &fdset_used,
                                     &fdset_size,
                                     child->ipc_fd,
                                     IPC_FD,
                                     child);
                    }
                    break;

                case IPC_FD:
                    child = (afp_child_t *)polldata[i].data;
                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);

                    if (ipc_server_read(server_children, child->ipc_fd) != 0) {
                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fd);
                        close(child->ipc_fd);
                        child->ipc_fd = -1;
                        if ((obj.options.flags & OPTION_KEEPSESSIONS) && child->disasociated) {
                            LOG(log_note, logtype_afpd, "main: removing reattached child[%u]", child->pid);
                            server_child_remove(server_children, CHILD_DSIFORK, child->pid);
                        }
                    }
                    break;

                case DISASOCIATED_IPC_FD:
                    LOG(log_debug, logtype_afpd, "main: IPC reconnect request");
                    if ((recon_ipc_fd = accept(disasociated_ipc_fd, NULL, NULL)) == -1) {
                        LOG(log_error, logtype_afpd, "main: accept: %s", strerror(errno));
                        break;
                    }
                    if (readt(recon_ipc_fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
                        LOG(log_error, logtype_afpd, "main: readt: %s", strerror(errno));
                        close(recon_ipc_fd);
                        break;
                    }
                    LOG(log_note, logtype_afpd, "main: IPC reconnect from pid [%u]", pid);

                    if ((child = server_child_add(server_children, CHILD_DSIFORK, pid, recon_ipc_fd)) == NULL) {
                        LOG(log_error, logtype_afpd, "main: server_child_add");
                        close(recon_ipc_fd);
                        break;
                    }
                    child->disasociated = 1;
                    fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY,
                                 &fdset,
                                 &polldata,
                                 &fdset_used,
                                 &fdset_size,
                                 recon_ipc_fd,
                                 IPC_FD,
                                 child);
                    break;

                default:
                    LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
                    break;
                } /* switch */
            }  /* if */
        } /* for (i)*/
    } /* while (1) */

    return 0;
}