static void child_handler(int sig _U_) { int fd; int status, i; pid_t pid; #ifndef WAIT_ANY #define WAIT_ANY (-1) #endif /* ! WAIT_ANY */ while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) { for (i = 0; i < server_children->nforks; i++) { if ((fd = server_child_remove(server_children, i, pid)) != -1) { fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd); break; } } if (WIFEXITED(status)) { if (WEXITSTATUS(status)) LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status)); else LOG(log_info, logtype_afpd, "child[%d]: done", pid); } else { if (WIFSIGNALED(status)) LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status)); else LOG(log_info, logtype_afpd, "child[%d]: died", pid); } } }
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; }
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; }