piler/src/piler.c

472 lines
11 KiB
C
Raw Normal View History

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>
#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);
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();
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;
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){
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;
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);
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){
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;
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;
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");
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
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);
clearrules(data.archiving_rules);
clearrules(data.retention_rules);
2011-11-19 21:25:44 +01: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);
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);
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
clearrules(data.archiving_rules);
clearrules(data.retention_rules);
2012-02-19 22:59:47 +01:00
clearhash(data.mydomains);
2013-07-12 22:54:45 +02:00
data.folder = 0;
data.recursive_folder_names = 0;
2014-01-13 13:06:10 +01: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;
}
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){
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' :
printf("%s %s, build %d, Janos SUTO <sj@acts.hu>\n\n%s\n", PROGNAME, VERSION, get_build(), CONFIGURE_PARAMS);
get_extractor_list();
2012-07-06 21:44:57 +02:00
return 0;
2011-11-14 15:57:52 +01:00
case 'h' :
default :
__fatal("usage: ...");
}
}
(void) openlog(PROGNAME, LOG_PID, LOG_MAIL);
data.folder = 0;
data.recursive_folder_names = 0;
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;
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-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-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
p_clean_exit();
2011-11-14 15:57:52 +01:00
return 0;
}