Logs: an ODBC log named by its data source, user and password its parameters

The string up to the first comma names an ODBC log; the user and the
password are kept apart as its parameters, never shown, and do not tell
logs apart. Another log or -l naming the same data source with other
parameters gives them to the logger, which reconnects with them. The
password is whatever follows the comma after the user.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-23 13:08:06 +03:00
parent e87ffcd7e1
commit 477a2d3822
5 changed files with 87 additions and 28 deletions

View File

@ -326,7 +326,11 @@ sets logfile for all gateways <br>
@ (for Unix) use syslog, filename is used as ident name <br> @ (for Unix) use syslog, filename is used as ident name <br>
&amp; use ODBC, filename consists of comma-delimited &amp; use ODBC, filename consists of comma-delimited
datasource,username,password (username and password are datasource,username,password (username and password are
optional) <br> optional; the password is everything after the second comma).
The datasource alone names the log: the username and password
are its parameters, never shown in messages, and a later log
or -l with the same datasource and other parameters reconnects
the log with them <br>
radius - use RADIUS for logging <br> radius - use RADIUS for logging <br>
LOGTYPE is one of: <b><br> LOGTYPE is one of: <b><br>
c</b> Minutely <b><br> c</b> Minutely <b><br>

View File

@ -325,7 +325,10 @@ proxy on a client with FTP proxy support. Username format is one of
.br .br
@ (for Unix) use syslog, filename is used as ident name @ (for Unix) use syslog, filename is used as ident name
.br .br
& use ODBC, filename consists of comma-delimited datasource,username,password (username and password are optional) & use ODBC, filename consists of comma-delimited datasource,username,password (username and password are optional;
the password is everything after the second comma). The datasource alone names the log: the username and password
are its parameters, never shown in messages, and a later log or -l with the same datasource and other parameters
reconnects the log with them
.br .br
radius - use RADIUS for logging radius - use RADIUS for logging
.br .br

View File

@ -675,27 +675,67 @@ void loginit(void){
#endif #endif
} }
/* The length of the name in a log string: for a type with parameters up to
the comma they follow. */
static size_t lognamelen(const struct logtype *type, const unsigned char *selector){
return type->params? strcspn((char *)selector, ",") : strlen((char *)selector);
}
/* Parameters which differ from those a logger has: given to it, and the
logger opened again with them. Called holding a reference. */
static void logsetparams(struct logger *lg, const unsigned char *params){
unsigned char *p = NULL;
if(params && !(p = (unsigned char *)strdup((char *)params))) return;
_3proxy_mutex_lock(&lg->mutex);
if(lg->params) free(lg->params);
lg->params = p;
if(lg->type->close) lg->type->close(lg);
lg->errors = 0;
lg->failed = 0;
if(lg->type->init && lg->type->init(lg)){
lg->failed = 1;
lg->failtime = time(NULL);
}
_3proxy_mutex_unlock(&lg->mutex);
}
/* The logger named so, opened if none is: held until logput(). NULL, with /* The logger named so, opened if none is: held until logput(). NULL, with
errno set, if it cannot be opened. */ errno set, if it cannot be opened. A logger found with other parameters
takes the new ones. */
struct logger *logget(const unsigned char *selector, ROTATION rotation){ struct logger *logget(const unsigned char *selector, ROTATION rotation){
struct logger *lg; struct logger *lg;
int err = 0; const struct logtype *type;
const unsigned char *params;
size_t len;
int err = 0, changed;
_3proxy_mutex_lock(&log_mutex); _3proxy_mutex_lock(&log_mutex);
type = logmatch(selector);
len = lognamelen(type, selector);
params = selector[len]? selector + len + 1 : NULL;
for(lg = loggers; lg; lg = lg->next){ for(lg = loggers; lg; lg = lg->next){
if(!strcmp((char *)lg->selector, (char *)selector)){ if(lg->type == type && !strncmp((char *)lg->selector, (char *)selector, len) && !lg->selector[len]){
lg->refs++; lg->refs++;
changed = (params && lg->params)? strcmp((char *)params, (char *)lg->params) : (params != NULL || lg->params != NULL);
_3proxy_mutex_unlock(&log_mutex); _3proxy_mutex_unlock(&log_mutex);
if(changed) logsetparams(lg, params);
return lg; return lg;
} }
} }
if(!(lg = calloc(1, sizeof(*lg))) || !(lg->selector = (unsigned char *)strdup((char *)selector))){ if(!(lg = calloc(1, sizeof(*lg))) || !(lg->selector = malloc(len + 1)) ||
if(lg) free(lg); (params && !(lg->params = (unsigned char *)strdup((char *)params)))){
if(lg){
if(lg->selector) free(lg->selector);
free(lg);
}
_3proxy_mutex_unlock(&log_mutex); _3proxy_mutex_unlock(&log_mutex);
errno = ENOMEM; errno = ENOMEM;
return NULL; return NULL;
} }
lg->type = logmatch(selector); memcpy(lg->selector, selector, len);
lg->selector[len] = 0;
lg->type = type;
lg->rotation = rotation; lg->rotation = rotation;
lg->rotated = time(NULL); lg->rotated = time(NULL);
_3proxy_mutex_init(&lg->mutex); _3proxy_mutex_init(&lg->mutex);
@ -708,6 +748,7 @@ struct logger *logget(const unsigned char *selector, ROTATION rotation){
err = errno; err = errno;
_3proxy_mutex_unlock(&log_mutex); _3proxy_mutex_unlock(&log_mutex);
_3proxy_mutex_destroy(&lg->mutex); _3proxy_mutex_destroy(&lg->mutex);
if(lg->params) free(lg->params);
free(lg->selector); free(lg->selector);
free(lg); free(lg);
errno = err; errno = err;
@ -758,6 +799,7 @@ void logput(struct logger *lg){
if(lg->type->close) lg->type->close(lg); if(lg->type->close) lg->type->close(lg);
_3proxy_mutex_destroy(&lg->mutex); _3proxy_mutex_destroy(&lg->mutex);
logfreearchiver(lg); logfreearchiver(lg);
if(lg->params) free(lg->params);
free(lg->selector); free(lg->selector);
free(lg); free(lg);
} }
@ -808,10 +850,10 @@ static void logfallback(struct logger *lg, struct clientparam *param, const unsi
logput(to); logput(to);
} }
/* The part of a log string fit to be shown: up to a comma, an ODBC string /* The part of a log string fit to be shown: its name, without parameters
going on with the user and the password, and at most 200 bytes. */ (an ODBC user and password), and at most 200 bytes. */
size_t logselen(const unsigned char *selector){ size_t logselen(const unsigned char *selector){
size_t len = strcspn((char *)selector, ","); size_t len = lognamelen(logmatch(selector), selector);
return len > 200? 200 : len; return len > 200? 200 : len;
} }
@ -846,8 +888,7 @@ static void logwrite(struct logger *lg, struct clientparam *param, const unsigne
if(report){ if(report){
unsigned char msg[300]; unsigned char msg[300];
/* up to a comma: an ODBC string goes on with user and password */ sprintf((char *)msg, "Log %.200s failed, its lines go to the error log", lg->selector);
sprintf((char *)msg, "Log %.*s failed, its lines go to the error log", (int)logselen(lg->selector), lg->selector);
logerror(msg); logerror(msg);
} }
} }

View File

@ -26,14 +26,17 @@ static void odbcdisconnect(struct logodbc *d){
} }
} }
/* &DATASOURCE,USERNAME,PASSWORD */ /* The log string is &DATASOURCE,USERNAME,PASSWORD. The data source
alone names the log; user and password are its parameters, the
password being whatever follows the comma after the user. */
static int odbcconnect(struct logger *lg){ static int odbcconnect(struct logger *lg){
struct logodbc *d = (struct logodbc *)lg->data; struct logodbc *d = (struct logodbc *)lg->data;
SQLRETURN retcode; SQLRETURN retcode;
char * datasource; char * datasource;
char * username; char * username = NULL;
char * password; char * password = NULL;
char * string; char * string = NULL;
size_t slen = 0;
if(d->hstmt || d->hdbc || d->henv) odbcdisconnect(d); if(d->hstmt || d->hdbc || d->henv) odbcdisconnect(d);
if(!d->henv){ if(!d->henv){
@ -58,20 +61,24 @@ static int odbcconnect(struct logger *lg){
} }
SQLSetConnectAttr(d->hdbc, SQL_LOGIN_TIMEOUT, (void*)15, 0); SQLSetConnectAttr(d->hdbc, SQL_LOGIN_TIMEOUT, (void*)15, 0);
} }
string = strdup((char *)lg->selector + 1); datasource = (char *)lg->selector + 1;
if(!string) return 0; if(lg->params){
datasource = strtok(string, ","); if(!(string = strdup((char *)lg->params))) return 0;
username = strtok(NULL, ","); slen = strlen(string);
password = strtok(NULL, ","); username = string;
if(!datasource) datasource = ""; if((password = strchr(string, ','))) *password++ = 0;
}
/* Connect to data source */ /* Connect to data source */
retcode = SQLConnect(d->hdbc, (SQLCHAR*) datasource, (SQLSMALLINT)strlen(datasource), retcode = SQLConnect(d->hdbc, (SQLCHAR*) datasource, (SQLSMALLINT)strlen(datasource),
(SQLCHAR*) username, (SQLSMALLINT)((username)?strlen(username):0), (SQLCHAR*) username, (SQLSMALLINT)((username)?strlen(username):0),
(SQLCHAR*) password, (SQLSMALLINT)((password)?strlen(password):0)); (SQLCHAR*) password, (SQLSMALLINT)((password)?strlen(password):0));
free(string); if(string) {
/* no copy of the password left behind */
memset(string, 0, slen);
free(string);
}
if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){ if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){
SQLFreeHandle(SQL_HANDLE_DBC, d->hdbc); SQLFreeHandle(SQL_HANDLE_DBC, d->hdbc);
d->hdbc = NULL; d->hdbc = NULL;
@ -125,7 +132,7 @@ static void odbcclose(struct logger *lg){
} }
const struct logtype logtype_odbc = { const struct logtype logtype_odbc = {
"odbc", odbcmatch, odbcinit, odbcformat, odbcwrite, odbcclose, NULL, LOGSTACKSIZE, 0, 0 "odbc", odbcmatch, odbcinit, odbcformat, odbcwrite, odbcclose, NULL, LOGSTACKSIZE, 0, 0, 1
}; };
#endif #endif

View File

@ -627,7 +627,9 @@ struct hashtable {
/* A log, named by the string the log command or -l gives it: "" standard /* A log, named by the string the log command or -l gives it: "" standard
output, "/dev/null" none, "@IDENT" syslog, "&DSN" ODBC, "radius" RADIUS output, "/dev/null" none, "@IDENT" syslog, "&DSN" ODBC, "radius" RADIUS
accounting, anything else a file. One logger serves every service and accounting, anything else a file. For a type with parameters, ODBC, the
string up to its first comma names the log and the rest, user and
password, are its parameters, neither shown nor telling logs apart. One logger serves every service and
configuration naming it the same way, and is closed once none uses it, configuration naming it the same way, and is closed once none uses it,
the standard ones (none, standard output and error, syslog) excepted. */ the standard ones (none, standard output and error, syslog) excepted. */
struct logtype { struct logtype {
@ -647,12 +649,14 @@ struct logtype {
int stacksize; /* the client thread stack its writing needs */ int stacksize; /* the client thread stack its writing needs */
int standard; /* kept open for the life of the process */ int standard; /* kept open for the life of the process */
int text; /* writes a line of text: errors can go there */ int text; /* writes a line of text: errors can go there */
int params; /* the string after a comma is parameters */
}; };
struct logger { struct logger {
struct logger *next; struct logger *next;
const struct logtype *type; const struct logtype *type;
unsigned char *selector; unsigned char *selector; /* the name, without parameters */
unsigned char *params; /* what follows the name's comma, or NULL */
void *data; /* the type's own */ void *data; /* the type's own */
int refs; int refs;
int errors; /* writes failed in a row */ int errors; /* writes failed in a row */