aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2022-06-05 07:57:56 +0200
committerHarald Welte <laforge@osmocom.org>2022-06-05 07:57:56 +0200
commitef712780bcda326157a4e991d2fb2def1bde523f (patch)
tree263233be7bec750da977e7f7d70c7c07af9da888
parent8ec1e3b61d836af23112b9f16cf70a0e5c755f51 (diff)
more sms storage WIPlaforge/nosql
-rw-r--r--configure.ac1
-rw-r--r--include/osmocom/msc/gsm_data.h17
-rw-r--r--include/osmocom/msc/sms_storage.h31
-rw-r--r--src/libmsc/gsm_04_11.c31
-rw-r--r--src/libmsc/msc_vty.c16
-rw-r--r--src/libmsc/sms_queue.c212
-rw-r--r--src/libmsc/sms_storage.c128
-rw-r--r--src/libmsc/smsc_vty.c13
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/sms_storage/Makefile.am54
-rw-r--r--tests/sms_storage/sms_storage_test.c49
11 files changed, 362 insertions, 191 deletions
diff --git a/configure.ac b/configure.ac
index 6e32ae620..e37f434ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -243,6 +243,7 @@ AC_OUTPUT(
tests/atlocal
tests/smpp/Makefile
tests/db_sms/Makefile
+ tests/sms_storage/Makefile
tests/sms_queue/Makefile
tests/msc_vlr/Makefile
tests/sdp_msg/Makefile
diff --git a/include/osmocom/msc/gsm_data.h b/include/osmocom/msc/gsm_data.h
index 465d2f41e..fac1176b5 100644
--- a/include/osmocom/msc/gsm_data.h
+++ b/include/osmocom/msc/gsm_data.h
@@ -280,6 +280,8 @@ enum gsm_sms_source_id {
SMS_SOURCE_SMPP, /* received via SMPP */
};
+extern const struct value_string gsm_sms_source_name[];
+
#define SMS_TEXT_SIZE 256
struct gsm_sms_addr {
@@ -288,8 +290,21 @@ struct gsm_sms_addr {
char addr[21+1];
};
+enum gsm_sms_state {
+ GSM_SMS_ST_ALLOCATED, /* memory allocated */
+ GSM_SMS_ST_STORAGE_PENDING, /* waiting for it to be stored on disk */
+ GSM_SMS_ST_DELIVERY_PENDING,
+ GSM_SMS_ST_PAGING, /* delivery pending, paging started */
+ GSM_SMS_ST_DELIVERING, /* delivery ongoing (after paging succeeded) */
+ GSM_SMS_ST_DELIVERED, /* successfully delivered */
+};
+
+extern const struct value_string gsm_sms_state_name[];
+
struct gsm_sms {
- struct llist_head list;
+ struct llist_head list; /* entry in global list of pending SMS */
+ struct llist_head vsub_list; /* entry in vlr_subscr.sms.pending */
+ enum gsm_sms_state state;
unsigned long long id;
struct vlr_subscr *receiver;
struct gsm_sms_addr src, dst;
diff --git a/include/osmocom/msc/sms_storage.h b/include/osmocom/msc/sms_storage.h
new file mode 100644
index 000000000..a70d9bfa0
--- /dev/null
+++ b/include/osmocom/msc/sms_storage.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <stdbool.h>
+#include <limits.h>
+
+struct sms_storage_inst;
+struct gsm_sms;
+
+
+/* configuration of SMS storage */
+struct sms_storage_cfg {
+ char storage_dir[PATH_MAX+1];
+ /* unlink messages after delivery, or just move them? */
+ bool unlink_delivered;
+ /* unlink messages after expiration, or just move them? */
+ bool unlink_expired;
+};
+
+enum smss_delete_cause {
+ SMSS_DELETE_CAUSE_UNKNOWN,
+ SMSS_DELETE_CAUSE_DELIVERED,
+ SMSS_DELETE_CAUSE_EXPIRED,
+};
+
+
+struct sms_storage_inst *sms_storage_init(void *ctx, const struct sms_storage_cfg *scfg);
+
+int sms_storage_to_disk_req(struct sms_storage_inst *ssi, struct gsm_sms *sms);
+
+int sms_storage_delete_from_disk_req(struct sms_storage_inst *ssi, unsigned long long id,
+ enum smss_delete_cause cause);
diff --git a/src/libmsc/gsm_04_11.c b/src/libmsc/gsm_04_11.c
index 81d58ad71..896270d3b 100644
--- a/src/libmsc/gsm_04_11.c
+++ b/src/libmsc/gsm_04_11.c
@@ -61,6 +61,26 @@
#include "smpp_smsc.h"
#endif
+const struct value_string gsm_sms_source_name[] = {
+ { SMS_SOURCE_UNKNOWN, "UNKNOWN" },
+ { SMS_SOURCE_MS_GSM, "MS-GSM" },
+ { SMS_SOURCE_MS_UMTS, "MS-UMTS" },
+ { SMS_SOURCE_MS_SGS, "MS-SGs" },
+ { SMS_SOURCE_VTY, "VTY" },
+ { SMS_SOURCE_SMPP, "SMPP" },
+ { 0, NULL }
+};
+
+const struct value_string gsm_sms_state_name[] = {
+ { GSM_SMS_ST_ALLOCATED, "ALLOCATED" },
+ { GSM_SMS_ST_STORAGE_PENDING, "STORAGE_PENDING" },
+ { GSM_SMS_ST_DELIVERY_PENDING, "DELIVERY_PENDING" },
+ { GSM_SMS_ST_PAGING, "PAGING" },
+ { GSM_SMS_ST_DELIVERING, "DELIVERING" },
+ { GSM_SMS_ST_DELIVERED, "DELIVERED" },
+ { 0, NULL }
+};
+
void *tall_gsms_ctx;
static pthread_mutex_t tall_sms_mutex;
static uint32_t new_callref = 0x40000001;
@@ -76,12 +96,17 @@ struct gsm_sms *sms_alloc(void)
pthread_mutex_lock(&tall_sms_mutex);
sms = talloc_zero(tall_gsms_ctx, struct gsm_sms);
pthread_mutex_unlock(&tall_sms_mutex);
+ if (sms)
+ sms->state = GSM_SMS_ST_ALLOCATED;
return sms;
}
/* MUST ONLY BE CALLED ON MAIN THREAD */
void sms_free(struct gsm_sms *sms)
{
+ llist_del(&sms->list);
+ llist_del(&sms->vsub_list);
+
/* drop references to subscriber structure */
if (sms->receiver)
vlr_subscr_put(sms->receiver, VSUB_USE_SMS_RECEIVER);
@@ -893,6 +918,10 @@ static int gsm411_rx_rp_ack(struct gsm_trans *trans,
/* mark this SMS as sent in database */
sms_storage_delete_from_disk_req(trans->net->sms_storage, sms->id, SMSS_DELETE_CAUSE_DELIVERED);
+ /* delete from lists before sending signal, as the latter will attempt to send another SMS */
+ llist_del(&sms->list);
+ llist_del(&sms->vsub_list);
+
send_signal(S_SMS_DELIVERED, trans, sms, 0);
if (sms->status_rep_req)
@@ -1192,6 +1221,8 @@ int gsm411_send_sms(struct gsm_network *net,
struct msgb *msg;
int rc;
+ sms->state = GSM_SMS_ST_DELIVERING;
+
/* Allocate a new transaction for MT SMS */
trans = gsm411_alloc_mt_trans(net, vsub);
if (!trans) {
diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c
index 85bba56d6..fc2e195d3 100644
--- a/src/libmsc/msc_vty.c
+++ b/src/libmsc/msc_vty.c
@@ -1180,6 +1180,7 @@ DEFUN(show_subscr_cache, show_subscr_cache_cmd,
return CMD_SUCCESS;
}
+#if 0
DEFUN(sms_send_pend,
sms_send_pend_cmd,
"sms send pending",
@@ -1237,6 +1238,7 @@ DEFUN(sms_delete_expired,
vty_out(vty, "Deleted %llu expired SMS from database%s", num_deleted, VTY_NEWLINE);
return CMD_SUCCESS;
}
+#endif
static int _send_sms_str(struct vlr_subscr *receiver,
const char *sender_msisdn,
@@ -1255,15 +1257,13 @@ static int _send_sms_str(struct vlr_subscr *receiver,
sms->source = SMS_SOURCE_VTY;
/* store in database for the queue */
- if (db_sms_store(sms) != 0) {
+ if (sms_storage_to_disk_req(net->sms_storage, sms) != 0) {
LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
sms_free(sms);
return CMD_WARNING;
}
LOGP(DLSMS, LOGL_DEBUG, "SMS stored in DB\n");
- sms_free(sms);
- sms_queue_trigger(net->sms_queue);
return CMD_SUCCESS;
}
@@ -1333,6 +1333,7 @@ DEFUN_DEPRECATED(subscriber_create, subscriber_create_cmd,
return CMD_WARNING;
}
+#if 0
DEFUN(subscriber_send_pending_sms,
subscriber_send_pending_sms_cmd,
"subscriber " SUBSCR_TYPES " ID sms pending-send",
@@ -1380,6 +1381,7 @@ DEFUN(subscriber_sms_delete_all,
return CMD_SUCCESS;
}
+#endif
DEFUN(subscriber_send_sms,
subscriber_send_sms_cmd,
@@ -2085,8 +2087,8 @@ void msc_vty_init(struct gsm_network *msc_network)
install_element_ve(&show_msc_transaction_cmd);
install_element_ve(&show_nri_cmd);
- install_element_ve(&sms_send_pend_cmd);
- install_element_ve(&sms_delete_expired_cmd);
+ //install_element_ve(&sms_send_pend_cmd);
+ //install_element_ve(&sms_delete_expired_cmd);
install_element_ve(&subscriber_create_cmd);
install_element_ve(&subscriber_send_sms_cmd);
@@ -2101,8 +2103,8 @@ void msc_vty_init(struct gsm_network *msc_network)
install_element_ve(&logging_fltr_imsi_cmd);
install_element(ENABLE_NODE, &ena_subscr_expire_cmd);
- install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd);
- install_element(ENABLE_NODE, &subscriber_sms_delete_all_cmd);
+ //install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd);
+ //install_element(ENABLE_NODE, &subscriber_sms_delete_all_cmd);
install_element(CONFIG_NODE, &cfg_mncc_int_cmd);
install_node(&mncc_int_node, config_write_mncc_int);
diff --git a/src/libmsc/sms_queue.c b/src/libmsc/sms_queue.c
index 2f5186438..05588c5de 100644
--- a/src/libmsc/sms_queue.c
+++ b/src/libmsc/sms_queue.c
@@ -106,28 +106,11 @@ static const struct rate_ctr_group_desc smsq_ctrg_desc = {
osmo_stat_item_set(osmo_stat_item_group_get_item((smsq)->statg, idx), val)
-/* One in-RAM record of a "pending SMS". This is not the SMS itself, but merely
- * a pointer to the database record. It holds a reference on the vlr_subscriber
- * and some counters. While this object exists in RAM, we are regularly attempting
- * to deliver the related SMS. */
-#if 0
-struct gsm_sms_pending {
- struct llist_head entry; /* gsm_sms_queue.pending_sms */
-
- struct vlr_subscr *vsub; /* destination subscriber for this SMS */
- struct msc_a *msc_a; /* MSC_A associated with this SMS */
- unsigned long long sms_id; /* unique ID (in SQL database) of this SMS */
- int failed_attempts; /* count of failed deliver attempts so far */
- int resend; /* should we try re-sending it (now) ? */
-};
-#endif
-
/* (global) state of the SMS queue. */
struct gsm_sms_queue {
struct osmo_timer_list resend_pending; /* timer triggering sms_resend_pending() */
struct osmo_timer_list push_queue; /* timer triggering sms_submit_pending() */
struct gsm_network *network;
- struct llist_head pending_sms; /* list of gsm_sms_pending */
struct sms_queue_config *cfg;
int pending; /* current number of gsm_sms_pending in RAM */
@@ -149,6 +132,7 @@ static void _gsm411_send_sms(struct gsm_network *net, struct vlr_subscr *vsub, s
static int sms_subscr_cb(unsigned int, unsigned int, void *, void *);
static int sms_sms_cb(unsigned int, unsigned int, void *, void *);
+#if 0
/* look-up a 'gsm_sms_pending' for the given sms_id; return NULL if none */
static struct gsm_sms_pending *sms_find_pending(struct gsm_sms_queue *smsq,
unsigned long long sms_id)
@@ -168,94 +152,42 @@ int sms_queue_sms_is_pending(struct gsm_sms_queue *smsq, unsigned long long sms_
{
return sms_find_pending(smsq, sms_id) != NULL;
}
+#endif
/* find the first pending SMS (in RAM) for the given subscriber */
-static struct gsm_sms_pending *sms_subscriber_find_pending(
- struct gsm_sms_queue *smsq,
- struct vlr_subscr *vsub)
+static struct gsm_sms *sms_subscriber_find_pending(struct vlr_subscr *vsub)
{
- struct gsm_sms_pending *pending;
+ struct gsm_sms *sms;
- llist_for_each_entry(pending, &smsq->pending_sms, entry) {
- if (pending->vsub == vsub)
- return pending;
+ llist_for_each_entry(sms, &vsub->sms.pending, vsub_list) {
+ if (sms->state == GSM_SMS_ST_DELIVERY_PENDING)
+ return sms;
}
return NULL;
}
+#if 0
/* do we have any pending SMS (in RAM) for the given subscriber? */
static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq,
struct vlr_subscr *vsub)
{
- return sms_subscriber_find_pending(smsq, vsub) != NULL;
-}
-
-/* allocate a new gsm_sms_pending record and fill it with information from 'sms' */
-static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq,
- struct gsm_sms *sms)
-{
- struct gsm_sms_pending *pending;
-
- pending = talloc_zero(smsq, struct gsm_sms_pending);
- if (!pending)
- return NULL;
-
- vlr_subscr_get(sms->receiver, VSUB_USE_SMS_PENDING);
- pending->vsub = sms->receiver;
- pending->sms_id = sms->id;
- llist_add_tail(&pending->entry, &smsq->pending_sms);
-
- smsq->pending += 1;
- smsq_stat_item_inc(smsq, SMSQ_STAT_SMS_RAM_PENDING);
-
- return pending;
+ return !llist_empty(&vsub->sms.pending);
}
+#endif
-/* release a gsm_sms_pending object */
-static void sms_pending_free(struct gsm_sms_queue *smsq, struct gsm_sms_pending *pending)
-{
- smsq->pending -= 1;
- smsq_stat_item_dec(smsq, SMSQ_STAT_SMS_RAM_PENDING);
- vlr_subscr_put(pending->vsub, VSUB_USE_SMS_PENDING);
- llist_del(&pending->entry);
- talloc_free(pending);
-}
-
-/* this sets the 'resend' flag of the gsm_sms_pending and schedules
- * the timer for re-sending */
-static void sms_pending_resend(struct gsm_sms_pending *pending)
-{
- struct gsm_network *net = pending->vsub->vlr->user_ctx;
- struct gsm_sms_queue *smsq;
- LOGP(DLSMS, LOGL_DEBUG,
- "Scheduling resend of SMS %llu.\n", pending->sms_id);
-
- pending->resend = 1;
-
- smsq = net->sms_queue;
- if (osmo_timer_pending(&smsq->resend_pending))
- return;
-
- osmo_timer_schedule(&smsq->resend_pending, 1, 0);
-}
/* call-back when a pending SMS has failed; try another re-send if number of
* attempts is < smsq->max_fail */
-static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error)
+static void sms_pending_failed(struct gsm_sms_queue *smsq, struct gsm_sms *sms, int paging_error)
{
- struct gsm_network *net = pending->vsub->vlr->user_ctx;
- struct gsm_sms_queue *smsq;
-
- pending->failed_attempts++;
+ sms->failed_attempts++;
+ sms->state = GSM_SMS_ST_DELIVERY_PENDING;
LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n",
- pending->sms_id, pending->failed_attempts);
-
- smsq = net->sms_queue;
- if (pending->failed_attempts < smsq->cfg->max_fail)
- return sms_pending_resend(pending);
+ sms->id, sms->failed_attempts);
- sms_pending_free(smsq, pending);
+ if (sms->failed_attempts >= smsq->cfg->max_fail)
+ sms_free(sms);
}
/* Resend all SMS that are scheduled for a resend. This is done to
@@ -264,27 +196,22 @@ static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error
* DB and attempts to send them via _gsm411_send_sms() */
static void sms_resend_pending(void *_data)
{
- struct gsm_sms_pending *pending, *tmp;
+#if 0
+ struct gsm_sms *sms, *tmp;
struct gsm_sms_queue *smsq = _data;
- llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
- struct gsm_sms *sms;
+ llist_for_each_entry_safe(sms, tmp, &smsq->pending_sms, list) {
if (!pending->resend)
continue;
- sms = db_sms_get(smsq->network, pending->sms_id);
-
- /* the sms is gone? Move to the next */
- if (!sms) {
- sms_pending_free(smsq, pending);
- sms_queue_trigger(smsq);
- } else {
- pending->resend = 0;
- _gsm411_send_sms(smsq->network, sms->receiver, sms);
- }
+ /* FIXME: limit the number of concurrent attempted SMS */
+ /* FIXME: have some per-sms state to avoid sending the same twice */
+ _gsm411_send_sms(smsq->network, sms->receiver, sms);
}
+#endif
}
+#if 0
/* Find the next pending SMS by cycling through the recipients. We could also
* cycle through the pending SMS, but that might cause us to keep trying to
* send SMS to the same few subscribers repeatedly while not servicing other
@@ -335,6 +262,7 @@ struct gsm_sms *smsq_take_next_sms(struct gsm_network *net,
DEBUGP(DLSMS, "SMS queue: no SMS to be sent\n");
return NULL;
}
+#endif
/* read up to 'max_pending' pending SMS from the database and add them to the in-memory
* sms_queue; trigger the first delivery attempt. 'submit' in this context means
@@ -342,6 +270,7 @@ struct gsm_sms *smsq_take_next_sms(struct gsm_network *net,
* confused with the SMS SUBMIT operation a MS performs when sending a MO-SMS. */
static void sms_submit_pending(void *_data)
{
+#if 0
struct gsm_sms_queue *smsq = _data;
int attempts = smsq->cfg->max_pending - smsq->pending;
int initialized = 0;
@@ -415,45 +344,19 @@ static void sms_submit_pending(void *_data)
} while (attempted < attempts && rounds < 1000);
LOGP(DLSMS, LOGL_DEBUG, "SMSqueue added %d messages in %d rounds\n", attempted, rounds);
+#endif
}
-/* obtain the next pending SMS for given subscriber from database,
- * create gsm_sms_pending object and attempt first delivery. If there
- * are no SMS pending for the given subscriber, call sms_submit_pending()
- * to read more SMS (for any subscriber) into the in-RAM pending queue */
+/* obtain the next pending SMS for given subscriber and attempt to deliver it. */
static void sms_send_next(struct vlr_subscr *vsub)
{
struct gsm_network *net = vsub->vlr->user_ctx;
- struct gsm_sms_queue *smsq = net->sms_queue;
- struct gsm_sms_pending *pending;
- struct gsm_sms *sms;
-
- /* the subscriber should not be in the queue */
- OSMO_ASSERT(!sms_subscriber_is_pending(smsq, vsub));
+ struct gsm_sms *sms = sms_subscriber_find_pending(vsub);
- /* check for more messages for this subscriber */
- sms = db_sms_get_unsent_for_subscr(vsub, INT_MAX);
if (!sms)
- goto no_pending_sms;
-
- /* The sms should not be scheduled right now */
- OSMO_ASSERT(!sms_queue_sms_is_pending(smsq, sms->id));
-
- /* Remember that we deliver this SMS and send it */
- pending = sms_pending_from(smsq, sms);
- if (!pending) {
- LOGP(DLSMS, LOGL_ERROR,
- "Failed to create pending SMS entry.\n");
- sms_free(sms);
- goto no_pending_sms;
- }
-
- _gsm411_send_sms(smsq->network, sms->receiver, sms);
- return;
+ return;
-no_pending_sms:
- /* Try to send the SMS to avoid the queue being stuck */
- sms_submit_pending(net->sms_queue);
+ _gsm411_send_sms(net, sms->receiver, sms);
}
/* Trigger a call to sms_submit_pending() in one second */
@@ -504,7 +407,7 @@ int sms_queue_start(struct gsm_network *network)
goto err_statg;
network->sms_queue = sms;
- INIT_LLIST_HEAD(&sms->pending_sms);
+ //INIT_LLIST_HEAD(&sms->pending_sms);
sms->network = network;
osmo_timer_setup(&sms->push_queue, sms_submit_pending, sms);
osmo_timer_setup(&sms->resend_pending, sms_resend_pending, sms);
@@ -512,6 +415,7 @@ int sms_queue_start(struct gsm_network *network)
osmo_signal_register_handler(SS_SUBSCR, sms_subscr_cb, network);
osmo_signal_register_handler(SS_SMS, sms_sms_cb, network);
+#if 0
if (db_init(sms, sms->cfg->db_file_path, true)) {
LOGP(DMSC, LOGL_FATAL, "DB: Failed to init database: %s\n",
osmo_quote_str(sms->cfg->db_file_path, -1));
@@ -522,6 +426,7 @@ int sms_queue_start(struct gsm_network *network)
LOGP(DMSC, LOGL_FATAL, "DB: Failed to prepare database.\n");
return -1;
}
+#endif
sms_submit_pending(sms);
@@ -538,9 +443,6 @@ err_free:
/* call-back: Given subscriber is now ready for short messages. */
static int sub_ready_for_sm(struct gsm_network *net, struct vlr_subscr *vsub)
{
- struct gsm_sms *sms;
- struct gsm_sms_pending *pending;
-
/*
* The code used to be very clever and tried to submit
* a SMS during the Location Updating Request. This has
@@ -555,21 +457,8 @@ static int sub_ready_for_sm(struct gsm_network *net, struct vlr_subscr *vsub)
* We need to be careful in what we try here.
*/
- /* check if we have pending requests */
- pending = sms_subscriber_find_pending(net->sms_queue, vsub);
- if (pending) {
- LOGP(DMSC, LOGL_NOTICE,
- "Pending paging while subscriber %llu attached.\n",
- vsub->id);
- return 0;
- }
-
- /* Now try to deliver any pending SMS to this sub */
- sms = db_sms_get_unsent_for_subscr(vsub, INT_MAX);
- if (!sms)
- return -1;
-
- _gsm411_send_sms(net, vsub, sms);
+ /* check if we have pending SMS + send the first of them */
+ sms_send_next(vsub);
return 0;
}
@@ -593,8 +482,6 @@ static int sms_sms_cb(unsigned int subsys, unsigned int signal,
struct gsm_network *network = handler_data;
struct gsm_sms_queue *smq = network->sms_queue;
struct sms_signal_data *sig_sms = signal_data;
- struct gsm_sms_pending *pending;
- struct vlr_subscr *vsub;
/* We got a new SMS and maybe should launch the queue again. */
if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) {
@@ -607,28 +494,16 @@ static int sms_sms_cb(unsigned int subsys, unsigned int signal,
return -1;
- /*
- * Find the entry of our queue. The SMS subsystem will submit
- * sms that are not in our control as we just have a channel
- * open anyway.
- */
- pending = sms_find_pending(smq, sig_sms->sms->id);
- if (!pending)
- return 0;
-
switch (signal) {
case S_SMS_DELIVERED:
+ /* core SMS code has already requested deletion from storage */
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_ACK);
- /* ask SMS thread to delete message from storage */
- ms_storage_delete_from_disk_req(network->sms_storage, sms->id,
- SMSS_DELETE_CAUSE_DELIVERED);
- sms_free(network->sms_storage, sms);
- /* Attempt to send another SMS to this subscriber */
- sms_send_next(vsub);
+ /* Attempt to send another SMS (if any pending) to this subscriber */
+ sms_send_next(sig_sms->sms->receiver);
break;
case S_SMS_MEM_EXCEEDED:
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_NOMEM);
- sms_pending_free(smq, pending);
+ /* TODO: set flag for subscriber in VLR; skip delivery until cleared */
sms_queue_trigger(smq);
break;
case S_SMS_UNKNOWN_ERROR:
@@ -645,11 +520,10 @@ static int sms_sms_cb(unsigned int subsys, unsigned int signal,
if (sig_sms->paging_result) {
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_ERR);
/* BAD SMS? */
- db_sms_inc_deliver_attempts(sig_sms->sms);
- sms_pending_failed(pending, 0);
+ sms_pending_failed(smq, sig_sms->sms, 0);
} else {
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_TIMEOUT);
- sms_pending_failed(pending, 1);
+ sms_pending_failed(smq, sig_sms->sms, 1);
}
break;
default:
@@ -660,6 +534,7 @@ static int sms_sms_cb(unsigned int subsys, unsigned int signal,
return 0;
}
+#if 0
/* VTY helper functions */
int sms_queue_stats(struct gsm_sms_queue *smsq, struct vty *vty)
{
@@ -687,3 +562,4 @@ int sms_queue_clear(struct gsm_sms_queue *smsq)
return 0;
}
+#endif
diff --git a/src/libmsc/sms_storage.c b/src/libmsc/sms_storage.c
index 7c9578da8..0d3a80ee6 100644
--- a/src/libmsc/sms_storage.c
+++ b/src/libmsc/sms_storage.c
@@ -83,10 +83,11 @@
#include <osmocom/msc/gsm_data.h>
#include <osmocom/msc/gsm_04_11.h>
#include <osmocom/msc/sms_storage.h>
+#include <osmocom/msc/vlr.h>
/* all the state of a SMS storage instance */
struct sms_storage_inst {
- struct sms_storage_cfg *cfg;
+ const struct sms_storage_cfg *cfg;
pthread_t thread;
struct {
@@ -102,6 +103,10 @@ struct sms_storage_inst {
int wd;
} inotify;
#endif
+
+ /* global list of penidng SMSs */
+ struct llist_head pending;
+
/* inter-thread message queues for both directions */
struct {
struct osmo_it_q *itq;
@@ -226,7 +231,7 @@ static int _sms_gen_fq_path(struct sms_storage_inst *ssi, char *fq_path, size_t
int rc;
rc = snprintf(fq_path, fq_path_len, "%s/%s/%llu.osms", ssi->cfg->storage_dir, subdir, id);
- if (rc >= sizeof(fq_path)) {
+ if (rc >= fq_path_len) {
LOGP(DSMSS, LOGL_ERROR, "Overflowing buffer while composing file path\n");
return -EINVAL;
}
@@ -677,6 +682,9 @@ static void boot_read_tmr_cb(void *data)
/* skip anything that's not a normal file */
if (dent->d_type != DT_REG) {
+ /* suppress printing log messages about . and .. */
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+ goto next;
LOGP(DSMSS, LOGL_NOTICE, "bootstrap read: skipping '%s' (not a regular file)\n",
dent->d_name);
goto next;
@@ -710,6 +718,8 @@ static void boot_read_tmr_cb(void *data)
if (rc < 0)
s2m_free(ssi, evt);
+ ssi->boot_read.count++;
+
next:
/* read next message in 50ms to avoid overloading the it_q or the MSC in general */
osmo_timer_schedule(&ssi->boot_read.timer, 0, 50000);
@@ -719,8 +729,13 @@ next:
static void *sms_storage_main(void *arg)
{
struct sms_storage_inst *ssi = arg;
+ char current_dir[PATH_MAX+8+1];
+
+ osmo_ctx_init("sms-storage");
+ osmo_select_init();
- ssi->boot_read.dir = opendir(ssi->cfg->storage_dir);
+ snprintf(current_dir, sizeof(current_dir), "%s/%s", ssi->cfg->storage_dir, SUBDIR_CURRENT);
+ ssi->boot_read.dir = opendir(current_dir);
if (!ssi->boot_read.dir) {
LOGP(DSMSS, LOGL_ERROR, "Cannot open SMS directory '%s': %s\n",
ssi->cfg->storage_dir, strerror(errno));
@@ -753,18 +768,34 @@ static void storage2main_read_cb(struct osmo_it_q *q, struct llist_head *item)
{
struct smss_s2m_evt *evt = container_of(item, struct smss_s2m_evt, list);
struct sms_storage_inst *ssi = q->data;
+ struct gsm_sms *sms = NULL;
switch (evt->op) {
case SMSS_S2M_OP_NULL:
break;
case SMSS_S2M_OP_SMS_FROM_DISK_IND:
/* SMS storage has read a SMS from disk, asks main thread to add it to queue */
+ sms = evt->sms_from_disk_ind.sms;
+ sms->state = GSM_SMS_ST_DELIVERY_PENDING;
+ /* add to global list of pending SMS */
+ llist_add_tail(&sms->list, &ssi->pending);
+ /* add to per-subscriber list of pending SMS */
+ if (sms->receiver)
+ llist_add_tail(&sms->vsub_list, &sms->receiver->sms.pending);
break;
case SMSS_S2M_OP_SMS_TO_DISK_CFM:
/* SMS storage confirms having written SMS to disk; main thread adds it to queue */
+ sms = evt->sms_to_disk_cfm.sms;
+ sms->state = GSM_SMS_ST_DELIVERY_PENDING;
+ /* add to global list of pending SMS */
+ llist_add_tail(&sms->list, &ssi->pending);
+ /* add to per-subscriber list of pending SMS */
+ if (sms->receiver)
+ llist_add_tail(&sms->vsub_list, &sms->receiver->sms.pending);
break;
case SMSS_S2M_OP_SMS_DELETED_ON_DISK_IND:
/* SMS storage has detected a sms was deleted from disk; main thread must forget it */
+ sms_free(sms);
break;
default:
break;
@@ -778,16 +809,19 @@ static void storage2main_read_cb(struct osmo_it_q *q, struct llist_head *item)
int sms_storage_to_disk_req(struct sms_storage_inst *ssi, struct gsm_sms *sms)
{
struct smss_m2s_evt *evt = m2s_alloc(ssi, SMSS_M2S_OP_SMS_TO_DISK_REQ);
+ enum gsm_sms_state st = sms->state;
int rc;
if (!evt)
return -ENOMEM;
+ sms->state = GSM_SMS_ST_STORAGE_PENDING;
evt->sms_to_disk_req.sms = sms;
rc = osmo_it_q_enqueue(ssi->main2storage.itq, evt, list);
if (rc < 0) {
m2s_free(ssi, evt);
+ sms->state = st;
return rc;
}
return 0;
@@ -819,31 +853,97 @@ int sms_storage_delete_from_disk_req(struct sms_storage_inst *ssi, unsigned long
* Initialization
***********************************************************************/
-int sms_storage_init(void *ctx, struct sms_storage_cfg *scfg)
+static int sms_storage_ensure_subdir(const struct sms_storage_cfg *scfg, const char *subdir)
+{
+ char sub_dir[PATH_MAX+8+1];
+ struct stat st;
+ int rc;
+
+ snprintf(sub_dir, sizeof(sub_dir), "%s/%s", scfg->storage_dir, subdir);
+
+ rc = stat(sub_dir, &st);
+ if (rc < 0) {
+ if (errno == ENOENT) {
+ LOGP(DSMSS, LOGL_NOTICE, "SMS storage sub-dir '%s' doesn't exist, attempting to "
+ "create it\n", sub_dir);
+ if (mkdir(sub_dir, 0700) != 0) {
+ LOGP(DSMSS, LOGL_ERROR, "Unable to create SMS storage sub-dir '%s': %s\n",
+ sub_dir, strerror(errno));
+ return -errno;
+ }
+ } else {
+ LOGP(DSMSS, LOGL_ERROR, "Unable to access SMS storage sub-dir '%s': %s\n",
+ sub_dir, strerror(errno));
+ return -errno;
+ }
+ }
+ /* TODO: test if we can write */
+
+ return 0;
+}
+
+static int sms_storage_ensure_subdirs(const struct sms_storage_cfg *scfg)
+{
+ int rc;
+
+ rc = sms_storage_ensure_subdir(scfg, SUBDIR_CURRENT);
+ if (rc < 0)
+ return rc;
+
+ rc = sms_storage_ensure_subdir(scfg, SUBDIR_DELIVERED);
+ if (rc < 0)
+ return rc;
+
+ rc = sms_storage_ensure_subdir(scfg, SUBDIR_EXPIRED);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+
+struct sms_storage_inst *sms_storage_init(void *ctx, const struct sms_storage_cfg *scfg)
{
struct sms_storage_inst *ssi = talloc_zero(ctx, struct sms_storage_inst);
struct stat st;
- int rc, ret = -1;
+ int rc;
if (!ssi)
- return -ENOMEM;
+ return NULL;
- /* test if scfq->storage_dir exists */
+ ssi->cfg = scfg;
+ INIT_LLIST_HEAD(&ssi->pending);
+
+ /* test if scfg->storage_dir exists */
rc = stat(scfg->storage_dir, &st);
if (rc < 0) {
- LOGP(DSMSS, LOGL_ERROR, "Unable to access storage path '%s': %s\n",
- scfg->storage_dir, strerror(errno));
- return -errno;
+ if (errno == ENOENT) {
+ LOGP(DSMSS, LOGL_NOTICE, "SMS storage path '%s' doesn't exist, attempting to "
+ "create it\n", scfg->storage_dir);
+ if (mkdir(scfg->storage_dir, 0700) != 0) {
+ LOGP(DSMSS, LOGL_ERROR, "Unable to create SMS storage dir '%s': %s\n",
+ scfg->storage_dir, strerror(errno));
+ return NULL;
+ }
+ } else {
+ LOGP(DSMSS, LOGL_ERROR, "Unable to access storage path '%s': %s\n",
+ scfg->storage_dir, strerror(errno));
+ return NULL;
+ }
}
/* TODO: test if we can write */
+ rc = sms_storage_ensure_subdirs(scfg);
+ if (rc < 0)
+ goto out_free;
+
ssi->main2storage.itq = osmo_it_q_alloc(ssi, "sms_main2storage", 1000, main2storage_read_cb, ssi);
if (!ssi->main2storage.itq)
goto out_free;
pthread_mutex_init(&ssi->main2storage.ctx_mutex, NULL);
ssi->storage2main.itq = osmo_it_q_alloc(ssi, "sms_storage2main", 1000, storage2main_read_cb, ssi);
- if (!ssi->main2storage.itq)
+ if (!ssi->storage2main.itq)
goto out_main2storage;
pthread_mutex_init(&ssi->storage2main.ctx_mutex, NULL);
@@ -858,7 +958,6 @@ int sms_storage_init(void *ctx, struct sms_storage_cfg *scfg)
if (inotify_fd < 0) {
LOGP(DSMSS, LOGL_ERROR, "Error during inotify_init(): %s\n", strerror(errno));
- ret = inotify_fd;
goto out_m2s_unreg;
}
/* just setup, don't register. We later register this in the storage thread! */
@@ -869,7 +968,6 @@ int sms_storage_init(void *ctx, struct sms_storage_cfg *scfg)
if (rc < 0) {
LOGP(DSMSS, LOGL_ERROR, "Cannot add inotify watcher for '%s': %s\n",
current_dir, strerror(errno));
- ret = -errno;
goto out_close_inotify;
}
ssi->inotify.wd = rc;
@@ -880,7 +978,7 @@ int sms_storage_init(void *ctx, struct sms_storage_cfg *scfg)
goto out_all;
}
- return 0;
+ return ssi;
out_all:
#ifdef HAVE_INOTIFY
@@ -896,5 +994,5 @@ out_main2storage:
out_free:
talloc_free(ssi);
- return ret;
+ return NULL;
}
diff --git a/src/libmsc/smsc_vty.c b/src/libmsc/smsc_vty.c
index e99b23657..a6212b366 100644
--- a/src/libmsc/smsc_vty.c
+++ b/src/libmsc/smsc_vty.c
@@ -117,6 +117,19 @@ DEFUN(cfg_sms_def_val_per, cfg_sms_def_val_per_cmd,
* View / Enable Node
***********************************************************************/
+void vty_out_sms(struct vty *vty, const struct gsm_sms *sms)
+{
+ vty_out(vty, "SMS ID: %llu, State: %s, Source: %s, %s -> %s %s", sms->id,
+ get_value_string(gsm_sms_state_name, sms->state),
+ get_value_string(gsm_sms_source_name, sms->source),
+ sms->src.addr, sms->dst.addr, VTY_NEWLINE);
+ vty_out(vty, " Created: %s, Validity Minutes: %lu, Failed Attempts: %d%s",
+ ctime(&sms->created), sms->validity_minutes, sms->failed_attempts, VTY_NEWLINE);
+ vty_out(vty, " PID: 0x%02x, DCS: 0x%02x, MsgRef: 0x%02x, UDHI: %d, IsReport: %d%s",
+ sms->protocol_id, sms->data_coding_scheme, sms->msg_ref, sms->ud_hdr_ind, sms->is_report,
+ VTY_NEWLINE);
+}
+
DEFUN(show_smsqueue,
show_smsqueue_cmd,
"show sms-queue",
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d25fe16ec..3a02ee51b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,5 @@
SUBDIRS = \
+ sms_storage \
sms_queue \
msc_vlr \
db_sms \
diff --git a/tests/sms_storage/Makefile.am b/tests/sms_storage/Makefile.am
new file mode 100644
index 000000000..9befdb619
--- /dev/null
+++ b/tests/sms_storage/Makefile.am
@@ -0,0 +1,54 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ -ggdb3 \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOVTY_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
+ $(LIBOSMORANAP_CFLAGS) \
+ $(LIBASN1C_CFLAGS) \
+ $(LIBOSMOMGCPCLIENT_CFLAGS) \
+ $(LIBOSMOGSUPCLIENT_CFLAGS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ sms_storage_test.ok \
+ sms_storage_test.err \
+ $(NULL)
+
+check_PROGRAMS = \
+ sms_storage_test \
+ $(NULL)
+
+sms_storage_test_SOURCES = \
+ sms_storage_test.c \
+ $(NULL)
+
+sms_storage_test_LDADD = \
+ -lsctp \
+ $(top_builddir)/src/libmsc/libmsc.a \
+ $(top_builddir)/src/libvlr/libvlr.a \
+ $(LIBSMPP34_LIBS) \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
+ $(LIBOSMONETIF_LIBS) \
+ $(LIBOSMOSIGTRAN_LIBS) \
+ $(LIBOSMORANAP_LIBS) \
+ $(LIBASN1C_LIBS) \
+ $(LIBOSMOMGCPCLIENT_LIBS) \
+ $(LIBOSMOGSUPCLIENT_LIBS) \
+ $(LIBRARY_GSM) \
+ $(NULL)
+
+sms_storage_test_LDFLAGS = \
+ -Wl,--wrap=db_sms_get_next_unsent_rr_msisdn \
+ $(NULL)
diff --git a/tests/sms_storage/sms_storage_test.c b/tests/sms_storage/sms_storage_test.c
new file mode 100644
index 000000000..d545dc13c
--- /dev/null
+++ b/tests/sms_storage/sms_storage_test.c
@@ -0,0 +1,49 @@
+#include <unistd.h>
+
+#include <osmocom/core/utils.h>
+
+#include <osmocom/msc/gsm_data.h>
+#include <osmocom/msc/gsm_04_11.h>
+#include <osmocom/msc/sms_storage.h>
+
+static const struct sms_storage_cfg scfg = {
+ .storage_dir = "/tmp/sms_storage",
+ .unlink_delivered = false,
+ .unlink_expired = false,
+};
+static struct sms_storage_inst *g_ssi;
+
+static struct gsm_sms *generate_sms(unsigned long long id, const char *src, const char *dst,
+ uint8_t pid, uint8_t dcs, uint8_t msg_ref)
+{
+ struct gsm_sms *sms = sms_alloc();
+ OSMO_ASSERT(sms);
+
+ sms->id = id;
+ OSMO_STRLCPY_ARRAY(sms->src.addr, src);
+ OSMO_STRLCPY_ARRAY(sms->dst.addr, dst);
+ sms->protocol_id = pid;
+ sms->data_coding_scheme = dcs;
+ sms->msg_ref = msg_ref;
+
+ return sms;
+}
+
+static void to_storage(void)
+{
+ struct gsm_sms *sms = generate_sms(1234, "1111", "2222", 1, 2, 3);
+ sms_storage_to_disk_req(g_ssi, sms);
+ sms_storage_delete_from_disk_req(g_ssi, sms->id, SMSS_DELETE_CAUSE_DELIVERED);
+}
+
+int main(int argc, char **argv)
+{
+ void *ctx = NULL;
+
+ g_ssi = sms_storage_init(ctx, &scfg);
+
+ to_storage();
+
+ usleep(10000000);
+}
+