libmsc: Track and update the location update expiry
Set the subscriber expiry timeout to twice the duration of the location
update period and provide functions subscr_expire() and
db_subscriber_expire() to mark subscribers offline that have missed two
location update periods.
This patch increases the DB revision to 3, so the hlr will be
incompatible with prior versions.
We should allow 0 for T3212 as well to disable the location update
period. In that case we will need a way to indicate that in the
database.
diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c
index 9a5f18d..4e20e23 100644
--- a/openbsc/src/libmsc/db.c
+++ b/openbsc/src/libmsc/db.c
@@ -42,6 +42,8 @@
static char *db_dirname = NULL;
static dbi_conn conn;
+#define SCHEMA_REVISION "3"
+
static char *create_stmts[] = {
"CREATE TABLE IF NOT EXISTS Meta ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
@@ -51,7 +53,7 @@
"INSERT OR IGNORE INTO Meta "
"(key, value) "
"VALUES "
- "('revision', '2')",
+ "('revision', " SCHEMA_REVISION ")",
"CREATE TABLE IF NOT EXISTS Subscriber ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"created TIMESTAMP NOT NULL, "
@@ -61,7 +63,8 @@
"extension TEXT UNIQUE, "
"authorized INTEGER NOT NULL DEFAULT 0, "
"tmsi TEXT UNIQUE, "
- "lac INTEGER NOT NULL DEFAULT 0"
+ "lac INTEGER NOT NULL DEFAULT 0, "
+ "expire_lu TIMESTAMP DEFAULT NULL"
")",
"CREATE TABLE IF NOT EXISTS AuthToken ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
@@ -158,10 +161,39 @@
LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
}
+static int update_db_revision_2(void)
+{
+ dbi_result result;
+
+ result = dbi_conn_query(conn,
+ "ALTER TABLE Subscriber "
+ "ADD COLUMN expire_lu "
+ "TIMESTAMP DEFAULT NULL");
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR,
+ "Failed to alter table Subscriber (upgrade vom rev 2).\n");
+ return -EINVAL;
+ }
+ dbi_result_free(result);
+
+ result = dbi_conn_query(conn,
+ "UPDATE Meta "
+ "SET value = '3' "
+ "WHERE key = 'revision'");
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR,
+ "Failed set new revision (upgrade vom rev 2).\n");
+ return -EINVAL;
+ }
+ dbi_result_free(result);
+
+ return 0;
+}
+
static int check_db_revision(void)
{
dbi_result result;
- const char *rev;
+ const char *rev_s;
result = dbi_conn_query(conn,
"SELECT value FROM Meta WHERE key='revision'");
@@ -172,11 +204,37 @@
dbi_result_free(result);
return -EINVAL;
}
- rev = dbi_result_get_string(result, "value");
- if (!rev || atoi(rev) != 2) {
+ rev_s = dbi_result_get_string(result, "value");
+ if (!rev_s) {
dbi_result_free(result);
return -EINVAL;
}
+ if (!strcmp(rev_s, "2")) {
+ if (update_db_revision_2()) {
+ LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%s'.\n", rev_s);
+ dbi_result_free(result);
+ return -EINVAL;
+ }
+ } else if (!strcmp(rev_s, SCHEMA_REVISION)) {
+ /* everything is fine */
+ } else {
+ LOGP(DDB, LOGL_FATAL, "Invalid database schema revision '%s'.\n", rev_s);
+ dbi_result_free(result);
+ return -EINVAL;
+ }
+
+ dbi_result_free(result);
+ return 0;
+}
+
+static int db_configure(void)
+{
+ dbi_result result;
+
+ result = dbi_conn_query(conn,
+ "PRAGMA synchronous = FULL");
+ if (!result)
+ return -EINVAL;
dbi_result_free(result);
return 0;
@@ -242,6 +300,8 @@
return -1;
}
+ db_configure();
+
return 0;
}
@@ -575,6 +635,12 @@
strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH);
subscr->lac = dbi_result_get_uint(result, "lac");
+
+ if (!dbi_result_field_is_null(result, "expire_lu"))
+ subscr->expire_lu = dbi_result_get_datetime(result, "expire_lu");
+ else
+ subscr->expire_lu = 0;
+
subscr->authorized = dbi_result_get_uint(result, "authorized");
}
@@ -707,13 +773,15 @@
"extension = %s, "
"authorized = %i, "
"tmsi = %s, "
- "lac = %i "
+ "lac = %i, "
+ "expire_lu = datetime(%i, 'unixepoch') "
"WHERE imsi = %s ",
q_name,
q_extension,
subscriber->authorized,
q_tmsi,
subscriber->lac,
+ subscriber->expire_lu,
subscriber->imsi);
free(q_tmsi);
@@ -776,6 +844,29 @@
return 0;
}
+int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id))
+{
+ dbi_result result;
+
+ result = dbi_conn_query(conn,
+ "SELECT id "
+ "FROM Subscriber "
+ "WHERE lac != 0 AND "
+ "( expire_lu is NULL "
+ "OR expire_lu < datetime('now') ) "
+ "LIMIT 1");
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to get expired subscribers\n");
+ return -EIO;
+ }
+
+ while (dbi_result_next_row(result))
+ callback(priv, dbi_result_get_ulonglong(result, "id"));
+
+ dbi_result_free(result);
+ return 0;
+}
+
int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber)
{
dbi_result result = NULL;