/*
- * $Id: main.c,v 1.24 2009-02-27 09:14:40 franklahm Exp $
- *
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include <stdlib.h>
#include <string.h>
#include <signal.h>
-
#include <sys/param.h>
#include <sys/uio.h>
-#include <atalk/logger.h>
#include <sys/time.h>
#include <sys/socket.h>
-
+#include <sys/poll.h>
#include <errno.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <atalk/logger.h>
#include <atalk/adouble.h>
-
-#include <netatalk/at.h>
#include <atalk/compat.h>
#include <atalk/dsi.h>
-#include <atalk/atp.h>
-#include <atalk/asp.h>
#include <atalk/afp.h>
#include <atalk/paths.h>
#include <atalk/util.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
+#include <atalk/errchk.h>
+#include <atalk/globals.h>
+#include <atalk/netatalk_conf.h>
-#include "globals.h"
#include "afp_config.h"
#include "status.h"
#include "fork.h"
#include "uam_auth.h"
+#include "afp_zeroconf.h"
+#include "afpstats.h"
-#ifdef TRU64
-#include <sys/security.h>
-#include <prot.h>
-#include <sia.h>
+#define ASEV_THRESHHOLD 10
-static int argc = 0;
-static char **argv = NULL;
-#endif /* TRU64 */
+unsigned char nologin = 0;
-unsigned char nologin = 0;
+static AFPObj obj;
+static server_child_t *server_children;
+static sig_atomic_t reloadconfig = 0;
+static sig_atomic_t gotsigchld = 0;
+static struct asev *asev;
-struct afp_options default_options;
-static AFPConfig *configs;
-static server_child *server_children;
-static fd_set save_rfds;
-static int Ipc_fd = -1;
+static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child_t *server_children);
-#ifdef TRU64
-void afp_get_cmdline( int *ac, char ***av)
+static void afp_exit(int ret)
{
- *ac = argc;
- *av = argv;
+ exit(ret);
}
-#endif /* TRU64 */
-static void afp_exit(const int i)
-{
- server_unlock(default_options.pidfile);
- exit(i);
-}
/* ------------------
initialize fd set we are waiting for.
*/
-static void set_fd(int ipc_fd)
+static bool init_listening_sockets(const AFPObj *config)
{
- AFPConfig *config;
+ DSI *dsi;
+ int numlisteners;
- FD_ZERO(&save_rfds);
- for (config = configs; config; config = config->next) {
- if (config->fd < 0) /* for proxies */
- continue;
- FD_SET(config->fd, &save_rfds);
+ for (numlisteners = 0, dsi = config->dsi; dsi; dsi = dsi->next) {
+ numlisteners++;
}
- if (ipc_fd >= 0) {
- FD_SET(ipc_fd, &save_rfds);
+
+ asev = asev_init(config->options.connections + numlisteners + ASEV_THRESHHOLD);
+ if (asev == NULL) {
+ return false;
}
+
+ for (dsi = config->dsi; dsi; dsi = dsi->next) {
+ if (!(asev_add_fd(asev, dsi->serversock, LISTEN_FD, dsi))) {
+ return false;
+ }
+ }
+
+ return true;
}
-/* ------------------ */
-static void afp_goaway(int sig)
+static bool reset_listening_sockets(const AFPObj *config)
{
+ const DSI *dsi;
-#ifndef NO_DDP
- asp_kill(sig);
-#endif /* ! NO_DDP */
+ for (dsi = config->dsi; dsi; dsi = dsi->next) {
+ if (!(asev_del_fd(asev, dsi->serversock))) {
+ return false;
+ }
+ }
+ return true;
+}
- dsi_kill(sig);
+/* ------------------ */
+static void afp_goaway(int sig)
+{
switch( sig ) {
- case SIGTERM :
- LOG(log_info, logtype_afpd, "shutting down on signal %d", sig );
+
+ case SIGTERM:
+ case SIGQUIT:
+ LOG(log_note, logtype_afpd, "AFP Server shutting down");
+ if (server_children)
+ server_child_kill(server_children, SIGTERM);
+ _exit(0);
break;
+
case SIGUSR1 :
- case SIGHUP :
- /* w/ a configuration file, we can force a re-read if we want */
nologin++;
auth_unload();
- if (sig == SIGHUP || ((nologin + 1) & 1)) {
- AFPConfig *config;
+ LOG(log_info, logtype_afpd, "disallowing logins");
- 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");
- afp_exit(EXITERR_CONF);
- }
- set_fd(Ipc_fd);
- } else {
- LOG(log_info, logtype_afpd, "disallowing logins");
- }
- if (sig == SIGHUP) {
- nologin = 0;
- }
+ if (server_children)
+ server_child_kill(server_children, sig);
break;
+
+ case SIGHUP :
+ /* w/ a configuration file, we can force a re-read if we want */
+ reloadconfig = 1;
+ break;
+
+ case SIGCHLD:
+ /* w/ a configuration file, we can force a re-read if we want */
+ gotsigchld = 1;
+ break;
+
default :
LOG(log_error, logtype_afpd, "afp_goaway: bad signal" );
}
- if ( sig == SIGTERM ) {
- AFPConfig *config;
+ return;
+}
- for (config = configs; config; config = config->next)
- if (config->server_cleanup)
- config->server_cleanup(config);
+static void child_handler(void)
+{
+ int fd;
+ int status;
+ pid_t pid;
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif /* ! WAIT_ANY */
+
+ while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+ 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);
+ }
- afp_exit(0);
+ fd = server_child_remove(server_children, pid);
+ if (fd == -1) {
+ continue;
+ }
+ if (!(asev_del_fd(asev, fd))) {
+ LOG(log_error, logtype_afpd, "child[%d]: asev_del_fd: %d", pid, fd);
+ }
}
- return;
}
-static void child_handler()
+static int setlimits(void)
{
- server_child_handler(server_children);
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_warning, logtype_afpd, "setlimits: reading current limits failed: %s", strerror(errno));
+ return -1;
+ }
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < 65535) {
+ rlim.rlim_cur = 65535;
+ if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < 65535)
+ rlim.rlim_max = 65535;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_warning, logtype_afpd, "setlimits: increasing limits failed: %s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
}
-int main( ac, av )
-int ac;
-char **av;
+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 */
+ /* Parse argv args and initialize default options */
+ afp_options_parse_cmdline(&obj, ac, av);
-#ifdef DEBUG1
- fault_setup(NULL);
-#endif
- afp_options_init(&default_options);
- if (!afp_options_parse(ac, av, &default_options))
- exit(EXITERR_CONF);
+ if (!(obj.cmdlineflags & OPTION_DEBUG) && (daemonize(0, 0) != 0))
+ exit(EXITERR_SYS);
- /* Save the user's current umask for use with CNID (and maybe some
- * other things, too). */
- default_options.save_mask = umask( default_options.umask );
+ /* Log SIGBUS/SIGSEGV SBT */
+ fault_setup(NULL);
- 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);
- }
+ if (afp_config_parse(&obj, "afpd") != 0)
+ afp_exit(EXITERR_CONF);
-#if 0
- /* Register CNID */
- cnid_init();
-#endif
+ /* 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(default_options.connections,
- CHILD_NFORKS))) {
+ if (!(server_children = server_child_alloc(obj.options.connections))) {
LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
afp_exit(EXITERR_SYS);
}
-#ifdef AFP3x
+ 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
- signal(SIGXFSZ , SIG_IGN);
+ 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
-#endif
+
+ sv.sa_handler = SIG_IGN;
+ sigemptyset( &sv.sa_mask );
+ if (sigaction(SIGPIPE, &sv, NULL ) < 0 ) {
+ LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
+ afp_exit(EXITERR_SYS);
+ }
- memset(&sv, 0, sizeof(sv));
- sv.sa_handler = child_handler;
+ 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, 0 ) < 0 ) {
+ if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
afp_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, 0 ) < 0 ) {
+ if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
afp_exit(EXITERR_SYS);
}
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, 0 ) < 0 ) {
+ 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( SIGTERM, &sv, 0 ) < 0 ) {
+ if (sigaction(SIGQUIT, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
afp_exit(EXITERR_SYS);
}
- /* afpd.conf: not in config file: lockfile, connections, configfile
+ /* afp.conf: not in config file: lockfile, configfile
* preference: command-line provides defaults.
* config file over-writes defaults.
*
#endif
sigaddset(&sigs, SIGCHLD);
- sigprocmask(SIG_BLOCK, &sigs, NULL);
- if (!(configs = configinit(&default_options))) {
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+#ifdef HAVE_DBUS_GLIB
+ /* Run dbus AFP statics thread */
+ if (obj.options.flags & OPTION_DBUS_AFPSTATS)
+ (void)afpstats_init(server_children);
+#endif
+ if (configinit(&obj) != 0) {
LOG(log_error, logtype_afpd, "main: no servers configured");
afp_exit(EXITERR_CONF);
}
- sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
- /* Register CNID */
+ /* Initialize */
cnid_init();
/* watch atp, dsi sockets and ipc parent/child file descriptor. */
- if ((ipc = server_ipc_create())) {
- Ipc_fd = server_ipc_parent(ipc);
+ if (!(init_listening_sockets(&obj))) {
+ LOG(log_error, logtype_afpd, "main: couldn't initialize socket handler");
+ afp_exit(EXITERR_CONF);
}
- set_fd(Ipc_fd);
+
+ /* set limits */
+ (void)setlimits();
+
+ afp_child_t *child;
+ int saveerrno;
/* wait for an appleshare connection. parent remains in the loop
* while the children get handled by afp_over_{asp,dsi}. this is
* afterwards. establishing timeouts for logins is a possible
* solution. */
while (1) {
- rfds = save_rfds;
- sigprocmask(SIG_UNBLOCK, &sigs, NULL);
- ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
- sigprocmask(SIG_BLOCK, &sigs, NULL);
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+ ret = poll(asev->fdset, asev->used, -1);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+ saveerrno = errno;
+
+ if (gotsigchld) {
+ gotsigchld = 0;
+ child_handler();
+ continue;
+ }
+
+ if (reloadconfig) {
+ nologin++;
+
+ if (!(reset_listening_sockets(&obj))) {
+ LOG(log_error, logtype_afpd, "main: reset socket handlers");
+ afp_exit(EXITERR_CONF);
+ }
+
+ LOG(log_info, logtype_afpd, "re-reading configuration file");
+
+ configfree(&obj, NULL);
+ afp_config_free(&obj);
+
+ if (afp_config_parse(&obj, "afpd") != 0)
+ afp_exit(EXITERR_CONF);
+
+ if (configinit(&obj) != 0) {
+ LOG(log_error, logtype_afpd, "config re-read: no servers configured");
+ afp_exit(EXITERR_CONF);
+ }
+
+ if (!(init_listening_sockets(&obj))) {
+ LOG(log_error, logtype_afpd, "main: couldn't initialize socket handler");
+ afp_exit(EXITERR_CONF);
+ }
+
+ 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;
}
- if (Ipc_fd >=0 && FD_ISSET(Ipc_fd, &rfds)) {
- server_ipc_read(server_children);
- }
- for (config = configs; config; config = config->next) {
- if (config->fd < 0)
- continue;
- if (FD_ISSET(config->fd, &rfds)) {
- config->server_start(config, configs, server_children);
- }
- }
- }
+
+ for (int i = 0; i < asev->used; i++) {
+ if (asev->fdset[i].revents & (POLLIN | POLLERR | POLLHUP | POLLNVAL)) {
+ switch (asev->data[i].fdtype) {
+
+ case LISTEN_FD:
+ if ((child = dsi_start(&obj, (DSI *)(asev->data[i].private), server_children))) {
+ if (!(asev_add_fd(asev, child->afpch_ipc_fd, IPC_FD, child))) {
+ LOG(log_error, logtype_afpd, "out of asev slots");
+
+ /*
+ * Close IPC fd here and mark it as unused
+ */
+ close(child->afpch_ipc_fd);
+ child->afpch_ipc_fd = -1;
+
+ /*
+ * Being unfriendly here, but we really
+ * want to get rid of it. The 'child'
+ * handle gets cleaned up in the SIGCLD
+ * handler.
+ */
+ kill(child->afpch_pid, SIGKILL);
+ }
+ }
+ break;
+
+ case IPC_FD:
+ child = (afp_child_t *)(asev->data[i].private);
+ LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->afpch_pid);
+
+ if (ipc_server_read(server_children, child->afpch_ipc_fd) != 0) {
+ if (!(asev_del_fd(asev, child->afpch_ipc_fd))) {
+ LOG(log_error, logtype_afpd, "child[%u]: no IPC fd");
+ }
+ close(child->afpch_ipc_fd);
+ child->afpch_ipc_fd = -1;
+ }
+ break;
+
+ default:
+ LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
+ break;
+ } /* switch */
+ } /* if */
+ } /* for (i)*/
+ } /* while (1) */
return 0;
}
+
+static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child_t *server_children)
+{
+ afp_child_t *child = NULL;
+
+ if (dsi_getsession(dsi, server_children, obj->options.tickleval, &child) != 0) {
+ LOG(log_error, logtype_afpd, "dsi_start: session error: %s", strerror(errno));
+ return NULL;
+ }
+
+ /* we've forked. */
+ if (child == NULL) {
+ configfree(obj, dsi);
+ afp_over_dsi(obj); /* start a session */
+ exit (0);
+ }
+
+ return child;
+}