/*
Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl
Copyright (c) 2005-2006 NFG Net Facilities Group BV support@nfg.nl
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* serverchild.c
*
* $Id: serverchild.c 2199 2006-07-18 11:07:53Z paul $
*
* function implementations of server children code (connection handling)
*/
#include "dbmail.h"
volatile sig_atomic_t ChildStopRequested = 0;
volatile sig_atomic_t childSig = 0;
volatile sig_atomic_t alarm_occured = 0;
int connected = 0;
volatile clientinfo_t client;
static void disconnect_all(void);
int PerformChildTask(ChildInfo_t * info);
void client_close(void)
{
if (client.tx) {
fflush(client.tx);
fclose(client.tx); /* closes clientSocket as well */
client.tx = NULL;
}
if (client.rx) {
shutdown(fileno(client.rx), SHUT_RDWR);
fclose(client.rx);
client.rx = NULL;
}
}
void disconnect_all(void)
{
if (! connected)
return;
db_disconnect();
auth_disconnect();
connected = 0;
}
void noop_child_sig_handler(int sig, siginfo_t *info UNUSED, void *data UNUSED)
{
if (sig == SIGSEGV)
_exit(0);
}
void active_child_sig_handler(int sig, siginfo_t * info UNUSED, void *data UNUSED)
{
int saved_errno = errno;
/* Perform reinit at SIGHUP otherwise exit, but do nothing on
* SIGCHLD. Make absolutely sure that everything downwind of
* this function is signal-safe! Symptoms of signal-unsafe
* calls are random errors like this:
* *** glibc detected *** corrupted double-linked list: 0x0805f028 ***
* Right, so keep that in mind! */
switch (sig) {
case SIGCHLD:
break;
case SIGALRM:
alarm_occured = 1;
break;
default:
ChildStopRequested = 1;
childSig = sig;
break;
}
errno = saved_errno;
}
/*
* SetChildSigHandler()
*
* sets the signal handler for a child proces
*/
int SetChildSigHandler()
{
struct sigaction act;
struct sigaction rstact;
memset(&act, 0, sizeof(act));
memset(&rstact, 0, sizeof(rstact));
act.sa_sigaction = active_child_sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
rstact.sa_sigaction = active_child_sig_handler;
sigemptyset(&rstact.sa_mask);
rstact.sa_flags = SA_SIGINFO | SA_RESETHAND;
sigaddset(&act.sa_mask, SIGINT);
sigaddset(&act.sa_mask, SIGQUIT);
sigaddset(&act.sa_mask, SIGILL);
sigaddset(&act.sa_mask, SIGBUS);
sigaddset(&act.sa_mask, SIGFPE);
sigaddset(&act.sa_mask, SIGSEGV);
sigaddset(&act.sa_mask, SIGTERM);
sigaddset(&act.sa_mask, SIGHUP);
sigaction(SIGINT, &rstact, 0);
sigaction(SIGQUIT, &rstact, 0);
sigaction(SIGILL, &rstact, 0);
sigaction(SIGBUS, &rstact, 0);
sigaction(SIGFPE, &rstact, 0);
sigaction(SIGSEGV, &rstact, 0);
sigaction(SIGTERM, &rstact, 0);
sigaction(SIGHUP, &rstact, 0);
sigaction(SIGALRM, &act, 0);
sigaction(SIGCHLD, &act, 0);
return 0;
}
int DelChildSigHandler()
{
struct sigaction act;
/* init & install signal handlers */
memset(&act, 0, sizeof(act));
act.sa_sigaction = noop_child_sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, 0);
sigaction(SIGQUIT, &act, 0);
sigaction(SIGILL, &act, 0);
sigaction(SIGBUS, &act, 0);
sigaction(SIGFPE, &act, 0);
sigaction(SIGSEGV, &act, 0);
sigaction(SIGTERM, &act, 0);
sigaction(SIGHUP, &act, 0);
sigaction(SIGALRM, &act, 0);
return 0;
}
/*
* CreateChild()
*
* creates a new child, returning only to the parent process
*/
pid_t CreateChild(ChildInfo_t * info)
{
pid_t pid = fork();
if (! pid) {
if (child_register() == -1) {
trace(TRACE_FATAL, "%s,%s: child_register failed",
__FILE__, __func__);
_exit(0);
}
ChildStopRequested = 0;
alarm_occured = 0;
childSig = 0;
SetChildSigHandler();
trace(TRACE_INFO, "%s,%s: signal handler placed, going to perform task now",
__FILE__, __func__);
if (PerformChildTask(info) == -1)
return -1;
child_unregister();
exit(0);
} else {
usleep(5000);
/* check for failed forkes */
if (waitpid(pid, NULL, WNOHANG|WUNTRACED) == pid)
return -1;
return pid;
}
}
int PerformChildTask(ChildInfo_t * info)
{
int i, len, serr, clientSocket;
struct sockaddr_in saClient;
struct hostent *clientHost;
if (!info) {
trace(TRACE_ERROR, "%s,%s: NULL info supplied",
__FILE__, __func__);
return -1;
}
if (db_connect() != 0) {
trace(TRACE_ERROR, "%s,%s: could not connect to database",
__FILE__, __func__);
return -1;
}
if (auth_connect() != 0) {
trace(TRACE_ERROR, "%s,%s: could not connect to authentication",
__FILE__, __func__);
return -1;
}
srand((int) ((int) time(NULL) + (int) getpid()));
connected = 1;
for (i = 0; i < info->maxConnect && !ChildStopRequested; i++) {
if (db_check_connection()) {
trace(TRACE_ERROR, "%s,%s: database has gone away",
__FILE__, __func__);
ChildStopRequested=1;
continue;
}
trace(TRACE_INFO, "%s,%s: waiting for connection",
__FILE__, __func__);
child_reg_disconnected();
/* wait for connect */
len = sizeof(saClient);
clientSocket = accept(info->listenSocket, (struct sockaddr *) &saClient, (socklen_t *)&len);
if (clientSocket == -1) {
serr = errno;
i--; /* don't count this as a connect */
trace(TRACE_INFO, "%s,%s: accept failed [%s]",
__FILE__, __func__, strerror(serr));
errno = serr;
continue; /* accept failed, refuse connection & continue */
}
child_reg_connected();
memset((void *)&client, 0, sizeof(client)); /* zero-init */
client.timeoutMsg = info->timeoutMsg;
client.timeout = info->timeout;
strncpy((char *)client.ip_src, inet_ntoa(saClient.sin_addr), IPNUM_LEN);
client.clientname[0] = '\0';
if (info->resolveIP) {
clientHost = gethostbyaddr((char *) &saClient.sin_addr,
sizeof(saClient.sin_addr), saClient.sin_family);
if (clientHost && clientHost->h_name)
strncpy((char *)client.clientname, clientHost->h_name, FIELDSIZE);
trace(TRACE_MESSAGE, "%s,%s: incoming connection from [%s (%s)] by pid [%d]",
__FILE__, __func__,
client.ip_src,
client.clientname[0] ? client.clientname : "Lookup failed", getpid());
} else {
trace(TRACE_MESSAGE, "%s,%s: incoming connection from [%s] by pid [%d]",
__FILE__, __func__,
client.ip_src, getpid());
}
/* make streams */
if (!(client.rx = fdopen(dup(clientSocket), "r"))) {
/* read-FILE opening failure */
trace(TRACE_ERROR, "%s,%s: error opening read file stream",
__FILE__, __func__);
close(clientSocket);
continue;
}
if (!(client.tx = fdopen(clientSocket, "w"))) {
/* write-FILE opening failure */
trace(TRACE_ERROR, "%s,%s: error opening write file stream",
__FILE__, __func__);
fclose(client.rx);
close(clientSocket);
memset((void *)&client, 0, sizeof(client));
continue;
}
setvbuf(client.tx, (char *) NULL, _IOLBF, 0);
setvbuf(client.rx, (char *) NULL, _IOLBF, 0);
trace(TRACE_DEBUG, "%s,%s: client info init complete, calling client handler",
__FILE__, __func__);
/* streams are ready, perform handling */
info->ClientHandler((clientinfo_t *)&client);
trace(TRACE_DEBUG, "%s,%s: client handling complete, closing streams",
__FILE__, __func__);
client_close();
trace(TRACE_INFO, "%s,%s: connection closed",
__FILE__, __func__);
}
if (!ChildStopRequested)
trace(TRACE_ERROR, "%s,%s: maximum number of connections reached, stopping now",
__FILE__, __func__);
else{
switch(childSig){
case SIGHUP:
case SIGTERM:
case SIGQUIT:
client_close();
disconnect_all();
child_unregister();
exit(1);
default:
child_unregister();
_exit(1);
}
trace(TRACE_ERROR, "%s,%s: stop requested",
__FILE__, __func__);
}
child_reg_disconnected();
disconnect_all();
return 0;
}
int manage_start_cli_server(ChildInfo_t * info)
{
if (!info) {
trace(TRACE_ERROR, "%s,%s: NULL info supplied",
__FILE__, __func__);
return -1;
}
if (db_connect() != 0) {
trace(TRACE_ERROR, "%s,%s: could not connect to database",
__FILE__, __func__);
return -1;
}
if (auth_connect() != 0) {
trace(TRACE_ERROR, "%s,%s: could not connect to authentication",
__FILE__, __func__);
return -1;
}
srand((int) ((int) time(NULL) + (int) getpid()));
connected = 1;
if (db_check_connection()) {
trace(TRACE_ERROR, "%s,%s: database has gone away",
__FILE__, __func__);
return -1;
}
memset((void *)&client, 0, sizeof(client)); /* zero-init */
client.timeoutMsg = info->timeoutMsg;
client.timeout = info->timeout;
/* make streams */
client.rx = stdin;
client.tx = stdout;
setvbuf(client.tx, (char *) NULL, _IOLBF, 0);
setvbuf(client.rx, (char *) NULL, _IOLBF, 0);
trace(TRACE_DEBUG,
"%s,%s: client info init complete, calling client handler", __FILE__, __func__);
/* streams are ready, perform handling */
info->ClientHandler((clientinfo_t *)&client);
trace(TRACE_DEBUG,
"%s,%s: client handling complete, closing streams", __FILE__, __func__);
client_close();
trace(TRACE_INFO, "%s,%s: connection closed", __FILE__, __func__);
disconnect_all();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1