diff src/listen.c @ 0:08114f7dcc23 0.2.21

this is masqmail-0.2.21 from oliver kurth
author meillo@marmaro.de
date Fri, 26 Sep 2008 17:05:23 +0200
parents
children 26e34ae9a3e3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/listen.c	Fri Sep 26 17:05:23 2008 +0200
@@ -0,0 +1,243 @@
+/*  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 "masqmail.h"
+#include <sys/wait.h>
+#include <sys/types.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) != EXIT_SUCCESS)
+      logwrite(LOG_WARNING, "process %d exited with %d\n",
+	       pid, WEXITSTATUS(status));
+    if(WIFSIGNALED(status))
+      logwrite(LOG_WARNING,
+	       "process with pid %d got signal: %d\n",
+	       pid, WTERMSIG(status));
+  }
+  signal(SIGCHLD, sigchld_handler);
+}
+
+#ifdef ENABLE_SMTP_SERVER
+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));
+#ifdef ENABLE_IDENT
+  {
+    gchar *id = NULL;
+    if((id = (gchar *)ident_id(sock, 60))){
+      ident = g_strdup(id);
+    }
+    logwrite(LOG_NOTICE, "connect from host %s, port %hd ident=%s\n",
+	     rem_host,
+	     ntohs (sock_addr->sin_port),
+	     ident ? ident : "(unknown)");
+  }
+#else
+  logwrite(LOG_NOTICE, "connect from host %s, port %hd\n",
+	 rem_host,
+	 ntohs (sock_addr->sin_port));
+#endif
+
+  // start child for connection:
+  signal(SIGCHLD, sigchld_handler);
+  pid = fork();
+  if(pid == 0){
+    close(listen_sock);
+    out = fdopen(sock, "w");
+    in = fdopen(dup_sock, "r");
+    
+    smtp_in(in, out, rem_host, ident);
+
+    _exit(EXIT_SUCCESS);
+  }else if(pid < 0){
+    logwrite(LOG_WARNING, "could not fork for incoming smtp connection: %s\n",
+	     strerror(errno));
+  }
+
+#ifdef ENABLE_IDENT
+  if(ident != NULL) g_free(ident);
+#endif
+
+  close(sock);
+  close(dup_sock);
+}
+#endif /*ifdef ENABLE_SMTP_SERVER*/
+
+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);
+#ifdef ENABLE_SMTP_SERVER
+  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 (EXIT_FAILURE);
+    }
+    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);
+  }
+#endif
+
+  /* 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){
+    if(setegid(conf.mail_gid) != 0){
+      logwrite(LOG_ALERT, "could not change gid to %d: %s\n",
+	       conf.mail_gid, strerror(errno));
+      exit(EXIT_FAILURE);
+    }
+    if(seteuid(conf.mail_uid) != 0){
+      logwrite(LOG_ALERT, "could not change uid to %d: %s\n",
+	       conf.mail_uid, strerror(errno));
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  /*  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 == 0){ /* we are either just starting or did 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, very 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 (EXIT_FAILURE);
+      }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(EXIT_FAILURE);
+	}
+      }
+    }
+    else if(sel_ret > 0){
+#ifdef ENABLE_SMTP_SERVER
+      for(i = 0; i < FD_SETSIZE; i++){
+	if (FD_ISSET (i, &read_fd_set)){
+	  int sock = i;
+	  int new;
+	  size = sizeof (clientname);
+	  new = accept (sock,
+			(struct sockaddr *) &clientname,
+			&size);
+	  if (new < 0){
+	    logwrite(LOG_ALERT, "accept: (ignoring): %s\n",
+		     strerror(errno));
+	  }else
+	    accept_connect(sock, new, &clientname);
+	}
+      }
+#else
+      ;
+#endif
+    }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(EXIT_SUCCESS);
+      }
+      else if(pid < 0){
+	logwrite(LOG_ALERT, "could not fork for queue run");
+      }
+    }
+  }
+}