/*
$Id: serverchild.c 2087 2006-04-28 14:26:28Z paul $
Copyright (C) 1999-2004 IC & S dbmail@ic-s.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
*
* function implementations of server children code (connection handling)
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "debug.h"
#include "serverchild.h"
#include "db.h"
#include "auth.h"
#include "clientinfo.h"
#include "pool.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#ifdef PROC_TITLES
#include "proctitleutils.h"
#endif
int ChildStopRequested = 0;
int connected = 0;
clientinfo_t client;
int PerformChildTask(ChildInfo_t * info);
static void client_close(void);
static void disconnect_all(void);
void client_close(void)
{
if (client.tx) {
trace(TRACE_DEBUG,"%s,%s: closing write stream", __FILE__,__func__);
fflush(client.tx);
fclose(client.tx); /* closes clientSocket as well */
client.tx = NULL;
}
if (client.rx) {
trace(TRACE_DEBUG,"%s,%s: closing read stream", __FILE__,__func__);
shutdown(fileno(client.rx), SHUT_RDWR);
fclose(client.rx);
client.rx = NULL;
}
}
void disconnect_all(void)
{
if (! connected)
return;
trace(TRACE_DEBUG,
"%s,%s: database connection still open, closing",
__FILE__,__func__);
db_disconnect();
auth_disconnect();
connected = 0; /* FIXME a signal between this line and the previous one
* would screw things up. Would like to have all this in
* db_disconnect() making 'connected' obsolete
*/
}
void noop_child_sig_handler(int sig UNUSED, siginfo_t *info UNUSED, void *data UNUSED)
{
return;
}
void active_child_sig_handler(int sig, siginfo_t * info UNUSED, void *data UNUSED)
{
int saved_errno = errno;
static int triedDisconnect = 0;
/* perform reinit at SIGHUP otherwise exit, but do nothing on
* SIGCHLD*/
switch (sig) {
case SIGCHLD:
break;
case SIGALRM:
client_close();
break;
case SIGHUP:
case SIGTERM:
case SIGQUIT:
case SIGSTOP:
if (ChildStopRequested) {
/* already caught this signal, exit the hard way now */
client_close();
disconnect_all();
child_unregister();
exit(1);
}
DelChildSigHandler();
ChildStopRequested = 1;
break;
default:
/* bad shtuff, exit */
/*
* For some reason i have not yet determined the process starts eating up
* all CPU time when trying to disconnect.
* For now: just bail out :-)
*/
child_unregister();
_exit(1);
if (!triedDisconnect) {
triedDisconnect = 1;
client_close();
disconnect_all();
}
child_unregister();
exit(1);
}
errno = saved_errno;
}
/*
* SetChildSigHandler()
*
* sets the signal handler for a child proces
*/
int SetChildSigHandler()
{
struct sigaction act;
/* init & install signal handlers */
memset(&act, 0, sizeof(act));
act.sa_sigaction = active_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(SIGPIPE, &act, 0);
sigaction(SIGFPE, &act, 0);
sigaction(SIGSEGV, &act, 0);
sigaction(SIGTERM, &act, 0);
sigaction(SIGHUP, &act, 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(SIGPIPE, &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;
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, clientSocket;
socklen_t len;
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 (db_check_version() != 0)
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__);
return -1;
}
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, &len);
if (clientSocket == -1) {
i--; /* don't count this as a connect */
trace(TRACE_INFO, "%s,%s: accept failed",
__FILE__, __func__);
continue; /* accept failed, refuse connection & continue */
}
child_reg_connected();
memset(&client, 0, sizeof(client)); /* zero-init */
client.timeoutMsg = info->timeoutMsg;
client.timeout = info->timeout;
strncpy(client.ip, 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(client.clientname, clientHost->h_name, FIELDSIZE);
trace(TRACE_MESSAGE, "%s,%s: incoming connection from [%s (%s)]",
__FILE__, __func__,
client.ip,
client.clientname[0] ? client.
clientname : "Lookup failed");
} else {
trace(TRACE_MESSAGE, "%s,%s: incoming connection from [%s]",
__FILE__, __func__, client.ip);
}
/* 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(&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(&client);
#ifdef PROC_TITLES
set_proc_title("%s", "Idle");
#endif
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
trace(TRACE_ERROR, "%s,%s: stop requested",
__FILE__, __func__);
child_reg_disconnected();
disconnect_all();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1