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:
parent
67b4b9a017
commit
12aa50d5a2
|
@ -1,4 +1,5 @@
|
|||
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -24,14 +25,16 @@
|
|||
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
|
||||
/* one time initialisation */
|
||||
int db_init(const char *name);
|
||||
int db_prepare();
|
||||
int db_fini();
|
||||
|
||||
struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]);
|
||||
int db_get_subscriber(enum gsm_subscriber_field field, struct gsm_subscriber* subscriber);
|
||||
int db_set_subscriber(struct gsm_subscriber* subscriber);
|
||||
/* subscriber management */
|
||||
struct gsm_subscriber* db_create_subscriber(char *imsi);
|
||||
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_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 */
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
#include "gsm_data.h"
|
||||
#include "linuxlist.h"
|
||||
|
||||
#define GSM_IMEI_LENGTH 17
|
||||
#define GSM_IMSI_LENGTH 17
|
||||
|
@ -18,6 +19,10 @@ struct gsm_subscriber {
|
|||
char name[GSM_NAME_LENGTH];
|
||||
char extension[GSM_EXTENSION_LENGTH];
|
||||
int authorized;
|
||||
|
||||
/* for internal management */
|
||||
int use_count;
|
||||
struct llist_head entry;
|
||||
};
|
||||
|
||||
enum gsm_subscriber_field {
|
||||
|
@ -25,4 +30,8 @@ enum gsm_subscriber_field {
|
|||
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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
lchan->type = GSM_LCHAN_NONE;
|
||||
lchan->subscr = 0;
|
||||
if (lchan->subscr) {
|
||||
subscr_put(lchan->subscr);
|
||||
lchan->subscr = 0;
|
||||
}
|
||||
|
||||
/* stop the timer */
|
||||
del_timer(&lchan->release_timer);
|
||||
|
|
81
src/db.c
81
src/db.c
|
@ -1,4 +1,6 @@
|
|||
/* Simple HLR/VLR database backend using dbi */
|
||||
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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;
|
||||
dbi_conn conn;
|
||||
|
||||
void db__error_func(dbi_conn conn, void* data) {
|
||||
void db_error_func(dbi_conn conn, void* data) {
|
||||
const char* msg;
|
||||
dbi_conn_error(conn, &msg);
|
||||
printf("DBI: %s\n", msg);
|
||||
|
@ -43,7 +45,7 @@ int db_init(const char *name) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
dbi_conn_error_handler( conn, db__error_func, NULL );
|
||||
dbi_conn_error_handler( conn, db_error_func, NULL );
|
||||
|
||||
/* MySQL
|
||||
dbi_conn_set_option(conn, "host", "localhost");
|
||||
|
@ -54,8 +56,8 @@ int db_init(const char *name) {
|
|||
*/
|
||||
|
||||
/* SqLite 3 */
|
||||
char *db_basename = strdup(name);
|
||||
char *db_dirname = strdup(name);
|
||||
db_basename = strdup(name);
|
||||
db_dirname = strdup(name);
|
||||
dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
|
||||
dbi_conn_set_option(conn, "dbname", basename(db_basename));
|
||||
|
||||
|
@ -154,15 +156,13 @@ int db_fini() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]) {
|
||||
struct gsm_subscriber* db_create_subscriber(char *imsi) {
|
||||
dbi_result result;
|
||||
struct gsm_subscriber* subscriber;
|
||||
subscriber = malloc(sizeof(*subscriber));
|
||||
if (!subscriber)
|
||||
return NULL;
|
||||
memset(subscriber, 0, sizeof(*subscriber));
|
||||
strncpy(subscriber->imsi, imsi, GSM_IMSI_LENGTH-1);
|
||||
if (!db_get_subscriber(GSM_SUBSCRIBER_IMSI, subscriber)) {
|
||||
struct gsm_subscriber* subscr;
|
||||
|
||||
/* Is this subscriber known in the db? */
|
||||
subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
|
||||
if (subscr) {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE Subscriber set updated = datetime('now') "
|
||||
"WHERE imsi = %s " , imsi);
|
||||
|
@ -171,8 +171,12 @@ struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]) {
|
|||
} else {
|
||||
dbi_result_free(result);
|
||||
}
|
||||
return subscriber;
|
||||
return subscr;
|
||||
}
|
||||
|
||||
subscr = subscr_alloc();
|
||||
if (!subscr)
|
||||
return NULL;
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT INTO Subscriber "
|
||||
"(imsi, created, updated) "
|
||||
|
@ -183,76 +187,81 @@ struct gsm_subscriber* db_create_subscriber(char imsi[GSM_IMSI_LENGTH]) {
|
|||
if (result==NULL) {
|
||||
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);
|
||||
printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscriber->id, subscriber->imsi);
|
||||
return subscriber;
|
||||
printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
|
||||
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;
|
||||
const char *string;
|
||||
char *quoted;
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
switch (field) {
|
||||
case GSM_SUBSCRIBER_IMSI:
|
||||
dbi_conn_quote_string_copy(conn, subscriber->imsi, "ed);
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
"WHERE imsi = %s ",
|
||||
quoted
|
||||
);
|
||||
free(quoted);
|
||||
break;
|
||||
case GSM_SUBSCRIBER_TMSI:
|
||||
dbi_conn_quote_string_copy(conn, subscriber->tmsi, "ed);
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
"WHERE tmsi = %s ",
|
||||
quoted
|
||||
);
|
||||
free(quoted);
|
||||
break;
|
||||
default:
|
||||
printf("DB: Unknown query selector for Subscriber.\n");
|
||||
return 1;
|
||||
return NULL;
|
||||
}
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Subscriber.\n");
|
||||
return 1;
|
||||
return NULL;
|
||||
}
|
||||
if (!dbi_result_next_row(result)) {
|
||||
printf("DB: Failed to find the Subscriber.\n");
|
||||
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");
|
||||
if (string)
|
||||
strncpy(subscriber->imsi, string, GSM_IMSI_LENGTH);
|
||||
strncpy(subscr->imsi, string, GSM_IMSI_LENGTH);
|
||||
|
||||
string = dbi_result_get_string(result, "tmsi");
|
||||
if (string)
|
||||
strncpy(subscriber->tmsi, string, GSM_TMSI_LENGTH);
|
||||
strncpy(subscr->tmsi, string, GSM_TMSI_LENGTH);
|
||||
|
||||
string = dbi_result_get_string(result, "name");
|
||||
if (string)
|
||||
strncpy(subscriber->name, string, GSM_NAME_LENGTH);
|
||||
strncpy(subscr->name, string, GSM_NAME_LENGTH);
|
||||
|
||||
string = dbi_result_get_string(result, "extension");
|
||||
if (string)
|
||||
strncpy(subscriber->extension, string, GSM_EXTENSION_LENGTH);
|
||||
strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH);
|
||||
|
||||
// FIXME handle extension
|
||||
subscriber->lac = dbi_result_get_uint(result, "lac");
|
||||
subscriber->authorized = dbi_result_get_uint(result, "authorized");
|
||||
subscr->lac = dbi_result_get_uint(result, "lac");
|
||||
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",
|
||||
subscriber->id, subscriber->imsi, subscriber->name, subscriber->tmsi,
|
||||
subscriber->lac, subscriber->authorized);
|
||||
subscr->id, subscr->imsi, subscr->name, subscr->tmsi,
|
||||
subscr->lac, subscr->authorized);
|
||||
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;
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE Subscriber "
|
||||
|
@ -263,6 +272,7 @@ int db_set_subscriber(struct gsm_subscriber* subscriber) {
|
|||
"WHERE imsi = %s ",
|
||||
subscriber->tmsi, subscriber->lac, subscriber->authorized, subscriber->imsi
|
||||
);
|
||||
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to update Subscriber (by IMSI).\n");
|
||||
return 1;
|
||||
|
@ -282,6 +292,7 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
|
|||
"WHERE tmsi = %s ",
|
||||
tmsi_quoted
|
||||
);
|
||||
free(tmsi_quoted);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Subscriber while allocating new TMSI.\n");
|
||||
return 1;
|
||||
|
@ -293,7 +304,7 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
|
|||
if (!dbi_result_next_row(result)) {
|
||||
dbi_result_free(result);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */
|
||||
|
||||
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
|
@ -28,6 +29,9 @@
|
|||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/db.h>
|
||||
|
||||
|
||||
LLIST_HEAD(active_subscribers);
|
||||
|
||||
struct gsm_subscriber *subscr_alloc(void)
|
||||
{
|
||||
struct gsm_subscriber *s;
|
||||
|
@ -37,46 +41,57 @@ struct gsm_subscriber *subscr_alloc(void)
|
|||
return NULL;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
llist_add_tail(&s->entry, &active_subscribers);
|
||||
s->use_count = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void subscr_free(struct gsm_subscriber *subscr)
|
||||
static void subscr_free(struct gsm_subscriber *subscr)
|
||||
{
|
||||
llist_del(&subscr->entry);
|
||||
free(subscr);
|
||||
}
|
||||
|
||||
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));
|
||||
subscr->tmsi[sizeof(subscr->tmsi)-1] = '\0';
|
||||
|
||||
if (db_get_subscriber(GSM_SUBSCRIBER_TMSI, subscr) != 0) {
|
||||
subscr_free(subscr);
|
||||
subscr = NULL;
|
||||
/* we might have a record in memory already */
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
if (strcmp(subscr->tmsi, tmsi) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
return subscr;
|
||||
return db_get_subscriber(GSM_SUBSCRIBER_TMSI, tmsi);
|
||||
}
|
||||
|
||||
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));
|
||||
subscr->imsi[sizeof(subscr->imsi)-1] = '\0';
|
||||
|
||||
if (db_get_subscriber(GSM_SUBSCRIBER_IMSI, subscr) != 0) {
|
||||
subscr_free(subscr);
|
||||
subscr = NULL;
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
if (strcmp(subscr->imsi, imsi) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
return subscr;
|
||||
return db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall
|
||||
AM_CFLAGS=-Wall -ggdb3
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -23,6 +24,30 @@
|
|||
#include <string.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() {
|
||||
|
||||
if (db_init("hlr.sqlite3")) {
|
||||
|
@ -38,28 +63,38 @@ int main() {
|
|||
printf("DB: Database prepared.\n");
|
||||
|
||||
struct gsm_subscriber *alice = NULL;
|
||||
struct gsm_subscriber *alice_db;
|
||||
|
||||
alice = db_create_subscriber("3243245432345");
|
||||
db_set_subscriber(alice);
|
||||
db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice);
|
||||
free(alice);
|
||||
char *alice_imsi = "3243245432345";
|
||||
alice = db_create_subscriber(alice_imsi);
|
||||
db_sync_subscriber(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_alloc_tmsi(alice);
|
||||
alice->lac=42;
|
||||
db_set_subscriber(alice);
|
||||
db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice);
|
||||
free(alice);
|
||||
db_sync_subscriber(alice);
|
||||
alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi);
|
||||
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);
|
||||
alice->lac=42;
|
||||
db_set_subscriber(alice);
|
||||
db_sync_subscriber(alice);
|
||||
db_subscriber_assoc_imei(alice, "1234567890");
|
||||
db_subscriber_assoc_imei(alice, "6543560920");
|
||||
db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice);
|
||||
free(alice);
|
||||
alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi);
|
||||
COMPARE(alice, alice_db);
|
||||
subscr_put(alice);
|
||||
subscr_put(alice_db);
|
||||
|
||||
db_fini();
|
||||
|
||||
|
|
Loading…
Reference in New Issue