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); }
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"))
/* ------------------ */ 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); } }
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); }
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; }
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; }
/* ------------------------------------------- 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); }
/* 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; }