Example #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);
}
Example #2
0
int main(int argc, char **argv)
{
    AFPObj obj = { 0 };

    if (argc < 2) {
        usage_main();
        return 1;
    }

    if (afp_config_parse(&obj, "ad") != 0)
        return 1;

    setuplog("default:note", "/dev/tty");

    if (load_volumes(&obj, LV_DEFAULT) != 0)
        return 1;

    if (STRCMP(argv[1], ==, "ls"))
        return ad_ls(argc - 1, argv + 1, &obj);
    else if (STRCMP(argv[1], ==, "cp"))
Example #3
0
/* ------------------ */
int main(int argc, char *argv[])
{
    char  volpath[MAXPATHLEN + 1];
    int   len, actual_len;
    pid_t pid;
    int   status;
    char  *dbdpn = _PATH_CNID_DBD;
    char  *host = DEFAULTHOST;
    char  *port = DEFAULTPORT;
    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");

    while (( cc = getopt( argc, argv, "ds:p:h:u:g:l:f:")) != -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);
            break;
        default:
            err++;
            break;
        }
    }

    if (loglevel) {
        strlcpy(logconfig + 8, loglevel, 13);
        free(loglevel);
        strcat(logconfig, " ");
    }
    if (logfile) {
        strlcat(logconfig, logfile, MAXPATHLEN);
        free(logfile);
    }
    setuplog(logconfig);

    if (err) {
        LOG(log_error, logtype_cnid, "main: bad arguments");
        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);
    }

    if ((srvfd = tsockfd_create(host, port, 10)) < 0)
        daemon_exit(1);

    /* switch uid/gid */
    if (uid || gid) {
        LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", uid, gid);
        if (gid) {
            if (SWITCH_TO_GID(gid) < 0) {
                LOG(log_info, logtype_cnid, "unable to switch to group %d", gid);
                daemon_exit(1);
            }
        }
        if (uid) {
            if (SWITCH_TO_UID(uid) < 0) {
                LOG(log_info, logtype_cnid, "unable to switch to user %d", uid);
                daemon_exit(1);
            }
        }
    }

    set_signal();

    sigemptyset(&set);
    sigprocmask(SIG_SETMASK, NULL, &set);
    sigdelset(&set, SIGCHLD);

    while (1) {
        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++) {
                if (srv[i].pid == pid) {
                    srv[i].pid = 0;
                    close(srv[i].control_fd);
                    break;
                }
            }
            if (WIFEXITED(status)) {
                LOG(log_info, logtype_cnid, "cnid_dbd pid %i exited with exit code %i",
                    pid, WEXITSTATUS(status));
            }
            else if (WIFSIGNALED(status)) {
                LOG(log_info, logtype_cnid, "cnid_dbd pid %i exited with signal %i",
                    pid, WTERMSIG(status));
            }
            sigchild = 0;
        }
        if (rqstfd <= 0)
            continue;

        ret = readt(rqstfd, &len, sizeof(int), 1, 4);

        if (!ret) {
            /* already close */
            goto loop_end;
        }
        else if (ret < 0) {
            LOG(log_severe, logtype_cnid, "error read: %s", strerror(errno));
            goto loop_end;
        }
        else if (ret != 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);
            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));
            goto loop_end;
        }
        if (actual_len != len) {
            LOG(log_error, logtype_cnid, "error/short read (dir): %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));
            goto loop_end;
        }

        if (set_dbdir(volinfo->v_dbpath) < 0) {
            goto loop_end;
        }

        maybe_start_dbd(dbdpn, volinfo);

        (void)closevolinfo(volinfo);

    loop_end:
        close(rqstfd);
    }
}
Example #4
0
int main(int argc, char **argv)
{
    int c, lockfd, ret = -1;
    int dump=0, scan=0, rebuild=0, prep_upgrade=0, rebuildindexes=0, dumpindexes=0, force=0;
    dbd_flags_t flags = 0;
    char *volpath;
    int cdir;

    if (geteuid() != 0) {
        usage();
        exit(EXIT_FAILURE);
    }
    /* Inhereting perms in ad_mkdir etc requires this */
    ad_setfuid(0);

    while ((c = getopt(argc, argv, ":cdefinrstuvx")) != -1) {
        switch(c) {
        case 'c':
            flags |= DBD_FLAGS_CLEANUP;
            break;
        case 'd':
            dump = 1;
            break;
        case 'i':
            dumpindexes = 1;
            break;
        case 's':
            scan = 1;
            flags |= DBD_FLAGS_SCAN;
            break;
        case 'n':
            nocniddb = 1; /* FIXME: this could/should be a flag too for consistency */
            break;
        case 'r':
            rebuild = 1;
            break;
        case 't':
            flags |= DBD_FLAGS_STATS;
            break;
        case 'u':
            prep_upgrade = 1;
            break;
        case 'v':
            verbose = 1;
            break;
        case 'e':
            exclusive = 1;
            flags |= DBD_FLAGS_EXCL;
            break;
        case 'x':
            rebuildindexes = 1;
            break;
        case 'f':
            force = 1;
            exclusive = 1;
            flags |= DBD_FLAGS_FORCE | DBD_FLAGS_EXCL;
            break;
        case ':':
        case '?':
            usage();
            exit(EXIT_FAILURE);
            break;
        }
    }

    if ((dump + scan + rebuild + prep_upgrade) != 1) {
        usage();
        exit(EXIT_FAILURE);
    }

    if ( (optind + 1) != argc ) {
        usage();
        exit(EXIT_FAILURE);
    }
    volpath = argv[optind];

    setvbuf(stdout, (char *) NULL, _IONBF, 0);

    /* Remember cwd */
    if ((cdir = open(".", O_RDONLY)) < 0) {
        dbd_log( LOGSTD, "Can't open dir: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
        
    /* Setup signal handling */
    set_signal();

    /* Setup logging. Should be portable among *NIXes */
    if (!verbose)
        setuplog("default log_info /dev/tty");
    else
        setuplog("default log_debug /dev/tty");

    /* Load .volinfo file */
    if (loadvolinfo(volpath, &volinfo) == -1) {
        dbd_log( LOGSTD, "Not a Netatalk volume at '%s', no .volinfo file at '%s/.AppleDesktop/.volinfo' or unknown volume options", volpath, volpath);
        exit(EXIT_FAILURE);
    }
    if (vol_load_charsets(&volinfo) == -1) {
        dbd_log( LOGSTD, "Error loading charsets!");
        exit(EXIT_FAILURE);
    }

    /* Sanity checks to ensure we can touch this volume */
    if (volinfo.v_vfs_ea != AFPVOL_EA_AD && volinfo.v_vfs_ea != AFPVOL_EA_SYS) {
        dbd_log( LOGSTD, "Unknown Extended Attributes option: %u", volinfo.v_vfs_ea);
        exit(EXIT_FAILURE);        
    }

    /* Enuser dbpath is there, create if necessary */
    struct stat st;
    if (stat(volinfo.v_dbpath, &st) != 0) {
        if (errno != ENOENT) {
            dbd_log( LOGSTD, "Can't stat dbpath \"%s\": %s", volinfo.v_dbpath, strerror(errno));
            exit(EXIT_FAILURE);        
        }
        if ((mkdir(volinfo.v_dbpath, 0755)) != 0) {
            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
            exit(EXIT_FAILURE);
        }        
    }

    /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
    if ( (strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
        dbd_log( LOGSTD, "Volume pathname too long");
        exit(EXIT_FAILURE);        
    }
    strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
    strcat(dbpath, "/.AppleDB");

    /* Check or create dbpath */
    int dbdirfd = open(dbpath, O_RDONLY);
    if (dbdirfd == -1 && errno == ENOENT) {
        if (errno == ENOENT) {
            if ((mkdir(dbpath, 0755)) != 0) {
                dbd_log( LOGSTD, "Can't create .AppleDB for \"%s\": %s", dbpath, strerror(errno));
                exit(EXIT_FAILURE);
            }
        } else {
            dbd_log( LOGSTD, "Somethings wrong with .AppleDB for \"%s\", giving up: %s", dbpath, strerror(errno));
            exit(EXIT_FAILURE);
        }
    } else {
        close(dbdirfd);
    }

    /* Get db lock */
    if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
        goto exit_failure;
    if (db_locked != LOCK_EXCL) {
        /* Couldn't get exclusive lock, try shared lock if -e wasn't requested */
        if (exclusive) {
            dbd_log(LOGSTD, "Database is in use and exlusive was requested");
            goto exit_failure;
        }
        if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD)
            goto exit_failure;
    }

    /* Prepare upgrade ? */
    if (prep_upgrade) {
        if (dbif_env_remove(dbpath))
            goto exit_failure;
        goto exit_success;
    }        

    /* Check if -f is requested and wipe db if yes */
    if ((flags & DBD_FLAGS_FORCE) && rebuild && (volinfo.v_flags & AFPVOL_CACHE)) {
        char cmd[8 + MAXPATHLEN];
        if ((db_locked = get_lock(LOCK_FREE, NULL)) != 0)
            goto exit_failure;

        snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath);
        dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath);
        system(cmd);
        if ((mkdir(dbpath, 0755)) != 0) {
            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
            exit(EXIT_FAILURE);
        }
        dbd_log( LOGDEBUG, "Removed old database.");
        if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
            goto exit_failure;
    }

    /* 
       Lets start with the BerkeleyDB stuff
    */
    if ( ! nocniddb) {
        if ((dbd = dbif_init(dbpath, "cnid2.db")) == NULL)
            goto exit_failure;
        
        if (dbif_env_open(dbd,
                          &db_param,
                          (db_locked == LOCK_EXCL) ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) {
            dbd_log( LOGSTD, "error opening database!");
            goto exit_failure;
        }

        if (db_locked == LOCK_EXCL)
            dbd_log( LOGDEBUG, "Finished recovery.");

        if (dbif_open(dbd, NULL, rebuildindexes) < 0) {
            dbif_close(dbd);
            goto exit_failure;
        }
    }

    /* Downgrade db lock if not running exclusive */
    if (!exclusive && (db_locked == LOCK_EXCL)) {
        if (get_lock(LOCK_UNLOCK, NULL) != 0)
            goto exit_failure;
        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD)
            goto exit_failure;
    }

    /* Now execute given command scan|rebuild|dump */
    if (dump && ! nocniddb) {
        if (dbif_dump(dbd, dumpindexes) < 0) {
            dbd_log( LOGSTD, "Error dumping database");
        }
    } else if ((rebuild && ! nocniddb) || scan) {
        if (cmd_dbd_scanvol(dbd, &volinfo, flags) < 0) {
            dbd_log( LOGSTD, "Error repairing database.");
        }
    }

    /* Cleanup */
    dbd_log(LOGDEBUG, "Closing db");
    if (! nocniddb) {
        if (dbif_close(dbd) < 0) {
            dbd_log( LOGSTD, "Error closing database");
            goto exit_failure;
        }
    }

exit_success:
    ret = 0;

exit_failure:
    get_lock(0, NULL);
    
    if ((fchdir(cdir)) < 0)
        dbd_log(LOGSTD, "fchdir: %s", strerror(errno));

    if (ret == 0)
        exit(EXIT_SUCCESS);
    else
        exit(EXIT_FAILURE);
}
Example #5
0
int main(int argc, char *argv[])
{
  set_processname("logger_Test");
#if 0
  LOG(log_severe, logtype_logger, "Logging Test starting: this should only log to syslog");

  /* syslog testing */
  LOG(log_severe, logtype_logger, "Disabling syslog logging.");
  unsetuplog("Default");
  LOG(log_error, logtype_default, "This shouldn't log to syslog: LOG(log_error, logtype_default).");
  LOG(log_error, logtype_logger, "This shouldn't log to syslog: LOG(log_error, logtype_logger).");
  setuplog("Default LOG_INFO");
  LOG(log_info, logtype_logger, "Set syslog logging to 'log_info', so this should log again. LOG(log_info, logtype_logger).");
  LOG(log_error, logtype_logger, "This should log to syslog: LOG(log_error, logtype_logger).");
  LOG(log_error, logtype_default, "This should log to syslog. LOG(log_error, logtype_default).");
  LOG(log_debug, logtype_logger, "This shouldn't log to syslog. LOG(log_debug, logtype_logger).");
  LOG(log_debug, logtype_default, "This shouldn't log to syslog. LOG(log_debug, logtype_default).");
  LOG(log_severe, logtype_logger, "Disabling syslog logging.");
  unsetuplog("Default");
#endif
  /* filelog testing */

  setuplog("DSI:maxdebug", "test.log");
  LOG(log_info, logtype_dsi, "This should log.");
  LOG(log_error, logtype_default, "This should not log.");

  setuplog("Default:debug", "test.log");
  LOG(log_debug, logtype_default, "This should log.");
  LOG(log_maxdebug, logtype_default, "This should not log.");

  LOG(log_maxdebug, logtype_dsi, "This should still log.");

  /* flooding prevention check */
  LOG(log_debug, logtype_default, "Flooding 3x");
  for (int i = 0; i < 3; i++) {
      LOG(log_debug, logtype_default, "Flooding...");
  }
  /* wipe the array */
  LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");

  LOG(log_debug, logtype_default, "-============");
  LOG(log_debug, logtype_default, "Flooding 5x");
  for (int i = 0; i < 5; i++) {
      LOG(log_debug, logtype_default, "Flooding...");
  }
  LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");

  LOG(log_debug, logtype_default, "o============");
  LOG(log_debug, logtype_default, "Flooding 2005x");
  for (int i = 0; i < 2005; i++) {
      LOG(log_debug, logtype_default, "Flooding...");
  }
  LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");

  LOG(log_debug, logtype_default, "0============");
  LOG(log_debug, logtype_default, "Flooding 11x1");
  for (int i = 0; i < 11; i++) {
      LOG(log_error, logtype_default, "flooding 11x1 1");
  }

  LOG(log_debug, logtype_default, "1============");
  LOG(log_debug, logtype_default, "Flooding 11x2");
  for (int i = 0; i < 11; i++) {
      LOG(log_error, logtype_default, "flooding 11x2 1");
      LOG(log_error, logtype_default, "flooding 11x2 2");
  }

  LOG(log_debug, logtype_default, "2============");
  LOG(log_debug, logtype_default, "Flooding 11x3");
  for (int i = 0; i < 11; i++) {
      LOG(log_error, logtype_default, "flooding 11x3 1");
      LOG(log_error, logtype_default, "flooding 11x3 2");
      LOG(log_error, logtype_default, "flooding 11x3 3");
  }

  LOG(log_debug, logtype_default, "3============");
  LOG(log_debug, logtype_default, "Flooding 11x4");
  for (int i = 0; i < 11; i++) {
      LOG(log_error, logtype_default, "flooding 11x4 1");
      LOG(log_error, logtype_default, "flooding 11x4 2");
      LOG(log_error, logtype_default, "flooding 11x4 3");
      LOG(log_error, logtype_default, "flooding 11x4 4");
  }


  return 0;
}
Example #6
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;
}
Example #7
0
/* -------------------------------------------
 afp over dsi. this never returns. 
*/
void afp_over_dsi(AFPObj *obj)
{
    DSI *dsi = (DSI *) obj->handle;
    int rc_idx;
    u_int32_t err, cmd;
    u_int8_t function;

    AFPobj = obj;
    obj->exit = afp_dsi_die;
    obj->reply = (int (*)()) dsi_cmdreply;
    obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
    dsi->tickle = 0;

    afp_over_dsi_sighandlers(obj);

    if (dircache_init(obj->options.dircachesize) != 0)
        afp_dsi_die(EXITERR_SYS);

    /* set TCP snd/rcv buf */
    if (obj->options.tcp_rcvbuf) {
        if (setsockopt(dsi->socket,
                       SOL_SOCKET,
                       SO_RCVBUF,
                       &obj->options.tcp_rcvbuf,
                       sizeof(obj->options.tcp_rcvbuf)) != 0) {
            LOG(log_error, logtype_dsi, "afp_over_dsi: setsockopt(SO_RCVBUF): %s", strerror(errno));
        }
    }
    if (obj->options.tcp_sndbuf) {
        if (setsockopt(dsi->socket,
                       SOL_SOCKET,
                       SO_SNDBUF,
                       &obj->options.tcp_sndbuf,
                       sizeof(obj->options.tcp_sndbuf)) != 0) {
            LOG(log_error, logtype_dsi, "afp_over_dsi: setsockopt(SO_SNDBUF): %s", strerror(errno));
        }
    }

    /* set TCP_NODELAY */
    int flag = 1;
    setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));

    /* get stuck here until the end */
    while (1) {
        if (sigsetjmp(recon_jmp, 1) != 0)
            /* returning from SIGALARM handler for a primary reconnect */
            continue;

        /* Blocking read on the network socket */
        cmd = dsi_stream_receive(dsi);

        if (cmd == 0) {
            /* cmd == 0 is the error condition */
            if (dsi->flags & DSI_RECONSOCKET) {
                /* we just got a reconnect so we immediately try again to receive on the new fd */
                dsi->flags &= ~DSI_RECONSOCKET;
                continue;
            }

            /* the client sometimes logs out (afp_logout) but doesn't close the DSI session */
            if (dsi->flags & DSI_AFP_LOGGED_OUT) {
                LOG(log_note, logtype_afpd, "afp_over_dsi: client logged out, terminating DSI session");
                afp_dsi_close(obj);
                exit(0);
            }

#if 0
            /*  got ECONNRESET in read from client => exit*/
            if (dsi->flags & DSI_GOT_ECONNRESET) {
                LOG(log_note, logtype_afpd, "afp_over_dsi: client connection reset");
                afp_dsi_close(obj);
                exit(0);
            }
#endif

            if (dsi->flags & DSI_RECONINPROG) {
                LOG(log_note, logtype_afpd, "afp_over_dsi: failed reconnect");
                afp_dsi_close(obj);
                exit(0);
            }

            /* Some error on the client connection, enter disconnected state */
            if (dsi_disconnect(dsi) != 0)
                afp_dsi_die(EXITERR_CLNT);

            while (dsi->flags & DSI_DISCONNECTED)
                pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
            continue; /* continue receiving until disconnect timer expires
                       * or a primary reconnect succeeds  */
        }

        if (!(dsi->flags & DSI_EXTSLEEP) && (dsi->flags & DSI_SLEEPING)) {
            LOG(log_debug, logtype_afpd, "afp_over_dsi: got data, ending normal sleep");
            dsi->flags &= ~DSI_SLEEPING;
            dsi->tickle = 0;
        }

        if (reload_request) {
            reload_request = 0;
            load_volumes(AFPobj);
        }

        /* The first SIGINT enables debugging, the next restores the config */
        if (debug_request) {
            static int debugging = 0;
            debug_request = 0;

            dircache_dump();
            uuidcache_dump();

            if (debugging) {
                if (obj->options.logconfig)
                    setuplog(obj->options.logconfig);
                else
                    setuplog("default log_note");
                debugging = 0;
            } else {
                char logstr[50];
                debugging = 1;
                sprintf(logstr, "default log_maxdebug /tmp/afpd.%u.XXXXXX", getpid());
                setuplog(logstr);
            }
        }


        dsi->flags |= DSI_DATA;
        dsi->tickle = 0;

        switch(cmd) {

        case DSIFUNC_CLOSE:
            LOG(log_debug, logtype_afpd, "DSI: close session request");
            afp_dsi_close(obj);
            LOG(log_note, logtype_afpd, "done");
            exit(0);

        case DSIFUNC_TICKLE:
            dsi->flags &= ~DSI_DATA; /* thats no data in the sense we use it in alarm_handler */
            LOG(log_debug, logtype_afpd, "DSI: client tickle");
            /* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
            if ((dsi->flags & DSI_DIE))
                dsi_tickle(dsi);
            break;

        case DSIFUNC_CMD:
#ifdef AFS
            if ( writtenfork ) {
                if ( flushfork( writtenfork ) < 0 ) {
                    LOG(log_error, logtype_afpd, "main flushfork: %s", strerror(errno) );
                }
                writtenfork = NULL;
            }
#endif /* AFS */

            function = (u_char) dsi->commands[0];

            /* AFP replay cache */
            rc_idx = dsi->clientID % REPLAYCACHE_SIZE;
            LOG(log_debug, logtype_dsi, "DSI request ID: %u", dsi->clientID);

            if (replaycache[rc_idx].DSIreqID == dsi->clientID
                && replaycache[rc_idx].AFPcommand == function) {
                LOG(log_note, logtype_afpd, "AFP Replay Cache match: id: %u / cmd: %s",
                    dsi->clientID, AfpNum2name(function));
                err = replaycache[rc_idx].result;
            /* AFP replay cache end */
            } else {
                /* send off an afp command. in a couple cases, we take advantage
                 * of the fact that we're a stream-based protocol. */
                if (afp_switch[function]) {
                    dsi->datalen = DSI_DATASIZ;
                    dsi->flags |= DSI_RUNNING;

                    LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));

                    err = (*afp_switch[function])(obj,
                                                  (char *)&dsi->commands, dsi->cmdlen,
                                                  (char *)&dsi->data, &dsi->datalen);

                    LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                        AfpNum2name(function), AfpErr2name(err));

                    dir_free_invalid_q();

#ifdef FORCE_UIDGID
                    /* bring everything back to old euid, egid */
                    if (obj->force_uid)
                        restore_uidgid ( &obj->uidgid );
#endif /* FORCE_UIDGID */
                    dsi->flags &= ~DSI_RUNNING;

                    /* Add result to the AFP replay cache */
                    replaycache[rc_idx].DSIreqID = dsi->clientID;
                    replaycache[rc_idx].AFPcommand = function;
                    replaycache[rc_idx].result = err;
                } else {
                    LOG(log_error, logtype_afpd, "bad function %X", function);
                    dsi->datalen = 0;
                    err = AFPERR_NOOP;
                }
            }

            /* single shot toggle that gets set by dsi_readinit. */
            if (dsi->flags & DSI_NOREPLY) {
                dsi->flags &= ~DSI_NOREPLY;
                break;
            } else if (!dsi_cmdreply(dsi, err)) {
                LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
                if (dsi_disconnect(dsi) != 0)
                    afp_dsi_die(EXITERR_CLNT);
            }
            break;

        case DSIFUNC_WRITE: /* FPWrite and FPAddIcon */
            function = (u_char) dsi->commands[0];
            if ( afp_switch[ function ] != NULL ) {
                dsi->datalen = DSI_DATASIZ;
                dsi->flags |= DSI_RUNNING;

                LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));

                err = (*afp_switch[function])(obj,
                                              (char *)&dsi->commands, dsi->cmdlen,
                                              (char *)&dsi->data, &dsi->datalen);

                LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                    AfpNum2name(function), AfpErr2name(err));

                dsi->flags &= ~DSI_RUNNING;
#ifdef FORCE_UIDGID
            	/* bring everything back to old euid, egid */
		if (obj->force_uid)
            	    restore_uidgid ( &obj->uidgid );
#endif /* FORCE_UIDGID */
            } else {
                LOG(log_error, logtype_afpd, "(write) bad function %x", function);
                dsi->datalen = 0;
                err = AFPERR_NOOP;
            }

            if (!dsi_wrtreply(dsi, err)) {
                LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
                if (dsi_disconnect(dsi) != 0)
                    afp_dsi_die(EXITERR_CLNT);
            }
            break;

        case DSIFUNC_ATTN: /* attention replies */
            break;

            /* error. this usually implies a mismatch of some kind
             * between server and client. if things are correct,
             * we need to flush the rest of the packet if necessary. */
        default:
            LOG(log_info, logtype_afpd,"afp_dsi: spurious command %d", cmd);
            dsi_writeinit(dsi, dsi->data, DSI_DATASIZ);
            dsi_writeflush(dsi);
            break;
        }
        pending_request(dsi);

        fce_pending_events(obj);
    }

    /* error */
    afp_dsi_die(EXITERR_CLNT);
}
Example #8
0
/* parse an afpd.conf line. i'm doing it this way because it's
 * easy. it is, however, massively hokey. sample afpd.conf:
 * server:AFPServer@zone -loginmesg "blah blah blah" -nodsi 
 * "private machine"@zone2 -noguest -port 11012
 * server2 -nocleartxt -nodsi
 *
 * NOTE: this ignores unknown options 
 */
int afp_options_parseline(char *buf, struct afp_options *options)
{
    char *c, *opt;

    /* handle server */
    if (*buf != '-' && (c = getoption(buf, NULL)) && (opt = strdup(c)))
        options->server = opt;

    /* parse toggles */
    if (strstr(buf, " -nodebug"))
        options->flags &= ~OPTION_DEBUG;
#ifdef USE_SRVLOC
    if (strstr(buf, " -slp"))
        options->flags &= ~OPTION_NOSLP;
#endif
#ifdef USE_ZEROCONF
    if (strstr(buf, " -nozeroconf"))
        options->flags |= OPTION_NOZEROCONF;
#endif
    if (strstr(buf, " -nouservolfirst"))
        options->flags &= ~OPTION_USERVOLFIRST;
    if (strstr(buf, " -uservolfirst"))
        options->flags |= OPTION_USERVOLFIRST;
    if (strstr(buf, " -nouservol"))
        options->flags |= OPTION_NOUSERVOL;
    if (strstr(buf, " -uservol"))
        options->flags &= ~OPTION_NOUSERVOL;
    if (strstr(buf, " -proxy"))
        options->flags |= OPTION_PROXY;
    if (strstr(buf, " -noicon"))
        options->flags &= ~OPTION_CUSTOMICON;
    if (strstr(buf, " -icon"))
        options->flags |= OPTION_CUSTOMICON;
    if (strstr(buf, " -advertise_ssh"))
        options->flags |= OPTION_ANNOUNCESSH;
    if (strstr(buf, " -noacl2maccess"))
        options->flags &= ~OPTION_ACL2MACCESS;

    /* passwd bits */
    if (strstr(buf, " -nosavepassword"))
        options->passwdbits |= PASSWD_NOSAVE;
    if (strstr(buf, " -savepassword"))
        options->passwdbits &= ~PASSWD_NOSAVE;
    if (strstr(buf, " -nosetpassword"))
        options->passwdbits &= ~PASSWD_SET;
    if (strstr(buf, " -setpassword"))
        options->passwdbits |= PASSWD_SET;

    /* transports */
    if (strstr(buf, " -transall"))
        options->transports = AFPTRANS_ALL;
    if (strstr(buf, " -notransall"))
        options->transports = AFPTRANS_NONE;
    if (strstr(buf, " -tcp"))
        options->transports |= AFPTRANS_TCP;
    if (strstr(buf, " -notcp"))
        options->transports &= ~AFPTRANS_TCP;
    if (strstr(buf, " -ddp"))
        options->transports |= AFPTRANS_DDP;
    if (strstr(buf, " -noddp"))
        options->transports &= ~AFPTRANS_DDP;
    if (strstr(buf, "-client_polling"))
        options->server_notif = 0;

    /* figure out options w/ values. currently, this will ignore the setting
     * if memory is lacking. */

    if ((c = getoption(buf, "-hostname"))) {
        int len = strlen (c);
        if (len <= MAXHOSTNAMELEN) {
            memcpy(options->hostname, c, len);
            options->hostname[len] = 0;
        }
        else
            LOG(log_info, logtype_afpd, "WARNING: hostname %s is too long (%d)",c,len);
    }

    if ((c = getoption(buf, "-defaultvol")) && (opt = strdup(c)))
        options->defaultvol.name = opt;
    if ((c = getoption(buf, "-systemvol")) && (opt = strdup(c)))
        options->systemvol.name = opt;
    if ((c = getoption(buf, "-loginmesg")) && (opt = strdup(c))) {
        int i = 0, j = 0;
        while (c[i]) {
            if (c[i] != '\\') {
                opt[j++] = c[i];
            } else {
                i++;
                if (c[i] == 'n')
                    opt[j++] = '\n';
            }
            i++;
        }
        opt[j] = 0;
        options->loginmesg = opt;
        
    }
    if ((c = getoption(buf, "-guestname")) && (opt = strdup(c)))
        options->guest = opt;
    if ((c = getoption(buf, "-passwdfile")) && (opt = strdup(c)))
        options->passwdfile = opt;
    if ((c = getoption(buf, "-passwdminlen")))
        options->passwdminlen = MIN(1, atoi(c));
    if ((c = getoption(buf, "-loginmaxfail")))
        options->loginmaxfail = atoi(c);
    if ((c = getoption(buf, "-tickleval"))) {
        options->tickleval = atoi(c);
        if (options->tickleval <= 0) {
            options->tickleval = 30;
        }
    }
    if ((c = getoption(buf, "-timeout"))) {
        options->timeout = atoi(c);
        if (options->timeout <= 0) {
            options->timeout = 4;
        }
    }

    if ((c = getoption(buf, "-sleep"))) {
        options->sleep = atoi(c) *120;
        if (options->sleep <= 4) {
            options->sleep = 4;
        }
    }

    if ((c = getoption(buf, "-dsireadbuf"))) {
        options->dsireadbuf = atoi(c);
        if (options->dsireadbuf < 6)
            options->dsireadbuf = 6;
    }

    if ((c = getoption(buf, "-server_quantum")))
        options->server_quantum = strtoul(c, NULL, 0);

    if ((c = getoption(buf, "-volnamelen"))) {
        options->volnamelen = atoi(c);
        if (options->volnamelen < 8) {
            options->volnamelen = 8; /* max mangled volname "???#FFFF" */
        }
        if (options->volnamelen > 255) {
	    options->volnamelen = 255; /* AFP3 spec */
        }
    }

    /* -[no]setuplog <logtype> <loglevel> [<filename>]*/
    c = buf;
    /* Now THIS is hokey! Multiple occurrences are not supported by our current code, */
    /* so I have to loop myself. */
    while (NULL != (c = strstr(c, "-setuplog"))) {
        char *optstr;
        if ((optstr = getoption(c, "-setuplog"))) {
            setuplog(optstr);
            options->logconfig = optstr; /* at least store the last (possibly only) one */
            c += sizeof("-setuplog");
        }
    }

    if ((c = getoption(buf, "-unsetuplog")))
      unsetuplog(c);

#ifdef ADMIN_GRP
    if ((c = getoption(buf, "-admingroup"))) {
        struct group *gr = getgrnam(c);
        if (gr != NULL) {
            options->admingid = gr->gr_gid;
        }
    }
#endif /* ADMIN_GRP */

    if ((c = getoption(buf, "-k5service")) && (opt = strdup(c)))
	options->k5service = opt;
    if ((c = getoption(buf, "-k5realm")) && (opt = strdup(c)))
	options->k5realm = opt;
    if ((c = getoption(buf, "-k5keytab"))) {
	if ( NULL == (options->k5keytab = (char *) malloc(sizeof(char)*(strlen(c)+14)) )) {
		LOG(log_error, logtype_afpd, "malloc failed");
		exit(-1);
	}
	snprintf(options->k5keytab, strlen(c)+14, "KRB5_KTNAME=%s", c);
	putenv(options->k5keytab);
	/* setenv( "KRB5_KTNAME", c, 1 ); */
    }
    if ((c = getoption(buf, "-authprintdir")) && (opt = strdup(c)))
        options->authprintdir = opt;
    if ((c = getoption(buf, "-uampath")) && (opt = strdup(c)))
        options->uampath = opt;
    if ((c = getoption(buf, "-uamlist")) && (opt = strdup(c)))
        options->uamlist = opt;

    if ((c = getoption(buf, "-ipaddr"))) {
#if 0
        struct in_addr inaddr;
        if (inet_aton(c, &inaddr) && (opt = strdup(c))) {
            if (!gethostbyaddr((const char *) &inaddr, sizeof(inaddr), AF_INET))
                LOG(log_info, logtype_afpd, "WARNING: can't find %s", opt);
            options->ipaddr = opt;
        }
        else {
            LOG(log_error, logtype_afpd, "Error parsing -ipaddr, is %s in numbers-and-dots notation?", c);
        }
#endif
        options->ipaddr = strdup(c);
    }

    /* FIXME CNID Cnid_srv is a server attribute */
    if ((c = getoption(buf, "-cnidserver"))) {
        char *p = strrchr(c, ':');
        if (p)
            *p = 0;
        Cnid_srv = strdup(c);
        if (p)
            Cnid_port = strdup(p + 1);
        LOG(log_debug, logtype_afpd, "CNID Server: %s:%s", Cnid_srv, Cnid_port);
    }

    if ((c = getoption(buf, "-port")))
        options->port = strdup(c);
    if ((c = getoption(buf, "-ddpaddr")))
        atalk_aton(c, &options->ddpaddr);
    if ((c = getoption(buf, "-signature")) && (opt = strdup(c)))
        options->signatureopt = opt;

    /* do a little checking for the domain name. */
    if ((c = getoption(buf, "-fqdn"))) {
        char *p = strchr(c, ':');
        if (p)
            *p = '\0';
        if (gethostbyname(c)) {
            if (p)
                *p = ':';
            if ((opt = strdup(c)))
                options->fqdn = opt;
        }
	else {
            LOG(log_error, logtype_afpd, "error parsing -fqdn, gethostbyname failed for: %s", c);
	}
    }

    if ((c = getoption(buf, "-unixcodepage"))) {
    	if ((charset_t)-1  == ( options->unixcharset = add_charset(c)) ) {
            options->unixcharset = CH_UNIX;
            LOG(log_warning, logtype_afpd, "setting Unix codepage to '%s' failed", c);
    	}
	else {
            if ((opt = strdup(c)))
                options->unixcodepage = opt;
	}
    }
	
    if ((c = getoption(buf, "-maccodepage"))) {
    	if ((charset_t)-1 == ( options->maccharset = add_charset(c)) ) {
            options->maccharset = CH_MAC;
            LOG(log_warning, logtype_afpd, "setting Mac codepage to '%s' failed", c);
    	}
	else {
            if ((opt = strdup(c)))
                options->maccodepage = opt;
	}
    }
    
    if ((c = strstr(buf, "-closevol"))) {
        options->closevol= 1;
    }

    if ((c = getoption(buf, "-ntdomain")) && (opt = strdup(c)))
       options->ntdomain = opt;

    if ((c = getoption(buf, "-ntseparator")) && (opt = strdup(c)))
       options->ntseparator = opt;

    if ((c = getoption(buf, "-dircachesize")))
        options->dircachesize = atoi(c);
     
    if ((c = getoption(buf, "-tcpsndbuf")))
        options->tcp_sndbuf = atoi(c);

    if ((c = getoption(buf, "-tcprcvbuf")))
        options->tcp_rcvbuf = atoi(c);

    return 1;
}