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 "masqmail.h" meillo@0: #include meillo@0: #include meillo@0: meillo@0: static int volatile sighup_seen = 0; meillo@0: meillo@0: static meillo@0: void sighup_handler(int sig) meillo@0: { meillo@0: sighup_seen = 1; meillo@0: signal(SIGHUP, sighup_handler); meillo@0: } meillo@0: meillo@0: static meillo@0: void sigchld_handler(int sig) meillo@0: { meillo@0: pid_t pid; meillo@0: int status; meillo@0: meillo@0: pid = waitpid(0, &status, 0); meillo@0: if(pid > 0){ meillo@0: if(WEXITSTATUS(status) != EXIT_SUCCESS) meillo@0: logwrite(LOG_WARNING, "process %d exited with %d\n", meillo@0: pid, WEXITSTATUS(status)); meillo@0: if(WIFSIGNALED(status)) meillo@0: logwrite(LOG_WARNING, meillo@0: "process with pid %d got signal: %d\n", meillo@0: pid, WTERMSIG(status)); meillo@0: } meillo@0: signal(SIGCHLD, sigchld_handler); meillo@0: } meillo@0: meillo@0: #ifdef ENABLE_SMTP_SERVER meillo@0: void accept_connect(int listen_sock, int sock, struct sockaddr_in* sock_addr) meillo@0: { meillo@0: pid_t pid; meillo@0: int dup_sock = dup(sock); meillo@0: FILE *out, *in; meillo@0: gchar *rem_host; meillo@0: gchar *ident = NULL; meillo@0: meillo@0: rem_host = g_strdup(inet_ntoa(sock_addr->sin_addr)); meillo@0: #ifdef ENABLE_IDENT meillo@0: { meillo@0: gchar *id = NULL; meillo@0: if((id = (gchar *)ident_id(sock, 60))){ meillo@0: ident = g_strdup(id); meillo@0: } meillo@0: logwrite(LOG_NOTICE, "connect from host %s, port %hd ident=%s\n", meillo@0: rem_host, meillo@0: ntohs (sock_addr->sin_port), meillo@0: ident ? ident : "(unknown)"); meillo@0: } meillo@0: #else meillo@0: logwrite(LOG_NOTICE, "connect from host %s, port %hd\n", meillo@0: rem_host, meillo@0: ntohs (sock_addr->sin_port)); meillo@0: #endif meillo@0: meillo@0: // start child for connection: meillo@0: signal(SIGCHLD, sigchld_handler); meillo@0: pid = fork(); meillo@0: if(pid == 0){ meillo@0: close(listen_sock); meillo@0: out = fdopen(sock, "w"); meillo@0: in = fdopen(dup_sock, "r"); meillo@0: meillo@0: smtp_in(in, out, rem_host, ident); meillo@0: meillo@0: _exit(EXIT_SUCCESS); meillo@0: }else if(pid < 0){ meillo@0: logwrite(LOG_WARNING, "could not fork for incoming smtp connection: %s\n", meillo@0: strerror(errno)); meillo@0: } meillo@0: meillo@0: #ifdef ENABLE_IDENT meillo@0: if(ident != NULL) g_free(ident); meillo@0: #endif meillo@0: meillo@0: close(sock); meillo@0: close(dup_sock); meillo@0: } meillo@0: #endif /*ifdef ENABLE_SMTP_SERVER*/ meillo@0: meillo@0: void listen_port(GList *iface_list, gint qival, char *argv[]) meillo@0: { meillo@0: int i; meillo@0: fd_set active_fd_set, read_fd_set; meillo@0: struct timeval tm; meillo@0: time_t time_before, time_now; meillo@0: struct sockaddr_in clientname; meillo@0: size_t size; meillo@0: GList *node, *node_next; meillo@0: int sel_ret; meillo@0: meillo@0: /* Create the sockets and set them up to accept connections. */ meillo@0: FD_ZERO (&active_fd_set); meillo@0: #ifdef ENABLE_SMTP_SERVER meillo@0: for(node = g_list_first(iface_list); meillo@0: node; meillo@0: node = node_next){ meillo@0: interface *iface = (interface *)(node->data); meillo@0: int sock; meillo@0: meillo@0: node_next=g_list_next(node); meillo@0: if ((sock = make_server_socket (iface))<0){ meillo@0: iface_list= g_list_remove_link(iface_list, node); meillo@0: g_list_free_1(node); meillo@0: continue; meillo@0: } meillo@0: if (listen (sock, 1) < 0){ meillo@0: logwrite(LOG_ALERT, "listen: (terminating): %s\n", strerror(errno)); meillo@0: exit (EXIT_FAILURE); meillo@0: } meillo@0: logwrite(LOG_NOTICE, "listening on interface %s:%d\n", meillo@0: iface->address, iface->port); meillo@0: DEBUG(5) debugf("sock = %d\n", sock); meillo@0: FD_SET (sock, &active_fd_set); meillo@0: } meillo@0: #endif meillo@0: meillo@0: /* setup handler for HUP signal: */ meillo@0: signal(SIGHUP, sighup_handler); meillo@0: signal(SIGCHLD, sigchld_handler); meillo@0: meillo@0: /* now that we have our socket(s), meillo@0: we can give up root privileges */ meillo@0: if(!conf.run_as_user){ meillo@0: if(setegid(conf.mail_gid) != 0){ meillo@0: logwrite(LOG_ALERT, "could not change gid to %d: %s\n", meillo@0: conf.mail_gid, strerror(errno)); meillo@0: exit(EXIT_FAILURE); meillo@0: } meillo@0: if(seteuid(conf.mail_uid) != 0){ meillo@0: logwrite(LOG_ALERT, "could not change uid to %d: %s\n", meillo@0: conf.mail_uid, strerror(errno)); meillo@0: exit(EXIT_FAILURE); meillo@0: } meillo@0: } meillo@0: meillo@0: /* sel_ret = 0;*/ meillo@0: time(&time_before); meillo@0: time_before -= qival; meillo@0: sel_ret = -1; meillo@0: meillo@0: while (1){ meillo@0: meillo@0: /* if we were interrupted by an incoming connection (or a signal) meillo@0: we have to recalculate the time until the next queue run should meillo@0: occur. select may put a value into tm, but doc for select() says meillo@0: we should not use it.*/ meillo@0: if(qival > 0){ meillo@0: time(&time_now); meillo@0: if(sel_ret == 0){ /* we are either just starting or did a queue run */ meillo@0: tm.tv_sec = qival; meillo@0: tm.tv_usec = 0; meillo@0: time_before = time_now; meillo@0: }else{ meillo@0: tm.tv_sec = qival - (time_now - time_before); meillo@0: tm.tv_usec = 0; meillo@0: meillo@0: /* race condition, very unlikely (but possible): */ meillo@0: if(tm.tv_sec < 0) meillo@0: tm.tv_sec = 0; meillo@0: } meillo@0: } meillo@0: /* Block until input arrives on one or more active sockets, meillo@0: or signal arrives, meillo@0: or queuing interval time elapsed (if qival > 0) */ meillo@0: read_fd_set = active_fd_set; meillo@0: if ((sel_ret = select(FD_SETSIZE, &read_fd_set, NULL, NULL, meillo@0: qival > 0 ? &tm : NULL)) < 0){ meillo@0: if(errno != EINTR){ meillo@0: logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno)); meillo@0: exit (EXIT_FAILURE); meillo@0: }else{ meillo@0: if(sighup_seen){ meillo@0: logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n"); meillo@0: meillo@0: for(i = 0; i < FD_SETSIZE; i++) meillo@0: if(FD_ISSET(i, &active_fd_set)) meillo@0: close(i); meillo@0: meillo@0: execv(argv[0], &(argv[0])); meillo@0: logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno)); meillo@0: exit(EXIT_FAILURE); meillo@0: } meillo@0: } meillo@0: } meillo@0: else if(sel_ret > 0){ meillo@0: #ifdef ENABLE_SMTP_SERVER meillo@0: for(i = 0; i < FD_SETSIZE; i++){ meillo@0: if (FD_ISSET (i, &read_fd_set)){ meillo@0: int sock = i; meillo@0: int new; meillo@0: size = sizeof (clientname); meillo@0: new = accept (sock, meillo@0: (struct sockaddr *) &clientname, meillo@0: &size); meillo@0: if (new < 0){ meillo@0: logwrite(LOG_ALERT, "accept: (ignoring): %s\n", meillo@0: strerror(errno)); meillo@0: }else meillo@0: accept_connect(sock, new, &clientname); meillo@0: } meillo@0: } meillo@0: #else meillo@0: ; meillo@0: #endif meillo@0: }else{ meillo@0: /* If select returns 0, the interval time has elapsed. meillo@0: We start a new queue runner process */ meillo@0: int pid; meillo@0: signal(SIGCHLD, sigchld_handler); meillo@0: if((pid = fork()) == 0){ meillo@0: queue_run(); meillo@0: meillo@0: _exit(EXIT_SUCCESS); meillo@0: } meillo@0: else if(pid < 0){ meillo@0: logwrite(LOG_ALERT, "could not fork for queue run"); meillo@0: } meillo@0: } meillo@0: } meillo@0: }