more sms storage WIP

This commit is contained in:
Harald Welte 2022-06-05 07:57:56 +02:00
parent 8ec1e3b61d
commit ef712780bc
11 changed files with 362 additions and 191 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;
return !llist_empty(&vsub->sms.pending);
}
#endif
/* 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;
}
/* 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);
sms->id, sms->failed_attempts);
smsq = net->sms_queue;
if (pending->failed_attempts < smsq->cfg->max_fail)
return sms_pending_resend(pending);
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;
struct gsm_sms *sms = sms_subscriber_find_pending(vsub);
/* the subscriber should not be in the queue */
OSMO_ASSERT(!sms_subscriber_is_pending(smsq, vsub));
/* check for more messages for this subscriber */
sms = db_sms_get_unsent_for_subscr(vsub, INT_MAX);
if (!sms)
goto no_pending_sms;
return;
/* 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;
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

View File

@ -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];
ssi->boot_read.dir = opendir(ssi->cfg->storage_dir);
osmo_ctx_init("sms-storage");
osmo_select_init();
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;
}

View File

@ -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",

View File

@ -1,4 +1,5 @@
SUBDIRS = \
sms_storage \
sms_queue \
msc_vlr \
db_sms \

View File

@ -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)

View File

@ -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);
}