meillo@367: /* meillo@367: ** MasqMail meillo@367: ** Copyright (C) 1999/2000 Oliver Kurth meillo@367: ** meillo@367: ** This program is free software; you can redistribute it and/or modify meillo@367: ** it under the terms of the GNU General Public License as published by meillo@367: ** the Free Software Foundation; either version 2 of the License, or meillo@367: ** (at your option) any later version. meillo@367: ** meillo@367: ** This program is distributed in the hope that it will be useful, meillo@367: ** but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@367: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@367: ** GNU General Public License for more details. meillo@367: ** meillo@367: ** You should have received a copy of the GNU General Public License meillo@367: ** along with this program; if not, write to the Free Software meillo@367: ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. meillo@0: */ meillo@0: meillo@0: #include meillo@0: #include meillo@0: meillo@15: #include "masqmail.h" meillo@15: meillo@0: static int volatile sighup_seen = 0; meillo@0: meillo@10: static void meillo@10: sighup_handler(int sig) meillo@0: { meillo@10: sighup_seen = 1; meillo@10: signal(SIGHUP, sighup_handler); meillo@0: } meillo@0: meillo@10: static void meillo@10: sigchld_handler(int sig) meillo@0: { meillo@10: pid_t pid; meillo@10: int status; meillo@10: meillo@10: pid = waitpid(0, &status, 0); meillo@10: if (pid > 0) { meillo@384: if (WEXITSTATUS(status) != 0) { meillo@384: logwrite(LOG_WARNING, "process %d exited with %d\n", meillo@384: pid, WEXITSTATUS(status)); meillo@384: } meillo@384: if (WIFSIGNALED(status)) { meillo@384: logwrite(LOG_WARNING, "process %d got signal: %d\n", meillo@384: pid, WTERMSIG(status)); meillo@384: } meillo@10: } meillo@10: signal(SIGCHLD, sigchld_handler); meillo@0: } meillo@0: meillo@10: void meillo@10: accept_connect(int listen_sock, int sock, struct sockaddr_in *sock_addr) meillo@0: { meillo@10: pid_t pid; meillo@10: int dup_sock = dup(sock); meillo@10: FILE *out, *in; meillo@10: gchar *rem_host; meillo@10: gchar *ident = NULL; meillo@0: meillo@10: rem_host = g_strdup(inet_ntoa(sock_addr->sin_addr)); meillo@383: logwrite(LOG_NOTICE, "connect from host %s, port %hu\n", meillo@383: rem_host, ntohs(sock_addr->sin_port)); meillo@0: meillo@207: /* start child for connection: */ meillo@10: signal(SIGCHLD, sigchld_handler); meillo@10: pid = fork(); meillo@384: if (pid < 0) { meillo@384: logwrite(LOG_WARNING, "could not fork for incoming smtp " meillo@384: "connection: %s\n", strerror(errno)); meillo@384: } else if (pid == 0) { meillo@384: /* child */ meillo@10: close(listen_sock); meillo@10: out = fdopen(sock, "w"); meillo@10: in = fdopen(dup_sock, "r"); meillo@10: smtp_in(in, out, rem_host, ident); meillo@262: _exit(0); meillo@10: } meillo@0: meillo@10: close(sock); meillo@10: close(dup_sock); meillo@0: } meillo@0: meillo@10: void meillo@366: listen_port(GList *iface_list, gint qival, char *argv[]) meillo@0: { meillo@10: int i; meillo@10: fd_set active_fd_set, read_fd_set; meillo@10: struct timeval tm; meillo@10: time_t time_before, time_now; meillo@10: struct sockaddr_in clientname; meillo@10: size_t size; meillo@10: GList *node, *node_next; meillo@10: int sel_ret; meillo@0: meillo@10: /* Create the sockets and set them up to accept connections. */ meillo@10: FD_ZERO(&active_fd_set); meillo@10: for (node = g_list_first(iface_list); node; node = node_next) { meillo@10: interface *iface = (interface *) (node->data); meillo@10: int sock; meillo@0: meillo@10: node_next = g_list_next(node); meillo@10: if ((sock = make_server_socket(iface)) < 0) { meillo@10: iface_list = g_list_remove_link(iface_list, node); meillo@10: g_list_free_1(node); meillo@10: continue; meillo@10: } meillo@10: if (listen(sock, 1) < 0) { meillo@384: logwrite(LOG_ALERT, "listen: (terminating): %s\n", meillo@384: strerror(errno)); meillo@262: exit(1); meillo@10: } meillo@384: logwrite(LOG_NOTICE, "listening on interface %s:%d\n", meillo@384: iface->address, iface->port); meillo@10: DEBUG(5) debugf("sock = %d\n", sock); meillo@10: FD_SET(sock, &active_fd_set); meillo@10: } meillo@0: meillo@10: /* setup handler for HUP signal: */ meillo@10: signal(SIGHUP, sighup_handler); meillo@10: signal(SIGCHLD, sigchld_handler); meillo@0: meillo@15: /* now that we have our socket(s), we can give up root privileges */ meillo@10: if (!conf.run_as_user) { meillo@332: set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL); meillo@10: } meillo@0: meillo@10: /* sel_ret = 0; */ meillo@10: time(&time_before); meillo@10: time_before -= qival; meillo@10: sel_ret = -1; meillo@0: meillo@10: while (1) { meillo@0: meillo@367: /* meillo@367: ** if we were interrupted by an incoming connection (or a meillo@367: ** signal) we have to recalculate the time until the next meillo@367: ** queue run should occur. select may put a value into tm, meillo@367: ** but doc for select() says we should not use it. meillo@367: */ meillo@10: if (qival > 0) { meillo@10: time(&time_now); meillo@384: if (!sel_ret) { meillo@384: /* either just starting or after a queue run */ meillo@10: tm.tv_sec = qival; meillo@10: tm.tv_usec = 0; meillo@10: time_before = time_now; meillo@10: } else { meillo@10: tm.tv_sec = qival - (time_now - time_before); meillo@10: tm.tv_usec = 0; meillo@0: meillo@384: /* race condition, unlikely (but possible): */ meillo@384: if (tm.tv_sec < 0) { meillo@10: tm.tv_sec = 0; meillo@384: } meillo@10: } meillo@10: } meillo@367: /* meillo@367: ** Block until input arrives on one or more active sockets, meillo@367: ** or signal arrives, or queuing interval time elapsed meillo@367: ** (if qival > 0) meillo@367: */ meillo@10: read_fd_set = active_fd_set; meillo@384: if ((sel_ret = select(FD_SETSIZE, &read_fd_set, NULL, NULL, meillo@384: qival > 0 ? &tm : NULL)) < 0) { meillo@10: if (errno != EINTR) { meillo@384: logwrite(LOG_ALERT, "select: (terminating): " meillo@384: "%s\n", strerror(errno)); meillo@262: exit(1); meillo@384: } else if (sighup_seen) { meillo@384: logwrite(LOG_NOTICE, "HUP signal received. " meillo@384: "Restarting daemon\n"); meillo@0: meillo@384: for (i = 0; i < FD_SETSIZE; i++) meillo@384: if (FD_ISSET(i, &active_fd_set)) meillo@384: close(i); meillo@0: meillo@384: execv(argv[0], &(argv[0])); meillo@384: logwrite(LOG_ALERT, "restarting failed: %s\n", meillo@384: strerror(errno)); meillo@384: exit(1); meillo@10: } meillo@10: } else if (sel_ret > 0) { meillo@10: for (i = 0; i < FD_SETSIZE; i++) { meillo@384: int sock = i; meillo@384: int new; meillo@384: meillo@384: if (!FD_ISSET(i, &read_fd_set)) { meillo@384: continue; meillo@384: } meillo@384: size = sizeof(clientname); meillo@384: new = accept(sock, (struct sockaddr *) meillo@384: &clientname, &size); meillo@384: if (new < 0) { meillo@384: logwrite(LOG_ALERT, "accept: (ignoring): %s\n", strerror(errno)); meillo@384: } else { meillo@384: accept_connect(sock, new, meillo@384: &clientname); meillo@10: } meillo@10: } meillo@10: } else { meillo@367: /* meillo@367: ** If select returns 0, the interval time has elapsed. meillo@367: ** We start a new queue runner process meillo@367: */ meillo@10: int pid; meillo@10: signal(SIGCHLD, sigchld_handler); meillo@10: if ((pid = fork()) == 0) { meillo@10: queue_run(); meillo@10: meillo@262: _exit(0); meillo@10: } else if (pid < 0) { meillo@384: logwrite(LOG_ALERT, "could not fork for " meillo@384: "queue run"); meillo@10: } meillo@10: } meillo@0: } meillo@0: }