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>
&amp; use ODBC, filename consists of comma-delimited
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>
LOGTYPE is one of: <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
@ (for Unix) use syslog, filename is used as ident name
.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
radius - use RADIUS for logging
.br

View File

@ -675,27 +675,67 @@ void loginit(void){
#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
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 *lg;
int err = 0;
const struct logtype *type;
const unsigned char *params;
size_t len;
int err = 0, changed;
_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){
if(!strcmp((char *)lg->selector, (char *)selector)){
if(lg->type == type && !strncmp((char *)lg->selector, (char *)selector, len) && !lg->selector[len]){
lg->refs++;
changed = (params && lg->params)? strcmp((char *)params, (char *)lg->params) : (params != NULL || lg->params != NULL);
_3proxy_mutex_unlock(&log_mutex);
if(changed) logsetparams(lg, params);
return lg;
}
}
if(!(lg = calloc(1, sizeof(*lg))) || !(lg->selector = (unsigned char *)strdup((char *)selector))){
if(lg) free(lg);
if(!(lg = calloc(1, sizeof(*lg))) || !(lg->selector = malloc(len + 1)) ||
(params && !(lg->params = (unsigned char *)strdup((char *)params)))){
if(lg){
if(lg->selector) free(lg->selector);
free(lg);
}
_3proxy_mutex_unlock(&log_mutex);
errno = ENOMEM;
return NULL;
}
lg->type = logmatch(selector);
memcpy(lg->selector, selector, len);
lg->selector[len] = 0;
lg->type = type;
lg->rotation = rotation;
lg->rotated = time(NULL);
_3proxy_mutex_init(&lg->mutex);
@ -708,6 +748,7 @@ struct logger *logget(const unsigned char *selector, ROTATION rotation){
err = errno;
_3proxy_mutex_unlock(&log_mutex);
_3proxy_mutex_destroy(&lg->mutex);
if(lg->params) free(lg->params);
free(lg->selector);
free(lg);
errno = err;
@ -758,6 +799,7 @@ void logput(struct logger *lg){
if(lg->type->close) lg->type->close(lg);
_3proxy_mutex_destroy(&lg->mutex);
logfreearchiver(lg);
if(lg->params) free(lg->params);
free(lg->selector);
free(lg);
}
@ -808,10 +850,10 @@ static void logfallback(struct logger *lg, struct clientparam *param, const unsi
logput(to);
}
/* The part of a log string fit to be shown: up to a comma, an ODBC string
going on with the user and the password, and at most 200 bytes. */
/* The part of a log string fit to be shown: its name, without parameters
(an ODBC user and password), and at most 200 bytes. */
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;
}
@ -846,8 +888,7 @@ static void logwrite(struct logger *lg, struct clientparam *param, const unsigne
if(report){
unsigned char msg[300];
/* up to a comma: an ODBC string goes on with user and password */
sprintf((char *)msg, "Log %.*s failed, its lines go to the error log", (int)logselen(lg->selector), lg->selector);
sprintf((char *)msg, "Log %.200s failed, its lines go to the error log", lg->selector);
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){
struct logodbc *d = (struct logodbc *)lg->data;
SQLRETURN retcode;
char * datasource;
char * username;
char * password;
char * string;
char * username = NULL;
char * password = NULL;
char * string = NULL;
size_t slen = 0;
if(d->hstmt || d->hdbc || d->henv) odbcdisconnect(d);
if(!d->henv){
@ -58,20 +61,24 @@ static int odbcconnect(struct logger *lg){
}
SQLSetConnectAttr(d->hdbc, SQL_LOGIN_TIMEOUT, (void*)15, 0);
}
string = strdup((char *)lg->selector + 1);
if(!string) return 0;
datasource = strtok(string, ",");
username = strtok(NULL, ",");
password = strtok(NULL, ",");
if(!datasource) datasource = "";
datasource = (char *)lg->selector + 1;
if(lg->params){
if(!(string = strdup((char *)lg->params))) return 0;
slen = strlen(string);
username = string;
if((password = strchr(string, ','))) *password++ = 0;
}
/* Connect to data source */
retcode = SQLConnect(d->hdbc, (SQLCHAR*) datasource, (SQLSMALLINT)strlen(datasource),
(SQLCHAR*) username, (SQLSMALLINT)((username)?strlen(username):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){
SQLFreeHandle(SQL_HANDLE_DBC, d->hdbc);
d->hdbc = NULL;
@ -125,7 +132,7 @@ static void odbcclose(struct logger *lg){
}
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

View File

@ -627,7 +627,9 @@ struct hashtable {
/* A log, named by the string the log command or -l gives it: "" standard
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,
the standard ones (none, standard output and error, syslog) excepted. */
struct logtype {
@ -647,12 +649,14 @@ struct logtype {
int stacksize; /* the client thread stack its writing needs */
int standard; /* kept open for the life of the process */
int text; /* writes a line of text: errors can go there */
int params; /* the string after a comma is parameters */
};
struct logger {
struct logger *next;
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 */
int refs;
int errors; /* writes failed in a row */