From adbf55ece666c71e882c370a74efa0bbf3239226 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 3 Aug 2007 10:07:38 +0000 Subject: - added CODE_STD_FINALIZERparseSelectorAct to module-generation macros - restructered rsyslogd startup - moved startWorker() to a more appropriate place - updated ommysql.c to fully support suspension/resumption by rule engine --- ommysql.c | 341 +++++++++++++++++++------------------------------------------- 1 file changed, 105 insertions(+), 236 deletions(-) (limited to 'ommysql.c') diff --git a/ommysql.c b/ommysql.c index ea0012b1..573e72ec 100644 --- a/ommysql.c +++ b/ommysql.c @@ -5,10 +5,6 @@ * works! * * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c) - * This file is under development and has not yet arrived at being fully - * self-contained and a real object. So far, it is mostly an excerpt - * of the "old" message code without any modifications. However, it - * helps to have things at the right place one we go to the meat of it. * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * @@ -58,10 +54,6 @@ typedef struct _instanceData { char f_dbname[_DB_MAXDBLEN+1]; /* DB name */ char f_dbuid[_DB_MAXUNAMELEN+1]; /* DB user */ char f_dbpwd[_DB_MAXPWDLEN+1]; /* DB user's password */ - time_t f_timeResumeOnError; /* 0 if no error is present, - otherwise is it set to the - time a retrail should be attampt */ - int f_iLastDBErrNo; /* Last db error number. 0 = no error */ } instanceData; @@ -84,16 +76,17 @@ ENDisCompatibleWithFeature static void closeMySQL(instanceData *pData) { assert(pData != NULL); + dprintf("in closeMySQL\n"); - if(pData->f_hmysql != NULL) /* just to be on the safe side... */ + if(pData->f_hmysql != NULL) { /* just to be on the safe side... */ mysql_close(pData->f_hmysql); + pData->f_hmysql = NULL; + } } BEGINfreeInstance CODESTARTfreeInstance -# ifdef WITH_DB closeMySQL(pData); -# endif ENDfreeInstance @@ -118,210 +111,93 @@ CODESTARTgetWriteFDForSelect ENDgetWriteFDForSelect -static rsRetVal reInitMySQL(instanceData *); - - -/** - * DBErrorHandler - * - * Call this function if an db error apears. It will initiate - * the "delay" handling which stopped the db logging for some - * time. - * - * We now check if we have a valid MySQL handle. If not, we simply +/* log a database error with descriptive message. + * We check if we have a valid MySQL handle. If not, we simply * report an error, but can not be specific. RGerhards, 2007-01-30 */ -static void DBErrorHandler(instanceData *pData) +static void reportDBError(instanceData *pData) { char errMsg[512]; assert(pData != NULL); - /* TODO: - * NO DB connection -> Can not log to DB - * -------------------- - * Case 1: db server unavailable - * We can check after a specified time interval if the server is up. - * Also a reason can be a down DNS service. - * Case 2: uid, pwd or dbname are incorrect - * If this is a fault in the syslog.conf we have no chance to recover. But - * if it is a problem of the DB we can make a retry after some time. Possible - * are that the admin has not already set up the database table. Or he has not - * created the database user yet. - * Case 3: unkown error - * If we get an unkowon error here, we should in any case try to recover after - * a specified time interval. - * - * Insert failed -> Can not log to DB - * -------------------- - * If the insert fails it is never a good idea to give up. Only an - * invalid sql sturcture (wrong template) force us to disable db - * logging. - * - * Think about different "delay" for different errors! - */ + /* output log message */ + errno = 0; if(pData->f_hmysql == NULL) { - logerror("unknown DB error occured - called error handler with NULL MySQL handle."); + logerror("unknown DB error occured - could not obtain MySQL handle"); } else { /* we can ask mysql for the error description... */ - errno = 0; - snprintf(errMsg, sizeof(errMsg)/sizeof(char), - "db error (%d): %s\n", mysql_errno(pData->f_hmysql), + snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", mysql_errno(pData->f_hmysql), mysql_error(pData->f_hmysql)); - pData->f_iLastDBErrNo = mysql_errno(pData->f_hmysql); logerror(errMsg); } - /* Enable "delay" */ - pData->f_timeResumeOnError = time(&pData->f_timeResumeOnError) + _DB_DELAYTIMEONERROR ; + return; } -/** - * checkDBErrorState - * - * Check if we can go on with database logging or if we should wait - * a little bit longer. It also check if the DB hanlde is still valid. - * If it is necessary, it takes action to reinitiate the db connection. - * - * \ret int Returns 0 if successful (no error) - */ -rsRetVal checkDBErrorState(instanceData *pData) -{ - time_t now; - assert(pData != NULL); - /* dprintf("in checkDBErrorState, timeResumeOnError: %d\n", pData->f_timeResumeOnError); */ - - /* If timeResumeOnError == 0 no error occured, - we can return with 0 (no error) */ - if (pData->f_timeResumeOnError == 0) - return RS_RET_OK; - - (void) time(&now); - /* Now we know an error occured. We check timeResumeOnError - if we can process. If we have not reach the resume time - yet, we return an error status. */ - if (pData->f_timeResumeOnError > now) - { - /* dprintf("Wait time is not over yet.\n"); */ - return RS_RET_ERR; - } - - /* Ok, we can try to resume the database logging. First - we have to reset the status (timeResumeOnError) and - the last error no. */ - /* To improve this code it would be better to check - if we really need to reInit the db connection. If - only the insert failed and the db conncetcion is - still valid, we need no reInit. - Of course, if an unkown error appeared, we should - reInit. */ - /* rgerhards 2004-12-08: I think it is pretty unlikely - * that we can re-use a connection after the error. So I guess - * the connection must be closed and re-opened in all cases - * (as it is done currently). When we come back to optimize - * this code, we should anyhow see if there are cases where - * we could keep it open. I just doubt this won't be the case. - * I added this comment (and did not remove Michaels) just so - * that we all know what we are looking for. - */ - pData->f_timeResumeOnError = 0; - pData->f_iLastDBErrNo = 0; - return reInitMySQL(pData); -} - -/* - * The following function is responsible for initializing a +/* The following function is responsible for initializing a * MySQL connection. * Initially added 2004-10-28 mmeckelein */ static rsRetVal initMySQL(instanceData *pData) { - int iCounter = 0; DEFiRet; + assert(pData != NULL); + assert(pData->f_hmysql == NULL); - if((iRet = checkDBErrorState(pData)) != RS_RET_OK) - return iRet; - pData->f_hmysql = mysql_init(NULL); if(pData->f_hmysql == NULL) { - logerror("can not initialize MySQL handle - ignoring this action"); - /* The next statement causes a redundant message, but it is the - * best thing we can do in this situation. -- rgerhards, 2007-01-30 - */ - iRet = RS_RET_DISABLE_ACTION; + logerror("can not initialize MySQL handle"); + iRet = RS_RET_SUSPENDED; } else { /* we could get the handle, now on with work... */ - do { - /* Connect to database */ - if (!mysql_real_connect(pData->f_hmysql, pData->f_dbsrv, pData->f_dbuid, - pData->f_dbpwd, pData->f_dbname, 0, NULL, 0)) { - /* if also the second attempt failed - we call the error handler */ - if(iCounter) - DBErrorHandler(pData); - } else { - pData->f_timeResumeOnError = 0; /* We have a working db connection */ - dprintf("connected successfully to db\n"); - } - iCounter++; - } while (mysql_errno(pData->f_hmysql) && iCounter<2); + /* Connect to database */ + if(mysql_real_connect(pData->f_hmysql, pData->f_dbsrv, pData->f_dbuid, + pData->f_dbpwd, pData->f_dbname, 0, NULL, 0) == NULL) { + reportDBError(pData); + closeMySQL(pData); /* ignore any error we may get */ + iRet = RS_RET_SUSPENDED; + } } - return iRet; -} -/* - * Reconnect a MySQL connection. - * Initially added 2004-12-02 - */ -static rsRetVal reInitMySQL(instanceData *pData) -{ - assert(pData != NULL); - - dprintf("reInitMySQL\n"); - closeMySQL(pData); /* close the current handle */ - return initMySQL(pData); /* new connection */ + return iRet; } - /* The following function writes the current log entry * to an established MySQL session. * Initially added 2004-10-28 mmeckelein */ rsRetVal writeMySQL(uchar *psz, instanceData *pData) { - int iCounter=0; DEFiRet; - assert(pData != NULL); - if((iRet = checkDBErrorState(pData)) != RS_RET_OK) - return iRet; + assert(psz != NULL); + assert(pData != NULL); - /* Now we are trying to insert the data. - * - * If the first attampt fails we simply try a second one. If that - * fails too, we discard the message and enable "delay" error handling. - */ - do { - /* query */ - if(mysql_query(pData->f_hmysql, (char*)psz)) { - /* if the second attempt fails - we call the error handler */ - if(iCounter) - DBErrorHandler(pData); - } - else { - /* dprintf("db insert sucessfull\n"); */ + /* try insert */ + if(mysql_query(pData->f_hmysql, (char*)psz)) { + /* error occured, try to re-init connection and retry */ + closeMySQL(pData); /* close the current handle */ + CHKiRet(initMySQL(pData)); /* try to re-open */ + if(mysql_query(pData->f_hmysql, (char*)psz)) { /* re-try insert */ + /* we failed, giving up for now */ + reportDBError(pData); + closeMySQL(pData); /* free ressources */ + ABORT_FINALIZE(RS_RET_SUSPENDED); } - iCounter++; - } while (mysql_errno(pData->f_hmysql) && iCounter<2); + } + +finalize_it: return iRet; } BEGINtryResume CODESTARTtryResume -dprintf("tryResume returns %d\n", iRet); + if(pData->f_hmysql == NULL) { + iRet = initMySQL(pData); + } ENDtryResume BEGINdoAction @@ -335,80 +211,73 @@ BEGINparseSelectorAct int iMySQLPropErr = 0; CODESTARTparseSelectorAct CODE_STD_STRING_REQUESTparseSelectorAct(1) - /* yes, the if below is redundant, but I need it now. Will go away as - * the code further changes. -- rgerhards, 2007-07-25 - */ - if(*p == '>') { - if((iRet = createInstance(&pData)) != RS_RET_OK) - return iRet; + /* first check if this config line is actually for us */ + if(*p != '>') { + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); } - switch (*p) - { - case '>': /* rger 2004-10-28: added support for MySQL - * >server,dbname,userid,password - * rgerhards 2005-08-12: changed rsyslogd so that - * if no DB is selected and > is used, an error - * message is logged. - */ - if(bModMySQLLoaded == 0) - logerror("To enable MySQL logging, a \"$ModLoad MySQL\" must be done - accepted for " - "the time being, but will fail in future releases."); + /* ok, if we reach this point, we have something for us */ + if((iRet = createInstance(&pData)) != RS_RET_OK) + return iRet; + + p++; /* eat '>' '*/ + + /* rger 2004-10-28: added support for MySQL + * >server,dbname,userid,password + * rgerhards 2005-08-12: changed rsyslogd so that + * if no DB is selected and > is used, an error + * message is logged. + */ + if(bModMySQLLoaded == 0) + logerror("To enable MySQL logging, a \"$ModLoad MySQL\" must be done - accepted for " + "the time being, but will fail in future releases."); #ifndef WITH_DB - iRet = RS_RET_ERROR; /* this goes away anyhow, so it's not worth putting much effort in the return code */ - errno = 0; - logerror("write to database action in config file, but rsyslogd compiled without " - "database functionality - ignored"); + iRet = RS_RET_ERROR; /* this goes away anyhow, so it's not worth putting much effort in the return code */ + errno = 0; + logerror("write to database action in config file, but rsyslogd compiled without " + "database functionality - ignored"); #else /* WITH_DB defined! */ - p++; - - /* Now we read the MySQL connection properties - * and verify that the properties are valid. - */ - if(getSubString(&p, pData->f_dbsrv, MAXHOSTNAMELEN+1, ',')) - iMySQLPropErr++; - if(*pData->f_dbsrv == '\0') - iMySQLPropErr++; - if(getSubString(&p, pData->f_dbname, _DB_MAXDBLEN+1, ',')) - iMySQLPropErr++; - if(*pData->f_dbname == '\0') - iMySQLPropErr++; - if(getSubString(&p, pData->f_dbuid, _DB_MAXUNAMELEN+1, ',')) - iMySQLPropErr++; - if(*pData->f_dbuid == '\0') - iMySQLPropErr++; - if(getSubString(&p, pData->f_dbpwd, _DB_MAXPWDLEN+1, ';')) - iMySQLPropErr++; - /* now check for template - * We specify that the SQL option must be present in the template. - * This is for your own protection (prevent sql injection). - */ - if(*p != ';') - --p; /* TODO: the whole parsing of the MySQL module needs to be re-thought - but this here - * is clean enough for the time being -- rgerhards, 2007-07-30 - */ - if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdDBFmt")) - != RS_RET_OK) - return iRet; - - /* If we detect invalid properties, we disable logging, - * because right properties are vital at this place. - * Retries make no sense. - */ - if (iMySQLPropErr) { - iRet = RS_RET_ERR; /* re-vist error code when working on this module */ - dprintf("Trouble with MySQL conncetion properties.\n" - "MySQL logging disabled.\n"); - break; - } else { - initMySQL(pData); - } -#endif /* #ifdef WITH_DB */ - break; - default: - iRet = RS_RET_CONFLINE_UNPROCESSED; - break; + + /* Now we read the MySQL connection properties + * and verify that the properties are valid. + */ + if(getSubString(&p, pData->f_dbsrv, MAXHOSTNAMELEN+1, ',')) + iMySQLPropErr++; + if(*pData->f_dbsrv == '\0') + iMySQLPropErr++; + if(getSubString(&p, pData->f_dbname, _DB_MAXDBLEN+1, ',')) + iMySQLPropErr++; + if(*pData->f_dbname == '\0') + iMySQLPropErr++; + if(getSubString(&p, pData->f_dbuid, _DB_MAXUNAMELEN+1, ',')) + iMySQLPropErr++; + if(*pData->f_dbuid == '\0') + iMySQLPropErr++; + if(getSubString(&p, pData->f_dbpwd, _DB_MAXPWDLEN+1, ';')) + iMySQLPropErr++; + /* now check for template + * We specify that the SQL option must be present in the template. + * This is for your own protection (prevent sql injection). + */ + if(*p != ';') + --p; /* TODO: the whole parsing of the MySQL module needs to be re-thought - but this here + * is clean enough for the time being -- rgerhards, 2007-07-30 + */ + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdDBFmt")); + + /* If we detect invalid properties, we disable logging, + * because right properties are vital at this place. + * Retries make no sense. + */ + if (iMySQLPropErr) { + logerror("Trouble with MySQL connection properties. -MySQL logging disabled"); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); + } else { + CHKiRet(initMySQL(pData)); } +#endif /* #ifdef WITH_DB */ + +CODE_STD_FINALIZERparseSelectorAct ENDparseSelectorAct -- cgit