2011-11-14 15:57:52 +01:00
/*
* piler . c , SJ
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <strings.h>
# include <sys/types.h>
# include <sys/socket.h>
2015-05-09 14:31:20 +02:00
# include <sys/mman.h>
2013-04-02 16:35:32 +02:00
# include <netdb.h>
2011-11-14 15:57:52 +01:00
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/time.h>
# include <sys/wait.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <pwd.h>
# include <signal.h>
# include <syslog.h>
# include <time.h>
# include <unistd.h>
# include <locale.h>
# include <errno.h>
2012-10-28 20:36:46 +01:00
# include <openssl/ssl.h>
# include <openssl/err.h>
2011-11-14 15:57:52 +01:00
# include <piler.h>
extern char * optarg ;
extern int optind ;
int sd ;
2012-01-07 00:00:36 +01:00
int quit = 0 ;
int received_sighup = 0 ;
2011-11-14 15:57:52 +01:00
char * configfile = CONFIG_FILE ;
struct __config cfg ;
struct __data data ;
struct passwd * pwd ;
2012-01-07 00:00:36 +01:00
struct child children [ MAXCHILDREN ] ;
static void takesig ( int sig ) ;
static void child_sighup_handler ( int sig ) ;
static void child_main ( struct child * ptr ) ;
static pid_t child_make ( struct child * ptr ) ;
int search_slot_by_pid ( pid_t pid ) ;
void kill_children ( int sig ) ;
2012-10-29 10:22:31 +01:00
void p_clean_exit ( ) ;
2012-08-16 12:31:10 +02:00
void fatal ( char * s ) ;
2012-01-07 00:00:36 +01:00
void initialise_configuration ( ) ;
static void takesig ( int sig ) {
int i , status ;
pid_t pid ;
switch ( sig ) {
case SIGHUP :
initialise_configuration ( ) ;
2012-08-16 12:15:14 +02:00
if ( read_key ( & cfg ) ) fatal ( ERR_READING_KEY ) ;
2012-01-07 00:00:36 +01:00
kill_children ( SIGHUP ) ;
break ;
case SIGTERM :
case SIGKILL :
quit = 1 ;
2012-10-29 10:22:31 +01:00
p_clean_exit ( ) ;
2012-01-07 00:00:36 +01:00
break ;
case SIGCHLD :
while ( ( pid = waitpid ( - 1 , & status , WNOHANG ) ) > 0 ) {
//syslog(LOG_PRIORITY, "child (pid: %d) has died", pid);
if ( quit = = 0 ) {
i = search_slot_by_pid ( pid ) ;
if ( i > = 0 ) {
2015-05-09 14:31:20 +02:00
children [ i ] . serial = i ;
2012-01-07 00:00:36 +01:00
children [ i ] . status = READY ;
children [ i ] . pid = child_make ( & children [ i ] ) ;
}
else syslog ( LOG_PRIORITY , " error: couldn't find slot for pid %d " , pid ) ;
}
}
break ;
}
return ;
}
static void child_sighup_handler ( int sig ) {
if ( sig = = SIGHUP ) {
received_sighup = 1 ;
}
}
static void child_main ( struct child * ptr ) {
int new_sd ;
2013-04-02 16:35:32 +02:00
char s [ INET6_ADDRSTRLEN ] ;
struct sockaddr_storage client_addr ;
socklen_t addr_size ;
2012-01-07 00:00:36 +01:00
ptr - > messages = 0 ;
2015-05-09 14:31:20 +02:00
if ( cfg . verbosity > = _LOG_DEBUG ) syslog ( LOG_PRIORITY , " child (pid: %d, serial: %d) started main() " , getpid ( ) , ptr - > serial ) ;
2012-01-07 00:00:36 +01:00
while ( 1 ) {
if ( received_sighup = = 1 ) {
if ( cfg . verbosity > = _LOG_DEBUG ) syslog ( LOG_PRIORITY , " child (pid: %d) caught HUP signal " , getpid ( ) ) ;
break ;
}
ptr - > status = READY ;
2013-04-02 16:35:32 +02:00
addr_size = sizeof ( client_addr ) ;
new_sd = accept ( sd , ( struct sockaddr * ) & client_addr , & addr_size ) ;
2012-01-07 00:00:36 +01:00
if ( new_sd = = - 1 ) continue ;
ptr - > status = BUSY ;
2013-04-02 16:35:32 +02:00
inet_ntop ( client_addr . ss_family , get_in_addr ( ( struct sockaddr * ) & client_addr ) , s , sizeof ( s ) ) ;
syslog ( LOG_PRIORITY , " connection from %s " , s ) ;
2015-05-09 14:31:20 +02:00
data . child_serial = ptr - > serial ;
2012-01-07 00:00:36 +01:00
sig_block ( SIGHUP ) ;
ptr - > messages + = handle_smtp_session ( new_sd , & data , & cfg ) ;
sig_unblock ( SIGHUP ) ;
close ( new_sd ) ;
if ( cfg . max_requests_per_child > 0 & & ptr - > messages > = cfg . max_requests_per_child ) {
2015-05-09 14:31:20 +02:00
if ( cfg . verbosity > = _LOG_DEBUG ) syslog ( LOG_PRIORITY , " child (pid: %d, serial: %d) served enough: %d " , getpid ( ) , ptr - > messages , ptr - > serial ) ;
2012-01-07 00:00:36 +01:00
break ;
}
}
ptr - > status = UNDEF ;
# ifdef HAVE_MEMCACHED
memcached_shutdown ( & ( data . memc ) ) ;
# endif
if ( cfg . verbosity > = _LOG_DEBUG ) syslog ( LOG_PRIORITY , " child decides to exit (pid: %d) " , getpid ( ) ) ;
exit ( 0 ) ;
}
static pid_t child_make ( struct child * ptr ) {
pid_t pid ;
if ( ( pid = fork ( ) ) > 0 ) return pid ;
if ( pid = = - 1 ) return - 1 ;
if ( cfg . verbosity > = _LOG_DEBUG ) syslog ( LOG_PRIORITY , " forked a child (pid: %d) " , getpid ( ) ) ;
/* reset signals */
set_signal_handler ( SIGCHLD , SIG_DFL ) ;
set_signal_handler ( SIGTERM , SIG_DFL ) ;
set_signal_handler ( SIGHUP , child_sighup_handler ) ;
child_main ( ptr ) ;
return - 1 ;
}
int child_pool_create ( ) {
int i ;
for ( i = 0 ; i < MAXCHILDREN ; i + + ) {
children [ i ] . pid = 0 ;
children [ i ] . messages = 0 ;
children [ i ] . status = UNDEF ;
2015-05-09 14:31:20 +02:00
children [ i ] . serial = - 1 ;
2012-01-07 00:00:36 +01:00
}
for ( i = 0 ; i < cfg . number_of_worker_processes ; i + + ) {
children [ i ] . status = READY ;
2015-05-09 14:31:20 +02:00
children [ i ] . serial = i ;
2012-01-07 00:00:36 +01:00
children [ i ] . pid = child_make ( & children [ i ] ) ;
if ( children [ i ] . pid = = - 1 ) {
syslog ( LOG_PRIORITY , " error: failed to fork a child " ) ;
2012-10-29 10:22:31 +01:00
p_clean_exit ( ) ;
2012-01-07 00:00:36 +01:00
}
}
return 0 ;
}
int search_slot_by_pid ( pid_t pid ) {
int i ;
for ( i = 0 ; i < MAXCHILDREN ; i + + ) {
if ( children [ i ] . pid = = pid ) return i ;
}
return - 1 ;
}
void kill_children ( int sig ) {
int i ;
for ( i = 0 ; i < MAXCHILDREN ; i + + ) {
if ( children [ i ] . status ! = UNDEF & & children [ i ] . pid > 1 ) {
if ( cfg . verbosity > = _LOG_DEBUG ) syslog ( LOG_PRIORITY , " sending signal to child (pid: %d) " , children [ i ] . pid ) ;
kill ( children [ i ] . pid , sig ) ;
}
}
}
2011-11-14 15:57:52 +01:00
2012-10-29 10:22:31 +01:00
void p_clean_exit ( ) {
2011-11-14 15:57:52 +01:00
if ( sd ! = - 1 ) close ( sd ) ;
2012-01-07 00:00:36 +01:00
kill_children ( SIGTERM ) ;
2013-08-14 14:24:30 +02:00
clearrules ( data . archiving_rules ) ;
clearrules ( data . retention_rules ) ;
2011-11-19 21:25:44 +01:00
2013-08-14 14:24:30 +02:00
clearhash ( data . mydomains ) ;
2013-07-12 22:54:45 +02:00
2011-11-14 15:57:52 +01:00
syslog ( LOG_PRIORITY , " %s has been terminated " , PROGNAME ) ;
unlink ( cfg . pidfile ) ;
2015-05-09 14:31:20 +02:00
if ( data . dedup ! = MAP_FAILED ) munmap ( data . dedup , MAXCHILDREN * DIGEST_LENGTH * 2 ) ;
2012-10-28 20:36:46 +01:00
# ifdef HAVE_STARTTLS
if ( data . ctx ) {
SSL_CTX_free ( data . ctx ) ;
ERR_free_strings ( ) ;
}
# endif
2011-11-14 15:57:52 +01:00
exit ( 1 ) ;
}
void fatal ( char * s ) {
syslog ( LOG_PRIORITY , " %s \n " , s ) ;
2012-10-29 10:22:31 +01:00
p_clean_exit ( ) ;
2011-11-14 15:57:52 +01:00
}
2012-10-28 20:36:46 +01:00
# ifdef HAVE_STARTTLS
int init_ssl ( ) {
SSL_library_init ( ) ;
SSL_load_error_strings ( ) ;
2014-10-15 22:53:09 +02:00
data . ctx = SSL_CTX_new ( TLSv1_server_method ( ) ) ;
2012-10-28 20:36:46 +01:00
if ( data . ctx = = NULL ) { syslog ( LOG_PRIORITY , " SSL_CTX_new() failed " ) ; return ERR ; }
if ( SSL_CTX_set_cipher_list ( data . ctx , cfg . cipher_list ) = = 0 ) { syslog ( LOG_PRIORITY , " failed to set cipher list: '%s' " , cfg . cipher_list ) ; return ERR ; }
if ( SSL_CTX_use_PrivateKey_file ( data . ctx , cfg . pemfile , SSL_FILETYPE_PEM ) ! = 1 ) { syslog ( LOG_PRIORITY , " cannot load private key from %s " , cfg . pemfile ) ; return ERR ; }
if ( SSL_CTX_use_certificate_file ( data . ctx , cfg . pemfile , SSL_FILETYPE_PEM ) ! = 1 ) { syslog ( LOG_PRIORITY , " cannot load certificate from %s " , cfg . pemfile ) ; return ERR ; }
return OK ;
}
# endif
2011-11-19 21:25:44 +01:00
void initialise_configuration ( ) {
struct session_data sdata ;
2011-11-14 15:57:52 +01:00
cfg = read_config ( configfile ) ;
2012-01-07 00:00:36 +01:00
if ( cfg . number_of_worker_processes < 5 ) cfg . number_of_worker_processes = 5 ;
if ( cfg . number_of_worker_processes > MAXCHILDREN ) cfg . number_of_worker_processes = MAXCHILDREN ;
2011-11-14 15:57:52 +01:00
if ( strlen ( cfg . username ) > 1 ) {
pwd = getpwnam ( cfg . username ) ;
if ( ! pwd ) fatal ( ERR_NON_EXISTENT_USER ) ;
}
if ( getuid ( ) = = 0 & & pwd ) {
check_and_create_directories ( & cfg , pwd - > pw_uid , pwd - > pw_gid ) ;
}
if ( chdir ( cfg . workdir ) ) {
syslog ( LOG_PRIORITY , " workdir: *%s* " , cfg . workdir ) ;
fatal ( ERR_CHDIR ) ;
}
setlocale ( LC_MESSAGES , cfg . locale ) ;
setlocale ( LC_CTYPE , cfg . locale ) ;
2011-11-19 21:25:44 +01:00
2013-08-14 14:24:30 +02:00
clearrules ( data . archiving_rules ) ;
clearrules ( data . retention_rules ) ;
2012-02-19 22:59:47 +01:00
2013-08-14 14:24:30 +02:00
clearhash ( data . mydomains ) ;
2013-07-12 22:54:45 +02:00
2012-08-23 10:23:58 +02:00
data . folder = 0 ;
2012-09-03 17:12:02 +02:00
data . recursive_folder_names = 0 ;
2014-01-13 13:06:10 +01:00
2013-08-14 14:24:30 +02:00
inithash ( data . mydomains ) ;
initrules ( data . archiving_rules ) ;
initrules ( data . retention_rules ) ;
2013-07-12 22:54:45 +02:00
2012-10-28 20:36:46 +01:00
# ifdef HAVE_STARTTLS
if ( cfg . tls_enable > 0 & & data . ctx = = NULL & & init_ssl ( ) = = OK ) {
2013-01-06 22:16:21 +01:00
snprintf ( data . starttls , sizeof ( data . starttls ) - 1 , " 250-STARTTLS \r \n " ) ;
2012-10-28 20:36:46 +01:00
}
# endif
2013-04-28 14:18:09 +02:00
if ( open_database ( & sdata , & cfg ) = = ERR ) {
2011-11-19 21:25:44 +01:00
syslog ( LOG_PRIORITY , " cannot connect to mysql server " ) ;
return ;
}
2013-08-14 14:24:30 +02:00
load_rules ( & sdata , & data , data . archiving_rules , SQL_ARCHIVING_RULE_TABLE ) ;
load_rules ( & sdata , & data , data . retention_rules , SQL_RETENTION_RULE_TABLE ) ;
2011-11-19 21:25:44 +01:00
2013-01-06 22:16:21 +01:00
load_mydomains ( & sdata , & data , & cfg ) ;
2013-02-15 20:14:35 +01:00
if ( cfg . server_id > 0 ) insert_offset ( & sdata , cfg . server_id ) ;
2013-04-28 14:18:09 +02:00
close_database ( & sdata ) ;
2011-11-19 21:25:44 +01:00
2011-11-14 15:57:52 +01:00
syslog ( LOG_PRIORITY , " reloaded config: %s " , configfile ) ;
# ifdef HAVE_MEMCACHED
memcached_init ( & ( data . memc ) , cfg . memcached_servers , 11211 ) ;
# endif
}
int main ( int argc , char * * argv ) {
2015-05-09 14:31:20 +02:00
int i , rc , yes = 1 , daemonise = 0 , dedupfd ;
2013-04-09 14:50:27 +02:00
char port_string [ 8 ] ;
2013-04-02 16:35:32 +02:00
struct addrinfo hints , * res ;
2011-11-14 15:57:52 +01:00
while ( ( i = getopt ( argc , argv , " c:dvVh " ) ) > 0 ) {
switch ( i ) {
case ' c ' :
configfile = optarg ;
break ;
case ' d ' :
daemonise = 1 ;
break ;
case ' v ' :
2015-03-18 11:20:46 +01:00
printf ( " %s build %d \n " , VERSION , get_build ( ) ) ;
return 0 ;
2011-11-14 15:57:52 +01:00
case ' V ' :
2012-07-06 21:44:57 +02:00
printf ( " %s %s, build %d, Janos SUTO <sj@acts.hu> \n \n %s \n \n " , PROGNAME , VERSION , get_build ( ) , CONFIGURE_PARAMS ) ;
return 0 ;
2011-11-14 15:57:52 +01:00
case ' h ' :
default :
__fatal ( " usage: ... " ) ;
}
}
( void ) openlog ( PROGNAME , LOG_PID , LOG_MAIL ) ;
2012-08-23 10:23:58 +02:00
data . folder = 0 ;
2012-09-03 17:12:02 +02:00
data . recursive_folder_names = 0 ;
2013-08-14 14:24:30 +02:00
inithash ( data . mydomains ) ;
initrules ( data . archiving_rules ) ;
initrules ( data . retention_rules ) ;
2012-10-28 20:36:46 +01:00
data . ctx = NULL ;
data . ssl = NULL ;
2015-05-09 14:31:20 +02:00
data . dedup = MAP_FAILED ;
2014-09-18 10:44:45 +02:00
memset ( data . starttls , 0 , sizeof ( data . starttls ) ) ;
2011-11-14 15:57:52 +01:00
2011-11-19 21:25:44 +01:00
initialise_configuration ( ) ;
2011-11-14 15:57:52 +01:00
2012-01-07 00:00:36 +01:00
set_signal_handler ( SIGPIPE , SIG_IGN ) ;
2011-11-14 15:57:52 +01:00
if ( read_key ( & cfg ) ) fatal ( ERR_READING_KEY ) ;
2013-04-02 16:35:32 +02:00
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = AF_UNSPEC ;
hints . ai_socktype = SOCK_STREAM ;
2011-11-14 15:57:52 +01:00
2013-04-02 16:35:32 +02:00
snprintf ( port_string , sizeof ( port_string ) - 1 , " %d " , cfg . listen_port ) ;
if ( ( rc = getaddrinfo ( cfg . listen_addr , port_string , & hints , & res ) ) ! = 0 ) {
fprintf ( stderr , " getaddrinfo for '%s': %s \n " , cfg . listen_addr , gai_strerror ( rc ) ) ;
return 1 ;
}
if ( ( sd = socket ( res - > ai_family , res - > ai_socktype , res - > ai_protocol ) ) = = - 1 )
fatal ( ERR_OPEN_SOCKET ) ;
2011-11-14 15:57:52 +01:00
if ( setsockopt ( sd , SOL_SOCKET , SO_REUSEADDR , & yes , sizeof ( int ) ) = = - 1 )
fatal ( ERR_SET_SOCK_OPT ) ;
2013-04-02 16:35:32 +02:00
if ( bind ( sd , res - > ai_addr , res - > ai_addrlen ) = = - 1 )
2011-11-14 15:57:52 +01:00
fatal ( ERR_BIND_TO_PORT ) ;
if ( listen ( sd , cfg . backlog ) = = - 1 )
fatal ( ERR_LISTEN ) ;
2013-04-02 16:35:32 +02:00
freeaddrinfo ( res ) ;
2011-11-14 15:57:52 +01:00
if ( drop_privileges ( pwd ) ) fatal ( ERR_SETUID ) ;
2015-05-14 14:35:07 +02:00
if ( cfg . mmap_dedup_test = = 1 ) {
dedupfd = open ( MESSAGE_ID_DEDUP_FILE , O_RDWR ) ;
if ( dedupfd = = - 1 ) fatal ( ERR_OPEN_DEDUP_FILE ) ;
2015-05-09 14:31:20 +02:00
2015-05-14 14:35:07 +02:00
data . dedup = mmap ( NULL , MAXCHILDREN * DIGEST_LENGTH * 2 , PROT_READ | PROT_WRITE , MAP_SHARED , dedupfd , 0 ) ;
close ( dedupfd ) ;
2015-05-09 14:31:20 +02:00
2015-05-14 14:35:07 +02:00
if ( data . dedup = = MAP_FAILED ) syslog ( LOG_INFO , " cannot mmap() %s, errno=%d " , MESSAGE_ID_DEDUP_FILE , errno ) ;
}
2011-11-14 15:57:52 +01:00
2012-07-06 21:44:57 +02:00
syslog ( LOG_PRIORITY , " %s %s, build %d starting " , PROGNAME , VERSION , get_build ( ) ) ;
2011-11-14 15:57:52 +01:00
# if HAVE_DAEMON == 1
if ( daemonise = = 1 ) i = daemon ( 1 , 0 ) ;
# endif
write_pid_file ( cfg . pidfile ) ;
2012-01-07 00:00:36 +01:00
child_pool_create ( ) ;
2011-11-14 15:57:52 +01:00
2012-01-07 00:00:36 +01:00
set_signal_handler ( SIGCHLD , takesig ) ;
set_signal_handler ( SIGTERM , takesig ) ;
set_signal_handler ( SIGKILL , takesig ) ;
set_signal_handler ( SIGHUP , takesig ) ;
2011-11-14 15:57:52 +01:00
2012-01-07 00:00:36 +01:00
for ( ; ; ) { sleep ( 1 ) ; }
2011-11-14 15:57:52 +01:00
2012-10-29 10:22:31 +01:00
p_clean_exit ( ) ;
2011-11-14 15:57:52 +01:00
return 0 ;
}