meillo@0: /* MasqMail meillo@0: Copyright (C) 1999/2000 Oliver Kurth meillo@0: meillo@0: This program is free software; you can redistribute it and/or modify meillo@0: it under the terms of the GNU General Public License as published by meillo@0: the Free Software Foundation; either version 2 of the License, or meillo@0: (at your option) any later version. meillo@0: meillo@0: This program is distributed in the hope that it will be useful, meillo@0: but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@0: GNU General Public License for more details. meillo@0: meillo@0: You should have received a copy of the GNU General Public License meillo@0: along with this program; if not, write to the Free Software meillo@0: 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@262: if (WEXITSTATUS(status) != 0) meillo@10: logwrite(LOG_WARNING, "process %d exited with %d\n", pid, WEXITSTATUS(status)); meillo@10: if (WIFSIGNALED(status)) meillo@10: logwrite(LOG_WARNING, "process with pid %d got signal: %d\n", pid, WTERMSIG(status)); 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@0: #ifdef ENABLE_IDENT meillo@10: { meillo@10: gchar *id = NULL; meillo@10: if ((id = (gchar *) ident_id(sock, 60))) { meillo@10: ident = g_strdup(id); meillo@10: } meillo@10: logwrite(LOG_NOTICE, "connect from host %s, port %hd ident=%s\n", rem_host, meillo@10: ntohs(sock_addr->sin_port), ident ? ident : "(unknown)"); meillo@10: } meillo@0: #else meillo@10: logwrite(LOG_NOTICE, "connect from host %s, port %hd\n", rem_host, ntohs(sock_addr->sin_port)); meillo@0: #endif meillo@0: meillo@207: /* start child for connection: */ meillo@10: signal(SIGCHLD, sigchld_handler); meillo@10: pid = fork(); meillo@10: if (pid == 0) { meillo@10: close(listen_sock); meillo@10: out = fdopen(sock, "w"); meillo@10: in = fdopen(dup_sock, "r"); meillo@0: meillo@10: smtp_in(in, out, rem_host, ident); meillo@0: meillo@262: _exit(0); meillo@10: } else if (pid < 0) { meillo@10: logwrite(LOG_WARNING, "could not fork for incoming smtp connection: %s\n", strerror(errno)); meillo@10: } meillo@0: #ifdef ENABLE_IDENT meillo@10: if (ident != NULL) meillo@10: g_free(ident); meillo@0: #endif 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@10: logwrite(LOG_ALERT, "listen: (terminating): %s\n", strerror(errno)); meillo@262: exit(1); meillo@10: } meillo@10: logwrite(LOG_NOTICE, "listening on interface %s:%d\n", 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@10: /* if we were interrupted by an incoming connection (or a signal) meillo@10: we have to recalculate the time until the next queue run should meillo@10: occur. select may put a value into tm, but doc for select() says meillo@10: we should not use it. */ meillo@10: if (qival > 0) { meillo@10: time(&time_now); meillo@10: if (sel_ret == 0) { /* we are either just starting or did 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@10: /* race condition, very unlikely (but possible): */ meillo@10: if (tm.tv_sec < 0) meillo@10: tm.tv_sec = 0; meillo@10: } meillo@10: } meillo@10: /* Block until input arrives on one or more active sockets, meillo@15: or signal arrives, or queuing interval time elapsed (if qival > 0) */ meillo@10: read_fd_set = active_fd_set; meillo@10: if ((sel_ret = select(FD_SETSIZE, &read_fd_set, NULL, NULL, qival > 0 ? &tm : NULL)) < 0) { meillo@10: if (errno != EINTR) { meillo@10: logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno)); meillo@262: exit(1); meillo@10: } else { meillo@10: if (sighup_seen) { meillo@10: logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n"); meillo@0: meillo@10: for (i = 0; i < FD_SETSIZE; i++) meillo@10: if (FD_ISSET(i, &active_fd_set)) meillo@10: close(i); meillo@0: meillo@10: execv(argv[0], &(argv[0])); meillo@10: logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno)); meillo@262: exit(1); meillo@10: } meillo@10: } meillo@10: } else if (sel_ret > 0) { meillo@10: for (i = 0; i < FD_SETSIZE; i++) { meillo@10: if (FD_ISSET(i, &read_fd_set)) { meillo@10: int sock = i; meillo@10: int new; meillo@10: size = sizeof(clientname); meillo@10: new = accept(sock, (struct sockaddr *) &clientname, &size); meillo@10: if (new < 0) { meillo@10: logwrite(LOG_ALERT, "accept: (ignoring): %s\n", strerror(errno)); meillo@10: } else meillo@10: accept_connect(sock, new, &clientname); meillo@10: } meillo@10: } meillo@10: } else { meillo@10: /* If select returns 0, the interval time has elapsed. meillo@10: We start a new queue runner process */ 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@10: logwrite(LOG_ALERT, "could not fork for queue run"); meillo@10: } meillo@10: } meillo@0: } meillo@0: }