815 lines
23 KiB
C
815 lines
23 KiB
C
/* NeoStats - IRC Statistical Services
|
|
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
|
|
** http://www.neostats.net/
|
|
**
|
|
** Portions Copyright (c) 2000-2001 ^Enigma^
|
|
**
|
|
** Portions Copyright (c) 1999 Johnathan George net@lite.net
|
|
**
|
|
** 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
** USA
|
|
**
|
|
** NeoStats CVS Identification
|
|
** $Id$
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "stats.h"
|
|
#include "dl.h"
|
|
#include "adns.h"
|
|
#include "conf.h"
|
|
#include "log.h"
|
|
#include "timer.h"
|
|
#include "dns.h"
|
|
#include "transfer.h"
|
|
#include "curl.h"
|
|
#include "ircstring.h"
|
|
#include "dotconf.h"
|
|
#ifdef SQLSRV
|
|
#include "sqlsrv/rta.h"
|
|
#endif
|
|
|
|
static void recvlog (char *line);
|
|
|
|
static struct sockaddr_in lsa;
|
|
static int dobind;
|
|
|
|
int servsock;
|
|
char recbuf[BUFSIZE];
|
|
|
|
|
|
#ifdef SQLSRV
|
|
|
|
#define MAXSQLCON 5
|
|
|
|
/* sqlsrv struct for tracking connections */
|
|
typedef struct Sql_Conn {
|
|
struct sockaddr_in cliskt;
|
|
int fd;
|
|
long long nbytein;
|
|
long long nbyteout;
|
|
char response[50000];
|
|
int responsefree;
|
|
int cmdpos;
|
|
int cmd[1000];
|
|
} Sql_Conn;
|
|
|
|
int sqlListenSock = -1;
|
|
|
|
list_t *sqlconnections;
|
|
|
|
|
|
static void sql_accept_conn(int srvfd);
|
|
static int sqllisten_on_port(int port);
|
|
static int sql_handle_ui_request(lnode_t *sqlnode);
|
|
static int sql_handle_ui_output(lnode_t *sqlnode);
|
|
int check_sql_sock();
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/** @brief Connect to a server
|
|
*
|
|
* also setups the SQL listen socket if defined
|
|
*
|
|
* @param host to connect to
|
|
* @param port on remote host to connect to
|
|
*
|
|
* @return socket connected to on success
|
|
* NS_FAILURE on failure
|
|
*/
|
|
int
|
|
ConnectTo (char *host, int port)
|
|
{
|
|
int ret;
|
|
struct hostent *hp;
|
|
struct sockaddr_in sa;
|
|
int s;
|
|
|
|
dobind = 0;
|
|
/* bind to a local ip */
|
|
memset (&lsa, 0, sizeof (lsa));
|
|
if (me.local[0] != 0) {
|
|
if ((hp = gethostbyname (me.local)) == NULL) {
|
|
nlog (LOG_WARNING, LOG_CORE, "Warning, Couldn't bind to IP address %s", me.local);
|
|
} else {
|
|
memcpy ((char *) &lsa.sin_addr, hp->h_addr, hp->h_length);
|
|
lsa.sin_family = hp->h_addrtype;
|
|
dobind = 1;
|
|
}
|
|
}
|
|
if ((hp = gethostbyname (host)) == NULL) {
|
|
return NS_FAILURE;
|
|
}
|
|
|
|
if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
free(hp);
|
|
return NS_FAILURE;
|
|
}
|
|
if (dobind > 0) {
|
|
if (bind (s, (struct sockaddr *) &lsa, sizeof (lsa)) < 0) {
|
|
nlog (LOG_WARNING, LOG_CORE, "bind(): Warning, Couldn't bind to IP address %s", strerror (errno));
|
|
}
|
|
}
|
|
|
|
bzero (&sa, sizeof (sa));
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = htons (port);
|
|
bcopy (hp->h_addr, (char *) &sa.sin_addr, hp->h_length);
|
|
|
|
ret=connect (s, (struct sockaddr *) &sa, sizeof (sa));
|
|
if (ret< 0) {
|
|
close (s);
|
|
return NS_FAILURE;
|
|
}
|
|
#ifdef SQLSRV
|
|
/* init the sql Listen Socket now as well */
|
|
sqlconnections = list_create(MAXSQLCON);
|
|
|
|
sqlListenSock = sqllisten_on_port(me.sqlport);
|
|
if (sqlListenSock == -1) {
|
|
nlog(LOG_CRITICAL, LOG_CORE, "Failed to Setup Sql Port. SQL not available");
|
|
}
|
|
#endif
|
|
return s;
|
|
}
|
|
|
|
/** @brief main recv loop
|
|
*
|
|
* @param none
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
read_loop ()
|
|
{
|
|
register int i, j, SelectResult;
|
|
struct timeval *TimeOut, tvbuf;
|
|
fd_set readfds, writefds, errfds;
|
|
int maxfdsunused;
|
|
char c;
|
|
char buf[BUFSIZE];
|
|
ModSock *mod_sock;
|
|
struct pollfd *ufds;
|
|
int pollsize, pollflag;
|
|
hscan_t ss;
|
|
hnode_t *sn;
|
|
#ifdef SQLSRV
|
|
lnode_t *sqlnode;
|
|
Sql_Conn *sqldata;
|
|
#endif
|
|
|
|
/* XXX Valgrind reports these two as lost memory, Should clean up before we exit */
|
|
TimeOut = malloc (sizeof (struct timeval));
|
|
ufds = malloc((sizeof *ufds) * me.maxsocks);
|
|
|
|
me.lastmsg = me.now;
|
|
while (1) {
|
|
SET_SEGV_LOCATION();
|
|
memset (buf, '\0', BUFSIZE);
|
|
me.now = time(NULL);
|
|
ircsnprintf (me.strnow, STR_TIME_T_SIZE, "%ld", (long)me.now);
|
|
CheckTimers ();
|
|
SET_SEGV_LOCATION();
|
|
me.now = time(NULL);
|
|
ircsnprintf (me.strnow, STR_TIME_T_SIZE, "%ld", (long)me.now);
|
|
FD_ZERO (&readfds);
|
|
FD_ZERO (&writefds);
|
|
FD_ZERO (&errfds);
|
|
// memset(ufds, 0, (sizeof *ufds) * me.maxsocks);
|
|
pollsize = 0;
|
|
FD_SET (servsock, &readfds);
|
|
hash_scan_begin (&ss, sockh);
|
|
me.cursocks = 1; /* always one socket for ircd */
|
|
while ((sn = hash_scan_next (&ss)) != NULL) {
|
|
mod_sock = hnode_get (sn);
|
|
if (mod_sock->socktype == SOCK_STANDARD) {
|
|
if (mod_sock->readfnc)
|
|
FD_SET (mod_sock->sock_no, &readfds);
|
|
if (mod_sock->writefnc)
|
|
FD_SET (mod_sock->sock_no, &writefds);
|
|
if (mod_sock->errfnc)
|
|
FD_SET (mod_sock->sock_no, &errfds);
|
|
++me.cursocks;
|
|
} else {
|
|
/* its a poll interface, setup for select instead */
|
|
SET_SEGV_INMODULE(mod_sock->modname);
|
|
j = mod_sock->beforepoll (mod_sock->data, ufds);
|
|
CLEAR_SEGV_INMODULE();
|
|
/* if we don't have any socks, just continue */
|
|
if (j == -1)
|
|
continue;
|
|
|
|
if (j > pollsize) pollsize = j;
|
|
/* run through the ufds set and translate to select FDSET's */
|
|
for (i = 0; i < j; i++) {
|
|
if (ufds[i].events & POLLIN) {
|
|
FD_SET (ufds[i].fd, &readfds);
|
|
}
|
|
if (ufds[i].events & POLLOUT) {
|
|
FD_SET (ufds[i].fd, &writefds);
|
|
}
|
|
if (ufds[i].events & POLLERR) {
|
|
FD_SET (ufds[i].fd, &errfds);
|
|
}
|
|
++me.cursocks;
|
|
}
|
|
}
|
|
}
|
|
/* adns stuff... whats its interested in */
|
|
adns_beforeselect (ads, &me.maxsocks, &readfds, &writefds, &errfds, &TimeOut, &tvbuf, 0);
|
|
/* adns may change this, but we tell it to go away!!! */
|
|
TimeOut->tv_sec = 1;
|
|
TimeOut->tv_usec = 0;
|
|
|
|
/* add the fds for the curl library as well */
|
|
/* XXX Should this be a pollsize or maxfdsunused... not sure yet */
|
|
curl_multi_fdset(curlmultihandle, &readfds, &writefds, &errfds, &maxfdsunused);
|
|
|
|
#ifdef SQLSRV
|
|
/* if we have sql support, add the Listen Socket to the fds */
|
|
if (sqlListenSock > 0)
|
|
FD_SET(sqlListenSock, &readfds);
|
|
|
|
/* if we have any existing connections, add them of course */
|
|
if (list_count(sqlconnections) > 0) {
|
|
sqlnode = list_first(sqlconnections);
|
|
while (sqlnode != NULL) {
|
|
sqldata = lnode_get(sqlnode);
|
|
if (sqldata->responsefree < 50000) {
|
|
FD_SET(sqldata->fd, &writefds);
|
|
} else {
|
|
FD_SET(sqldata->fd, &readfds);
|
|
}
|
|
sqlnode = list_next(sqlconnections, sqlnode);
|
|
}
|
|
}
|
|
#endif
|
|
SelectResult = select (FD_SETSIZE, &readfds, &writefds, &errfds, TimeOut);
|
|
me.now = time(NULL);
|
|
ircsnprintf (me.strnow, STR_TIME_T_SIZE, "%ld", (long)me.now);
|
|
if (SelectResult > 0) {
|
|
/* check ADNS fds */
|
|
adns_afterselect (ads, me.maxsocks, &readfds, &writefds, &errfds, 0);
|
|
|
|
/* do any dns related callbacks now */
|
|
do_dns ();
|
|
|
|
/* check CURL fds */
|
|
while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curlmultihandle, &maxfdsunused)) {
|
|
}
|
|
transfer_status();
|
|
#ifdef SQLSRV
|
|
/* did we get a connection to the SQL listen sock */
|
|
if ((sqlListenSock > 0) && (FD_ISSET(sqlListenSock, &readfds)))
|
|
sql_accept_conn(sqlListenSock);
|
|
restartsql:
|
|
/* don't bother checking the sql connections if we dont have any active connections! */
|
|
if (list_count(sqlconnections) > 0) {
|
|
sqlnode = list_first(sqlconnections);
|
|
while (sqlnode != NULL) {
|
|
sqldata = lnode_get(sqlnode);
|
|
if (FD_ISSET(sqldata->fd, &readfds)) {
|
|
if (sql_handle_ui_request(sqlnode) == NS_FAILURE) {
|
|
goto restartsql;
|
|
}
|
|
} else if (FD_ISSET(sqldata->fd, &writefds)) {
|
|
if (sql_handle_ui_output(sqlnode) == NS_FAILURE) {
|
|
goto restartsql;
|
|
}
|
|
}
|
|
sqlnode = list_next(sqlconnections, sqlnode);
|
|
}
|
|
}
|
|
#endif
|
|
if (FD_ISSET (servsock, &readfds)) {
|
|
for (j = 0; j < BUFSIZE; j++) {
|
|
i = read (servsock, &c, 1);
|
|
me.RcveBytes++;
|
|
if (i >= 0) {
|
|
buf[j] = c;
|
|
if ((c == '\n') || (c == '\r')) {
|
|
me.RcveM++;
|
|
me.lastmsg = me.now;
|
|
if (config.recvlog)
|
|
recvlog (buf);
|
|
parse (buf);
|
|
break;
|
|
}
|
|
} else {
|
|
nlog (LOG_WARNING, LOG_CORE, "read returned an Error");
|
|
servsock = -1;
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
/* this checks if there is any data waiting on a socket for a module */
|
|
hash_scan_begin (&ss, sockh);
|
|
while ((sn = hash_scan_next (&ss)) != NULL) {
|
|
pollflag = 0;
|
|
mod_sock = hnode_get (sn);
|
|
SET_SEGV_INMODULE(mod_sock->modname);
|
|
if (mod_sock->socktype == SOCK_STANDARD) {
|
|
if (FD_ISSET (mod_sock->sock_no, &readfds)) {
|
|
nlog (LOG_DEBUG3, LOG_CORE, "Running module %s readsock function for %s", mod_sock->modname, mod_sock->sockname);
|
|
if (mod_sock->readfnc (mod_sock->sock_no, mod_sock->sockname) < 0)
|
|
continue;
|
|
}
|
|
if (FD_ISSET (mod_sock->sock_no, &writefds)) {
|
|
nlog (LOG_DEBUG3, LOG_CORE, "Running module %s writesock function for %s", mod_sock->modname, mod_sock->sockname);
|
|
if (mod_sock->writefnc (mod_sock->sock_no, mod_sock->sockname) < 0)
|
|
continue;
|
|
}
|
|
if (FD_ISSET (mod_sock->sock_no, &errfds)) {
|
|
nlog (LOG_DEBUG3, LOG_CORE, "Running module %s errorsock function for %s", mod_sock->modname, mod_sock->sockname);
|
|
if (mod_sock->errfnc (mod_sock->sock_no, mod_sock->sockname) < 0)
|
|
continue;
|
|
}
|
|
} else {
|
|
if (pollflag != 1) {
|
|
for (i = 0; i < pollsize; i++) {
|
|
if (FD_ISSET(ufds[i].fd, &readfds)) {
|
|
ufds[i].revents |= POLLIN;
|
|
pollflag = 1;
|
|
}
|
|
if (FD_ISSET(ufds[i].fd, &writefds)) {
|
|
ufds[i].revents |= POLLOUT;
|
|
pollflag = 1;
|
|
}
|
|
if (FD_ISSET(ufds[i].fd, &errfds)) {
|
|
ufds[i].revents |= POLLERR;
|
|
pollflag = 1;
|
|
}
|
|
}
|
|
}
|
|
if (pollflag == 1) {
|
|
mod_sock->afterpoll(mod_sock->data, ufds, pollsize);
|
|
}
|
|
}
|
|
}
|
|
CLEAR_SEGV_INMODULE();
|
|
continue;
|
|
}
|
|
} else if (SelectResult == 0) {
|
|
if ((me.now - me.lastmsg) > 180) {
|
|
/* if we havnt had a message for 3 minutes, more than likely, we are on a zombie server */
|
|
/* disconnect and try to reconnect */
|
|
nlog (LOG_WARNING, LOG_CORE, "Eeek, Zombie Server, Reconnecting");
|
|
return;
|
|
}
|
|
} else if (SelectResult == -1) {
|
|
if (errno != EINTR) {
|
|
nlog (LOG_WARNING, LOG_CORE, "Lost connection to server.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
nlog (LOG_NORMAL, LOG_CORE, "hu, how did we get here");
|
|
}
|
|
|
|
/** @brief get max available sockets
|
|
*
|
|
* @param none
|
|
*
|
|
* @return returns the max available socket
|
|
*/
|
|
int
|
|
getmaxsock ()
|
|
{
|
|
struct rlimit *lim;
|
|
int ret;
|
|
|
|
lim = malloc (sizeof (struct rlimit));
|
|
getrlimit (RLIMIT_NOFILE, lim);
|
|
ret = lim->rlim_max;
|
|
free (lim);
|
|
return ret;
|
|
}
|
|
|
|
/** @brief recv logging for all data received by us
|
|
*
|
|
* @param line of text received
|
|
*
|
|
* @return none
|
|
*/
|
|
static void
|
|
recvlog (char *line)
|
|
{
|
|
FILE *logfile;
|
|
|
|
if ((logfile = fopen (RECV_LOG, "a")) == NULL)
|
|
return;
|
|
if (logfile)
|
|
fprintf (logfile, "%s", line);
|
|
fclose (logfile);
|
|
}
|
|
|
|
/** @brief connect to a socket
|
|
*
|
|
* @param socktype type of socket
|
|
* @param ipaddr ip address of target
|
|
* @param port to connect to
|
|
* @param sockname name of this socket
|
|
* @param module name of the module
|
|
* @param func_read read socket function
|
|
* @param func_write write socket function
|
|
* @param func_error socket error function
|
|
*
|
|
* @return socket number if connect successful
|
|
* NS_FAILURE if unsuccessful
|
|
*/
|
|
int
|
|
sock_connect (int socktype, unsigned long ipaddr, int port, char *sockname, char *module, char *func_read, char *func_write, char *func_error)
|
|
{
|
|
struct sockaddr_in sa;
|
|
int s;
|
|
int i;
|
|
|
|
/* socktype = SOCK_STREAM */
|
|
if ((s = socket (AF_INET, socktype, 0)) < 0)
|
|
return NS_FAILURE;
|
|
|
|
/* bind to an IP address */
|
|
if (dobind > 0) {
|
|
if (bind (s, (struct sockaddr *) &lsa, sizeof (lsa)) < 0) {
|
|
nlog (LOG_WARNING, LOG_CORE, "sock_connect(): Warning, Couldn't bind to IP address %s", strerror (errno));
|
|
}
|
|
}
|
|
|
|
bzero (&sa, sizeof (sa));
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = htons (port);
|
|
sa.sin_addr.s_addr = ipaddr;
|
|
|
|
/* set non blocking */
|
|
|
|
if ((i = fcntl (s, F_SETFL, O_NONBLOCK)) < 0) {
|
|
nlog (LOG_CRITICAL, LOG_CORE, "can't set socket %s(%s) non-blocking: %s", sockname, module, strerror (i));
|
|
return NS_FAILURE;
|
|
}
|
|
|
|
if ((i = connect (s, (struct sockaddr *) &sa, sizeof (sa))) < 0) {
|
|
switch (errno) {
|
|
case EINPROGRESS:
|
|
break;
|
|
default:
|
|
nlog (LOG_WARNING, LOG_CORE, "Socket %s(%s) cant connect %s", sockname, module, strerror (errno));
|
|
close (s);
|
|
return NS_FAILURE;
|
|
}
|
|
}
|
|
|
|
add_socket (func_read, func_write, func_error, sockname, s, module);
|
|
return s;
|
|
}
|
|
|
|
/** @brief disconnect socket
|
|
*
|
|
* @param name of socket to disconnect
|
|
*
|
|
* @return NS_SUCCESS if disconnect successful
|
|
* NS_FAILURE if unsuccessful
|
|
*/
|
|
int
|
|
sock_disconnect (char *sockname)
|
|
{
|
|
ModSock *mod_sock;
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
int i;
|
|
|
|
mod_sock = findsock (sockname);
|
|
if (!mod_sock) {
|
|
nlog (LOG_WARNING, LOG_CORE, "Warning, Can not find Socket %s in list", sockname);
|
|
return NS_FAILURE;
|
|
}
|
|
|
|
/* the following code makes sure its a valid file descriptor */
|
|
FD_ZERO (&fds);
|
|
FD_SET (mod_sock->sock_no, &fds);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
i = select (1, &fds, NULL, NULL, &tv);
|
|
if (!i && errno == EBADF) {
|
|
nlog (LOG_WARNING, LOG_CORE, "Warning, Bad File Descriptor %s in list", sockname);
|
|
return NS_FAILURE;
|
|
}
|
|
nlog (LOG_DEBUG3, LOG_CORE, "Closing Socket %s with Number %d", sockname, mod_sock->sock_no);
|
|
close (mod_sock->sock_no);
|
|
del_socket (sockname);
|
|
return NS_SUCCESS;
|
|
}
|
|
|
|
/** @brief send to socket
|
|
*
|
|
* @param fmt printf style vaarg list of text to send
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
sts (const char *buf, const int buflen)
|
|
{
|
|
int sent;
|
|
|
|
if (servsock == -1) {
|
|
nlog(LOG_WARNING, LOG_CORE, "Not sending to server as we have a invalid socket");
|
|
return;
|
|
}
|
|
sent = write (servsock, buf, buflen);
|
|
if (sent == -1) {
|
|
nlog (LOG_CRITICAL, LOG_CORE, "Write error: %s", strerror(errno));
|
|
close (servsock);
|
|
servsock = -1;
|
|
do_exit (NS_EXIT_ERROR, NULL);
|
|
}
|
|
me.SendM++;
|
|
me.SendBytes = me.SendBytes + sent;
|
|
}
|
|
|
|
#if SQLSRV
|
|
/* the following functions are taken from the RTA example app shipped with the library,
|
|
* and modified to work with NeoStats.
|
|
* Credit for these apps should be given to the respective authors of the RTA library.
|
|
* more info can be found at http://www.linuxappliancedesign.com for more
|
|
* information
|
|
*/
|
|
|
|
/* rehash handler */
|
|
int check_sql_sock() {
|
|
if (sqlListenSock < 1) {
|
|
nlog(LOG_DEBUG1, LOG_CORE, "Rehashing SQL sock");
|
|
sqlListenSock = sqllisten_on_port(me.sqlport);
|
|
if (sqlListenSock == -1) {
|
|
nlog(LOG_CRITICAL, LOG_CORE, "Failed to Setup Sql Port. SQL not available");
|
|
return NS_FAILURE;
|
|
}
|
|
}
|
|
return NS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************
|
|
* listen_on_port(int port): - Open a socket to listen for
|
|
* incoming TCP connections on the port given. Return the file
|
|
* descriptor if OK, and -1 on any error. The calling routine
|
|
* can handle any error condition.
|
|
*
|
|
* Input: The interger value of the port number to bind to
|
|
* Output: The file descriptor of the socket
|
|
* Effects: none
|
|
***************************************************************/
|
|
int
|
|
sqllisten_on_port(int port)
|
|
{
|
|
int srvfd; /* FD for our listen server socket */
|
|
struct sockaddr_in srvskt;
|
|
int adrlen;
|
|
int flags;
|
|
|
|
adrlen = sizeof(struct sockaddr_in);
|
|
(void) memset((void *) &srvskt, 0, (size_t) adrlen);
|
|
srvskt.sin_family = AF_INET;
|
|
/* bind to the local IP */
|
|
if (dobind) {
|
|
srvskt.sin_addr = lsa.sin_addr;
|
|
} else {
|
|
srvskt.sin_addr.s_addr = INADDR_ANY;
|
|
}
|
|
srvskt.sin_port = htons(me.sqlport);
|
|
if ((srvfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
{
|
|
nlog(LOG_CRITICAL,LOG_CORE, "SqlSrv: Unable to get socket for port %d.", port);
|
|
return -1;
|
|
}
|
|
flags = fcntl(srvfd, F_GETFL, 0);
|
|
flags |= O_NONBLOCK;
|
|
(void) fcntl(srvfd, F_SETFL, flags);
|
|
if (bind(srvfd, (struct sockaddr *) &srvskt, adrlen) < 0)
|
|
{
|
|
nlog(LOG_CRITICAL, LOG_CORE, "Unable to bind to port %d", port);
|
|
return -1;
|
|
}
|
|
if (listen(srvfd, 1) < 0)
|
|
{
|
|
nlog(LOG_CRITICAL, LOG_CORE, "Unable to listen on port %d", port);
|
|
return -1;
|
|
}
|
|
return (srvfd);
|
|
}
|
|
|
|
/***************************************************************
|
|
* accept_ui_session(): - Accept a new UI/DB/manager session.
|
|
* This routine is called when a user interface program such
|
|
* as Apache (for the web interface), the SNMP manager, or one
|
|
* of the console interface programs tries to connect to the
|
|
* data base interface socket to do DB like get's and set's.
|
|
* The connection is actually established by the PostgreSQL
|
|
* library attached to the UI program by an XXXXXX call.
|
|
*
|
|
* Input: The file descriptor of the DB server socket
|
|
* Output: none
|
|
* Effects: manager connection table (ui)
|
|
***************************************************************/
|
|
void
|
|
sql_accept_conn(int srvfd)
|
|
{
|
|
int adrlen; /* length of an inet socket address */
|
|
int flags; /* helps set non-blocking IO */
|
|
lnode_t *newuinode;
|
|
Sql_Conn *newui;
|
|
char tmp[16];
|
|
|
|
/* if we reached our max connection, just exit */
|
|
if (list_count(sqlconnections) > 5) {
|
|
nlog(LOG_NOTICE, LOG_CORE, "Can not accept new SQL connection. Full");
|
|
close (srvfd);
|
|
return;
|
|
}
|
|
|
|
/* We have a new UI/DB/manager connection request. So make a free
|
|
slot and allocate it */
|
|
newui = malloc(sizeof(Sql_Conn));
|
|
|
|
|
|
/* OK, we've got the ui slot, now accept the conn */
|
|
adrlen = sizeof(struct sockaddr_in);
|
|
newui->fd = accept(srvfd, (struct sockaddr *) &newui->cliskt, &adrlen);
|
|
|
|
if (newui->fd < 0)
|
|
{
|
|
nlog(LOG_WARNING, LOG_CORE, "SqlSrv: Manager accept() error (%s). \n", strerror(errno));
|
|
free(newui);
|
|
close(srvfd);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
inet_ntop(AF_INET, &newui->cliskt.sin_addr.s_addr, tmp, 16);
|
|
if (!match(me.sqlhost, tmp)) {
|
|
/* we didnt get a match, bye bye */
|
|
nlog(LOG_NOTICE, LOG_CORE, "SqlSrv: Rejecting SQL Connection from %s", tmp);
|
|
close(newui->fd);
|
|
free(newui);
|
|
return;
|
|
}
|
|
/* inc number ui, then init new ui */
|
|
flags = fcntl(newui->fd, F_GETFL, 0);
|
|
flags |= O_NONBLOCK;
|
|
(void) fcntl(newui->fd, F_SETFL, flags);
|
|
newui->cmdpos = 0;
|
|
newui->responsefree = 50000; /* max response packetsize if 50000 */
|
|
newui->nbytein = 0;
|
|
newui->nbyteout = 0;
|
|
newuinode = lnode_create(newui);
|
|
list_append(sqlconnections, newuinode);
|
|
inet_ntop(AF_INET, &newui->cliskt.sin_addr.s_addr, tmp, 16);
|
|
nlog(LOG_DEBUG1, LOG_CORE, "New SqlConnection from %s", tmp);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************
|
|
* handle_ui_request(): - This routine is called to read data
|
|
* from the TCP connection to the UI programs such as the web
|
|
* UI and consoles. The protocol used is that of Postgres and
|
|
* the data is an encoded SQL request to select or update a
|
|
* system variable. Note that the use of callbacks on reading
|
|
* or writing means a lot of the operation of the program
|
|
* starts from this execution path. The input is an index into
|
|
* the ui table for the manager with data ready.
|
|
*
|
|
* Input: index of the relevant entry in the ui table
|
|
* Output: none
|
|
* Effects: many, many side effects via table callbacks
|
|
***************************************************************/
|
|
int
|
|
sql_handle_ui_request(lnode_t *sqlnode)
|
|
{
|
|
int ret; /* a return value */
|
|
int dbstat; /* a return value */
|
|
int t; /* a temp int */
|
|
Sql_Conn *sqlconn;
|
|
|
|
if ((sqlconn = lnode_get(sqlnode)) == NULL) {
|
|
nlog(LOG_WARNING, LOG_CORE, "Got a Sql Handle without a valid node");
|
|
return NS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* We read data from the connection into the buffer in the ui struct.
|
|
Once we've read all of the data we can, we call the DB routine to
|
|
parse out the SQL command and to execute it. */
|
|
ret = read(sqlconn->fd,
|
|
&(sqlconn->cmd[sqlconn->cmdpos]), (1000 - sqlconn->cmdpos));
|
|
|
|
/* shutdown manager conn on error or on zero bytes read */
|
|
if (ret <= 0)
|
|
{
|
|
/* log this since a normal close is with an 'X' command from the
|
|
client program? */
|
|
nlog(LOG_DEBUG1, LOG_CORE, "Disconnecting SqlClient for failed read");
|
|
deldbconnection(sqlconn->fd);
|
|
close(sqlconn->fd);
|
|
list_delete(sqlconnections, sqlnode);
|
|
lnode_destroy(sqlnode);
|
|
free(sqlconn);
|
|
return NS_FAILURE;
|
|
}
|
|
sqlconn->cmdpos += ret;
|
|
sqlconn->nbytein += ret;
|
|
|
|
/* The commands are in the buffer. Call the DB to parse and execute
|
|
them */
|
|
do
|
|
{
|
|
t = sqlconn->cmdpos, /* packet in length */
|
|
dbstat = dbcommand((char *)sqlconn->cmd, /* packet in */
|
|
&sqlconn->cmdpos, /* packet in length */
|
|
&sqlconn->response[50000 - sqlconn->responsefree], &sqlconn->responsefree, sqlconn->fd);
|
|
t -= sqlconn->cmdpos, /* t = # bytes consumed */
|
|
/* move any trailing SQL cmd text up in the buffer */
|
|
(void) memmove(sqlconn->cmd, &sqlconn->cmd[t], t);
|
|
} while (dbstat == RTA_SUCCESS);
|
|
if (dbstat == RTA_ERROR) {
|
|
deldbconnection(sqlconn->fd);
|
|
}
|
|
/* the command is done (including side effects). Send any reply back
|
|
to the UI */
|
|
sql_handle_ui_output(sqlnode);
|
|
return NS_SUCCESS;
|
|
}
|
|
|
|
|
|
/***************************************************************
|
|
* handle_ui_output() - This routine is called to write data
|
|
* to the TCP connection to the UI programs. It is useful for
|
|
* slow clients which can not accept the output in one big gulp.
|
|
*
|
|
* Input: index of the relevant entry in the ui table
|
|
* Output: none
|
|
* Effects: none
|
|
***************************************************************/
|
|
int
|
|
sql_handle_ui_output(lnode_t *sqlnode)
|
|
{
|
|
int ret; /* write() return value */
|
|
Sql_Conn *sqlconn;
|
|
|
|
if ((sqlconn = lnode_get(sqlnode)) == NULL) {
|
|
nlog(LOG_WARNING, LOG_CORE, "Got a Sql write Handle without a valid node");
|
|
return NS_SUCCESS;
|
|
}
|
|
|
|
if (sqlconn->responsefree < 50000)
|
|
{
|
|
ret = write(sqlconn->fd, sqlconn->response, (50000 - sqlconn->responsefree));
|
|
if (ret < 0)
|
|
{
|
|
nlog(LOG_WARNING, LOG_CORE, "Got a write error when attempting to return data to the SQL Server");
|
|
deldbconnection(sqlconn->fd);
|
|
close(sqlconn->fd);
|
|
list_delete(sqlconnections, sqlnode);
|
|
lnode_destroy(sqlnode);
|
|
free(sqlconn);
|
|
return NS_FAILURE;
|
|
}
|
|
else if (ret == (50000 - sqlconn->responsefree))
|
|
{
|
|
sqlconn->responsefree = 50000;
|
|
sqlconn->nbyteout += ret;
|
|
}
|
|
else
|
|
{
|
|
/* we had a partial write. Adjust the buffer */
|
|
(void) memmove(sqlconn->response, &sqlconn->response[ret],
|
|
(50000 - sqlconn->responsefree - ret));
|
|
sqlconn->responsefree += ret;
|
|
sqlconn->nbyteout += ret; /* # bytes sent on conn */
|
|
}
|
|
}
|
|
return NS_SUCCESS;
|
|
}
|
|
|
|
|
|
#endif
|