<p>Stefan Sperling <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12202">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Pau Espin Pedrol: Looks good to me, approved
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">display last location update timestamp in vty<br><br>Read the subscriber's last location update timestamp from the<br>database and display it in the output of 'show subscriber'.<br><br>For example:<br>  OsmoHLR> show subscriber id 1<br>      ID: 1<br>      IMSI: 123456789000000<br>      MSISDN: 543210123456789<br>      VLR number: 712<br>      SGSN number: 5952<br>      last LU seen: Fri Dec  7 11:30:51 2018 UTC<br><br>While the database stores the timestamp as a string, we<br>convert the timestamp into time_t for internal use.<br>This allows for flexible potential use of the timestamp<br>in contexts other than the VTY in the future.<br><br>The timestamp displayed in the VTY is created with ctime_r(3).<br>It does not match the format of the raw string in the database:<br>  sqlite> select id,last_lu_seen from subscriber;<br>  1|2018-12-07 11:30:51<br><br>Related: OS#2838<br>Change-Id: Ie180c434f02ffec0d4b2f651a73258a8126b2e1a<br>---<br>M src/db.h<br>M src/db_hlr.c<br>M src/hlr_vty_subscr.c<br>3 files changed, 44 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/db.h b/src/db.h</span><br><span>index 5129b8d..ae592fb 100644</span><br><span>--- a/src/db.h</span><br><span>+++ b/src/db.h</span><br><span>@@ -84,8 +84,14 @@</span><br><span>    uint32_t        lmsi;</span><br><span>        bool            ms_purged_cs;</span><br><span>        bool            ms_purged_ps;</span><br><span style="color: hsl(120, 100%, 40%);">+ time_t          last_lu_seen;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* A format string for use with strptime(3). This format string is</span><br><span style="color: hsl(120, 100%, 40%);">+ * used to parse the last_lu_seen column stored in the HLR database.</span><br><span style="color: hsl(120, 100%, 40%);">+ * See https://sqlite.org/lang_datefunc.html, function datetime(). */</span><br><span style="color: hsl(120, 100%, 40%);">+#define DB_LAST_LU_SEEN_FMT "%Y-%m-%d %H:%M:%S"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Like struct osmo_sub_auth_data, but the keys are in hexdump representation.</span><br><span>  * This is useful because SQLite requires them in hexdump format, and callers</span><br><span>  * like the VTY and CTRL interface also have them available as hexdump to begin</span><br><span>diff --git a/src/db_hlr.c b/src/db_hlr.c</span><br><span>index db31009..c97cd82 100644</span><br><span>--- a/src/db_hlr.c</span><br><span>+++ b/src/db_hlr.c</span><br><span>@@ -17,6 +17,11 @@</span><br><span>  *</span><br><span>  */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define _POSIX_C_SOURCE 200809L /* for strptime(3) */</span><br><span style="color: hsl(120, 100%, 40%);">+/* These are needed as well due to the above _POSIX_C_SOURCE definition: */</span><br><span style="color: hsl(120, 100%, 40%);">+#define _DEFAULT_SOURCE             /* for struct timezone */</span><br><span style="color: hsl(120, 100%, 40%);">+#define _XOPEN_SOURCE                /* for clockid_t */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #include <string.h></span><br><span> #include <errno.h></span><br><span> #include <inttypes.h></span><br><span>@@ -387,6 +392,8 @@</span><br><span> {</span><br><span>     int rc;</span><br><span>      int ret = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *last_lu_seen_str;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct tm tm;</span><br><span> </span><br><span>    /* execute the statement */</span><br><span>  rc = sqlite3_step(stmt);</span><br><span>@@ -419,6 +426,20 @@</span><br><span>      subscr->lmsi = sqlite3_column_int(stmt, 10);</span><br><span>      subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);</span><br><span>      subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);</span><br><span style="color: hsl(120, 100%, 40%);">+       last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 13);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         last_lu_seen_str, subscr->imsi, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      subscr->last_lu_seen = mktime(&tm);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (subscr->last_lu_seen == -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          LOGP(DAUC, LOGL_ERROR, "Cannot convert LU timestamp '%s' to time_t: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                               last_lu_seen_str, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+                              subscr->last_lu_seen = 0;</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> </span><br><span> out:</span><br><span>        db_remove_reset(stmt);</span><br><span>diff --git a/src/hlr_vty_subscr.c b/src/hlr_vty_subscr.c</span><br><span>index bc6f6a5..92cfa2a 100644</span><br><span>--- a/src/hlr_vty_subscr.c</span><br><span>+++ b/src/hlr_vty_subscr.c</span><br><span>@@ -20,6 +20,8 @@</span><br><span> #include <inttypes.h></span><br><span> #include <string.h></span><br><span> #include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/types.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <time.h></span><br><span> </span><br><span> #include <osmocom/gsm/gsm23003.h></span><br><span> #include <osmocom/vty/vty.h></span><br><span>@@ -33,11 +35,24 @@</span><br><span> </span><br><span> #define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static char *</span><br><span style="color: hsl(120, 100%, 40%);">+get_datestr(const time_t *t, char *datebuf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      char *p, *s = ctime_r(t, datebuf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Strip trailing newline. */</span><br><span style="color: hsl(120, 100%, 40%);">+ p = strchr(s, '\n');</span><br><span style="color: hsl(120, 100%, 40%);">+  if (p)</span><br><span style="color: hsl(120, 100%, 40%);">+                *p = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+    return s;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)</span><br><span> {</span><br><span>      int rc;</span><br><span>      struct osmo_sub_auth_data aud2g;</span><br><span>     struct osmo_sub_auth_data aud3g;</span><br><span style="color: hsl(120, 100%, 40%);">+      char datebuf[26]; /* for ctime_r(3) */</span><br><span> </span><br><span>   vty_out(vty, "    ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);</span><br><span> </span><br><span>@@ -63,6 +78,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(120, 100%, 40%);">+       if (subscr->last_lu_seen)</span><br><span style="color: hsl(120, 100%, 40%);">+          vty_out(vty, "    last LU seen: %s UTC%s", get_datestr(&subscr->last_lu_seen, datebuf), VTY_NEWLINE);</span><br><span> </span><br><span>   if (!*subscr->imsi)</span><br><span>               return;</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12202">change 12202</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/12202"/><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-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ie180c434f02ffec0d4b2f651a73258a8126b2e1a </div>
<div style="display:none"> Gerrit-Change-Number: 12202 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: Stefan Sperling <stsp@stsp.name> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Stefan Sperling <stsp@stsp.name> </div>
<div style="display:none"> Gerrit-CC: Harald Welte <laforge@gnumonks.org> </div>