/*
* ----------------------------------------------------------------
* Night Light IRC Proxy - Main Functions
* ----------------------------------------------------------------
* Copyright (C) 1997-2007 Jonas Kvinge <jonas@night-light.net>
* All rights reserved.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Last modified by:
* Jonas Kvinge (27.11.2007)
*
*/
#define MAIN_C
#define NEED_SYS_TYPES_H 1 /* Extra types */
#define NEED_SYS_PARAM_H 1 /* Some systems need this */
#define NEED_LIMITS_H 0 /* Kernel limits */
#define NEED_STDARG_H 1 /* va_list, etc */
#define NEED_ERRNO_H 1 /* errno */
#define NEED_CTYPE_H 0 /* isdigit(), etc */
#define NEED_NETINET_IN_H 1 /* in_addr, sockaddr_in, etc */
#define NEED_ARPA_INET_H 0 /* inet_ntoa(), inet_aton(), etc */
#define NEED_STDIO_H 1 /* Standard C UNIX functions */
#define NEED_STDLIB_H 1 /* malloc(), exit(), atoi(), etc */
#define NEED_TIME_H 1 /* time(), etc */
#define NEED_SYSCTL_H 0 /* sysctl(), etc */
#define NEED_SYS_STAT_H 0 /* chmod(), mkdir(), etc */
#define NEED_SYS_UIO_H 0 /* iovec, etc */
#define NEED_FCNTL_H 1 /* open(), creat(), fcntl(), etc */
#define NEED_SYS_IOCTL_H 0 /* ioctl(), etc */
#define NEED_SYS_FILIO_H 0 /* Solaris need this for ioctl(), etc */
#define NEED_UNISTD_H 1 /* Unix standard functions */
#define NEED_STRING_H 1 /* C string functions */
#define NEED_SIGNAL_H 0 /* Signal functions */
#define NEED_SYS_SOCKET_H 0 /* Socket functions */
#define NEED_NETDB_H 0 /* Network database functions */
#define NEED_ARPA_NAMESER_H 0 /* Nameserver definitions */
#define NEED_GETUSERPW_HEADERS 0 /* Functions to retrive system passwords */
#define NEED_ARES 1 /* Functions needed for ares */
#define NEED_SSL 1 /* Needed for SSL support */
#include "includes.h"
#include "irc.h"
#if MEMDEBUG
#include "memlist.h"
#endif
#include "main.h"
#include "conf.h"
#if SSL_SUPPORT
#include "ssl.h"
#endif
#include "access_conf.h"
#include "listen_conf.h"
#include "user_conf.h"
#include "conn_conf.h"
#include "autoop_conf.h"
#include "listen.h"
#include "listen_io.h"
#include "client.h"
#include "client_io.h"
#include "client_connection.h"
#include "client_handle.h"
#include "client_notice.h"
#include "conn.h"
#include "conn_io.h"
#include "conn_connection.h"
#if IDENTD
#include "ident_conf.h"
#include "ident_listen.h"
#include "ident_conn.h"
#endif
/* VARIABLES - JONAS (29.07.2001) */
unsigned long int ScreenBitmask =
(
BITMASK_MAIN
|
BITMASK_ERROR
|
BITMASK_EXTRA
#if DEBUG
|
BITMASK_DEBUG
#define SCREENMODE 1
#endif
);
char *STDIN_RecvBufferPT = NULL;
#if ARES
ares_channel Ares_Channel = { 0 };
#endif
extern unsigned short int ScreenMode;
extern unsigned short int ScreenPrint;
extern unsigned short int Run;
extern unsigned short int Exit;
extern time_t ExitTime;
extern time_t FlushTime;
extern struct Conf_Struct ConfS;
extern struct Listen_Struct *Listen_Head;
extern struct Client_Struct *Client_Head;
extern struct Conn_Struct *Conn_Head;
#if IDENTD
struct IdentListen_Struct *IdentListen_Head;
#endif
struct PrintFile_Struct PrintFileS[] = {
{
"/dev/null",
0,
NULL
},
{
MAINLOGFILE,
(BITMASK_MAIN|BITMASK_ERROR
#if EXTRAINFO
|BITMASK_EXTRA
#endif
),
NULL
},
{
ERRORLOGFILE,
BITMASK_ERROR,
NULL
},
#if DEBUG
{
DEBUGLOGFILE,
(BITMASK_MAIN|BITMASK_ERROR|BITMASK_DEBUG),
NULL
}
#endif
};
unsigned short int PrintFileS_Len = (sizeof(PrintFileS) / sizeof(*PrintFileS));
/* MAIN FUNCTION - JONAS (30.07.2001) */
int main(int argc, char **argv) {
signed char Char = 0;
#if SCREENMODE
unsigned short int Fork = FALSE;
#else
unsigned short int Fork = TRUE;
#endif
unsigned short int Term = FALSE;
unsigned short int Rehash = FALSE;
#if FDDEBUG
socktable_add(0, __FILE__, __LINE__, __FUNCTION__);
socktable_add(1, __FILE__, __LINE__, __FUNCTION__);
socktable_add(2, __FILE__, __LINE__, __FUNCTION__);
fdtable_add(stdin, __FILE__, __LINE__, __FUNCTION__, "0", "0");
fdtable_add(stdout, __FILE__, __LINE__, __FUNCTION__, "0", "0");
fdtable_add(stderr, __FILE__, __LINE__, __FUNCTION__, "0", "0");
#endif
sysinit();
while (Char != ERROR) {
Char = getopt(argc, argv, "vfrtc:");
switch(Char) {
case 'v':
printf("%s v%s\n", PACKAGE, VERSION);
printf("%s\n", COPYRIGHT);
exit(0);
case 'c':
if (optarg != NULL) ConfS.File = strdup(optarg);
printf("Using configuration file: %s\n", ConfS.File);
break;
case 'f':
Fork = FALSE;
break;
case 'r':
Rehash = TRUE;
break;
case 't':
Term = TRUE;
break;
case '?':
printf("%s v%s\n", PACKAGE, VERSION);
printf("Syntax: %s [-vfcrt] <configuration file>\n", argv[0]);
printf("v = Print version\n");
printf("f = Fullscreen mode\n");
printf("c = Specify configuration file\n");
printf("r = Rehash program\n");
printf("t = Terminate program\n");
exit(0);
default:
break;
}
}
conf_read();
if (Rehash == TRUE) { sysrehash(); exit(0); }
if (Term == TRUE) { systerm(); exit(0); }
PrintFileS[0].Name = strdup("/dev/null");
PrintFileS[1].Name = strdup(ConfS.MainLogFile);
PrintFileS[2].Name = strdup(ConfS.ErrorLogFile);
#if DEBUG
PrintFileS[3].Name = strdup(ConfS.DebugLogFile);
#endif
sysinitfiles();
sysopenfiles_fg();
#if !WIN32
sysinitid();
#endif
#if !WIN32
syscheckpid();
syswritepid();
if (Fork == TRUE) {
sysfork();
}
#endif
syssetsignals();
sysprint(BITMASK_MAIN, STARTUPLN1);
sysprint(BITMASK_MAIN, STARTUPLN2);
#if ARES
ares_init(&Ares_Channel);
#endif
#if SSL_SUPPORT
if (ConfS.SSLSupport == TRUE) { ssl_init(); }
#endif
main_read();
main_loop();
assert(FALSE);
return(SUCCESS);
}
/* MAIN_READ FUNCTION - JONAS (25.06.2000) */
void main_read(void) {
access_conf_read();
listen_conf_read();
user_conf_read();
conn_conf_read();
#if IDENTD
ident_conf_read();
#endif
autoop_conf_read();
}
/* MAIN_WRITE FUNCTION - JONAS (25.06.2000) */
void main_write(void) {
}
/* MAIN_REHASH FUNCTION - JONAS (25.06.2000) */
void main_rehash(void) {
struct Listen_Struct *ListenS = NULL;
struct Client_Struct *ClientS = NULL;
struct Conn_Struct *ConnS = NULL;
struct ConnServer_Struct *ConnServer = NULL;
struct ConnServer_Struct *ConnServer_DEL = NULL;
struct ListenConf_Struct *ListenConf = NULL;
struct UserConf_Struct *UserConf = NULL;
struct ConnConf_Struct *ConnConf = NULL;
struct ConnConfServer_Struct *ConnConfServer = NULL;
#if IDENTD
struct IdentConf_Struct *IdentConfS = NULL;
struct IdentListen_Struct *IdentListenS = NULL;
#endif
main_read();
for (ListenS = Listen_Head ; ListenS != NULL ; ListenS = ListenS->Next) {
ListenS->Time = 0;
ListenConf = listen_conf_get(ListenS->Host, ListenS->PortH, ((Listen_IsIPv6(ListenS)) ? TRUE : FALSE), ((Listen_IsSSL(ListenS)) ? TRUE : FALSE));
if (ListenConf == NULL) {
listen_stop(ListenS);
Listen_SetRemove(ListenS);
continue;
}
}
for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {
UserConf = user_conf_get(ClientS->User);
if (UserConf == NULL) { client_close(ClientS, "User no longer exist after rehashing configuration."); }
}
for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) {
ConnConf = conn_conf_get(ConnS->Name);
if (ConnConf == NULL) {
if (Conn_IsSocket(ConnS)) {
if (Conn_IsWelcome(ConnS)) { conn_quit(ConnS, "Connection %s removed on configuration rehash.", ConnS->Name); }
else { conn_disconnect(ConnS, "Connection %s removed on configuration rehash.", ConnS->Name); }
}
Conn_SetRemove(ConnS);
}
else {
if (strcmp(ConnS->User, ConnConf->User) != FALSE) {
for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {
if (ClientS->ConnS == ConnS) {
client_notice(ClientS, "You are automatically detached from %s: New owner of connection after rehashing configuration.", ConnS->Name);
client_detach(ClientS);
}
}
ConnS->User = strrealloc(ConnS->User, ConnConf->User);
}
ConnS->Host = strrealloc(ConnS->Host, ConnConf->Host);
ConnS->Nick = strrealloc(ConnS->Nick, ConnConf->Nick);
ConnS->AwayNick = strrealloc(ConnS->AwayNick, ConnConf->AwayNick);
ConnS->Mode = strrealloc(ConnS->Mode, ConnConf->Mode);
ConnS->Info = strrealloc(ConnS->Info, ConnConf->Info);
ConnS->Chans = strrealloc(ConnS->Chans, ConnConf->Chans);
ConnS->ConfFlags = ConnConf->ConfFlags;
ConnS->MaxClients = ConnConf->MaxClients;
ConnS->SendMaxLines = ConnConf->SendMaxLines;
ConnS->SendLineTime = ConnConf->SendLineTime;
ConnS->SendMaxBuffer = ConnConf->SendMaxBuffer;
ConnS->SendBufferTime = ConnConf->SendBufferTime;
ConnS->AwayMsg = strrealloc(ConnS->AwayMsg, ConnConf->AwayMsg);
ConnS->PublicDetachMsg = strrealloc(ConnS->PublicDetachMsg, ConnConf->PublicDetachMsg);
ConnS->PublicAttachMsg = strrealloc(ConnS->PublicAttachMsg, ConnConf->PublicAttachMsg);
ConnS->NickServNUH = strrealloc(ConnS->NickServNUH, ConnConf->NickServNUH);
ConnS->NickServPass = strrealloc(ConnS->NickServPass, ConnConf->NickServPass);
for (ConnServer = ConnS->Server_Head ; ConnServer != NULL ;) {
ConnConfServer = conn_conf_getserver(ConnConf, ConnServer->Host);
if (ConnConfServer == NULL) {
ConnServer_DEL = ConnServer;
ConnServer = ConnServer->Next;
conn_remserver(ConnS, ConnServer_DEL);
continue;
}
else {
ConnServer->Host = strrealloc(ConnServer->Host, ConnConfServer->Host);
ConnServer->Port = ConnConfServer->Port;
ConnServer = ConnServer->Next;
continue;
}
}
}
}
#if IDENTD
for (IdentListenS = IdentListen_Head ; IdentListenS != NULL ; IdentListenS = IdentListenS->Next) {
IdentConfS = ident_conf_get(IdentListenS->Host, IdentListenS->PortH);
if (IdentConfS == NULL) {
ident_listen_stop(IdentListenS);
IdentListen_SetRemove(IdentListenS);
continue;
}
}
#endif
}
/* MAIN_LOOP FUNCTION - JONAS (29.07.2001) */
void main_loop(void) {
signed long int Result = 0;
unsigned long int FDS = 0;
time_t Duration = 0;
fd_set ReadFDS;
fd_set WriteFDS;
struct timeval TV = { 0 };
FOREVERLOOP {
NOW = time(NULL);
Duration = (NOW - FlushTime);
if (Duration >= FLUSHTIME) { sysflushfiles(); }
assert(geteuid() != 0);
if (Exit == TRUE) { TV.tv_sec = 1; }
else { TV.tv_sec = NLPRGSEC; }
TV.tv_usec = NLPRGUSEC;
FD_ZERO(&ReadFDS);
FD_ZERO(&WriteFDS);
if (ScreenMode == TRUE) { FD_SET(STDIN_FILENO, &ReadFDS); }
if (Run == TRUE) {
#if ARES
ares_fds(Ares_Channel, &ReadFDS, &WriteFDS);
#endif
listen_fds(&ReadFDS, &WriteFDS, &FDS);
client_fds(&ReadFDS, &WriteFDS, &FDS);
conn_fds(&ReadFDS, &WriteFDS, &FDS);
#if IDENTD
ident_listen_fds(&ReadFDS, &WriteFDS, &FDS);
ident_conn_fds(&ReadFDS, &WriteFDS, &FDS);
#endif
}
Result = select(FD_SETSIZE, &ReadFDS, &WriteFDS, NULL, &TV);
if (Result <= ERROR) {
if (errno == EINTR) { continue; }
sysprint(BITMASK_ERROR, "select() I/O error: [%d]: %s", errno, strerror(errno));
exit(1);
}
FDS = Result;
NOW = time(NULL);
if (ScreenMode == TRUE) { main_io(&ReadFDS, &WriteFDS, &FDS); }
if (Run == TRUE) {
#if ARES
ares_process(Ares_Channel, &ReadFDS, &WriteFDS);
#endif
listen_io(&ReadFDS, &WriteFDS, &FDS);
client_io(&ReadFDS, &WriteFDS, &FDS);
conn_io(&ReadFDS, &WriteFDS, &FDS);
#if IDENTD
ident_listen_io(&ReadFDS, &WriteFDS, &FDS);
ident_conn_io(&ReadFDS, &WriteFDS, &FDS);
#endif
}
#if 0 /* Doesn't work with C-Ares */
assert(FDS == 0);
#endif
if (Exit == TRUE) { main_exit("Exit."); }
}
}
/* MAIN_IO FUNCTION - JONAS (01.03.2000) */
static void main_io(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) {
signed long int Result = 0;
unsigned long int Len = 0;
unsigned long int OldLen = 0;
unsigned long int NewLen = 0;
char RecvBuffer[RECVBUFFERLEN+1] = "";
char *RecvBufferPT = NULL;
char *TempPT = NULL;
if (!FD_ISSET(STDIN_FILENO, ReadFDS)) { return; }
*FDS = *FDS - 1;
do {
memset(&RecvBuffer, 0, RECVBUFFERLEN+1);
Result = read(STDIN_FILENO, RecvBuffer, RECVBUFFERLEN);
if (Result <= ERROR) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { break; }
sysprint(BITMASK_ERROR, "Read error to stdin: [%d]: %s", errno, strerror(errno));
exit(1);
}
Len = Result;
if (Len == 0) {
sysprint(BITMASK_ERROR, "EOF to stdin.");
exit(1);
}
if (Len <= 1) { return; }
assert(Len == strlen(RecvBuffer));
if (RecvBufferPT == NULL) { OldLen = 0; }
else { OldLen = strlen(RecvBufferPT); }
NewLen = OldLen + Len + 1;
TempPT = realloc(RecvBufferPT, NewLen);
if (TempPT == NULL) { exit(1); }
RecvBufferPT = TempPT;
TempPT += OldLen;
strcpy(TempPT, RecvBuffer);
}
while (Len >= RECVBUFFERLEN);
STDIN_RecvBufferPT = RecvBufferPT;
main_parse();
}
/* MAIN_PARSE FUNCTION - JONAS (01.07.2000) */
static void main_parse(void) {
char *BufferPT = NULL;
char *LinePT = NULL;
char *DumbPT = NULL;
unsigned long int Count = 0;
unsigned long int Len = 0;
assert(STDIN_RecvBufferPT != NULL);
BufferPT = STDIN_RecvBufferPT;
for (Count = 1 ; BufferPT != NULL ; Count++) {
DumbPT = strchr(BufferPT, '\n');
if (DumbPT == NULL) {
if (Count == 1) { return; }
BufferPT = strtok(BufferPT, "\0");
if (BufferPT != NULL) {
Len = strlen(BufferPT) + 1;
strcpy(STDIN_RecvBufferPT, BufferPT);
DumbPT = realloc(STDIN_RecvBufferPT, Len);
if (DumbPT == NULL) { exit(1); }
STDIN_RecvBufferPT = DumbPT;
return;
}
FREE(STDIN_RecvBufferPT);
return;
}
LinePT = strtok(BufferPT, "\n");
BufferPT = strtok(NULL, "\0");
if (LinePT == NULL) { continue; }
Len = strlen(LinePT);
if (LinePT[Len-1] == '\r') { LinePT[Len-1] = 0; }
if (strcasecmp(LinePT, "START") == FALSE) {
if (Run == TRUE) {
printf("I/O loop already started.\n");
continue;
}
Run = TRUE;
printf("I/O loop started.\n");
}
else if (strcasecmp(LinePT, "STOP") == FALSE) {
if (Run == FALSE) {
printf("I/O loop already stopped.\n");
continue;
}
Run = FALSE;
printf("I/O loop stopped.\n");
}
#if !WIN32
else if (strcasecmp(LinePT, "FORK") == FALSE) {
printf("Going into the background...\n");
sysfork();
continue;
}
#endif
else if (strcasecmp(LinePT, "REHASH") == FALSE) {
printf("Rehashing configuration...\n");
main_rehash();
continue;
}
else if (strcasecmp(LinePT, "EXIT") == FALSE) {
main_exit("EXIT from console.");
continue;
}
else if (strcasecmp(LinePT, "EXITNOW") == FALSE) {
exit(0);
}
#if MEMDEBUG
else if (strcasecmp(LinePT, "MEMLIST") == FALSE) {
mem_list();
continue;
}
else if (strcasecmp(LinePT, "MEMLEAKS") == FALSE) {
mem_leaks();
continue;
}
#endif
#if FDDEBUG
else if (strcasecmp(LinePT, "FDPRINT") == FALSE) {
fd_print();
continue;
}
#endif
else if (strcasecmp(LinePT, "PRINT1") == FALSE) {
sysprint(BITMASK_MAIN, "This is a test of MAIN print.");
continue;
}
else if (strcasecmp(LinePT, "PRINT2") == FALSE) {
sysprint(BITMASK_ERROR, "This is a test of ERROR print.");
continue;
}
else if (strcasecmp(LinePT, "PRINT3") == FALSE) {
continue;
}
else {
printf("Unknown command.\n");
continue;
}
if (STDIN_RecvBufferPT == NULL) { break; }
}
FREE(STDIN_RecvBufferPT);
}
/* MAIN_EXIT FUNCTION - JONAS (01.07.2000) */
void main_exit(const char *const MessagePT, ...) {
char Message[LINELEN+1] = "";
va_list Args = { 0 };
unsigned short int Count = 0;
time_t Duration = 0;
va_start(Args, MessagePT);
vsnprintf(Message, LINELEN+1, MessagePT, Args);
va_end(Args);
if (Exit == FALSE) {
DEBUGPRINT(BITMASK_DEBUG_MAIN, "Shutting down: %s", Message);
client_noticeall("Shutting down: %s", Message);
Exit = TRUE;
ExitTime = NOW;
}
Count += client_closeall("%s", Message);
Count += conn_closeall("%s", Message);
Count += listen_closeall("%s", Message);
#if IDENTD
Count += ident_listen_closeall("%s", Message);
Count += ident_conn_closeall();
#endif
#if ARES && !HAVE_ARES_CANCELQUERY && HAVE_ARES_CANCEL
ares_cancel(Ares_Channel);
#endif
Duration = (NOW - ExitTime);
if ((Count == 0) || (Exit >= EXITTTL)) {
access_conf_destroy();
listen_conf_destroy();
user_conf_destroy();
conn_conf_destroy();
#if IDENTD
ident_conf_destroy();
#endif
autoop_conf_destroy();
conf_destroy();
#if ARES
ares_destroy(Ares_Channel);
#endif
#if SSL_SUPPORT
ssl_cleanup();
#endif
irc_cleanup();
strcleanup();
syscleanup();
#if FDDEBUG
fd_print();
fdtable_clear();
socktable_clear();
#endif
#if MEMDEBUG
mem_list();
mem_leaks();
#endif
exit(0);
}
}
syntax highlighted by Code2HTML, v. 0.9.1