display last location update timestamp in vty

Read the subscriber's last location update timestamp from the
database and display it in the output of 'show subscriber'.

For example:
  OsmoHLR> show subscriber id 1
      ID: 1
      IMSI: 123456789000000
      MSISDN: 543210123456789
      VLR number: 712
      SGSN number: 5952
      last LU seen: Fri Dec  7 11:30:51 2018 UTC

While the database stores the timestamp as a string, we
convert the timestamp into time_t for internal use.
This allows for flexible potential use of the timestamp
in contexts other than the VTY in the future.

The timestamp displayed in the VTY is created with ctime_r(3).
It does not match the format of the raw string in the database:
  sqlite> select id,last_lu_seen from subscriber;
  1|2018-12-07 11:30:51

Related: OS#2838
Change-Id: Ie180c434f02ffec0d4b2f651a73258a8126b2e1a
diff --git a/src/db_hlr.c b/src/db_hlr.c
index db31009..c97cd82 100644
--- a/src/db_hlr.c
+++ b/src/db_hlr.c
@@ -17,6 +17,11 @@
  *
  */
 
+#define _POSIX_C_SOURCE 200809L /* for strptime(3) */
+/* These are needed as well due to the above _POSIX_C_SOURCE definition: */
+#define _DEFAULT_SOURCE		/* for struct timezone */
+#define _XOPEN_SOURCE		/* for clockid_t */
+
 #include <string.h>
 #include <errno.h>
 #include <inttypes.h>
@@ -387,6 +392,8 @@
 {
 	int rc;
 	int ret = 0;
+	const char *last_lu_seen_str;
+	struct tm tm;
 
 	/* execute the statement */
 	rc = sqlite3_step(stmt);
@@ -419,6 +426,20 @@
 	subscr->lmsi = sqlite3_column_int(stmt, 10);
 	subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
 	subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
+	last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 13);
+	if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {
+		if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
+			LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",
+			     last_lu_seen_str, subscr->imsi, strerror(errno));
+		} else {
+			subscr->last_lu_seen = mktime(&tm);
+			if (subscr->last_lu_seen == -1) {
+				LOGP(DAUC, LOGL_ERROR, "Cannot convert LU timestamp '%s' to time_t: %s\n",
+				     last_lu_seen_str, strerror(errno));
+				subscr->last_lu_seen = 0;
+			}
+		}
+	}
 
 out:
 	db_remove_reset(stmt);