stats: Send all values on reporter update

Currently only counter changes and new item values are being
reported. This makes it cumbersome to configure reporting clients,
since there is nothing like a list of all parameters.

This commit changes this behaviour such that all currently existing
counters and items that would be reported eventually, are passed to
the reporter when it has been reconfigured or enabled. If a counter
has not been incremented, 0 is sent. If a stat item value has not
been added, the last item value (or the default value if there is
none) is resent again.

Note that this will not catch transient counters/items that will be
created later on, e.g. triggered by new peers or subscribers.

To just force this kind of dump on a running reporter, it is
sufficient to invoke the 'enable' command in its configuration node.

Sponsored-by: On-Waves ehf
diff --git a/include/osmocom/core/stats.h b/include/osmocom/core/stats.h
index 362d3fb..39eae08 100644
--- a/include/osmocom/core/stats.h
+++ b/include/osmocom/core/stats.h
@@ -66,6 +66,7 @@
 	int fd;
 	struct msgb *buffer;
 	int agg_enabled;
+	int force_single_flush;
 
 	struct llist_head list;
 	int (*open)(struct osmo_stats_reporter *srep);
diff --git a/src/stats.c b/src/stats.c
index fa56f50..0a1a148 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -101,6 +101,8 @@
 	else
 		srep->running = 1;
 
+	srep->force_single_flush = 1;
+
 	return rc;
 }
 
@@ -582,13 +584,13 @@
 	struct osmo_stats_reporter *srep;
 	int64_t delta = rate_ctr_difference(ctr);
 
-	if (delta == 0)
-		return 0;
-
 	llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
 		if (!srep->running)
 			continue;
 
+		if (delta == 0 && !srep->force_single_flush)
+			continue;
+
 		if (!osmo_stats_reporter_check_config(srep,
 			       ctrg->idx, ctrg->desc->class_id))
 			return 0;
@@ -628,12 +630,21 @@
 	struct osmo_stats_reporter *srep;
 	int32_t idx = current_stat_item_index;
 	int32_t value;
+	int have_value;
 
-	while (osmo_stat_item_get_next(item, &idx, &value) > 0) {
+	have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
+	if (!have_value)
+		/* Send the last value in case a flush is requested */
+		value = osmo_stat_item_get_last(item);
+
+	do {
 		llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
 			if (!srep->running)
 				continue;
 
+			if (!have_value && !srep->force_single_flush)
+				continue;
+
 			if (!osmo_stats_reporter_check_config(srep,
 					statg->idx, statg->desc->class_id))
 				return 0;
@@ -641,7 +652,12 @@
 			osmo_stats_reporter_send_item(srep, statg,
 				item->desc, value);
 		}
-	}
+
+		if (!have_value)
+			break;
+
+		have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
+	} while (have_value);
 
 	return 0;
 }
@@ -666,13 +682,13 @@
 
 	int delta = osmo_counter_difference(counter);
 
-	if (delta == 0)
-		return 0;
-
 	llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
 		if (!srep->running)
 			continue;
 
+		if (delta == 0 && !srep->force_single_flush)
+			continue;
+
 		osmo_stats_reporter_send_counter(srep, NULL, &desc,
 			counter->value, delta);
 
@@ -694,6 +710,7 @@
 			continue;
 
 		osmo_stats_reporter_send_buffer(srep);
+		srep->force_single_flush = 0;
 	}
 }