Change the subscriber and database backend

gsm_subscriber is now refcounted, the db backend is leaking
a lot less, db_get_subscriber will allocate the subscr record
now, subscr_* will look up a subscriber in the list of currently
active subscribers and add an ref to this one.

The db test cases pass, more testing will be when next to the bts
This commit is contained in:
Holger Freyther 2009-01-01 18:02:05 +00:00
parent 67b4b9a017
commit 12aa50d5a2
7 changed files with 148 additions and 72 deletions

View File

@ -1,4 +1,5 @@
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> /* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved * All Rights Reserved
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -24,14 +25,16 @@
#include <openbsc/gsm_subscriber.h> #include <openbsc/gsm_subscriber.h>
/* one time initialisation */
int db_init(const char *name); int db_init(const char *name);
int db_prepare(); int db_prepare();
int db_fini(); int db_fini();
struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]); /* subscriber management */
int db_get_subscriber(enum gsm_subscriber_field field, struct gsm_subscriber* subscriber); struct gsm_subscriber* db_create_subscriber(char *imsi);
int db_set_subscriber(struct gsm_subscriber* subscriber); struct gsm_subscriber* db_get_subscriber(enum gsm_subscriber_field field, char *subscr);
int db_sync_subscriber(struct gsm_subscriber* subscriber);
int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber); int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber);
int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]); int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char *imei);
#endif /* _DB_H */ #endif /* _DB_H */

View File

@ -3,6 +3,7 @@
#include <sys/types.h> #include <sys/types.h>
#include "gsm_data.h" #include "gsm_data.h"
#include "linuxlist.h"
#define GSM_IMEI_LENGTH 17 #define GSM_IMEI_LENGTH 17
#define GSM_IMSI_LENGTH 17 #define GSM_IMSI_LENGTH 17
@ -18,6 +19,10 @@ struct gsm_subscriber {
char name[GSM_NAME_LENGTH]; char name[GSM_NAME_LENGTH];
char extension[GSM_EXTENSION_LENGTH]; char extension[GSM_EXTENSION_LENGTH];
int authorized; int authorized;
/* for internal management */
int use_count;
struct llist_head entry;
}; };
enum gsm_subscriber_field { enum gsm_subscriber_field {
@ -25,4 +30,8 @@ enum gsm_subscriber_field {
GSM_SUBSCRIBER_TMSI, GSM_SUBSCRIBER_TMSI,
}; };
struct gsm_subscriber *subscr_alloc();
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr);
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr);
#endif /* _GSM_SUBSCR_H */ #endif /* _GSM_SUBSCR_H */

View File

@ -188,7 +188,10 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
void lchan_free(struct gsm_lchan *lchan) void lchan_free(struct gsm_lchan *lchan)
{ {
lchan->type = GSM_LCHAN_NONE; lchan->type = GSM_LCHAN_NONE;
lchan->subscr = 0; if (lchan->subscr) {
subscr_put(lchan->subscr);
lchan->subscr = 0;
}
/* stop the timer */ /* stop the timer */
del_timer(&lchan->release_timer); del_timer(&lchan->release_timer);

View File

@ -1,4 +1,6 @@
/* Simple HLR/VLR database backend using dbi */
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> /* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved * All Rights Reserved
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -29,7 +31,7 @@ static char *db_basename = NULL;
static char *db_dirname = NULL; static char *db_dirname = NULL;
dbi_conn conn; dbi_conn conn;
void db__error_func(dbi_conn conn, void* data) { void db_error_func(dbi_conn conn, void* data) {
const char* msg; const char* msg;
dbi_conn_error(conn, &msg); dbi_conn_error(conn, &msg);
printf("DBI: %s\n", msg); printf("DBI: %s\n", msg);
@ -43,7 +45,7 @@ int db_init(const char *name) {
return 1; return 1;
} }
dbi_conn_error_handler( conn, db__error_func, NULL ); dbi_conn_error_handler( conn, db_error_func, NULL );
/* MySQL /* MySQL
dbi_conn_set_option(conn, "host", "localhost"); dbi_conn_set_option(conn, "host", "localhost");
@ -54,8 +56,8 @@ int db_init(const char *name) {
*/ */
/* SqLite 3 */ /* SqLite 3 */
char *db_basename = strdup(name); db_basename = strdup(name);
char *db_dirname = strdup(name); db_dirname = strdup(name);
dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
dbi_conn_set_option(conn, "dbname", basename(db_basename)); dbi_conn_set_option(conn, "dbname", basename(db_basename));
@ -154,15 +156,13 @@ int db_fini() {
return 0; return 0;
} }
struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]) { struct gsm_subscriber* db_create_subscriber(char *imsi) {
dbi_result result; dbi_result result;
struct gsm_subscriber* subscriber; struct gsm_subscriber* subscr;
subscriber = malloc(sizeof(*subscriber));
if (!subscriber) /* Is this subscriber known in the db? */
return NULL; subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
memset(subscriber, 0, sizeof(*subscriber)); if (subscr) {
strncpy(subscriber->imsi, imsi, GSM_IMSI_LENGTH-1);
if (!db_get_subscriber(GSM_SUBSCRIBER_IMSI, subscriber)) {
result = dbi_conn_queryf(conn, result = dbi_conn_queryf(conn,
"UPDATE Subscriber set updated = datetime('now') " "UPDATE Subscriber set updated = datetime('now') "
"WHERE imsi = %s " , imsi); "WHERE imsi = %s " , imsi);
@ -171,8 +171,12 @@ struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]) {
} else { } else {
dbi_result_free(result); dbi_result_free(result);
} }
return subscriber; return subscr;
} }
subscr = subscr_alloc();
if (!subscr)
return NULL;
result = dbi_conn_queryf(conn, result = dbi_conn_queryf(conn,
"INSERT INTO Subscriber " "INSERT INTO Subscriber "
"(imsi, created, updated) " "(imsi, created, updated) "
@ -183,76 +187,81 @@ struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]) {
if (result==NULL) { if (result==NULL) {
printf("DB: Failed to create Subscriber by IMSI.\n"); printf("DB: Failed to create Subscriber by IMSI.\n");
} }
subscriber->id = dbi_conn_sequence_last(conn, NULL); subscr->id = dbi_conn_sequence_last(conn, NULL);
strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1);
dbi_result_free(result); dbi_result_free(result);
printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscriber->id, subscriber->imsi); printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
return subscriber; return subscr;
} }
int db_get_subscriber(enum gsm_subscriber_field field, struct gsm_subscriber* subscriber) { struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, char *id) {
dbi_result result; dbi_result result;
const char *string; const char *string;
char *quoted; char *quoted;
struct gsm_subscriber *subscr;
switch (field) { switch (field) {
case GSM_SUBSCRIBER_IMSI: case GSM_SUBSCRIBER_IMSI:
dbi_conn_quote_string_copy(conn, subscriber->imsi, &quoted); dbi_conn_quote_string_copy(conn, id, &quoted);
result = dbi_conn_queryf(conn, result = dbi_conn_queryf(conn,
"SELECT * FROM Subscriber " "SELECT * FROM Subscriber "
"WHERE imsi = %s ", "WHERE imsi = %s ",
quoted quoted
); );
free(quoted);
break; break;
case GSM_SUBSCRIBER_TMSI: case GSM_SUBSCRIBER_TMSI:
dbi_conn_quote_string_copy(conn, subscriber->tmsi, &quoted); dbi_conn_quote_string_copy(conn, id, &quoted);
result = dbi_conn_queryf(conn, result = dbi_conn_queryf(conn,
"SELECT * FROM Subscriber " "SELECT * FROM Subscriber "
"WHERE tmsi = %s ", "WHERE tmsi = %s ",
quoted quoted
); );
free(quoted);
break; break;
default: default:
printf("DB: Unknown query selector for Subscriber.\n"); printf("DB: Unknown query selector for Subscriber.\n");
return 1; return NULL;
} }
if (result==NULL) { if (result==NULL) {
printf("DB: Failed to query Subscriber.\n"); printf("DB: Failed to query Subscriber.\n");
return 1; return NULL;
} }
if (!dbi_result_next_row(result)) { if (!dbi_result_next_row(result)) {
printf("DB: Failed to find the Subscriber.\n"); printf("DB: Failed to find the Subscriber.\n");
dbi_result_free(result); dbi_result_free(result);
return 1; return NULL;
} }
memset(subscriber, 0, sizeof(*subscriber));
subscriber->id = dbi_result_get_ulonglong(result, "id"); subscr = subscr_alloc();
subscr->id = dbi_result_get_ulonglong(result, "id");
string = dbi_result_get_string(result, "imsi"); string = dbi_result_get_string(result, "imsi");
if (string) if (string)
strncpy(subscriber->imsi, string, GSM_IMSI_LENGTH); strncpy(subscr->imsi, string, GSM_IMSI_LENGTH);
string = dbi_result_get_string(result, "tmsi"); string = dbi_result_get_string(result, "tmsi");
if (string) if (string)
strncpy(subscriber->tmsi, string, GSM_TMSI_LENGTH); strncpy(subscr->tmsi, string, GSM_TMSI_LENGTH);
string = dbi_result_get_string(result, "name"); string = dbi_result_get_string(result, "name");
if (string) if (string)
strncpy(subscriber->name, string, GSM_NAME_LENGTH); strncpy(subscr->name, string, GSM_NAME_LENGTH);
string = dbi_result_get_string(result, "extension"); string = dbi_result_get_string(result, "extension");
if (string) if (string)
strncpy(subscriber->extension, string, GSM_EXTENSION_LENGTH); strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH);
// FIXME handle extension // FIXME handle extension
subscriber->lac = dbi_result_get_uint(result, "lac"); subscr->lac = dbi_result_get_uint(result, "lac");
subscriber->authorized = dbi_result_get_uint(result, "authorized"); subscr->authorized = dbi_result_get_uint(result, "authorized");
printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %s, LAC %hu, AUTH %u\n", printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %s, LAC %hu, AUTH %u\n",
subscriber->id, subscriber->imsi, subscriber->name, subscriber->tmsi, subscr->id, subscr->imsi, subscr->name, subscr->tmsi,
subscriber->lac, subscriber->authorized); subscr->lac, subscr->authorized);
dbi_result_free(result); dbi_result_free(result);
return 0; return subscr;
} }
int db_set_subscriber(struct gsm_subscriber* subscriber) { int db_sync_subscriber(struct gsm_subscriber* subscriber) {
dbi_result result; dbi_result result;
result = dbi_conn_queryf(conn, result = dbi_conn_queryf(conn,
"UPDATE Subscriber " "UPDATE Subscriber "
@ -263,6 +272,7 @@ int db_set_subscriber(struct gsm_subscriber* subscriber) {
"WHERE imsi = %s ", "WHERE imsi = %s ",
subscriber->tmsi, subscriber->lac, subscriber->authorized, subscriber->imsi subscriber->tmsi, subscriber->lac, subscriber->authorized, subscriber->imsi
); );
if (result==NULL) { if (result==NULL) {
printf("DB: Failed to update Subscriber (by IMSI).\n"); printf("DB: Failed to update Subscriber (by IMSI).\n");
return 1; return 1;
@ -282,6 +292,7 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
"WHERE tmsi = %s ", "WHERE tmsi = %s ",
tmsi_quoted tmsi_quoted
); );
free(tmsi_quoted);
if (result==NULL) { if (result==NULL) {
printf("DB: Failed to query Subscriber while allocating new TMSI.\n"); printf("DB: Failed to query Subscriber while allocating new TMSI.\n");
return 1; return 1;
@ -293,7 +304,7 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
if (!dbi_result_next_row(result)) { if (!dbi_result_next_row(result)) {
dbi_result_free(result); dbi_result_free(result);
printf("DB: Allocated TMSI %s for IMSI %s.\n", subscriber->tmsi, subscriber->imsi); printf("DB: Allocated TMSI %s for IMSI %s.\n", subscriber->tmsi, subscriber->imsi);
return db_set_subscriber(subscriber); return db_sync_subscriber(subscriber);
} }
dbi_result_free(result); dbi_result_free(result);
} }

View File

@ -1,6 +1,7 @@
/* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */ /* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */
/* (C) 2008 by Harald Welte <laforge@gnumonks.org> /* (C) 2008 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@ -28,6 +29,9 @@
#include <openbsc/gsm_subscriber.h> #include <openbsc/gsm_subscriber.h>
#include <openbsc/db.h> #include <openbsc/db.h>
LLIST_HEAD(active_subscribers);
struct gsm_subscriber *subscr_alloc(void) struct gsm_subscriber *subscr_alloc(void)
{ {
struct gsm_subscriber *s; struct gsm_subscriber *s;
@ -37,46 +41,57 @@ struct gsm_subscriber *subscr_alloc(void)
return NULL; return NULL;
memset(s, 0, sizeof(*s)); memset(s, 0, sizeof(*s));
llist_add_tail(&s->entry, &active_subscribers);
s->use_count = 1;
return s; return s;
} }
void subscr_free(struct gsm_subscriber *subscr) static void subscr_free(struct gsm_subscriber *subscr)
{ {
llist_del(&subscr->entry);
free(subscr); free(subscr);
} }
struct gsm_subscriber *subscr_get_by_tmsi(char *tmsi) struct gsm_subscriber *subscr_get_by_tmsi(char *tmsi)
{ {
struct gsm_subscriber *subscr = subscr_alloc(); struct gsm_subscriber *subscr;
strncpy(subscr->tmsi, tmsi, sizeof(subscr->tmsi)); /* we might have a record in memory already */
subscr->tmsi[sizeof(subscr->tmsi)-1] = '\0'; llist_for_each_entry(subscr, &active_subscribers, entry) {
if (strcmp(subscr->tmsi, tmsi) == 0)
if (db_get_subscriber(GSM_SUBSCRIBER_TMSI, subscr) != 0) { return subscr_get(subscr);
subscr_free(subscr);
subscr = NULL;
} }
return subscr; return db_get_subscriber(GSM_SUBSCRIBER_TMSI, tmsi);
} }
struct gsm_subscriber *subscr_get_by_imsi(char *imsi) struct gsm_subscriber *subscr_get_by_imsi(char *imsi)
{ {
struct gsm_subscriber *subscr = subscr_alloc(); struct gsm_subscriber *subscr;
strncpy(subscr->imsi, imsi, sizeof(subscr->imsi)); llist_for_each_entry(subscr, &active_subscribers, entry) {
subscr->imsi[sizeof(subscr->imsi)-1] = '\0'; if (strcmp(subscr->imsi, imsi) == 0)
return subscr_get(subscr);
if (db_get_subscriber(GSM_SUBSCRIBER_IMSI, subscr) != 0) {
subscr_free(subscr);
subscr = NULL;
} }
return subscr; return db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
} }
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts) int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts)
{ {
return db_set_subscriber(s); return db_sync_subscriber(s);
}
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
{
subscr->use_count++;
return subscr;
}
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
{
if (--subscr->use_count <= 0)
subscr_free(subscr);
return NULL;
} }

View File

@ -1,8 +1,8 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall AM_CFLAGS=-Wall -ggdb3
noinst_PROGRAMS = db_test noinst_PROGRAMS = db_test
db_test_SOURCES = db_test.c $(top_srcdir)/src/db.c db_test_SOURCES = db_test.c $(top_srcdir)/src/db.c $(top_srcdir)/src/gsm_subscriber.c
db_test_LDADD = -ldl -ldbi db_test_LDADD = -ldl -ldbi

View File

@ -1,4 +1,5 @@
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> /* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved * All Rights Reserved
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -23,6 +24,30 @@
#include <string.h> #include <string.h>
#include <malloc.h> #include <malloc.h>
#define COMPARE(original, copy) \
if (original->id != copy->id) \
fprintf(stderr, "Ids do not match in %s:%d %llu %llu\n", \
__FUNCTION__, __LINE__, original->id, copy->id); \
if (original->lac != copy->lac) \
fprintf(stderr, "LAC do not match in %s:%d %d %d\n", \
__FUNCTION__, __LINE__, original->lac, copy->lac); \
if (original->authorized != copy->authorized) \
fprintf(stderr, "Authorize do not match in %s:%d %d %d\n", \
__FUNCTION__, __LINE__, original->authorized, \
copy->authorized); \
if (strcmp(original->imsi, copy->imsi) != 0) \
fprintf(stderr, "IMSIs do not match in %s:%d '%s' '%s'\n", \
__FUNCTION__, __LINE__, original->imsi, copy->imsi); \
if (strcmp(original->tmsi, copy->tmsi) != 0) \
fprintf(stderr, "TMSIs do not match in %s:%d '%s' '%s'\n", \
__FUNCTION__, __LINE__, original->tmsi, copy->tmsi); \
if (strcmp(original->name, copy->name) != 0) \
fprintf(stderr, "names do not match in %s:%d '%s' '%s'\n", \
__FUNCTION__, __LINE__, original->name, copy->name); \
if (strcmp(original->extension, copy->extension) != 0) \
fprintf(stderr, "names do not match in %s:%d '%s' '%s'\n", \
__FUNCTION__, __LINE__, original->extension, copy->extension); \
int main() { int main() {
if (db_init("hlr.sqlite3")) { if (db_init("hlr.sqlite3")) {
@ -38,28 +63,38 @@ int main() {
printf("DB: Database prepared.\n"); printf("DB: Database prepared.\n");
struct gsm_subscriber *alice = NULL; struct gsm_subscriber *alice = NULL;
struct gsm_subscriber *alice_db;
alice = db_create_subscriber("3243245432345"); char *alice_imsi = "3243245432345";
db_set_subscriber(alice); alice = db_create_subscriber(alice_imsi);
db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice); db_sync_subscriber(alice);
free(alice); alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice->imsi);
COMPARE(alice, alice_db);
subscr_put(alice_db);
subscr_put(alice);
alice = db_create_subscriber("3693245423445"); alice_imsi = "3693245423445";
alice = db_create_subscriber(alice_imsi);
db_subscriber_assoc_imei(alice, "1234567890"); db_subscriber_assoc_imei(alice, "1234567890");
db_subscriber_alloc_tmsi(alice); db_subscriber_alloc_tmsi(alice);
alice->lac=42; alice->lac=42;
db_set_subscriber(alice); db_sync_subscriber(alice);
db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice); alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi);
free(alice); COMPARE(alice, alice_db);
subscr_put(alice);
subscr_put(alice_db);
alice = db_create_subscriber("9993245423445"); alice_imsi = "9993245423445";
alice = db_create_subscriber(alice_imsi);
db_subscriber_alloc_tmsi(alice); db_subscriber_alloc_tmsi(alice);
alice->lac=42; alice->lac=42;
db_set_subscriber(alice); db_sync_subscriber(alice);
db_subscriber_assoc_imei(alice, "1234567890"); db_subscriber_assoc_imei(alice, "1234567890");
db_subscriber_assoc_imei(alice, "6543560920"); db_subscriber_assoc_imei(alice, "6543560920");
db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice); alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi);
free(alice); COMPARE(alice, alice_db);
subscr_put(alice);
subscr_put(alice_db);
db_fini(); db_fini();