Fix support for 'stats_mysql_users' for LDAP plugin

This commit also introduces the following changes:

- Fix invalid formatting of 'stats_mysql_users' when username exceeds
  '256' characters.
- Improve error handling in utility function 'string_format'.
- Add new utility functions 'cstr_format', as improved versions of
  previous 'string_format' function.
pull/4016/head
Javier Jaramago Fernández 3 years ago
parent 07011da773
commit d4aea7c9db

@ -1,6 +1,15 @@
#ifndef CLASS_MYSQL_LDAP_AUTHENTICATION_H
#define CLASS_MYSQL_LDAP_AUTHENTICATION_H
struct LDAP_USER_FIELD_IDX {
enum index {
USERNAME = 0,
FRONTEND_CONNECTIONS = 1,
FRONTED_MAX_CONNECTIONS = 2,
__SIZE
};
};
class MySQL_LDAP_Authentication {
public:
virtual char * lookup(char *username, char *pass,
@ -11,6 +20,7 @@ public:
virtual int increase_frontend_user_connections(char *username, int *max_connections = NULL) { return 0; };
virtual void decrease_frontend_user_connections(char *username) {};
virtual std::unique_ptr<SQLite3_result> dump_all_users() { return 0; };
virtual void wrlock() {};
virtual void wrunlock() {};

@ -1,6 +1,7 @@
#ifndef __PROXYSQL_UTILS_H
#define __PROXYSQL_UTILS_H
#include <cstdarg>
#include <type_traits>
#include <memory>
#include <string>
@ -38,18 +39,88 @@ template<
#endif // CXX17
>
int string_format(const std::string& str, std::string& result, Args... args) {
int err = 0;
size_t size = snprintf(nullptr, 0, str.c_str(), args... ) + 1;
int size = snprintf(nullptr, 0, str.c_str(), args...);
if(size <= 0) {
err = size;
if (size <= 0) {
return size;
} else {
size += 1;
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), size, str.c_str(), args...);
result = std::string(buf.get(), buf.get() + size - 1);
size = snprintf(buf.get(), size, str.c_str(), args...);
result = std::string(buf.get(), buf.get() + size);
}
return err;
return size;
}
/**
* @brief Output struct of 'cstr_format' functions family.
*/
struct cfmt_t {
// @brief If negative, the error returned from 'snprintf' while formatting. Otherwise the number of bytes
// copied into the resulting formatted string.
int size;
// @brief In case of success the resulting formatted string, empty otherwise.
std::string str;
};
/**
* @brief Formats the provided string literal with the extra variadic arguments.
* @details This is an improved version on 'string_format' function. When used against an string literal,
* allows the compiler to issue the proper warnings in case the format parameters are ill-formed.
* @param fmt The string literal to be formatted with variadic arguments.
* @param ... The variadic arguments to use for formatting.
* @return An 'cfmt_t' holding the number of bytes copied to the resulting string and the formatted string
* itself. In case of error the 'size' field will hold 'snprintf' returned error and 'str' will be empty.
*/
__attribute__((__format__ (__printf__, 1, 2)))
cfmt_t cstr_format(const char* fmt, ...);
/**
* @brief Formats the provided string literal with the extra variadic arguments, and place the formatted
* string either in the returned 'cfmt_t::string' or in the supplied buffer.
* @details This is an improved version on 'string_format' function. When used against an string literal,
* allows the compiler to issue the proper warnings in case the format parameters are ill-formed.
* @param out_buf The output buffer in which to place the resulting formatted string in case it fits.
* @param fmt The string literal to be formatted with variadic arguments.
* @param ... The variadic arguments to use for formatting.
* @return On success, an 'cfmt_t' holding the number of bytes copied to the resulting string, in case this
* result fits in the provided buffer, this buffer is directly written and the returned 'cfmt_t::str' will
* be empty. In case of error the 'size' field will hold 'snprintf' returned error and 'str' will be empty.
*/
template <int N> __attribute__((__format__ (__printf__, 2, 3)))
cfmt_t cstr_format(char (&out_buf)[N], const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int size = vsnprintf(nullptr, 0, fmt, args);
va_end(args);
if (size <= 0) {
return { size, {} };
} else {
size += 1;
if (size <= N) {
va_start(args, fmt);
size = vsnprintf(out_buf, size, fmt, args);
va_end(args);
return { size, {} };
} else {
std::unique_ptr<char[]> buf(new char[size]);
va_start(args, fmt);
size = vsnprintf(buf.get(), size, fmt, args);
va_end(args);
if (size <= 0) {
return { size, {} };
} else {
return { size, std::string(buf.get(), buf.get() + size) };
}
}
}
}
/**

@ -10939,7 +10939,7 @@ SQLite3_result* ProxySQL_Admin::__add_active_users(
if (sqlite_result != nullptr) {
vector<char*> pta(static_cast<size_t>(resultset->columns));
for (uint32_t i = 0; i < resultset->columns; i++) {
for (int i = 0; i < resultset->columns; i++) {
if (i == 1) {
pta[i] = password;
} else {
@ -11441,31 +11441,49 @@ void ProxySQL_Admin::save_clickhouse_users_runtime_to_database(bool _runtime) {
void ProxySQL_Admin::stats___mysql_users() {
account_details_t **ads=NULL;
int num_users;
int i;
statsdb->execute("DELETE FROM stats_mysql_users");
char *q=(char *)"INSERT INTO stats_mysql_users(username,frontend_connections,frontend_max_connections) VALUES ('%s',%d,%d)";
int l=strlen(q);
char buf[256];
num_users=GloMyAuth->dump_all_users(&ads, false);
int num_users=GloMyAuth->dump_all_users(&ads, false);
if (num_users==0) return;
for (i=0; i<num_users; i++) {
const char q[] {
"INSERT INTO stats_mysql_users(username,frontend_connections,frontend_max_connections) VALUES ('%s',%d,%d)"
};
char buf[256] = { 0 };
for (int i=0; i<num_users; i++) {
account_details_t *ad=ads[i];
if (ad->default_hostgroup>= 0) { // only not admin/stats
if ( (strlen(ad->username) + l) > 210) {
char *query=(char *)malloc(strlen(ad->username)+l+32);
sprintf(query,q,ad->username,ad->num_connections_used);
sprintf(query,q,ad->username,ad->max_connections);
statsdb->execute(query);
free(query);
cfmt_t q_fmt { cstr_format(buf, q, ad->username, ad->num_connections_used, ad->max_connections) };
if (q_fmt.str.size()) {
statsdb->execute(q_fmt.str.c_str());
} else {
sprintf(buf,q,ad->username,ad->num_connections_used,ad->max_connections);
statsdb->execute(buf);
}
}
free(ad->username);
free(ad);
}
if (GloMyLdapAuth) {
std::unique_ptr<SQLite3_result> ldap_users { GloMyLdapAuth->dump_all_users() };
for (const SQLite3_row* row : ldap_users->rows) {
const char* username = row->fields[LDAP_USER_FIELD_IDX::USERNAME];
int f_conns = atoi(row->fields[LDAP_USER_FIELD_IDX::FRONTEND_CONNECTIONS]);
int f_max_conns = atoi(row->fields[LDAP_USER_FIELD_IDX::FRONTED_MAX_CONNECTIONS]);
cfmt_t q_fmt { cstr_format(buf, q, username, f_conns, f_max_conns) };
if (q_fmt.str.size()) {
statsdb->execute(q_fmt.str.c_str());
} else {
statsdb->execute(buf);
}
}
}
free(ads);
}

@ -10,6 +10,32 @@
#include <sys/wait.h>
#include <unistd.h>
__attribute__((__format__ (__printf__, 1, 2)))
cfmt_t cstr_format(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int size = vsnprintf(nullptr, 0, fmt, args);
va_end(args);
if (size <= 0) {
return { size, {} };
} else {
size += 1;
std::unique_ptr<char[]> buf(new char[size]);
va_start(args, fmt);
size = vsnprintf(buf.get(), size, fmt, args);
va_end(args);
if (size <= 0) {
return { size, {} };
} else {
return { size, std::string(buf.get(), buf.get() + size) };
}
}
}
/**
* @brief Kills the given process sending an initial 'SIGTERM' to it. If the process doesn't
* respond within 'timeout_us' to the initial signal a 'SIGKILL' is issued to it.

Loading…
Cancel
Save