/* ------------ */ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask, time_t *now) { char *nametmp; int b; if ((cur_fd = check_fd(timeout, sigmask, now)) < 0) return -1; if (!cur_fd) return 0; LOG(log_maxdebug, logtype_cnid, "comm_rcv: got data on fd %u", cur_fd); if (setnonblock(cur_fd, 1) != 0) { LOG(log_error, logtype_cnid, "comm_rcv: setnonblock: %s", strerror(errno)); return -1; } nametmp = (char *)rqst->name; if ((b = readt(cur_fd, rqst, sizeof(struct cnid_dbd_rqst), 1, CNID_DBD_TIMEOUT)) != sizeof(struct cnid_dbd_rqst)) { if (b) LOG(log_error, logtype_cnid, "error reading message header: %s", strerror(errno)); invalidate_fd(cur_fd); rqst->name = nametmp; return 0; } rqst->name = nametmp; if (rqst->namelen && readt(cur_fd, (char *)rqst->name, rqst->namelen, 1, CNID_DBD_TIMEOUT) != rqst->namelen) { LOG(log_error, logtype_cnid, "error reading message name: %s", strerror(errno)); invalidate_fd(cur_fd); return 0; } /* We set this to make life easier for logging. None of the other stuff needs zero terminated strings. */ ((char *)(rqst->name))[rqst->namelen] = '\0'; LOG(log_maxdebug, logtype_cnid, "comm_rcv: got %u bytes", b + rqst->namelen); return 1; }
/* --------------------- * send a request and get reply * assume send is non blocking * if no answer after sometime (at least MAX_DELAY secondes) return an error */ static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply) { ssize_t ret; char *nametmp; size_t len; if (send_packet(db, rqst) < 0) { return -1; } len = rply->namelen; nametmp = rply->name; ret = readt(db->fd, rply, sizeof(struct cnid_dbd_rply), 0, ONE_DELAY); if (ret != sizeof(struct cnid_dbd_rply)) { LOG(log_debug, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s", db->db_dir, ret == -1 ? strerror(errno) : "closed"); rply->name = nametmp; return -1; } rply->name = nametmp; if (rply->namelen && rply->namelen > len) { LOG(log_error, logtype_cnid, "dbd_rpc: Error reading name (db_dir %s): %s name too long: %d. only wanted %d, garbage?", db->db_dir, rply->name, rply->namelen, len); return -1; } if (rply->namelen && (ret = readt(db->fd, rply->name, rply->namelen, 0, ONE_DELAY)) != (ssize_t)rply->namelen) { LOG(log_error, logtype_cnid, "dbd_rpc: Error reading name from fd (db_dir %s): %s", db->db_dir, ret == -1?strerror(errno):"closed"); return -1; } LOG(log_maxdebug, logtype_cnid, "dbd_rpc: {done}"); return 0; }
/* SIGURG handler (primary reconnect) */ static void afp_dsi_transfer_session(int sig _U_) { uint16_t dsiID; int socket; DSI *dsi = (DSI *)AFPobj->handle; LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session"); if (readt(AFPobj->ipc_fd, &dsiID, 2, 0, 2) != 2) { LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive DSI id, goodbye"); afp_dsi_close(AFPobj); exit(EXITERR_SYS); } if ((socket = recv_fd(AFPobj->ipc_fd, 1)) == -1) { LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye"); afp_dsi_close(AFPobj); exit(EXITERR_SYS); } LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: received socket fd: %i", socket); dsi->proto_close(dsi); dsi->socket = socket; dsi->flags = DSI_RECONSOCKET; dsi->datalen = 0; dsi->eof = dsi->start = dsi->buffer; dsi->in_write = 0; dsi->header.dsi_requestID = dsiID; dsi->header.dsi_command = DSIFUNC_CMD; /* * The session transfer happens in the middle of FPDisconnect old session, thus we * have to send the reply now. */ if (!dsi_cmdreply(dsi, AFP_OK)) { LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) ); afp_dsi_close(AFPobj); exit(EXITERR_CLNT); } LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: succesfull primary reconnect"); /* * Now returning from this signal handler return to dsi_receive which should start * reading/continuing from the connected socket that was passed via the parent from * another session. The parent will terminate that session. */ siglongjmp(recon_jmp, 1); }
/* * Get bytes from buffer dsi->buffer or read from socket * * 1. Check if there are bytes in the the dsi->buffer buffer. * 2. Return bytes from (1) if yes. * Note: this may return fewer bytes then requested in count !! * 3. If the buffer was empty, read from the socket. */ static ssize_t buf_read(DSI *dsi, uint8_t *buf, size_t count) { ssize_t len; LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes)", count); if (!count) return 0; len = from_buf(dsi, buf, count); /* 1. */ if (len) return len; /* 2. */ len = readt(dsi->socket, buf, count, 0, 0); /* 3. */ LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len); return len; }
/* ------------------ */ 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); } }
/*! * Read a IPC message from a child * * @args children (rw) pointer to our structure with all childs * @args fd (r) IPC socket with child * * @returns number of bytes transfered, -1 on error, 0 on EOF */ int ipc_server_read(server_child *children, int fd) { int ret = 0; struct ipc_header ipc; char buf[IPC_MAXMSGSIZE], *p; if ((ret = read(fd, buf, IPC_HEADERLEN)) != IPC_HEADERLEN) { LOG(log_error, logtype_afpd, "Reading IPC header failed (%i of %u bytes read): %s", ret, IPC_HEADERLEN, strerror(errno)); return ret; } p = buf; memcpy(&ipc.command, p, sizeof(ipc.command)); p += sizeof(ipc.command); memcpy(&ipc.child_pid, p, sizeof(ipc.child_pid)); p += sizeof(ipc.child_pid); memcpy(&ipc.uid, p, sizeof(ipc.uid)); p += sizeof(ipc.uid); memcpy(&ipc.len, p, sizeof(ipc.len)); /* This should never happen */ if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN)) { LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len); return -1; } memset (buf, 0, IPC_MAXMSGSIZE); if ( ipc.len != 0) { if ((ret = read(fd, buf, ipc.len)) != (int) ipc.len) { LOG(log_info, logtype_afpd, "Reading IPC message failed (%u of %u bytes read): %s", ret, ipc.len, strerror(errno)); return ret; } } ipc.msg = buf; LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u", ipc_cmd_str[ipc.command], ipc.child_pid); int afp_socket; switch (ipc.command) { case IPC_DISCOLDSESSION: if (readt(fd, &ipc.DSI_requestID, 2, 0, 2) != 2) { LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): couldnt read DSI id: %s", ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno)); } if ((ipc.afp_socket = recv_fd(fd, 1)) == -1) { LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): recv_fd: %s", ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno)); return -1; } if (ipc_kill_token(&ipc, children) == 1) { /* Transfered session (ie afp_socket) to old disconnected child, now kill the new one */ LOG(log_note, logtype_afpd, "Reconnect: killing new session child[%u] after transfer", ipc.child_pid); kill(ipc.child_pid, SIGTERM); } close(ipc.afp_socket); break; case IPC_GETSESSION: if (ipc_get_session(&ipc, children) != 0) return -1; break; default: LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command); return -1; } return ret; }
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; }