masqmail

annotate src/listen.c @ 281:ea5f86e0a81c

modes are now enforced exclusive Other MTAs (exim, postfix) are more relaxing, but as combinations of exclusive modes are senseless we behave more obvious if we fail early. This makes understanding the behavior easier too.
author markus schnalke <meillo@marmaro.de>
date Tue, 07 Dec 2010 14:04:56 -0300
parents dcb315792513
children 63efd381e27b
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999/2000 Oliver Kurth
meillo@0 3
meillo@0 4 This program is free software; you can redistribute it and/or modify
meillo@0 5 it under the terms of the GNU General Public License as published by
meillo@0 6 the Free Software Foundation; either version 2 of the License, or
meillo@0 7 (at your option) any later version.
meillo@0 8
meillo@0 9 This program is distributed in the hope that it will be useful,
meillo@0 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 12 GNU General Public License for more details.
meillo@0 13
meillo@0 14 You should have received a copy of the GNU General Public License
meillo@0 15 along with this program; if not, write to the Free Software
meillo@0 16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 17 */
meillo@0 18
meillo@0 19 #include <sys/wait.h>
meillo@0 20 #include <sys/types.h>
meillo@0 21
meillo@15 22 #include "masqmail.h"
meillo@15 23
meillo@0 24 static int volatile sighup_seen = 0;
meillo@0 25
meillo@10 26 static void
meillo@10 27 sighup_handler(int sig)
meillo@0 28 {
meillo@10 29 sighup_seen = 1;
meillo@10 30 signal(SIGHUP, sighup_handler);
meillo@0 31 }
meillo@0 32
meillo@10 33 static void
meillo@10 34 sigchld_handler(int sig)
meillo@0 35 {
meillo@10 36 pid_t pid;
meillo@10 37 int status;
meillo@10 38
meillo@10 39 pid = waitpid(0, &status, 0);
meillo@10 40 if (pid > 0) {
meillo@262 41 if (WEXITSTATUS(status) != 0)
meillo@10 42 logwrite(LOG_WARNING, "process %d exited with %d\n", pid, WEXITSTATUS(status));
meillo@10 43 if (WIFSIGNALED(status))
meillo@10 44 logwrite(LOG_WARNING, "process with pid %d got signal: %d\n", pid, WTERMSIG(status));
meillo@10 45 }
meillo@10 46 signal(SIGCHLD, sigchld_handler);
meillo@0 47 }
meillo@0 48
meillo@10 49 void
meillo@10 50 accept_connect(int listen_sock, int sock, struct sockaddr_in *sock_addr)
meillo@0 51 {
meillo@10 52 pid_t pid;
meillo@10 53 int dup_sock = dup(sock);
meillo@10 54 FILE *out, *in;
meillo@10 55 gchar *rem_host;
meillo@10 56 gchar *ident = NULL;
meillo@0 57
meillo@10 58 rem_host = g_strdup(inet_ntoa(sock_addr->sin_addr));
meillo@0 59 #ifdef ENABLE_IDENT
meillo@10 60 {
meillo@10 61 gchar *id = NULL;
meillo@10 62 if ((id = (gchar *) ident_id(sock, 60))) {
meillo@10 63 ident = g_strdup(id);
meillo@10 64 }
meillo@10 65 logwrite(LOG_NOTICE, "connect from host %s, port %hd ident=%s\n", rem_host,
meillo@10 66 ntohs(sock_addr->sin_port), ident ? ident : "(unknown)");
meillo@10 67 }
meillo@0 68 #else
meillo@10 69 logwrite(LOG_NOTICE, "connect from host %s, port %hd\n", rem_host, ntohs(sock_addr->sin_port));
meillo@0 70 #endif
meillo@0 71
meillo@207 72 /* start child for connection: */
meillo@10 73 signal(SIGCHLD, sigchld_handler);
meillo@10 74 pid = fork();
meillo@10 75 if (pid == 0) {
meillo@10 76 close(listen_sock);
meillo@10 77 out = fdopen(sock, "w");
meillo@10 78 in = fdopen(dup_sock, "r");
meillo@0 79
meillo@10 80 smtp_in(in, out, rem_host, ident);
meillo@0 81
meillo@262 82 _exit(0);
meillo@10 83 } else if (pid < 0) {
meillo@10 84 logwrite(LOG_WARNING, "could not fork for incoming smtp connection: %s\n", strerror(errno));
meillo@10 85 }
meillo@0 86 #ifdef ENABLE_IDENT
meillo@10 87 if (ident != NULL)
meillo@10 88 g_free(ident);
meillo@0 89 #endif
meillo@0 90
meillo@10 91 close(sock);
meillo@10 92 close(dup_sock);
meillo@0 93 }
meillo@0 94
meillo@10 95 void
meillo@10 96 listen_port(GList * iface_list, gint qival, char *argv[])
meillo@0 97 {
meillo@10 98 int i;
meillo@10 99 fd_set active_fd_set, read_fd_set;
meillo@10 100 struct timeval tm;
meillo@10 101 time_t time_before, time_now;
meillo@10 102 struct sockaddr_in clientname;
meillo@10 103 size_t size;
meillo@10 104 GList *node, *node_next;
meillo@10 105 int sel_ret;
meillo@0 106
meillo@10 107 /* Create the sockets and set them up to accept connections. */
meillo@10 108 FD_ZERO(&active_fd_set);
meillo@10 109 for (node = g_list_first(iface_list); node; node = node_next) {
meillo@10 110 interface *iface = (interface *) (node->data);
meillo@10 111 int sock;
meillo@0 112
meillo@10 113 node_next = g_list_next(node);
meillo@10 114 if ((sock = make_server_socket(iface)) < 0) {
meillo@10 115 iface_list = g_list_remove_link(iface_list, node);
meillo@10 116 g_list_free_1(node);
meillo@10 117 continue;
meillo@10 118 }
meillo@10 119 if (listen(sock, 1) < 0) {
meillo@10 120 logwrite(LOG_ALERT, "listen: (terminating): %s\n", strerror(errno));
meillo@262 121 exit(1);
meillo@10 122 }
meillo@10 123 logwrite(LOG_NOTICE, "listening on interface %s:%d\n", iface->address, iface->port);
meillo@10 124 DEBUG(5) debugf("sock = %d\n", sock);
meillo@10 125 FD_SET(sock, &active_fd_set);
meillo@10 126 }
meillo@0 127
meillo@10 128 /* setup handler for HUP signal: */
meillo@10 129 signal(SIGHUP, sighup_handler);
meillo@10 130 signal(SIGCHLD, sigchld_handler);
meillo@0 131
meillo@15 132 /* now that we have our socket(s), we can give up root privileges */
meillo@10 133 if (!conf.run_as_user) {
meillo@10 134 if (setegid(conf.mail_gid) != 0) {
meillo@10 135 logwrite(LOG_ALERT, "could not change gid to %d: %s\n", conf.mail_gid, strerror(errno));
meillo@262 136 exit(1);
meillo@10 137 }
meillo@10 138 if (seteuid(conf.mail_uid) != 0) {
meillo@10 139 logwrite(LOG_ALERT, "could not change uid to %d: %s\n", conf.mail_uid, strerror(errno));
meillo@262 140 exit(1);
meillo@10 141 }
meillo@10 142 }
meillo@0 143
meillo@10 144 /* sel_ret = 0; */
meillo@10 145 time(&time_before);
meillo@10 146 time_before -= qival;
meillo@10 147 sel_ret = -1;
meillo@0 148
meillo@10 149 while (1) {
meillo@0 150
meillo@10 151 /* if we were interrupted by an incoming connection (or a signal)
meillo@10 152 we have to recalculate the time until the next queue run should
meillo@10 153 occur. select may put a value into tm, but doc for select() says
meillo@10 154 we should not use it. */
meillo@10 155 if (qival > 0) {
meillo@10 156 time(&time_now);
meillo@10 157 if (sel_ret == 0) { /* we are either just starting or did a queue run */
meillo@10 158 tm.tv_sec = qival;
meillo@10 159 tm.tv_usec = 0;
meillo@10 160 time_before = time_now;
meillo@10 161 } else {
meillo@10 162 tm.tv_sec = qival - (time_now - time_before);
meillo@10 163 tm.tv_usec = 0;
meillo@0 164
meillo@10 165 /* race condition, very unlikely (but possible): */
meillo@10 166 if (tm.tv_sec < 0)
meillo@10 167 tm.tv_sec = 0;
meillo@10 168 }
meillo@10 169 }
meillo@10 170 /* Block until input arrives on one or more active sockets,
meillo@15 171 or signal arrives, or queuing interval time elapsed (if qival > 0) */
meillo@10 172 read_fd_set = active_fd_set;
meillo@10 173 if ((sel_ret = select(FD_SETSIZE, &read_fd_set, NULL, NULL, qival > 0 ? &tm : NULL)) < 0) {
meillo@10 174 if (errno != EINTR) {
meillo@10 175 logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno));
meillo@262 176 exit(1);
meillo@10 177 } else {
meillo@10 178 if (sighup_seen) {
meillo@10 179 logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n");
meillo@0 180
meillo@10 181 for (i = 0; i < FD_SETSIZE; i++)
meillo@10 182 if (FD_ISSET(i, &active_fd_set))
meillo@10 183 close(i);
meillo@0 184
meillo@10 185 execv(argv[0], &(argv[0]));
meillo@10 186 logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno));
meillo@262 187 exit(1);
meillo@10 188 }
meillo@10 189 }
meillo@10 190 } else if (sel_ret > 0) {
meillo@10 191 for (i = 0; i < FD_SETSIZE; i++) {
meillo@10 192 if (FD_ISSET(i, &read_fd_set)) {
meillo@10 193 int sock = i;
meillo@10 194 int new;
meillo@10 195 size = sizeof(clientname);
meillo@10 196 new = accept(sock, (struct sockaddr *) &clientname, &size);
meillo@10 197 if (new < 0) {
meillo@10 198 logwrite(LOG_ALERT, "accept: (ignoring): %s\n", strerror(errno));
meillo@10 199 } else
meillo@10 200 accept_connect(sock, new, &clientname);
meillo@10 201 }
meillo@10 202 }
meillo@10 203 } else {
meillo@10 204 /* If select returns 0, the interval time has elapsed.
meillo@10 205 We start a new queue runner process */
meillo@10 206 int pid;
meillo@10 207 signal(SIGCHLD, sigchld_handler);
meillo@10 208 if ((pid = fork()) == 0) {
meillo@10 209 queue_run();
meillo@10 210
meillo@262 211 _exit(0);
meillo@10 212 } else if (pid < 0) {
meillo@10 213 logwrite(LOG_ALERT, "could not fork for queue run");
meillo@10 214 }
meillo@10 215 }
meillo@0 216 }
meillo@0 217 }