view src/listen.c @ 434:f2a7271746d1 default tip

Removes Freshmeat.net from the docs The site, which was later renamed to freecode.com, is no longer maintained (contains only a static copy).
author markus schnalke <meillo@marmaro.de>
date Sat, 07 Feb 2015 11:45:07 +0100
parents 180a7f6a9383
children
line wrap: on
line source

/*
**  MasqMail
**  Copyright (C) 1999/2000 Oliver Kurth
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <sys/wait.h>
#include <sys/types.h>

#include "masqmail.h"

static int volatile sighup_seen = 0;

static void
sighup_handler(int sig)
{
	sighup_seen = 1;
	signal(SIGHUP, sighup_handler);
}

static void
sigchld_handler(int sig)
{
	pid_t pid;
	int status;

	pid = waitpid(0, &status, 0);
	if (pid > 0) {
		if (WEXITSTATUS(status) != 0) {
			logwrite(LOG_WARNING, "process %d exited with %d\n",
					pid, WEXITSTATUS(status));
		}
		if (WIFSIGNALED(status)) {
			logwrite(LOG_WARNING, "process %d got signal: %d\n",
					pid, WTERMSIG(status));
		}
	}
	signal(SIGCHLD, sigchld_handler);
}

void
accept_connect(int listen_sock, int sock, struct sockaddr_in *sock_addr)
{
	pid_t pid;
	int dup_sock = dup(sock);
	FILE *out, *in;
	gchar *rem_host;
	gchar *ident = NULL;

	rem_host = g_strdup(inet_ntoa(sock_addr->sin_addr));
	logwrite(LOG_NOTICE, "connect from host %s, port %hu\n",
			rem_host, ntohs(sock_addr->sin_port));

	/* start child for connection: */
	signal(SIGCHLD, sigchld_handler);
	pid = fork();
	if (pid < 0) {
		logwrite(LOG_WARNING, "could not fork for incoming smtp "
				"connection: %s\n", strerror(errno));
	} else if (pid == 0) {
		/* child */
		close(listen_sock);
		out = fdopen(sock, "w");
		in = fdopen(dup_sock, "r");
		smtp_in(in, out, rem_host, ident);
		_exit(0);
	}

	close(sock);
	close(dup_sock);
}

void
listen_port(GList *iface_list, gint qival, char *argv[])
{
	int i;
	fd_set active_fd_set, read_fd_set;
	struct timeval tm;
	time_t time_before, time_now;
	struct sockaddr_in clientname;
	size_t size;
	GList *node, *node_next;
	int sel_ret;

	/* Create the sockets and set them up to accept connections. */
	FD_ZERO(&active_fd_set);
	for (node = g_list_first(iface_list); node; node = node_next) {
		interface *iface = (interface *) (node->data);
		int sock;

		node_next = g_list_next(node);
		if ((sock = make_server_socket(iface)) < 0) {
			iface_list = g_list_remove_link(iface_list, node);
			g_list_free_1(node);
			continue;
		}
		if (listen(sock, 1) < 0) {
			logwrite(LOG_ALERT, "listen: (terminating): %s\n",
					strerror(errno));
			exit(1);
		}
		logwrite(LOG_NOTICE, "listening on interface %s:%d\n",
				iface->address, iface->port);
		DEBUG(5) debugf("sock = %d\n", sock);
		FD_SET(sock, &active_fd_set);
	}

	/* setup handler for HUP signal: */
	signal(SIGHUP, sighup_handler);
	signal(SIGCHLD, sigchld_handler);

	/* now that we have our socket(s), we can give up root privileges */
	if (!conf.run_as_user) {
		set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
	}

	/*  sel_ret = 0; */
	time(&time_before);
	time_before -= qival;
	sel_ret = -1;

	while (1) {

		/*
		**  if we were interrupted by an incoming connection (or a
		**  signal) we have to recalculate the time until the next
		**  queue run should occur. select may put a value into tm,
		**  but doc for select() says we should not use it.
		*/
		if (qival > 0) {
			time(&time_now);
			if (!sel_ret) {
				/* either just starting or after a queue run */
				tm.tv_sec = qival;
				tm.tv_usec = 0;
				time_before = time_now;
			} else {
				tm.tv_sec = qival - (time_now - time_before);
				tm.tv_usec = 0;

				/* race condition, unlikely (but possible): */
				if (tm.tv_sec < 0) {
					tm.tv_sec = 0;
				}
			}
		}
		/*
		**  Block until input arrives on one or more active sockets,
		**  or signal arrives, or queuing interval time elapsed
		**  (if qival > 0)
		*/
		read_fd_set = active_fd_set;
		if ((sel_ret = select(FD_SETSIZE, &read_fd_set, NULL, NULL,
				qival > 0 ? &tm : NULL)) < 0) {
			if (errno != EINTR) {
				logwrite(LOG_ALERT, "select: (terminating): "
						"%s\n", strerror(errno));
				exit(1);
			} else if (sighup_seen) {
				logwrite(LOG_NOTICE, "HUP signal received. "
						"Restarting daemon\n");

				for (i = 0; i < FD_SETSIZE; i++)
					if (FD_ISSET(i, &active_fd_set))
						close(i);

				execv(argv[0], &(argv[0]));
				logwrite(LOG_ALERT, "restarting failed: %s\n",
						strerror(errno));
				exit(1);
			}
		} else if (sel_ret > 0) {
			for (i = 0; i < FD_SETSIZE; i++) {
				int sock = i;
				int new;

				if (!FD_ISSET(i, &read_fd_set)) {
					continue;
				}
				size = sizeof(clientname);
				new = accept(sock, (struct sockaddr *)
						&clientname,
						(socklen_t *)&size);
				if (new < 0) {
					logwrite(LOG_ALERT, "accept: (ignoring): %s\n", strerror(errno));
				} else {
					accept_connect(sock, new,
							&clientname);
				}
			}
		} else {
			/*
			**  If select returns 0, the interval time has elapsed.
			**  We start a new queue runner process
			*/
			int pid;
			signal(SIGCHLD, sigchld_handler);
			if ((pid = fork()) == 0) {
				queue_run();

				_exit(0);
			} else if (pid < 0) {
				logwrite(LOG_ALERT, "could not fork for "
						"queue run");
			}
		}
	}
}