/*
* POP3Lite - 3lite POP3 Daemon
* Copyright (C) 2000, 2001 Gergely Nagy <8@free.bsd.hu>
*
* This file is part of POP3Lite.
*
* POP3Lite 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.
*
* POP3Lite 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 "main.h"
#include <pop3lite.h>
#include <ltdl.h>
#include <glib.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#include <stdio.h>
#include <signal.h>
#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>
#include "cfg.h"
#include "core.h"
#include "module.h"
#ifdef WITH_STANDALONE_SUPPORT
# include <errno.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include "standalone.h"
int errno;
unsigned long child_cnt = 0;
char *pidfn = LOCALSTATEDIR "/run/pop3lite.pid";
pid_t main_pid;
#endif
#define LINELEN 512 /* RFC 2449 */
static const char rcsid[]="$Id: main.c,v 1.29.2.3 2001/05/30 12:38:57 algernon Exp $";
extern int opterr;
static gboolean logout_timer_reset = 1;
static unsigned long logout_iv;
static void sig_handler ( int number );
static void sigalrm_handler ( int sig );
static void setup_signal_handlers ( void );
static void setup_sigalrm_handler ( void );
static int auto_logout_handler ( P3LControl *control );
static void main_loop_body ( GHashTable *command_table );
static void exit_hook ( void );
static void print_version ( void );
static void pop3lite_main_loop ( P3LControl *control );
/**
* control: the main P3LControl struct
*
* This is declared global, becuase sig_handler uses it.
* BTW, this is the struct through which the whole daemon is
* implemented.
**/
static P3LControl *control;
/**
* setup_sigalrm_handler: sets up SIGALRM hander
*
* This is used to setup/reset the SIGALRM handler
*
* Returns: nothing
**/
static void
setup_sigalrm_handler ( void )
{
/* This clears the handler, just in case... */
signal ( SIGALRM, SIG_IGN );
signal ( SIGALRM, &sigalrm_handler );
alarm ( 0 );
alarm ( SIGALRM_INTERVAL );
}
/**
* sigchld_handler: SIGC(H)LD handler
*
* SIGC(H)LD handler
*
* Returns: nothing
**/
#ifdef WITH_STANDALONE_SUPPORT
static void
sigchld_handler ( int signo )
{
pid_t child;
int status;
while ( ( child = waitpid ( -1, &status, WNOHANG ) ) > (pid_t) 0 )
child_cnt--;
signal ( signo, &sigchld_handler );
}
#endif
/**
* setup_signal_handlers: setup signal handlers
*
* This function sets up the signal handlers
*
* Returns: nothing
**/
static void
setup_signal_handlers ( void )
{
/*
* Is use signal here, because...
* well. sigaction wouldn't provide any
* benefit here.
*/
signal ( SIGHUP, sig_handler );
signal ( SIGINT, sig_handler );
signal ( SIGQUIT, sig_handler );
signal ( SIGILL, sig_handler );
signal ( SIGABRT, sig_handler );
signal ( SIGFPE, sig_handler );
signal ( SIGSEGV, sig_handler );
signal ( SIGPIPE, SIG_IGN );
signal ( SIGTERM, sig_handler );
signal ( SIGUSR1, sig_handler );
signal ( SIGUSR2, sig_handler );
setup_sigalrm_handler ();
}
/**
* sig_handler: signal handler
* @number: signal number
*
* Handles signals gracefully.
*
* Returns: nothing. it exits.
**/
static void
sig_handler ( int number )
{
control->system->log ( control, LOG_CRIT, "Terminating on signal %d: %s",
number, g_strsignal ( number ) );
if ( P3L_GET_DATA ( "USER" ) )
control->send_response ( control, POP3_FATAL, "internal error" );
exit(1);
}
/**
* sigalrm_handler: SIGALRM handler
* @sig: signal number, unused
*
* This is the master SIGALRM handler. It increments
* the tick counter of all registered handlers, and
* runs those that reach their time.
*
* Returns: nothing
**/
static void
sigalrm_handler ( int sig )
{
unsigned long i;
HandlerInfo *hinfo;
GList *handlers = p3l_get_alarm_hooks ();
for ( i = 0; i < g_list_length ( handlers ); i++ )
{
hinfo = (HandlerInfo *) g_list_nth_data ( handlers, i );
hinfo->counter++;
if ( hinfo->counter >= hinfo->tick_max )
{
hinfo->counter=0;
(* (hinfo->hook) ) ( control );
}
}
setup_sigalrm_handler ();
}
/**
* auto_logout_handler: auto-logout SIGALRM handler
* @control: the main control struct
*
* This function is called whenever auto-logout time is
* reached.
*
* Returns: 0
**/
static int
auto_logout_handler ( P3LControl *control )
{
if ( logout_timer_reset == 0 )
{
control->system->log ( control, LOG_NOTICE, "auto-logout time elapsed (%s)",
P3L_GET_DATA ( "USER" ) );
control->send_response ( control, POP3_FATAL, "auto-logout time elapsed" );
exit ( 1 );
return 0;
}
if ( logout_timer_reset == 1 )
logout_timer_reset = 0;
return 0;
}
/**
* main_loop_body: main loop body
* @command_table: table to search for commands
*
* This one is called in the main loops, this reads
* input and executes commands.
*
* Returns: nothing.
**/
static void
main_loop_body ( GHashTable *command_table )
{
char *line;
char **cmdarg;
unsigned int c, len;
p3l_pop3_command cmd_ptr;
CommandResponse *resp;
/*
* Read input, max LINELEN chars. Ended with \n or \r
*/
line = (char *) g_malloc ( LINELEN + 2 );
len = 0;
logout_timer_reset = 0;
while ( ( c = fgetc ( stdin ) ) != '\r' && c != '\n' && len < LINELEN )
{
if ( c != '\r' && c != '\n' )
line[len++] = c;
else
len++;
}
logout_timer_reset = 2;
/*
* If the first character is 0xff, ignore it, because
* under heavy load, that gets prepended to some commands.
* FIXME: find the REAL reason for this!
*/
if ( line[0] == (char) 0xff )
{
line = &line[1];
len--;
}
/*
* Is it too long ?
*/
if ( len >= LINELEN )
{
control->send_response ( control, POP3_ERR, "command too long");
exit ( 1 );
}
/*
* Split into command + arg, and execute.
*/
line[len] = '\0';
line = g_strdup ( g_strstrip ( line ) );
cmdarg = g_strsplit ( line, " ", 1 );
if ( cmdarg[0] != NULL )
{
g_strup ( cmdarg[0] );
cmd_ptr = (p3l_pop3_command) g_hash_table_lookup ( command_table, cmdarg[0] );
if ( cmd_ptr == NULL )
control->send_response ( control, POP3_ERR, "Invalid command" );
else
{
resp = (*cmd_ptr) ( control, cmdarg[1] );
control->send_response ( control, resp->code, resp->message );
g_free ( resp );
}
}
g_strfreev ( cmdarg );
g_free ( line );
logout_timer_reset = 1;
}
/**
* exit_hook: g_atexit hook
*
* Called when the program terminates.
*
* Returns: nothing.
**/
static void
exit_hook ( void )
{
if ( p3l_is_enabled ( P3L_GET_FIRST_OPTION ( "QUIT_ON_ERROR" ) ) &&
P3L_GET_DATA ( "USER" ) )
control->update ( control );
#ifndef WITH_STANDALONE_SUPPORT
free_modules ( control );
#endif
control->system->closelog ( control );
#ifdef WITH_STANDALONE_SUPPORT
if ( getpid () == main_pid )
{
unlink ( pidfn );
free_modules ( control );
}
#endif
}
/**
* print_version: print version information
*
* Prints something like this:
* POP3Lite version 0.1.4-devel (1.107) [i386-pc-gnu]
* Copyright (C) 2000, 2001 Gergely Nagy <8@free.bsd.hu>
*
* Returns: nothing
**/
static void
print_version ( void )
{
printf ( "POP3Lite version %s%s [%s]\n",
POP3LITE_VERSION, EXTRA_VERSION, __MACHINE__ );
printf ( "%s\n", "Copyright (C) 2000, 2001 Gergely Nagy <8@free.bsd.hu>" );
}
/**
* pop3lite_main_loop: main loop
* @control: the main control struct
*
* This is the main loop. In standalone mode, it is the only job
* of the child process.
*
* Returns: nothing
**/
static void
pop3lite_main_loop ( P3LControl *control )
{
CommandResponse *res;
char *alt;
setup_signal_handlers ();
/*
* Greeting
*/
res = control->greeting ( control );
control->send_response ( control, res->code, res->message );
g_free ( res );
/*
* Setup auto-logout handler
*/
alt = P3L_GET_FIRST_OPTION ( "AUTO_LOGOUT_TIME" );
if ( alt == NULL )
alt = "10";
logout_iv = strtoul ( alt, (char **) NULL, 10 ) * 60;
p3l_register_alarm ( auto_logout_handler, logout_iv );
/*
* AUTHENTICATION state
*/
while ( control->state == POP3_STATE_AUTH )
main_loop_body ( control->auth_commands );
/*
* If we're authenticated, we MUST get rid of all
* our special privileges
*/
control->system->drop_privileges ( control );
/*
* If we're still root, log a warning and exit
* if ALLOW_ROOT is off
*/
if ( getuid () == 0 )
{
control->system->log ( control, LOG_ALERT, "Still running as root!" );
if ( ! p3l_is_enabled ( P3L_GET_FIRST_OPTION ( "ALLOW_ROOT" ) ) )
{
control->send_response ( control, POP3_FATAL, "Internal error" );
exit ( 1 ); /* In case send_response didn't do it... */
}
}
/*
* Get into transaction state
*/
res = control->trans_init ( control );
control->send_response ( control, res->code, res->message );
g_free ( res );
/*
* TRANSACTION state
*/
while ( control->state == POP3_STATE_TRANS )
main_loop_body ( control->trans_commands );
/*
* Do update
*/
res = control->update ( control );
control->send_response ( control, res->code, res->message );
g_free ( res );
/*
* Exit
*/
exit ( 0 );
}
/**
* main: CC hack
* @argc: argument count
* @argv: argument vector
*
* This is here, because stupid C compilers want to do
* nasty things with main. ;>
*
* Returns: bogus numbers
**/
int
main ( int argc, char **argv )
{
struct option long_options[] = {
{"config", required_argument, 0, 'c'},
{"define", required_argument, 0, 'D'},
{"help", no_argument, 0, 'h'},
{"undefine", required_argument, 0, 'U'},
{"version", no_argument, 0, 'v'},
#ifdef WITH_STANDALONE_SUPPORT
{"inetd", no_argument, 0, 'i'},
{"pidfile", required_argument, 0, 'p'},
#endif
{0, 0, 0, 0}
};
int optc;
char *cfg_file = NULL;
GList *cmd_def = NULL;
GList *cmd_udef= NULL;
#ifdef WITH_STANDALONE_SUPPORT
FILE *pidfile;
int sd;
pid_t child, self;
char *host, *port;
gboolean from_inetd = FALSE;
char *getopt_list = "c:hvD:U:ip:";
unsigned long MAX_CHILDREN = 20;
#else
char *getopt_list = "c:hvD:U:";
#endif
opterr = 0;
/*
* Argument parsing
*/
while ( ( optc = getopt_long ( argc, argv, getopt_list, long_options, 0 ) ) != - 1 )
{
switch ( optc )
{
case 0:
break;
case 'c':
cfg_file = optarg;
break;
case 'D':
cmd_def = g_list_append ( cmd_def, g_strdup ( optarg ) );
break;
case 'U':
cmd_udef = g_list_append ( cmd_udef, g_strdup ( optarg ) );
break;
case 'h':
print_version ();
printf ( "%s", "\nOptions:\n"
"\t--config, -c [file] use [file] as the "
"configuration file.\n"
"\t--define, -D [field]=[value] define [field] as [value].\n" );
#ifdef WITH_STANDALONE_SUPPORT
printf ( "%s", "\t--inetd, -i run from inetd.\n" );
#endif
printf ( "%s", "\t--help, -h this help screen.\n" );
#ifdef WITH_STANDALONE_SUPPORT
printf ( "%s", "\t--pidfile, -p [file] write the pid to [file] in daemon mode.\n" );
#endif
printf ( "%s", "\t--undefine, -U [field] undefine [field].\n"
"\t--version, -v print version information.\n"
"\nReport bugs to <8@free.bsd.hu>\n" );
exit ( 0 );
break;
case 'v':
print_version ();
printf ( "%s", "\nPOP3Lite comes with ABSOLUTELY NO WARRANTY.\n"
"You may redistribute copies of POP3Lite under the terms of the GNU\n"
"General Public License, either version two, or any later version.\n" );
#ifdef DEBIAN
printf ( "%s", "For more information see /usr/share/common-licenses/GPL.\n" );
#else
printf ( "%s", "For more information about these matters, see the file named COPYING.\n" );
#endif
exit ( 0 );
break;
#ifdef WITH_STANDALONE_SUPPORT
case 'i':
from_inetd=TRUE;
break;
case 'p':
pidfn = g_strdup ( optarg );
break;
#endif
default:
break;
}
}
/*
* Control structure initialisation
*/
control = (P3LControl *) g_malloc ( sizeof ( P3LControl ) );
control->greeting = NULL;
control->send_response = NULL;
control->send_raw = NULL;
control->trans_init = NULL;
control->state = POP3_STATE_AUTH;
control->msg_info=NULL;
control->auth_commands = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
control->trans_commands = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
control->data = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
control->config = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
control->capabilities = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
control->system = (SysControl *) g_malloc ( sizeof ( SysControl ) );
control->system->getuinam = NULL;
control->system->authenticate = NULL;
control->system->drop_privileges = NULL;
control->system->openlog = NULL;
control->system->log = NULL;
control->system->closelog = NULL;
control->system->users = NULL;
control->system->data = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
control->hooks = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
control->modules = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
alarm_handlers = NULL;
/*
* Load default module
*/
core_init ( control );
/*
* Setup signal handlers.
*/
setup_signal_handlers ();
control->system->openlog ( control );
/*
* AtExit magic. If we must exit, do it in an elegant way.
*/
if ( g_ATEXIT ( exit_hook ) < 0 )
{
control->system->log ( control, LOG_ERR, "g_ATEXIT failed" );
exit(1);
}
/*
* Load configuration
*/
p3l_load_config ( control, cfg_file, control->config );
/*
* Setup configuration specified on the commandline
*/
for ( optc = 0; optc < g_list_length ( cmd_def ); optc++ )
cfg_parse_line ( control, g_list_nth_data ( cmd_def, optc ),
0, NULL, control->config );
/*
* Undefine options specified with --undefine
*/
for ( optc = 0; optc < g_list_length ( cmd_udef ); optc++ )
g_hash_table_remove ( control->config,
g_list_nth_data ( cmd_udef, optc ) );
/*
* Reinit core, in case the configuration has changed something
*/
core_reinit ( control );
/*
* Load modules
*/
load_modules ( control );
/*
* Main loop
*/
#ifdef WITH_STANDALONE_SUPPORT
if ( P3L_GET_FIRST_OPTION ( "MAX_CHILDREN" ) )
strtoul ( P3L_GET_FIRST_OPTION ( "MAX_CHILDREN" ), (char **) NULL, 10 );
if ( from_inetd || p3l_get_peer_name () != NULL )
{
main_pid = getpid ();
pop3lite_main_loop ( control );
}
else if ( ( self = fork () ) == 0 )
{
close ( 0 );
close ( 1 );
close ( 2 );
close ( 3 );
host = P3L_GET_FIRST_OPTION ( "LISTEN.HOST" );
port = P3L_GET_FIRST_OPTION ( "LISTEN.PORT" );
if ( port == NULL )
port = "110";
if ( ( sd = tcp_listen ( host, port ) ) < 0 )
{
if ( host == NULL )
control->system->log ( control, LOG_ERR,
g_strdup_printf ( "Cannot listen on port %s", port ) );
else
control->system->log ( control, LOG_ERR,
g_strdup_printf ( "Cannot listen on %s:%s", host, port ) );
exit ( 1 );
}
#ifdef DEBUG
if ( host == NULL )
control->system->log ( control, LOG_DEBUG,
g_strdup_printf ( "Listening on port %s", port ) );
else
control->system->log ( control, LOG_DEBUG,
g_strdup_printf ( "Listening on %s:%s", host, port ) );
#endif
pidfile = fopen ( pidfn, "w" );
if ( pidfile != NULL )
{
fprintf ( pidfile, "%ld\n", (long) getpid () );
fclose ( pidfile );
}
else
control->system->log ( control, LOG_WARNING,
g_strdup_printf ( "Cannot write to pidfile: %s", pidfn ) );
main_pid = getpid ();
#if defined(SIGCHLD)
signal ( SIGCHLD, &sigchld_handler );
#elif defined(SIGCLD)
signal ( SIGCLD, &sigcld_handler );
#endif
while ( 1 )
{
struct sockaddr_in them;
socklen_t len = sizeof them;
int ns = accept ( sd, (struct sockaddr*) &them, &len );
if ( ns < 0 )
{
if ( errno == EINTR || errno == ECONNRESET )
continue;
control->system->log ( control, LOG_ERR,
g_strdup_printf ( "Accept failed: %s", g_strerror ( errno ) ) );
exit ( 1 );
}
if ( MAX_CHILDREN == 0 || child_cnt <= MAX_CHILDREN )
{
switch ( child = fork() )
{
case -1:
control->system->log ( control, LOG_ERR,
g_strdup_printf ( "Cannot fork child: %s",
g_strerror ( errno ) ) );
break;
case 0:
close ( sd );
dup2 ( ns, 0 );
dup2 ( ns, 1 );
setsid ();
if ( p3l_get_peer_name () )
pop3lite_main_loop ( control );
else
{
control->system->log ( control, LOG_WARNING,
"Remote end not connected." );
return 1;
}
break;
default:
child_cnt++;
close ( ns );
break;
}
}
else
{
control->system->log ( control, LOG_ERR, "Too many open connections!" );
close ( ns );
}
}
}
else if ( self == -1 )
control->system->log ( control, LOG_ERR, "Cannot fork main daemon" );
#else
pop3lite_main_loop ( control );
#endif
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1