<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-hlr/+/22311">View Change</a></p><div style="white-space:pre-wrap">Approvals:
laforge: Looks good to me, approved
Jenkins Builder: Verified
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add vty command to show summary of all or filtered subscribers<br><br>Adds the following commands:<br><br>show subscribers all - Display summary of all entries in HLR<br>show subscribers (imsi|msisdn|cs|ps) ... As above but filter on search field/string<br>show subscribers last seen - Display only subscribers with data in<br>Last LU update field, and sorts by Last LU.<br><br>Change-Id: I7f0573381a6d0d13841ac6d42d50f0e8389decf4<br>---<br>M include/osmocom/hlr/db.h<br>M src/db.c<br>M src/db_hlr.c<br>M src/hlr_vty_subscr.c<br>M tests/test_nodes.vty<br>5 files changed, 228 insertions(+), 5 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/hlr/db.h b/include/osmocom/hlr/db.h</span><br><span>index ca336a0..f70df83 100644</span><br><span>--- a/include/osmocom/hlr/db.h</span><br><span>+++ b/include/osmocom/hlr/db.h</span><br><span>@@ -8,6 +8,12 @@</span><br><span> struct hlr;</span><br><span> </span><br><span> enum stmt_idx {</span><br><span style="color: hsl(120, 100%, 40%);">+ DB_STMT_SEL_ALL,</span><br><span style="color: hsl(120, 100%, 40%);">+ DB_STMT_SEL_ALL_ORDER_LAST_SEEN,</span><br><span style="color: hsl(120, 100%, 40%);">+ DB_STMT_SEL_FILTER_MSISDN,</span><br><span style="color: hsl(120, 100%, 40%);">+ DB_STMT_SEL_FILTER_IMSI,</span><br><span style="color: hsl(120, 100%, 40%);">+ DB_STMT_SEL_FILTER_CS,</span><br><span style="color: hsl(120, 100%, 40%);">+ DB_STMT_SEL_FILTER_PS,</span><br><span> DB_STMT_SEL_BY_IMSI,</span><br><span> DB_STMT_SEL_BY_MSISDN,</span><br><span> DB_STMT_SEL_BY_ID,</span><br><span>@@ -148,6 +154,9 @@</span><br><span> int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);</span><br><span> int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,</span><br><span style="color: hsl(120, 100%, 40%);">+ void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+ int *count, const char **err);</span><br><span> int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,</span><br><span> struct hlr_subscriber *subscr);</span><br><span> int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,</span><br><span>diff --git a/src/db.c b/src/db.c</span><br><span>index 5ec20e2..09a17b6 100644</span><br><span>--- a/src/db.c</span><br><span>+++ b/src/db.c</span><br><span>@@ -51,6 +51,13 @@</span><br><span> "sgsn_via_proxy"</span><br><span> </span><br><span> static const char *stmt_sql[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [DB_STMT_SEL_ALL] = "SELECT " SEL_COLUMNS " FROM subscriber;",</span><br><span style="color: hsl(120, 100%, 40%);">+ [DB_STMT_SEL_ALL_ORDER_LAST_SEEN] = "SELECT " SEL_COLUMNS " FROM subscriber "</span><br><span style="color: hsl(120, 100%, 40%);">+ "WHERE last_lu_seen IS NOT NULL ORDER BY last_lu_seen;",</span><br><span style="color: hsl(120, 100%, 40%);">+ [DB_STMT_SEL_FILTER_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn LIKE $search ORDER BY msisdn",</span><br><span style="color: hsl(120, 100%, 40%);">+ [DB_STMT_SEL_FILTER_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi LIKE $search ORDER BY imsi",</span><br><span style="color: hsl(120, 100%, 40%);">+ [DB_STMT_SEL_FILTER_CS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_cs = $search ORDER BY last_lu_seen",</span><br><span style="color: hsl(120, 100%, 40%);">+ [DB_STMT_SEL_FILTER_PS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_ps = $search ORDER BY last_lu_seen",</span><br><span> [DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",</span><br><span> [DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",</span><br><span> [DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",</span><br><span>diff --git a/src/db_hlr.c b/src/db_hlr.c</span><br><span>index 6ba43c2..a181c0c 100644</span><br><span>--- a/src/db_hlr.c</span><br><span>+++ b/src/db_hlr.c</span><br><span>@@ -627,6 +627,96 @@</span><br><span> </span><br><span> /*! Retrieve subscriber data from the HLR database.</span><br><span> * \param[in,out] dbc database context.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] filter_type ASCII string of identifier type to search.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] filter ASCII string to search.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] get_cb pointer to call back function for data.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in,out] data pointer to pass to callback function.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in,out] count counter for number of matched subscribers.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in,our] err</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -ENOENT if no subscriber was found, -EIO on</span><br><span style="color: hsl(120, 100%, 40%);">+ * database error.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,</span><br><span style="color: hsl(120, 100%, 40%);">+ void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+ int *count, const char **err)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ sqlite3_stmt *stmt;</span><br><span style="color: hsl(120, 100%, 40%);">+ char search[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct hlr_subscriber subscr;</span><br><span style="color: hsl(120, 100%, 40%);">+ bool show_ls = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!filter_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stmt = dbc->stmt[DB_STMT_SEL_ALL];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strcmp(filter_type, "imsi") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMSI];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strcmp(filter_type, "msisdn") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stmt = dbc->stmt[DB_STMT_SEL_FILTER_MSISDN];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strcmp(filter_type, "cs") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stmt = dbc->stmt[DB_STMT_SEL_FILTER_CS];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strcmp(filter_type, "ps") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stmt = dbc->stmt[DB_STMT_SEL_FILTER_PS];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strcmp(filter_type, "last_lu_seen") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ show_ls = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ stmt = dbc->stmt[DB_STMT_SEL_ALL_ORDER_LAST_SEEN];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -EIO;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (filter && strcmp(filter_type, "last_lu_seen") != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(filter, "on") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sprintf(search, "%s", "1");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strcmp(filter, "off") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sprintf(search, "%s", "0");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ sprintf(search, "%%%s%%", filter);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!db_bind_text(stmt, "$search", search)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ *err = sqlite3_errmsg(dbc->db);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -EIO;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = sqlite3_step(stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc == SQLITE_DONE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ db_remove_reset(stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+ *err = "No matching subscriber(s)";</span><br><span style="color: hsl(120, 100%, 40%);">+ return -ENOENT;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (rc == SQLITE_ROW) {</span><br><span style="color: hsl(120, 100%, 40%);">+ subscr = (struct hlr_subscriber){</span><br><span style="color: hsl(120, 100%, 40%);">+ .id = sqlite3_column_int64(stmt, 0),};</span><br><span style="color: hsl(120, 100%, 40%);">+ copy_sqlite3_text_to_buf(subscr.imsi, stmt, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+ copy_sqlite3_text_to_buf(subscr.imei, stmt, 3);</span><br><span style="color: hsl(120, 100%, 40%);">+ subscr.nam_cs = sqlite3_column_int(stmt, 9);</span><br><span style="color: hsl(120, 100%, 40%);">+ subscr.nam_ps = sqlite3_column_int(stmt, 10);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (show_ls)</span><br><span style="color: hsl(120, 100%, 40%);">+ parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),</span><br><span style="color: hsl(120, 100%, 40%);">+ subscr.imsi, "CS");</span><br><span style="color: hsl(120, 100%, 40%);">+ get_cb(&subscr, data);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = sqlite3_step(stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+ (*count)++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ db_remove_reset(stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc != SQLITE_DONE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ *err = sqlite3_errmsg(dbc->db);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -EIO;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (rc == SQLITE_DONE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ *err = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ *err = sqlite3_errmsg(dbc->db);</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGP(DAUC, LOGL_ERROR, "Cannot read subscribers from db:: %s\n", *err);</span><br><span style="color: hsl(120, 100%, 40%);">+ return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Retrieve subscriber data from the HLR database.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in,out] dbc database context.</span><br><span> * \param[in] id ID of the subscriber in the HLR db.</span><br><span> * \param[out] subscr place retrieved data in this struct.</span><br><span> * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on</span><br><span>diff --git a/src/hlr_vty_subscr.c b/src/hlr_vty_subscr.c</span><br><span>index f5066c1..ad16045 100644</span><br><span>--- a/src/hlr_vty_subscr.c</span><br><span>+++ b/src/hlr_vty_subscr.c</span><br><span>@@ -44,13 +44,14 @@</span><br><span> return buf;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen)</span><br><span style="color: hsl(120, 100%, 40%);">+static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, bool only_age)</span><br><span> {</span><br><span> uint32_t age;</span><br><span> char datebuf[32];</span><br><span> if (!last_lu_seen)</span><br><span> return;</span><br><span style="color: hsl(0, 100%, 40%);">- vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!only_age)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));</span><br><span> if (!timestamp_age(&last_lu_seen, &age))</span><br><span> vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);</span><br><span> else {</span><br><span>@@ -64,7 +65,10 @@</span><br><span> UNIT_AGO("h", 60*60);</span><br><span> UNIT_AGO("m", 60);</span><br><span> UNIT_AGO("s", 1);</span><br><span style="color: hsl(0, 100%, 40%);">- vty_out(vty, " ago)%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!only_age)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " ago)%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " ago)");</span><br><span> #undef UNIT_AGO</span><br><span> }</span><br><span> }</span><br><span>@@ -108,8 +112,8 @@</span><br><span> vty_out(vty, " PS disabled%s", VTY_NEWLINE);</span><br><span> if (subscr->ms_purged_ps)</span><br><span> vty_out(vty, " PS purged%s", VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">- dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);</span><br><span style="color: hsl(0, 100%, 40%);">- dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);</span><br><span style="color: hsl(120, 100%, 40%);">+ dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false);</span><br><span style="color: hsl(120, 100%, 40%);">+ dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false);</span><br><span> </span><br><span> if (!*subscr->imsi)</span><br><span> return;</span><br><span>@@ -159,6 +163,28 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct vty *vty = data;</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%-5"PRIu64" %-12s %-16s", subscr->id,</span><br><span style="color: hsl(120, 100%, 40%);">+ *subscr->msisdn ? subscr->msisdn : "none",</span><br><span style="color: hsl(120, 100%, 40%);">+ *subscr->imsi ? subscr->imsi : "none");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*subscr->imei) {</span><br><span style="color: hsl(120, 100%, 40%);">+ char checksum = osmo_luhn(subscr->imei, 14);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (checksum == -EINVAL)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " %-14s (INVALID LENGTH!)", subscr->imei);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " %-14s%c", subscr->imei, checksum);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty," ------------- ");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " %-2s%-2s ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subscr->last_lu_seen)</span><br><span style="color: hsl(120, 100%, 40%);">+ dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out_newline(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)</span><br><span> {</span><br><span> char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];</span><br><span>@@ -186,11 +212,52 @@</span><br><span> return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void dump_summary_table_vty(struct vty *vty, bool header, bool show_ls)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *texts = "ID MSISDN IMSI IMEI NAM";</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *lines = "----- ------------ ---------------- ---------------- -----";</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ls_text = " LAST SEEN";</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ls_line = " ------------";</span><br><span style="color: hsl(120, 100%, 40%);">+ if (header) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!show_ls)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%s%s%s%s%s%s", texts, ls_text, VTY_NEWLINE, lines, ls_line, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!show_ls)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%s%s%s%s", lines, VTY_NEWLINE, texts, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%s%s%s%s%s%s", lines, ls_line, VTY_NEWLINE, texts, ls_text, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int get_subscrs(struct vty *vty, const char *filter_type, const char *filter)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *err;</span><br><span style="color: hsl(120, 100%, 40%);">+ bool show_ls = (filter_type && strcmp(filter_type, "last_lu_seen") == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ dump_summary_table_vty(vty, true, show_ls);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = db_subscrs_get(g_hlr->dbc, filter_type, filter, subscr_dump_summary_vty, vty, &count, &err);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (count > 40) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dump_summary_table_vty(vty, false, show_ls);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (count > 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " Subscribers Shown: %d%s", count, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%% %s%s", err, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define SUBSCR_CMD "subscriber "</span><br><span> #define SUBSCR_CMD_HELP "Subscriber management commands\n"</span><br><span> #define SUBSCR_SHOW_HELP "Show subscriber information\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define SUBSCRS_SHOW_HELP "Show all subscribers (with filter possibility)\n"</span><br><span> </span><br><span> #define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"</span><br><span style="color: hsl(120, 100%, 40%);">+#define SUBSCR_FILTER "(imsi|msisdn) FILTER"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define SUBSCR_ID_HELP \</span><br><span> "Identify subscriber by IMSI\n" \</span><br><span> "Identify subscriber by MSISDN (phone number)\n" \</span><br><span>@@ -225,6 +292,48 @@</span><br><span> "show " SUBSCR_CMD SUBSCR_ID,</span><br><span> SHOW_STR SUBSCR_SHOW_HELP SUBSCR_ID_HELP);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(show_subscriber_all,</span><br><span style="color: hsl(120, 100%, 40%);">+ show_subscriber_all_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "show subscribers all",</span><br><span style="color: hsl(120, 100%, 40%);">+ SHOW_STR SUBSCRS_SHOW_HELP "Show summary of all subscribers\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (get_subscrs(vty, NULL, NULL))</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(show_subscriber_filtered,</span><br><span style="color: hsl(120, 100%, 40%);">+ show_subscriber_filtered_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "show subscribers " SUBSCR_FILTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ SHOW_STR SUBSCRS_SHOW_HELP</span><br><span style="color: hsl(120, 100%, 40%);">+ "Filter Subscribers by IMSI\n" "Filter Subscribers by MSISDN\n" "String to match in msisdn or imsi\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *filter_type = argv[0];</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *filter = argv[1];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (get_subscrs(vty, filter_type, filter))</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ALIAS(show_subscriber_filtered, show_subscriber_filtered_cmd2,</span><br><span style="color: hsl(120, 100%, 40%);">+ "show subscribers (cs|ps) (on|off)",</span><br><span style="color: hsl(120, 100%, 40%);">+ SHOW_STR SUBSCR_SHOW_HELP</span><br><span style="color: hsl(120, 100%, 40%);">+ "Filter Subscribers by CS Network Access Mode\n" "Filter Subscribers by PS Network Access Mode\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Authorised\n" "Not Authorised\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(show_subscriber_order_last_seen, show_subscriber_order_last_seen_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "show subscribers last-seen",</span><br><span style="color: hsl(120, 100%, 40%);">+ SHOW_STR SUBSCR_SHOW_HELP "Show Subscribers Ordered by Last Seen Time\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (get_subscrs(vty, "last_lu_seen", NULL))</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> DEFUN(subscriber_create,</span><br><span> subscriber_create_cmd,</span><br><span> SUBSCR_CMD "imsi IDENT create",</span><br><span>@@ -678,6 +787,10 @@</span><br><span> </span><br><span> void hlr_vty_subscriber_init(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+ install_element_ve(&show_subscriber_all_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_element_ve(&show_subscriber_filtered_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_element_ve(&show_subscriber_filtered_cmd2);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_element_ve(&show_subscriber_order_last_seen_cmd);</span><br><span> install_element_ve(&subscriber_show_cmd);</span><br><span> install_element_ve(&show_subscriber_cmd);</span><br><span> install_element(ENABLE_NODE, &subscriber_create_cmd);</span><br><span>diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty</span><br><span>index cf1707a..5848fe1 100644</span><br><span>--- a/tests/test_nodes.vty</span><br><span>+++ b/tests/test_nodes.vty</span><br><span>@@ -13,6 +13,10 @@</span><br><span> OsmoHLR> list</span><br><span> ...</span><br><span> show gsup-connections</span><br><span style="color: hsl(120, 100%, 40%);">+ show subscribers all</span><br><span style="color: hsl(120, 100%, 40%);">+ show subscribers (imsi|msisdn) FILTER</span><br><span style="color: hsl(120, 100%, 40%);">+ show subscribers (cs|ps) (on|off)</span><br><span style="color: hsl(120, 100%, 40%);">+ show subscribers last-seen</span><br><span> subscriber (imsi|msisdn|id|imei) IDENT show</span><br><span> show subscriber (imsi|msisdn|id|imei) IDENT</span><br><span> show mslookup services</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-hlr/+/22311">change 22311</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-hlr/+/22311"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: osmo-hlr </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I7f0573381a6d0d13841ac6d42d50f0e8389decf4 </div>
<div style="display:none"> Gerrit-Change-Number: 22311 </div>
<div style="display:none"> Gerrit-PatchSet: 12 </div>
<div style="display:none"> Gerrit-Owner: keith <keith@rhizomatica.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-CC: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>