masqmail

annotate src/listen.c @ 156:ee2afbf92428

require host_name to be set in config file exit otherwise there is no portable way to determine the hostname (actually the hostname that masqmail should use) thus it must be set by the administrator
author meillo@marmaro.de
date Thu, 08 Jul 2010 09:49:05 +0200
parents 26e34ae9a3e3
children 5745edd5b769
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@10 41 if (WEXITSTATUS(status) != EXIT_SUCCESS)
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@0 49 #ifdef ENABLE_SMTP_SERVER
meillo@10 50 void
meillo@10 51 accept_connect(int listen_sock, int sock, struct sockaddr_in *sock_addr)
meillo@0 52 {
meillo@10 53 pid_t pid;
meillo@10 54 int dup_sock = dup(sock);
meillo@10 55 FILE *out, *in;
meillo@10 56 gchar *rem_host;
meillo@10 57 gchar *ident = NULL;
meillo@0 58
meillo@10 59 rem_host = g_strdup(inet_ntoa(sock_addr->sin_addr));
meillo@0 60 #ifdef ENABLE_IDENT
meillo@10 61 {
meillo@10 62 gchar *id = NULL;
meillo@10 63 if ((id = (gchar *) ident_id(sock, 60))) {
meillo@10 64 ident = g_strdup(id);
meillo@10 65 }
meillo@10 66 logwrite(LOG_NOTICE, "connect from host %s, port %hd ident=%s\n", rem_host,
meillo@10 67 ntohs(sock_addr->sin_port), ident ? ident : "(unknown)");
meillo@10 68 }
meillo@0 69 #else
meillo@10 70 logwrite(LOG_NOTICE, "connect from host %s, port %hd\n", rem_host, ntohs(sock_addr->sin_port));
meillo@0 71 #endif
meillo@0 72
meillo@10 73 // start child for connection:
meillo@10 74 signal(SIGCHLD, sigchld_handler);
meillo@10 75 pid = fork();
meillo@10 76 if (pid == 0) {
meillo@10 77 close(listen_sock);
meillo@10 78 out = fdopen(sock, "w");
meillo@10 79 in = fdopen(dup_sock, "r");
meillo@0 80
meillo@10 81 smtp_in(in, out, rem_host, ident);
meillo@0 82
meillo@10 83 _exit(EXIT_SUCCESS);
meillo@10 84 } else if (pid < 0) {
meillo@10 85 logwrite(LOG_WARNING, "could not fork for incoming smtp connection: %s\n", strerror(errno));
meillo@10 86 }
meillo@0 87 #ifdef ENABLE_IDENT
meillo@10 88 if (ident != NULL)
meillo@10 89 g_free(ident);
meillo@0 90 #endif
meillo@0 91
meillo@10 92 close(sock);
meillo@10 93 close(dup_sock);
meillo@0 94 }
meillo@10 95 #endif /*ifdef ENABLE_SMTP_SERVER */
meillo@0 96
meillo@10 97 void
meillo@10 98 listen_port(GList * iface_list, gint qival, char *argv[])
meillo@0 99 {
meillo@10 100 int i;
meillo@10 101 fd_set active_fd_set, read_fd_set;
meillo@10 102 struct timeval tm;
meillo@10 103 time_t time_before, time_now;
meillo@10 104 struct sockaddr_in clientname;
meillo@10 105 size_t size;
meillo@10 106 GList *node, *node_next;
meillo@10 107 int sel_ret;
meillo@0 108
meillo@10 109 /* Create the sockets and set them up to accept connections. */
meillo@10 110 FD_ZERO(&active_fd_set);
meillo@0 111 #ifdef ENABLE_SMTP_SERVER
meillo@10 112 for (node = g_list_first(iface_list); node; node = node_next) {
meillo@10 113 interface *iface = (interface *) (node->data);
meillo@10 114 int sock;
meillo@0 115
meillo@10 116 node_next = g_list_next(node);
meillo@10 117 if ((sock = make_server_socket(iface)) < 0) {
meillo@10 118 iface_list = g_list_remove_link(iface_list, node);
meillo@10 119 g_list_free_1(node);
meillo@10 120 continue;
meillo@10 121 }
meillo@10 122 if (listen(sock, 1) < 0) {
meillo@10 123 logwrite(LOG_ALERT, "listen: (terminating): %s\n", strerror(errno));
meillo@10 124 exit(EXIT_FAILURE);
meillo@10 125 }
meillo@10 126 logwrite(LOG_NOTICE, "listening on interface %s:%d\n", iface->address, iface->port);
meillo@10 127 DEBUG(5) debugf("sock = %d\n", sock);
meillo@10 128 FD_SET(sock, &active_fd_set);
meillo@10 129 }
meillo@0 130 #endif
meillo@0 131
meillo@10 132 /* setup handler for HUP signal: */
meillo@10 133 signal(SIGHUP, sighup_handler);
meillo@10 134 signal(SIGCHLD, sigchld_handler);
meillo@0 135
meillo@15 136 /* now that we have our socket(s), we can give up root privileges */
meillo@10 137 if (!conf.run_as_user) {
meillo@10 138 if (setegid(conf.mail_gid) != 0) {
meillo@10 139 logwrite(LOG_ALERT, "could not change gid to %d: %s\n", conf.mail_gid, strerror(errno));
meillo@10 140 exit(EXIT_FAILURE);
meillo@10 141 }
meillo@10 142 if (seteuid(conf.mail_uid) != 0) {
meillo@10 143 logwrite(LOG_ALERT, "could not change uid to %d: %s\n", conf.mail_uid, strerror(errno));
meillo@10 144 exit(EXIT_FAILURE);
meillo@10 145 }
meillo@10 146 }
meillo@0 147
meillo@10 148 /* sel_ret = 0; */
meillo@10 149 time(&time_before);
meillo@10 150 time_before -= qival;
meillo@10 151 sel_ret = -1;
meillo@0 152
meillo@10 153 while (1) {
meillo@0 154
meillo@10 155 /* if we were interrupted by an incoming connection (or a signal)
meillo@10 156 we have to recalculate the time until the next queue run should
meillo@10 157 occur. select may put a value into tm, but doc for select() says
meillo@10 158 we should not use it. */
meillo@10 159 if (qival > 0) {
meillo@10 160 time(&time_now);
meillo@10 161 if (sel_ret == 0) { /* we are either just starting or did a queue run */
meillo@10 162 tm.tv_sec = qival;
meillo@10 163 tm.tv_usec = 0;
meillo@10 164 time_before = time_now;
meillo@10 165 } else {
meillo@10 166 tm.tv_sec = qival - (time_now - time_before);
meillo@10 167 tm.tv_usec = 0;
meillo@0 168
meillo@10 169 /* race condition, very unlikely (but possible): */
meillo@10 170 if (tm.tv_sec < 0)
meillo@10 171 tm.tv_sec = 0;
meillo@10 172 }
meillo@10 173 }
meillo@10 174 /* Block until input arrives on one or more active sockets,
meillo@15 175 or signal arrives, or queuing interval time elapsed (if qival > 0) */
meillo@10 176 read_fd_set = active_fd_set;
meillo@10 177 if ((sel_ret = select(FD_SETSIZE, &read_fd_set, NULL, NULL, qival > 0 ? &tm : NULL)) < 0) {
meillo@10 178 if (errno != EINTR) {
meillo@10 179 logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno));
meillo@10 180 exit(EXIT_FAILURE);
meillo@10 181 } else {
meillo@10 182 if (sighup_seen) {
meillo@10 183 logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n");
meillo@0 184
meillo@10 185 for (i = 0; i < FD_SETSIZE; i++)
meillo@10 186 if (FD_ISSET(i, &active_fd_set))
meillo@10 187 close(i);
meillo@0 188
meillo@10 189 execv(argv[0], &(argv[0]));
meillo@10 190 logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno));
meillo@10 191 exit(EXIT_FAILURE);
meillo@10 192 }
meillo@10 193 }
meillo@10 194 } else if (sel_ret > 0) {
meillo@10 195 #ifdef ENABLE_SMTP_SERVER
meillo@10 196 for (i = 0; i < FD_SETSIZE; i++) {
meillo@10 197 if (FD_ISSET(i, &read_fd_set)) {
meillo@10 198 int sock = i;
meillo@10 199 int new;
meillo@10 200 size = sizeof(clientname);
meillo@10 201 new = accept(sock, (struct sockaddr *) &clientname, &size);
meillo@10 202 if (new < 0) {
meillo@10 203 logwrite(LOG_ALERT, "accept: (ignoring): %s\n", strerror(errno));
meillo@10 204 } else
meillo@10 205 accept_connect(sock, new, &clientname);
meillo@10 206 }
meillo@10 207 }
meillo@10 208 #else
meillo@10 209 ;
meillo@10 210 #endif
meillo@10 211 } else {
meillo@10 212 /* If select returns 0, the interval time has elapsed.
meillo@10 213 We start a new queue runner process */
meillo@10 214 int pid;
meillo@10 215 signal(SIGCHLD, sigchld_handler);
meillo@10 216 if ((pid = fork()) == 0) {
meillo@10 217 queue_run();
meillo@10 218
meillo@10 219 _exit(EXIT_SUCCESS);
meillo@10 220 } else if (pid < 0) {
meillo@10 221 logwrite(LOG_ALERT, "could not fork for queue run");
meillo@10 222 }
meillo@10 223 }
meillo@0 224 }
meillo@0 225 }