Add saving of hashtables to a file
Cache the user information to file in order to allow authentication without backend databases
This commit is contained in:
@ -1193,3 +1193,185 @@ int useorig = 0;
|
|||||||
|
|
||||||
return netmask;
|
return netmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialise a key for the dbusers hashtable to a file
|
||||||
|
*
|
||||||
|
* @param fd File descriptor to write to
|
||||||
|
* @param key The key to write
|
||||||
|
* @return 0 on error, 1 if the key was written
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
dbusers_keywrite(int fd, void *key)
|
||||||
|
{
|
||||||
|
MYSQL_USER_HOST *dbkey = (MYSQL_USER_HOST *)key;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
tmp = strlen(dbkey->user);
|
||||||
|
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||||
|
return 0;
|
||||||
|
if (write(fd, dbkey->user, tmp) != tmp)
|
||||||
|
return 0;
|
||||||
|
if (write(fd, &dbkey->ipv4, sizeof(dbkey->ipv4)) != sizeof(dbkey->ipv4))
|
||||||
|
return 0;
|
||||||
|
if (write(fd, &dbkey->netmask, sizeof(dbkey->netmask)) != sizeof(dbkey->netmask))
|
||||||
|
return 0;
|
||||||
|
if (dbkey->resource)
|
||||||
|
{
|
||||||
|
tmp = strlen(dbkey->resource);
|
||||||
|
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||||
|
return 0;
|
||||||
|
if (write(fd, dbkey->resource, tmp) != tmp)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else // NULL is valid, so represent with a length of -1
|
||||||
|
{
|
||||||
|
tmp = -1;
|
||||||
|
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialise a value for the dbusers hashtable to a file
|
||||||
|
*
|
||||||
|
* @param fd File descriptor to write to
|
||||||
|
* @param value The value to write
|
||||||
|
* @return 0 on error, 1 if the value was written
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
dbusers_valuewrite(int fd, void *value)
|
||||||
|
{
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
tmp = strlen(value);
|
||||||
|
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||||
|
return 0;
|
||||||
|
if (write(fd, value, tmp) != tmp)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialise a key for the dbusers hashtable from a file
|
||||||
|
*
|
||||||
|
* @param fd File descriptor to read from
|
||||||
|
* @return Pointer to the new key or NULL on error
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
dbusers_keyread(int fd)
|
||||||
|
{
|
||||||
|
MYSQL_USER_HOST *dbkey;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
if ((dbkey = (MYSQL_USER_HOST *)malloc(sizeof(MYSQL_USER_HOST))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||||
|
{
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if ((dbkey->user = (char *)malloc(tmp + 1)) == NULL)
|
||||||
|
{
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (read(fd, dbkey->user, tmp) != tmp)
|
||||||
|
{
|
||||||
|
free(dbkey->user);
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
dbkey->user[tmp] = 0; // NULL Terminate
|
||||||
|
if (read(fd, &dbkey->ipv4, sizeof(dbkey->ipv4)) != sizeof(dbkey->ipv4))
|
||||||
|
{
|
||||||
|
free(dbkey->user);
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (read(fd, &dbkey->netmask, sizeof(dbkey->netmask)) != sizeof(dbkey->netmask))
|
||||||
|
{
|
||||||
|
free(dbkey->user);
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||||
|
{
|
||||||
|
free(dbkey->user);
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (tmp != -1)
|
||||||
|
{
|
||||||
|
if ((dbkey->resource = (char *)malloc(tmp + 1)) == NULL)
|
||||||
|
{
|
||||||
|
free(dbkey->user);
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (read(fd, dbkey->resource, tmp) != tmp)
|
||||||
|
{
|
||||||
|
free(dbkey->resource);
|
||||||
|
free(dbkey->user);
|
||||||
|
free(dbkey);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // NULL is valid, so represent with a length of -1
|
||||||
|
{
|
||||||
|
dbkey->resource = NULL;
|
||||||
|
}
|
||||||
|
return (void *)dbkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialise a value for the dbusers hashtable from a file
|
||||||
|
*
|
||||||
|
* @param fd File descriptor to read from
|
||||||
|
* @return Return the new value data or NULL on error
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
dbusers_valueread(int fd)
|
||||||
|
{
|
||||||
|
char *value;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||||
|
return NULL;
|
||||||
|
if ((value = (char *)malloc(tmp + 1)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (read(fd, value, tmp) != tmp)
|
||||||
|
{
|
||||||
|
free(value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
value[tmp] = 0;
|
||||||
|
return (void *)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the dbusers data to a hashtable file
|
||||||
|
*
|
||||||
|
* @param users The hashtable that stores the user data
|
||||||
|
* @param filename The filename to save the data in
|
||||||
|
* @return The number of entries saved
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
dbusers_save(USERS *users, char *filename)
|
||||||
|
{
|
||||||
|
return hashtable_save(users->data, filename, dbusers_keywrite, dbusers_valuewrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the dbusers data from a saved hashtable file
|
||||||
|
*
|
||||||
|
* @param users The hashtable that stores the user data
|
||||||
|
* @param filename The filename to laod the data from
|
||||||
|
* @return The number of entries loaded
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
dbusers_load(USERS *users, char *filename)
|
||||||
|
{
|
||||||
|
return hashtable_load(users->data, filename, dbusers_keyread, dbusers_valueread);
|
||||||
|
}
|
||||||
|
@ -17,7 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <hashtable.h>
|
#include <hashtable.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,6 +59,7 @@
|
|||||||
* 08/01/2014 Massimiliano Pinto Added copy and free funtion pointers for keys and values:
|
* 08/01/2014 Massimiliano Pinto Added copy and free funtion pointers for keys and values:
|
||||||
* it's possible to copy and free different data types via
|
* it's possible to copy and free different data types via
|
||||||
* kcopyfn/kfreefn, vcopyfn/vfreefn
|
* kcopyfn/kfreefn, vcopyfn/vfreefn
|
||||||
|
* 06/02/2015 Mark Riddoch Addition of hashtable_save and hashtable_load
|
||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
@ -627,3 +632,109 @@ hashtable_iterator_free(HASHITERATOR *iter)
|
|||||||
{
|
{
|
||||||
free(iter);
|
free(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a hashtable to disk
|
||||||
|
*
|
||||||
|
* @param table Hashtable to save
|
||||||
|
* @param filename Filename to write hashtable into
|
||||||
|
* @param keywrite Pointer to function that writes a single key
|
||||||
|
* @param valuewrite Pointer to function that writes a single value
|
||||||
|
* @return Number of entries written or -1 on error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
hashtable_save(HASHTABLE *table, char *filename,
|
||||||
|
int (*keywrite)(int, void*),
|
||||||
|
int (*valuewrite)(int, void*))
|
||||||
|
{
|
||||||
|
int fd, rval = 0;
|
||||||
|
HASHITERATOR *iter;
|
||||||
|
void *key, *value;
|
||||||
|
|
||||||
|
if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (write(fd, "HASHTABLE", 7) != 7) // Magic number
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
write(fd, &rval, sizeof(rval)); // Write zero counter, will be overrwriten at end
|
||||||
|
if ((iter = hashtable_iterator(table)) != NULL)
|
||||||
|
{
|
||||||
|
while ((key = hashtable_next(iter)) != NULL)
|
||||||
|
{
|
||||||
|
if (!(*keywrite)(fd, key))
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((value = hashtable_fetch(table, key)) == NULL ||
|
||||||
|
(*valuewrite)(fd, value) == 0)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
rval++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now go back and write the count of entries */
|
||||||
|
lseek(fd, 7L, SEEK_SET);
|
||||||
|
write(fd, &rval, sizeof(rval));
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a hashtable from disk
|
||||||
|
*
|
||||||
|
* @param table Hashtable to load
|
||||||
|
* @param filename Filename to read hashtable from
|
||||||
|
* @param keyread Pointer to function that reads a single key
|
||||||
|
* @param valueread Pointer to function that reads a single value
|
||||||
|
* @return Number of entries read or -1 on error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
hashtable_load(HASHTABLE *table, char *filename,
|
||||||
|
void *(*keyread)(int),
|
||||||
|
void *(*valueread)(int))
|
||||||
|
{
|
||||||
|
int fd, count, rval = 0;
|
||||||
|
void *key, *value;
|
||||||
|
char buf[40];
|
||||||
|
|
||||||
|
if ((fd = open(filename, O_RDONLY)) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (read(fd, buf, 7) != 7)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (strncmp(buf, "HASHTABLE", 7) != 0)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (read(fd, &count, sizeof(count)) != sizeof(count))
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
key = keyread(fd);
|
||||||
|
value = valueread(fd);
|
||||||
|
if (key == NULL || value == NULL)
|
||||||
|
break;
|
||||||
|
hashtable_add(table, key, value);
|
||||||
|
rval++;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
* 29/05/14 Mark Riddoch Filter API implementation
|
* 29/05/14 Mark Riddoch Filter API implementation
|
||||||
* 09/09/14 Massimiliano Pinto Added service option for localhost authentication
|
* 09/09/14 Massimiliano Pinto Added service option for localhost authentication
|
||||||
* 13/10/14 Massimiliano Pinto Added hashtable for resources (i.e database names for MySQL services)
|
* 13/10/14 Massimiliano Pinto Added hashtable for resources (i.e database names for MySQL services)
|
||||||
|
* 06/02/15 Mark Riddoch Added caching of authentication data
|
||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
@ -54,6 +55,8 @@
|
|||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <skygw_utils.h>
|
#include <skygw_utils.h>
|
||||||
#include <log_manager.h>
|
#include <log_manager.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
/** Defined in log_manager.cc */
|
/** Defined in log_manager.cc */
|
||||||
extern int lm_enabled_logfiles_bitmask;
|
extern int lm_enabled_logfiles_bitmask;
|
||||||
@ -233,12 +236,55 @@ GWPROTOCOL *funcs;
|
|||||||
(port->address == NULL ? "0.0.0.0" : port->address),
|
(port->address == NULL ? "0.0.0.0" : port->address),
|
||||||
port->port,
|
port->port,
|
||||||
service->name)));
|
service->name)));
|
||||||
hashtable_free(service->users->data);
|
|
||||||
free(service->users);
|
{
|
||||||
dcb_free(port->listener);
|
/* Try loading authentication data from file cache */
|
||||||
port->listener = NULL;
|
char *ptr, path[4096];
|
||||||
goto retblock;
|
strcpy(path, "/usr/local/skysql/MaxScale");
|
||||||
|
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||||
|
{
|
||||||
|
strncpy(path, ptr, 4096);
|
||||||
|
}
|
||||||
|
strncat(path, "/", 4096);
|
||||||
|
strncat(path, service->name, 4096);
|
||||||
|
strncat(path, "/.cache/dbusers", 4096);
|
||||||
|
loaded = dbusers_load(service->users, path);
|
||||||
|
if (loaded != -1)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Using cached credential information.")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loaded == -1)
|
||||||
|
{
|
||||||
|
hashtable_free(service->users->data);
|
||||||
|
free(service->users);
|
||||||
|
dcb_free(port->listener);
|
||||||
|
port->listener = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Save authentication data to file cache */
|
||||||
|
char *ptr, path[4096];
|
||||||
|
strcpy(path, "/usr/local/skysql/MaxScale");
|
||||||
|
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||||
|
{
|
||||||
|
strncpy(path, ptr, 4096);
|
||||||
|
}
|
||||||
|
strncat(path, "/", 4096);
|
||||||
|
strncat(path, service->name, 4096);
|
||||||
|
if (access(path, R_OK) == -1)
|
||||||
|
mkdir(path, 0777);
|
||||||
|
strncat(path, "/.cache", 4096);
|
||||||
|
if (access(path, R_OK) == -1)
|
||||||
|
mkdir(path, 0777);
|
||||||
|
strncat(path, "/dbusers", 4096);
|
||||||
|
dbusers_save(service->users, path);
|
||||||
|
}
|
||||||
|
|
||||||
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
|
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
|
||||||
* This way MaxScale could try reloading users' just after startup
|
* This way MaxScale could try reloading users' just after startup
|
||||||
*/
|
*/
|
||||||
@ -356,7 +402,7 @@ int listeners = 0;
|
|||||||
"%s: Failed to create router instance for service. Service not started.",
|
"%s: Failed to create router instance for service. Service not started.",
|
||||||
service->name)));
|
service->name)));
|
||||||
service->state = SERVICE_STATE_FAILED;
|
service->state = SERVICE_STATE_FAILED;
|
||||||
return NULL;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
port = service->ports;
|
port = service->ports;
|
||||||
@ -825,9 +871,9 @@ struct tm result;
|
|||||||
char time_buf[30];
|
char time_buf[30];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
printf("Service %p\n", service);
|
printf("Service %p\n", (void *)service);
|
||||||
printf("\tService: %s\n", service->name);
|
printf("\tService: %s\n", service->name);
|
||||||
printf("\tRouter: %s (%p)\n", service->routerModule, service->router);
|
printf("\tRouter: %s (%p)\n", service->routerModule, (void *)service->router);
|
||||||
printf("\tStarted: %s",
|
printf("\tStarted: %s",
|
||||||
asctime_r(localtime_r(&service->stats.started, &result), time_buf));
|
asctime_r(localtime_r(&service->stats.started, &result), time_buf));
|
||||||
printf("\tBackend databases\n");
|
printf("\tBackend databases\n");
|
||||||
@ -846,7 +892,7 @@ int i;
|
|||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
printf("\tUsers data: %p\n", service->users);
|
printf("\tUsers data: %p\n", (void *)service->users);
|
||||||
printf("\tTotal connections: %d\n", service->stats.n_sessions);
|
printf("\tTotal connections: %d\n", service->stats.n_sessions);
|
||||||
printf("\tCurrently connected: %d\n", service->stats.n_current);
|
printf("\tCurrently connected: %d\n", service->stats.n_current);
|
||||||
}
|
}
|
||||||
|
@ -65,4 +65,6 @@ extern int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host,
|
|||||||
extern USERS *mysql_users_alloc();
|
extern USERS *mysql_users_alloc();
|
||||||
extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
|
extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
|
||||||
extern int replace_mysql_users(SERVICE *service);
|
extern int replace_mysql_users(SERVICE *service);
|
||||||
|
extern int dbusers_save(USERS *, char *);
|
||||||
|
extern int dbusers_load(USERS *, char *);
|
||||||
#endif
|
#endif
|
||||||
|
@ -112,6 +112,14 @@ void hashtable_get_stats(
|
|||||||
int* hashsize,
|
int* hashsize,
|
||||||
int* nelems,
|
int* nelems,
|
||||||
int* longest);
|
int* longest);
|
||||||
|
extern int hashtable_save(HASHTABLE *,
|
||||||
|
char *,
|
||||||
|
int (*keywrite)(int, void*),
|
||||||
|
int (*valuewrite)(int, void*));
|
||||||
|
extern int hashtable_load(HASHTABLE *,
|
||||||
|
char *,
|
||||||
|
void *(*keyread)(int),
|
||||||
|
void *(*valueread)(int));
|
||||||
|
|
||||||
extern HASHITERATOR *hashtable_iterator(HASHTABLE *);
|
extern HASHITERATOR *hashtable_iterator(HASHTABLE *);
|
||||||
/**< Allocate an iterator on the hashtable */
|
/**< Allocate an iterator on the hashtable */
|
||||||
|
Reference in New Issue
Block a user