statistics: Introduce 'struct counter' instead of using unsigned long

This has the advantage that counters can be added all over the code
very easily, while having only one routine that stores all of the
current counter values to the database.  The counters are synced
every 60 seconds, providing relatively fine grained statistics
about the network usage as time passes by.
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 27c88b7..b476025 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -5,4 +5,4 @@
 		 gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
 		 bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h \
 		 silent_call.h mgcp.h meas_rep.h bitvec.h rest_octets.h \
-		 system_information.h handover.h
+		 system_information.h handover.h statistics.h
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index 0713593..fca7364 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -53,4 +53,8 @@
 int db_apdu_blob_store(struct gsm_subscriber *subscr, 
 			u_int8_t apdu_id_flags, u_int8_t len,
 			u_int8_t *apdu);
+
+/* Statistics counter storage */
+int db_store_counter(struct counter *ctr);
+
 #endif /* _DB_H */
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 74e1938..c2eec41 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -61,6 +61,7 @@
 #include <openbsc/mncc.h>
 #include <openbsc/tlv.h>
 #include <openbsc/bitvec.h>
+#include <openbsc/statistics.h>
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
@@ -460,43 +461,43 @@
 /* Some statistics of our network */
 struct gsmnet_stats {
 	struct {
-		unsigned long total;
-		unsigned long no_channel;
+		struct counter *total;
+		struct counter *no_channel;
 	} chreq;
 	struct {
-		unsigned long attempted;
-		unsigned long no_channel;	/* no channel available */
-		unsigned long timeout;		/* T3103 timeout */
-		unsigned long completed;	/* HO COMPL received */
-		unsigned long failed;		/* HO FAIL received */
+		struct counter *attempted;
+		struct counter *no_channel;	/* no channel available */
+		struct counter *timeout;		/* T3103 timeout */
+		struct counter *completed;	/* HO COMPL received */
+		struct counter *failed;		/* HO FAIL received */
 	} handover;
 	struct {
-		unsigned long attach;
-		unsigned long normal;
-		unsigned long periodic;
-		unsigned long detach;
+		struct counter *attach;
+		struct counter *normal;
+		struct counter *periodic;
+		struct counter *detach;
 	} loc_upd_type;
 	struct {
-		unsigned long reject;
-		unsigned long accept;
+		struct counter *reject;
+		struct counter *accept;
 	} loc_upd_resp;
 	struct {
-		unsigned long attempted;
-		unsigned long detached;
-		unsigned long completed;
-		unsigned long expired;
+		struct counter *attempted;
+		struct counter *detached;
+		struct counter *completed;
+		struct counter *expired;
 	} paging;
 	struct {
-		unsigned long submitted; /* MO SMS submissions */
-		unsigned long no_receiver;
-		unsigned long delivered; /* MT SMS deliveries */
-		unsigned long rp_err_mem;
-		unsigned long rp_err_other;
+		struct counter *submitted; /* MO SMS submissions */
+		struct counter *no_receiver;
+		struct counter *delivered; /* MT SMS deliveries */
+		struct counter *rp_err_mem;
+		struct counter *rp_err_other;
 	} sms;
 	struct {
-		unsigned long dialled;	/* total number of dialled calls */
-		unsigned long alerted;	/* we alerted the other end */
-		unsigned long connected;/* how many calls were accepted */
+		struct counter *dialled;	/* total number of dialled calls */
+		struct counter *alerted;	/* we alerted the other end */
+		struct counter *connected;/* how many calls were accepted */
 	} call;
 };
 
diff --git a/openbsc/include/openbsc/statistics.h b/openbsc/include/openbsc/statistics.h
new file mode 100644
index 0000000..9291a8c
--- /dev/null
+++ b/openbsc/include/openbsc/statistics.h
@@ -0,0 +1,30 @@
+#ifndef _STATISTICS_H
+#define _STATISTICS_H
+
+struct counter {
+	struct llist_head list;
+	const char *name;
+	const char *description;
+	unsigned long value;
+};
+
+static inline void counter_inc(struct counter *ctr)
+{
+	ctr->value++;
+}
+
+static inline unsigned long counter_get(struct counter *ctr)
+{
+	return ctr->value;
+}
+
+static inline void counter_reset(struct counter *ctr)
+{
+	ctr->value = 0;
+}
+
+struct counter *counter_alloc(const char *name);
+void counter_free(struct counter *ctr);
+int counters_store_db(void);
+
+#endif /* _STATISTICS_H */
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 7abec69..de413d5 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -12,7 +12,7 @@
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
 		input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
 		talloc_ctx.c system_information.c bitvec.c rest_octets.c \
-		rtp_proxy.c
+		rtp_proxy.c statistics.c
 
 libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
 		mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index ae1d6af..787a803 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1261,14 +1261,14 @@
 	lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci);
 	chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci);
 
-	bts->network->stats.chreq.total++;
+	counter_inc(bts->network->stats.chreq.total);
 
 	/* check availability / allocate channel */
 	lchan = lchan_alloc(bts, lctype);
 	if (!lchan) {
 		DEBUGP(DRSL, "CHAN RQD: no resources for %s 0x%x\n",
 			gsm_lchan_name(lctype), rqd_ref->ra);
-		bts->network->stats.chreq.no_channel++;
+		counter_inc(bts->network->stats.chreq.no_channel);
 		/* FIXME: send some kind of reject ?!? */
 		return -ENOMEM;
 	}
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
index 3e8bf88..2a80a49 100644
--- a/openbsc/src/bs11_config.c
+++ b/openbsc/src/bs11_config.c
@@ -71,6 +71,11 @@
 
 static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
 
+/* dummy function to keep gsm_data.c happy */
+struct counter *counter_alloc(const char *name)
+{
+	return NULL;
+}
 
 int handle_serial_msg(struct msgb *rx_msg);
 
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 707d877..916527d 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -25,6 +25,7 @@
 #include <openbsc/db.h>
 #include <openbsc/talloc.h>
 #include <openbsc/debug.h>
+#include <openbsc/statistics.h>
 
 #include <libgen.h>
 #include <stdio.h>
@@ -117,6 +118,12 @@
 		"subscriber_id INTEGER NOT NULL, "
 		"apdu BLOB "
 		")",
+	"CREATE TABLE IF NOT EXISTS Counters ("
+		"id INTEGER PRIMARY KEY AUTOINCREMENT, "
+		"timestamp TIMESTAMP NOT NULL, "
+		"value INTEGER NOT NULL "
+		"name TEXT NOT NULL, "
+		")",
 };
 
 void db_error_func(dbi_conn conn, void* data) {
@@ -902,3 +909,24 @@
 	dbi_result_free(result);
 	return 0;
 }
+
+int db_store_counter(struct counter *ctr)
+{
+	dbi_result result;
+	char *q_name;
+
+	dbi_conn_quote_string_copy(conn, ctr->name, &q_name);
+
+	result = dbi_conn_queryf(conn,
+		"INSERT INTO Counters "
+		"(timestamp,name,value) VALUES "
+		"(datetime('now'),%s,%lu)", q_name, ctr->value);
+
+	free(q_name);
+
+	if (!result)
+		return -EIO;
+
+	dbi_result_free(result);
+	return 0;
+}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index f89d8c5..2b22122 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -831,7 +831,7 @@
 
 	DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
 
-	bts->network->stats.loc_upd_resp.reject++;
+	counter_inc(bts->network->stats.loc_upd_resp.reject);
 	
 	return gsm48_sendmsg(msg, NULL);
 }
@@ -860,7 +860,7 @@
 
 	DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
 
-	bts->network->stats.loc_upd_resp.accept++;
+	counter_inc(bts->network->stats.loc_upd_resp.accept);
 
 	return gsm48_sendmsg(msg, NULL);
 }
@@ -982,13 +982,13 @@
 
 	switch (lu->type) {
 	case GSM48_LUPD_NORMAL:
-		bts->network->stats.loc_upd_type.normal++;
+		counter_inc(bts->network->stats.loc_upd_type.normal);
 		break;
 	case GSM48_LUPD_IMSI_ATT:
-		bts->network->stats.loc_upd_type.attach++;
+		counter_inc(bts->network->stats.loc_upd_type.attach);
 		break;
 	case GSM48_LUPD_PERIODIC:
-		bts->network->stats.loc_upd_type.periodic++;
+		counter_inc(bts->network->stats.loc_upd_type.periodic);
 		break;
 	}
 
@@ -1318,7 +1318,7 @@
 	DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
 		mi_type, mi_string);
 
-	bts->network->stats.loc_upd_type.detach++;
+	counter_inc(bts->network->stats.loc_upd_type.detach);
 
 	switch (mi_type) {
 	case GSM_MI_TYPE_TMSI:
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index a5f64f1..d5b0116 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -517,7 +517,7 @@
 	u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
 	int rc = 0;
 
-	bts->network->stats.sms.submitted++;
+	counter_inc(bts->network->stats.sms.submitted);
 
 	gsms = sms_alloc();
 	if (!gsms)
@@ -607,7 +607,7 @@
 	gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
 	if (!gsms->receiver) {
 		rc = 1; /* cause 1: unknown subscriber */
-		bts->network->stats.sms.no_receiver++;
+		counter_inc(bts->network->stats.sms.no_receiver);
 		goto out;
 	}
 
@@ -761,6 +761,7 @@
 static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
 			      struct gsm411_rp_hdr *rph)
 {
+	struct gsm_network *net = trans->lchan->ts->trx->bts->network;
 	struct gsm_sms *sms = trans->sms.sms;
 	u_int8_t cause_len = rph->data[0];
 	u_int8_t cause = rph->data[1];
@@ -794,9 +795,9 @@
 		 * to store this in our database and wati for a SMMA message */
 		/* FIXME */
 		dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr);
-		trans->lchan->ts->trx->bts->network->stats.sms.rp_err_mem++;
+		counter_inc(net->stats.sms.rp_err_mem);
 	} else
-		trans->lchan->ts->trx->bts->network->stats.sms.rp_err_other++;
+		counter_inc(net->stats.sms.rp_err_other);
 
 	sms_free(sms);
 	trans->sms.sms = NULL;
@@ -1073,7 +1074,7 @@
 
 	DEBUGP(DSMS, "TX: SMS DELIVER\n");
 
-	lchan->ts->trx->bts->network->stats.sms.delivered++;
+	counter_inc(lchan->ts->trx->bts->network->stats.sms.delivered);
 
 	return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
 	/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index d6ebd92..e2f56d5 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -28,6 +28,7 @@
 #include <openbsc/gsm_data.h>
 #include <openbsc/talloc.h>
 #include <openbsc/abis_nm.h>
+#include <openbsc/statistics.h>
 
 void *tall_bsc_ctx;
 
@@ -226,6 +227,32 @@
 	INIT_LLIST_HEAD(&net->upqueue);
 	INIT_LLIST_HEAD(&net->bts_list);
 
+	net->stats.chreq.total = counter_alloc("net.chreq.total");
+	net->stats.chreq.no_channel = counter_alloc("net.chreq.no_channel");
+	net->stats.handover.attempted = counter_alloc("net.handover.attempted");
+	net->stats.handover.no_channel = counter_alloc("net.handover.no_channel");
+	net->stats.handover.timeout = counter_alloc("net.handover.timeout");
+	net->stats.handover.completed = counter_alloc("net.handover.completed");
+	net->stats.handover.failed = counter_alloc("net.handover.failed");
+	net->stats.loc_upd_type.attach = counter_alloc("net.loc_upd_type.attach");
+	net->stats.loc_upd_type.normal = counter_alloc("net.loc_upd_type.normal");
+	net->stats.loc_upd_type.periodic = counter_alloc("net.loc_upd_type.periodic");
+	net->stats.loc_upd_type.detach = counter_alloc("net.imsi_detach.count");
+	net->stats.loc_upd_resp.reject = counter_alloc("net.loc_upd_resp.reject");
+	net->stats.loc_upd_resp.accept = counter_alloc("net.loc_upd_resp.accept");
+	net->stats.paging.attempted = counter_alloc("net.paging.attempted");
+	net->stats.paging.detached = counter_alloc("net.paging.detached");
+	net->stats.paging.completed = counter_alloc("net.paging.completed");
+	net->stats.paging.expired = counter_alloc("net.paging.expired");
+	net->stats.sms.submitted = counter_alloc("net.sms.submitted");
+	net->stats.sms.no_receiver = counter_alloc("net.sms.no_receiver");
+	net->stats.sms.delivered = counter_alloc("net.sms.delivered");
+	net->stats.sms.rp_err_mem = counter_alloc("net.sms.rp_err_mem");
+	net->stats.sms.rp_err_other = counter_alloc("net.sms.rp_err_other");
+	net->stats.call.dialled = counter_alloc("net.call.dialled");
+	net->stats.call.alerted = counter_alloc("net.call.alerted");
+	net->stats.call.connected = counter_alloc("net.call.connected");
+
 	net->mncc_recv = mncc_recv;
 
 	return net;
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
index 393b0f2..5297ab6 100644
--- a/openbsc/src/handover_logic.c
+++ b/openbsc/src/handover_logic.c
@@ -97,12 +97,12 @@
 	DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
 		old_lchan->ts->trx->bts->nr, bts->nr);
 
-	bts->network->stats.handover.attempted++;
+	counter_inc(bts->network->stats.handover.attempted);
 
 	new_lchan = lchan_alloc(bts, old_lchan->type);
 	if (!new_lchan) {
 		LOGP(DHO, LOGL_NOTICE, "No free channel\n");
-		bts->network->stats.handover.no_channel++;
+		counter_inc(bts->network->stats.handover.no_channel);
 		return -ENOSPC;
 	}
 
@@ -144,9 +144,10 @@
 static void ho_T3103_cb(void *_ho)
 {
 	struct bsc_handover *ho = _ho;
+	struct gsm_network *net = ho->new_lchan->ts->trx->bts->network;
 
 	DEBUGP(DHO, "HO T3103 expired\n");
-	ho->new_lchan->ts->trx->bts->network->stats.handover.timeout++;
+	counter_inc(net->stats.handover.timeout);
 
 	lchan_free(ho->new_lchan);
 	llist_del(&ho->list);
@@ -207,6 +208,7 @@
 /* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
 static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
 {
+	struct gsm_network *net = new_lchan->ts->trx->bts->network;
 	struct bsc_handover *ho;
 
 	ho = bsc_ho_by_new_lchan(new_lchan);
@@ -215,7 +217,7 @@
 		return -ENODEV;
 	}
 
-	new_lchan->ts->trx->bts->network->stats.handover.completed++;
+	counter_inc(net->stats.handover.completed);
 
 	bsc_del_timer(&ho->T3103);
 
@@ -236,6 +238,7 @@
 /* GSM 04.08 HANDOVER FAIL has been received */
 static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
 {
+	struct gsm_network *net = old_lchan->ts->trx->bts->network;
 	struct bsc_handover *ho;
 
 	ho = bsc_ho_by_old_lchan(old_lchan);
@@ -244,7 +247,7 @@
 		return -ENODEV;
 	}
 
-	old_lchan->ts->trx->bts->network->stats.handover.failed++;
+	counter_inc(net->stats.handover.failed);
 
 	bsc_del_timer(&ho->T3103);
 	llist_del(&ho->list);
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 538e0a8..820773a 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -212,7 +212,7 @@
 	cbfn = req->cbfn;
 	paging_remove_request(&req->bts->paging, req);
 
-	req->bts->network->stats.paging.expired++;
+	counter_inc(req->bts->network->stats.paging.expired);
 
 	dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
 	if (cbfn)
@@ -256,7 +256,7 @@
 	struct gsm_bts *bts = NULL;
 	int num_pages = 0;
 
-	network->stats.paging.attempted++;
+	counter_inc(network->stats.paging.attempted);
 
 	/* start paging subscriber on all BTS within Location Area */
 	do {
@@ -274,7 +274,7 @@
 	} while (1);
 
 	if (num_pages == 0)
-		network->stats.paging.detached++;
+		counter_inc(network->stats.paging.detached);
 
 	return num_pages;
 }
diff --git a/openbsc/src/statistics.c b/openbsc/src/statistics.c
new file mode 100644
index 0000000..3429d6e
--- /dev/null
+++ b/openbsc/src/statistics.c
@@ -0,0 +1,85 @@
+/* utility routines for keeping some statistics */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/signal.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/talloc.h>
+#include <openbsc/statistics.h>
+#include <openbsc/db.h>
+#include <openbsc/timer.h>
+
+static LLIST_HEAD(counters);
+
+static struct timer_list db_sync_timer;
+
+#define DB_SYNC_INTERVAL	60, 0
+
+struct counter *counter_alloc(const char *name)
+{
+	struct counter *ctr = talloc_zero(tall_bsc_ctx, struct counter);
+
+	if (!ctr)
+		return NULL;
+
+	ctr->name = name;
+	llist_add_tail(&ctr->list, &counters);
+
+	return ctr;
+}
+
+void counter_free(struct counter *ctr)
+{
+	llist_del(&ctr->list);
+	talloc_free(ctr);
+}
+
+int counters_store_db(void)
+{
+	struct counter *ctr;
+	int rc = 0;
+
+	llist_for_each_entry(ctr, &counters, list) {
+		rc = db_store_counter(ctr);
+		if (rc < 0)
+			return rc;
+	}
+
+	return rc;
+}
+
+static void db_sync_timer_cb(void *data)
+{
+	/* store counters to database and re-schedule */
+	counters_store_db();
+	bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
+}
+
+static __attribute__((constructor)) void on_dso_load_stat(void)
+{
+	db_sync_timer.cb = db_sync_timer_cb;
+	db_sync_timer.data = NULL;
+	bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
+}
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 808db3f..75fe54c 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -811,30 +811,35 @@
 	struct gsm_network *net = gsmnet;
 
 	vty_out(vty, "Channel Requests: %lu total, %lu no channel%s",
-		net->stats.chreq.total, net->stats.chreq.no_channel,
-		VTY_NEWLINE);
+		counter_get(net->stats.chreq.total),
+		counter_get(net->stats.chreq.no_channel), VTY_NEWLINE);
 	vty_out(vty, "Location Update: %lu attach, %lu normal, %lu periodic%s",
-		net->stats.loc_upd_type.attach, net->stats.loc_upd_type.normal,
-		net->stats.loc_upd_type.periodic, VTY_NEWLINE);
-	vty_out(vty, "IMSI Detach Indications: %lu%s\n",
-		net->stats.loc_upd_type.detach, VTY_NEWLINE);
+		counter_get(net->stats.loc_upd_type.attach),
+		counter_get(net->stats.loc_upd_type.normal),
+		counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE);
+	vty_out(vty, "IMSI Detach Indications: %lu%s",
+		counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE);
 	vty_out(vty, "Location Update Response: %lu accept, %lu reject%s",
-		net->stats.loc_upd_resp.accept,
-		net->stats.loc_upd_resp.reject, VTY_NEWLINE);
+		counter_get(net->stats.loc_upd_resp.accept),
+		counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE);
 	vty_out(vty, "Paging: %lu attempted, %lu complete, %lu expired%s",
-		net->stats.paging.attempted, net->stats.paging.completed,
-		net->stats.paging.expired, VTY_NEWLINE);
+		counter_get(net->stats.paging.attempted),
+		counter_get(net->stats.paging.completed),
+		counter_get(net->stats.paging.expired), VTY_NEWLINE);
 	vty_out(vty, "Handover: %lu attempted, %lu no_channel, %lu timeout, "
-		"%lu completed, %lu failed%s", net->stats.handover.attempted,
-		net->stats.handover.no_channel, net->stats.handover.timeout,
-		net->stats.handover.completed, net->stats.handover.failed,
-		VTY_NEWLINE);
+		"%lu completed, %lu failed%s",
+		counter_get(net->stats.handover.attempted),
+		counter_get(net->stats.handover.no_channel),
+		counter_get(net->stats.handover.timeout),
+		counter_get(net->stats.handover.completed),
+		counter_get(net->stats.handover.failed), VTY_NEWLINE);
 	vty_out(vty, "SMS MO: %lu submitted, %lu no receiver%s",
-		net->stats.sms.submitted, net->stats.sms.no_receiver,
-		VTY_NEWLINE);
+		counter_get(net->stats.sms.submitted),
+		counter_get(net->stats.sms.no_receiver), VTY_NEWLINE);
 	vty_out(vty, "SMS MT: %lu delivered, %lu no memory, %lu other error%s",
-		net->stats.sms.delivered, net->stats.sms.rp_err_mem,
-		net->stats.sms.rp_err_other, VTY_NEWLINE);
+		counter_get(net->stats.sms.delivered),
+		counter_get(net->stats.sms.rp_err_mem),
+		counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE);
 	return CMD_SUCCESS;
 }