Files
bongo/src/libs/nmap/bongoagent.c
T
pfelt 2b417a9c7d -- remove BongoArray and replace it with GArray
-- StrictCompile antispam and fix all the accompanying warnings
2009-07-18 18:09:53 +00:00

851 lines
23 KiB
C

/****************************************************************************
* <Novell-copyright>
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* 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, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail, you
* may find current contact information at www.novell.com.
* </Novell-copyright>
****************************************************************************/
// Parts (C) 2007 Alex Hudson. See Bongo Project Licensing.
/** \file
* Helpers for Bongo agents. Mainly for queue agents, also contains configuration reading helpers
*/
#include <config.h>
#include "bongoagent.h"
#include "msgapi.h"
#include "nmlib.h"
#include <execinfo.h>
#define CONNECTION_TIMEOUT (15 * 60)
#define MONITOR_SLEEP_INTERVAL 5000
struct _BongoGlobals BongoGlobals;
void
BongoAgentHandleSignal(BongoAgent *agent,
int sigtype)
{
switch(sigtype) {
// handle various fatal errors to spurt a backtrace when possible
case SIGILL:
case SIGABRT:
case SIGPIPE:
case SIGKILL:
case SIGSEGV:
{
char path[XPL_MAX_PATH + 1];
int boomfile;
const int buff_size = 50;
snprintf(path, XPL_MAX_PATH, "%s/guru-meditation-%d", XPL_DEFAULT_WORK_DIR, (int)time(NULL));
boomfile = open(path, O_CREAT | O_WRONLY);
if (boomfile != -1) {
void * buffer[buff_size];
int buff_used;
buff_used = backtrace(buffer, buff_size);
backtrace_symbols_fd(buffer, buff_used, boomfile);
close(boomfile);
}
XplExit(-1);
}
break;
case SIGHUP:
if (agent->state < BONGO_AGENT_STATE_UNLOADING) {
agent->state = BONGO_AGENT_STATE_UNLOADING;
}
break;
case SIGINT:
case SIGTERM:
if (agent->state == BONGO_AGENT_STATE_STOPPING) {
XplExit(0);
} else if (agent->state < BONGO_AGENT_STATE_STOPPING) {
agent->state = BONGO_AGENT_STATE_STOPPING;
}
break;
default:
break;
}
return;
}
BOOL ReadConfiguration(BongoAgent *agent);
BOOL ReadConfiguration(BongoAgent *agent) {
return TRUE;
}
/**
* Free any memory allocated as part of reading the agent's configuration.
* \param config BongoConfigItem array which defines configuration variables
*/
void
FreeBongoConfiguration(BongoConfigItem *config) {
while (config->type != BONGO_JSON_NULL) {
switch (config->type) {
case BONGO_JSON_STRING:
if (config->destination) MemFree(config->destination);
break;
case BONGO_JSON_ARRAY: {
BongoConfigItem *sub = config->destination;
/* sub->destination here is the address of a pointer to an array */
GArray **output = (GArray **)sub->destination;
if (sub->type == BONGO_JSON_STRING) {
int x;
for(x=0;x<(*output)->len;x++) {
MemFree(g_array_index(*output, char *, x));
}
}
g_array_free(*output, TRUE);
}
break;
default:
// nothing to do?
break;
}
config++;
}
}
/**
* \internal
* Take an individual BongoConfigItem, and a JSON tree, and try to find the config item
* \param schema pointer to a single BongoConfigItem
* \param node JSON tree we want to look in
* \return Whether or not it was successful
*/
BOOL
SetBongoConfigItem(BongoConfigItem *schema, BongoJsonNode *node) {
if (!node || node->type != schema->type) {
XplConsolePrintf("config: didn't find data at %s (type: %d, want: %d) \r\n", schema->source, node->type, schema->type);
return FALSE;
}
switch (node->type) {
case BONGO_JSON_BOOL: {
BOOL *dest = (BOOL *)schema->destination;
*dest = BongoJsonNodeAsBool(node);
}
break;
case BONGO_JSON_DOUBLE: {
double *dest = (double *)schema->destination;
*dest = BongoJsonNodeAsDouble(node);
}
break;
case BONGO_JSON_INT: {
int *dest = (int *)schema->destination;
*dest = BongoJsonNodeAsInt(node);
}
break;
case BONGO_JSON_STRING: {
char **dest = (char **)schema->destination;
*dest = MemStrdup(BongoJsonNodeAsString(node));
}
break;
case BONGO_JSON_ARRAY: {
BongoConfigItem *subschema = (BongoConfigItem *)schema->destination;
GArray **output = (GArray **)subschema->destination;
GArray *data, *result = NULL;
int size;
data = BongoJsonNodeAsArray(node);
size = data->len;
switch (subschema->type) {
case BONGO_JSON_BOOL: {
BOOL dest;
int i;
result = g_array_sized_new(FALSE, FALSE, sizeof(BOOL), size);
for(i = 0; i < size; i++) {
if (BongoJsonArrayGetBool(data, i, &dest) == BONGO_JSON_OK) {
g_array_append_val(result, dest);
} else {
return FALSE;
}
}
}
break;
case BONGO_JSON_INT: {
int dest, i;
result = g_array_sized_new(FALSE, FALSE, sizeof(int), size);
for(i = 0; i < size; i++) {
if (BongoJsonArrayGetInt(data, i, &dest) == BONGO_JSON_OK) {
g_array_append_val(result, dest);
} else {
return FALSE;
}
}
}
break;
case BONGO_JSON_STRING: {
const char *dest;
int i;
char *newstr;
result = g_array_sized_new(FALSE, FALSE, sizeof(char *), size);
for (i=0; i<size; i++) {
if (BongoJsonArrayGetString(data, i, &dest) == BONGO_JSON_OK) {
newstr = MemStrdup(dest);
g_array_append_val(result, newstr);
} else {
return FALSE;
}
}
}
break;
default:
XplConsolePrintf("config: type not yet implemented\r\n");
break;
}
*output = result;
}
break;
case BONGO_JSON_OBJECT:
/* What to do here? We could take ->destination as a pointer
to a BongoConfigItem[], and recurse. Makes less sense for
objects, though */
XplConsolePrintf("config: object type not implemented\r\n");
break;
default:
XplConsolePrintf("config: unknown type %d!\r\n", node->type);
return FALSE;
break;
}
return TRUE;
}
/**
* Read a configuration document from the Bongo Store. This is a conveniance wrapper
* around ParseBongoConfiguration()
* \param config BongoConfigItem array which defines configuration variables
* \param filename Filename we wish to read from the store
* \return Whether or not we were successful
*/
BOOL
ReadBongoConfiguration(BongoConfigItem *config, char *filename) {
unsigned char *pconfig = NULL;
BongoJsonNode *node = NULL;
BOOL retcode;
if (! NMAPReadConfigFile(filename, &pconfig)) {
XplConsolePrintf("config: couldn't read config '%s' from store\r\n", filename);
return FALSE;
}
if (BongoJsonParseString(pconfig, &node) != BONGO_JSON_OK) {
retcode = FALSE;
} else {
retcode = ProcessBongoConfiguration(config, node);
}
if (node) BongoJsonNodeFree(node);
if (pconfig) MemFree(pconfig);
return retcode;
}
/**
* Process a configuration definition
* \param config BongoConfigItem array which defines configuration variables
* \param node JSON node tree we want to look in
* \return Whether or not we were successful
*/
BOOL
ProcessBongoConfiguration(BongoConfigItem *config, const BongoJsonNode *node)
{
while (config->type != BONGO_JSON_NULL) {
BongoJsonNode *result = BongoJsonJPath(node, config->source);
if (!result) {
XplConsolePrintf("config: JSON tree for schema source %s not found\r\n", config->source);
return FALSE;
}
if (!SetBongoConfigItem(config, result)) {
// can't set item - should return FALSE here?
XplConsolePrintf("config: schema source %s not found\r\n", config->source);
}
config++;
}
return TRUE;
}
int
BongoAgentInit(BongoAgent *agent,
const char *agentName,
const unsigned long timeOut,
int startupResources)
{
agent->state = BONGO_AGENT_STATE_RUNNING;
LogOpen(agentName);
if (!MemoryManagerOpen(agentName)) {
Log(LOG_ERROR, "Unable to initialize memory manager; shutting down.");
return -1;
}
if (startupResources & BA_STARTUP_CONNIO) {
ConnStartup(timeOut, TRUE);
}
if (startupResources & BA_STARTUP_MSGLIB) {
MsgInit();
if (startupResources & BA_STARTUP_MSGAUTH) {
if (MsgAuthInit()) {
Log(LOG_ERROR, "Could not initialize auth subsystem.");
return -1;
}
}
}
if ((startupResources & BA_STARTUP_NMAP) && !NMAPInitialize()) {
Log(LOG_ERROR, "Could not initialize nmap library.");
return -1;
} else {
agent->sslContext = NMAPSSLContextAlloc();
NMAPSetEncryption(agent->sslContext);
}
agent->name = MemStrdup(agentName);
CONN_TRACE_INIT(XPL_DEFAULT_WORK_DIR, agentName);
Log(LOG_DEBUG, "Agent Initialized");
return 0;
}
typedef struct {
BongoAgent *agent;
Connection *conn;
void *clientPool;
BongoAgentClientFree clientFree;
BongoAgentClientHandler handler;
int clientSize;
} ListenCallbackData;
static void
QueueHandleConnection (void *datap)
{
ListenCallbackData *data = datap;
void *client;
int ccode;
char line[CONN_BUFSIZE];
client = MemPrivatePoolGetEntryDirect(data->clientPool, __FILE__, __LINE__);
memset(client, 0, data->clientSize);
if (client == NULL) {
XplConsolePrintf("%s: New worker failed to startup; out of memory.\r\n", data->agent->name);
NMAPSendCommand(data->conn, "QDONE\r\n", 7);
NMAPReadAnswer(data->conn, line, CONN_BUFSIZE, FALSE);
return;
}
if (ConnNegotiate(data->conn, data->agent->sslContext)) {
ccode = data->handler(client, data->conn);
}
NMAPSendCommand(data->conn, "QDONE\r\n", 7);
NMAPReadAnswer(data->conn, line, CONN_BUFSIZE, FALSE);
ConnClose(data->conn, 1);
ConnFree(data->conn);
data->clientFree(client);
MemFree(data);
}
static void
HandleConnection (void *datap)
{
ListenCallbackData *data = datap;
void *client;
int ccode;
client = MemPrivatePoolGetEntryDirect(data->clientPool, __FILE__, __LINE__);
memset(client, 0, data->clientSize);
if (client == NULL) {
XplConsolePrintf("%s: New worker failed to startup; out of memory.\r\n", data->agent->name);
return;
}
if (ConnNegotiate(data->conn, data->agent->sslContext)) {
ccode = data->handler(client, data->conn);
}
ConnClose(data->conn, 1);
ConnFree(data->conn);
data->clientFree(client);
MemFree(data);
}
static void
ListenInternal(BongoAgent *agent,
Connection *serverConn,
BongoThreadPool *threadPool,
BongoThreadPoolHandler connectionHandler,
void *clientPool,
BongoAgentClientFree clientFree,
int clientSize,
BongoAgentClientHandler clientHandler,
BOOL queueSocket)
{
while (agent->state < BONGO_AGENT_STATE_STOPPING) {
Connection *conn;
if (ConnAccept(serverConn, &conn) != -1) {
if (agent->state < BONGO_AGENT_STATE_STOPPING) {
void *data;
conn->ssl.enable = FALSE;
if (clientPool) {
ListenCallbackData *clientData;
clientData = MemMalloc(sizeof(ListenCallbackData));
clientData->conn = conn;
clientData->agent = agent;
clientData->handler = clientHandler;
clientData->clientFree = clientFree;
clientData->clientPool = clientPool;
clientData->clientSize = clientSize;
data = clientData;
} else {
data = conn;
}
BongoThreadPoolAddWork(threadPool,
(BongoThreadPoolHandler)connectionHandler,
data);
} else {
if (queueSocket) {
ConnWrite(conn, "QDONE\r\n", 7);
}
ConnClose(conn, 0);
ConnFree(conn);
conn = NULL;
}
continue;
}
switch (errno) {
case ECONNABORTED:
#ifdef EPROTO
case EPROTO:
#endif
case EINTR: {
if (agent->state < BONGO_AGENT_STATE_STOPPING) {
Log(LOG_INFO, "accept() failed with error: %d");
}
continue;
}
default: {
if (agent->state < BONGO_AGENT_STATE_STOPPING) {
Log(LOG_ERROR, "Exiting after an accept() failure; error %d.", errno);
agent->state = BONGO_AGENT_STATE_STOPPING;
}
break;
}
}
break;
}
}
void
BongoQueueAgentListenWithClientPool(BongoAgent *agent,
Connection *serverConn,
BongoThreadPool *threadPool,
int clientSize,
BongoAgentClientFree clientFree,
BongoAgentClientHandler handler,
void **memPool)
{
void *clientPool = MemPrivatePoolAlloc("QueueClientConnections",
clientSize,
0, BONGO_QUEUE_AGENT_MAX_CLIENTS,
TRUE, FALSE,
NULL,
NULL,
NULL);
if (memPool) {
*memPool = clientPool;
}
if (clientPool == NULL) {
agent->state = BONGO_AGENT_STATE_STOPPING;
XplConsolePrintf("%s: Unable to create connection pool.\r\n", agent->name);
return;
}
ListenInternal(agent,
serverConn,
threadPool,
QueueHandleConnection,
clientPool,
clientFree,
clientSize,
handler,
TRUE);
/* MemPrivatePoolFree(clientPool); ** FIXME: see bug 204732 */
}
void
BongoAgentListenWithClientPool(BongoAgent *agent,
Connection *serverConn,
BongoThreadPool *threadPool,
int clientSize,
int maxClients,
BongoAgentClientFree clientFree,
BongoAgentClientHandler handler,
void **memPool)
{
void *clientPool = MemPrivatePoolAlloc("ClientConnections",
clientSize,
0, maxClients,
TRUE, FALSE,
NULL,
NULL,
NULL);
if (memPool) {
*memPool = clientPool;
}
if (clientPool == NULL) {
agent->state = BONGO_AGENT_STATE_STOPPING;
XplConsolePrintf("%s: Unable to create connection pool.\r\n", agent->name);
return;
}
ListenInternal(agent,
serverConn,
threadPool,
HandleConnection,
clientPool,
clientFree,
clientSize,
handler,
FALSE);
/* MemPrivatePoolFree(clientPool); ** FIXME: see bug 204732 */
}
void
BongoAgentListen(BongoAgent *agent,
Connection *serverConn,
BongoThreadPool *threadPool,
BongoAgentConnectionHandler handler)
{
ListenInternal(agent,
serverConn,
threadPool,
(BongoThreadPoolHandler)handler,
NULL,
NULL,
0,
NULL,
FALSE);
}
void
BongoQueueAgentListen(BongoAgent *agent,
Connection *serverConn,
BongoThreadPool *threadPool,
BongoAgentConnectionHandler handler)
{
ListenInternal(agent,
serverConn,
threadPool,
(BongoThreadPoolHandler)handler,
NULL,
NULL,
0,
NULL,
FALSE);
}
void
BongoAgentShutdown(BongoAgent *agent)
{
if (agent->sslContext) {
ConnSSLContextFree(agent->sslContext);
}
ConnCloseAll(1);
LogClose();
MsgShutdown();
CONN_TRACE_SHUTDOWN();
ConnShutdown();
MemoryManagerClose(agent->name);
}
Connection *
BongoQueueConnectionInit(BongoAgent *agent,
int queue)
{
Connection *conn;
conn = ConnAlloc(FALSE);
if (conn) {
int reg;
memset(&(conn->socketAddress), 0, sizeof(conn->socketAddress));
conn->socketAddress.sin_family = AF_INET;
conn->socketAddress.sin_addr.s_addr = MsgGetAgentBindIPAddress();
conn->socket = ConnServerSocket(conn, 2048);
if (conn->socket == -1) {
XplConsolePrintf("%s: Could not bind to dynamic port\r\n", agent->name);
ConnFree(conn);
return NULL;
}
reg = QueueRegister(agent->name, queue, conn->socketAddress.sin_port);
if (reg != REGISTRATION_COMPLETED) {
XplConsolePrintf("%s: Could not register with bongonmap (%d)\r\n", agent->name, reg);
ConnFree(conn);
return NULL;
}
} else {
XplConsolePrintf("%s: Could not allocate connection.\r\n", agent->name);
return NULL;
}
return conn;
}
void
BongoQueueAgentGetThreadPoolParameters(BongoAgent *agent,
int *minThreads,
int *maxThreads,
int *minSleep)
{
*minThreads = BONGO_QUEUE_AGENT_MIN_THREADS;
*maxThreads = BONGO_QUEUE_AGENT_MAX_THREADS;
*minSleep = BONGO_QUEUE_AGENT_MIN_SLEEP;
}
int
BongoQueueAgentHandshake(Connection *conn,
char *buffer,
char *qID,
int *envelopeLength,
int *messageLength)
{
int ccode, Result = -1;
char *ptr;
ccode = NMAPReadAnswer(conn, buffer, CONN_BUFSIZE, TRUE);
if (ccode == 6020) {
ptr = strchr(buffer, ' ');
if (ptr == NULL) {
return Result;
}
/* copy out the qID */
if (qID) {
*ptr = '\0';
strcpy(qID, buffer);
*ptr = ' ';
}
Result = 0;
ptr++; /* increment past the space */
if (*ptr && envelopeLength) {
*envelopeLength = atoi(ptr);
ptr = strchr(ptr, ' ');
if (ptr == NULL) {
return Result;
}
/* grabbing the message length only makes sense if the envelope length was there */
ptr++;
if (*ptr && messageLength) {
*messageLength = atoi(ptr);
}
}
}
return Result;
}
char *
BongoQueueAgentReadEnvelope(Connection *conn,
char *buffer,
int envelopeLength,
int *Lines)
{
int ccode;
char *envelope;
char *cur;
int LineCount=0;
envelope = MemMalloc(envelopeLength + 5);
if (XPL_UNLIKELY (envelope == NULL)) {
return NULL;
}
ccode = NMAPReadCount(conn, envelope, envelopeLength);
if (XPL_UNLIKELY(ccode != envelopeLength)) {
MemFree(envelope);
return NULL;
}
ccode = NMAPReadAnswer(conn, buffer, CONN_BUFSIZE, TRUE);
if (XPL_UNLIKELY (ccode != 6021)) {
MemFree(envelope);
return NULL;
}
envelope[envelopeLength] = '\0';
/* Split the envelope into lines. A line might be terminated by
* one or two nulls, and the whole envelope will be terminated by
* three */
cur = envelope;
while (ccode != -1 && *cur) {
char *line;
line = strchr(cur, 0x0A);
if (line) {
*line = '\0';
if (line[-1] == 0x0D) {
*(line - 1) = '\0';
}
LineCount++;
} else {
line = cur + strlen(cur);
line[1] = '\0';
line[2] = '\0';
LineCount++;
}
cur = line + 1;
}
if (Lines) {
*Lines = LineCount;
}
return envelope;
}
void
BongoAgentShutdownFunc (BongoJsonRpcServer *server,
BongoJsonRpc *rpc,
int requestId,
const char *method,
GArray *args,
void *userData)
{
kill (getpid (), SIGTERM);
/* Might not make it here */
BongoJsonRpcSuccess (rpc, requestId, NULL);
}
/* Special cases are here so we can tweak when they're called */
void
BongoAgentAddConfigMonitor(BongoAgent *agent,
BongoAgentMonitorCallback configMonitor)
{
BongoAgentAddMonitor(agent, configMonitor, 300000);
}
void
BongoAgentAddDiskMonitor(BongoAgent *agent,
BongoAgentMonitorCallback diskMonitor)
{
BongoAgentAddMonitor(agent, diskMonitor, 600000);
}
void
BongoAgentAddLoadMonitor(BongoAgent *agent,
BongoAgentMonitorCallback loadMonitor)
{
BongoAgentAddMonitor(agent, loadMonitor, 5000);
}
/* Add a monitor. This isn't threadsafe, all monitors should be added
* before starting the monitor thread */
void
BongoAgentAddMonitor(BongoAgent *agent,
BongoAgentMonitorCallback callback,
int interval)
{
if (agent->maxMonitors == agent->numMonitors) {
if (agent->maxMonitors == 0) {
agent->maxMonitors = 5;
} else {
agent->maxMonitors *= 2;
}
agent->monitors = MemRealloc(agent->monitors, sizeof(BongoAgentMonitor) * agent->maxMonitors);
}
agent->monitors[agent->numMonitors].callback = callback;
agent->monitors[agent->numMonitors++].interval = interval / MONITOR_SLEEP_INTERVAL;
}
static void
Monitor(void *agentp)
{
BongoAgent *agent = agentp;
int iteration = 0;
while (agent->state < BONGO_AGENT_STATE_STOPPING) {
int i;
iteration++;
for (i = 0; i < agent->numMonitors; i++) {
if ((agent->monitors[i].interval % iteration) == 0) {
agent->monitors[i].callback(agent);
}
}
XplDelay(MONITOR_SLEEP_INTERVAL);
}
}
void
BongoAgentStartMonitor(BongoAgent *agent)
{
int ccode;
XplBeginThread(&agent->monitorID, Monitor, 10240, agent, ccode);
}