nitb/ctrl: Implement a command to list all active subscribers

This is only useful for small networks. List the IMSI and MSISDN
of all active subscribers.

Fixes: SYS#266
diff --git a/openbsc/src/libmsc/ctrl_commands.c b/openbsc/src/libmsc/ctrl_commands.c
index b66cece..58558fd 100644
--- a/openbsc/src/libmsc/ctrl_commands.c
+++ b/openbsc/src/libmsc/ctrl_commands.c
@@ -140,11 +140,40 @@
 }
 CTRL_CMD_DEFINE(subscriber_delete, "subscriber-delete-v1");
 
+static int verify_subscriber_list(struct ctrl_cmd *cmd, const char *value, void *d)
+{
+	return 1;
+}
+
+static int set_subscriber_list(struct ctrl_cmd *cmd, void *d)
+{
+	cmd->reply = "Get only attribute";
+	return CTRL_CMD_ERROR;
+}
+
+static void list_cb(struct gsm_subscriber *subscr, void *d)
+{
+	char **data = (char **) d;
+	*data = talloc_asprintf_append(*data, "%s,%s\n",
+				subscr->imsi, subscr->extension);
+}
+
+static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
+{
+	cmd->reply = talloc_strdup(cmd, "");
+
+	db_subscriber_list_active(list_cb, &cmd->reply);
+	printf("%s\n", cmd->reply);
+	return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE(subscriber_list, "subscriber-list-active-v1");
+
 int msc_ctrl_cmds_install(void)
 {
 	int rc = 0;
 
 	rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_modify);
 	rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_delete);
+	rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list);
 	return rc;
 }
diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c
index a21258d..bfa4e75 100644
--- a/openbsc/src/libmsc/db.c
+++ b/openbsc/src/libmsc/db.c
@@ -902,6 +902,39 @@
 	return 0;
 }
 
+/**
+ * List all the authorized and non-expired subscribers. The callback will
+ * be called one by one. The subscr argument is not fully initialize and
+ * subscr_get/subscr_put must not be called. The passed in pointer will be
+ * deleted after the callback by the database call.
+ */
+int db_subscriber_list_active(void (*cb)(struct gsm_subscriber*,void*), void *closure)
+{
+	dbi_result result;
+
+	result = dbi_conn_queryf(conn,
+			"SELECT * from Subscriber WHERE LAC != 0 AND authorized = 1");
+	if (!result) {
+		LOGP(DDB, LOGL_ERROR, "Failed to list active subscribers\n");
+		return -1;
+	}
+
+	while (dbi_result_next_row(result)) {
+		struct gsm_subscriber *subscr;
+
+		subscr = subscr_alloc();
+		subscr->id = dbi_result_get_ulonglong(result, "id");
+		db_set_from_query(subscr, result);
+		cb(subscr, closure);
+		OSMO_ASSERT(subscr->use_count == 1);
+		llist_del(&subscr->entry);
+		talloc_free(subscr);
+	}
+
+	dbi_result_free(result);
+	return 0;
+}
+
 int db_sync_equipment(struct gsm_equipment *equip)
 {
 	dbi_result result;