Hello,

  Following up on the comments from the previous patch

"me realize that we could possibly do exactly the same
with them : having per-listener and per-frontend last accept
could be useful to detect the ones which are never used in
some environment"

  I have added additional fields to the stats. I looked into the
comments on using the stats listener but found it easier to
implement last activity using bytes transferred time

Summary:
Added fields for last activity/session to stats page.
Last session for front end tracks the last accept time where as
for backends tracks the last lb connection time.

Last activity on all (be/fe/listener) tracks the time of
last byte.



Thanks
Bhaskar
From 331c995f64afac6d7b722e8658b2a15bb5cc91b8 Mon Sep 17 00:00:00 2001
From: Bhaskar Maddala <[email protected]>
Date: Sun, 9 Feb 2014 03:13:22 -0500
Subject: [PATCH] Add last activity/session enhancements to stats page

Summary:
Added fields for last activity/session to stats page.
Last session for front end tracks the last accept time where as
for backends tracks the last lb connection time.

Last activity on all (be/fe/listener) tracks the time of
last bytes.
---
 include/proto/backend.h  |    1 +
 include/proto/frontend.h |    2 ++
 include/proto/listener.h |    6 +++++
 include/proto/proxy.h    |    8 ++++++
 include/proto/server.h   |    1 +
 include/types/counters.h |    9 +++++--
 src/backend.c            |   10 ++++++++
 src/dumpstats.c          |   63 ++++++++++++++++++++++++++--------------------
 src/frontend.c           |   19 ++++++++++++++
 src/listener.c           |   18 +++++++++++++
 src/server.c             |    9 +++++++
 src/session.c            |   21 +++++++++++++---
 12 files changed, 134 insertions(+), 33 deletions(-)

diff --git a/include/proto/backend.h b/include/proto/backend.h
index 68ba3d6..cc7338b 100644
--- a/include/proto/backend.h
+++ b/include/proto/backend.h
@@ -45,6 +45,7 @@ void update_backend_weight(struct proxy *px);
 struct server *get_server_sh(struct proxy *px, const char *addr, int len);
 struct server *get_server_uh(struct proxy *px, char *uri, int uri_len);
 int be_lastsession(const struct proxy *be);
+int be_lastactv(const struct proxy *be);
 
 /* set the time of last session on the backend */
 static void inline be_set_sess_last(struct proxy *be)
diff --git a/include/proto/frontend.h b/include/proto/frontend.h
index 77d3206..c44ec47 100644
--- a/include/proto/frontend.h
+++ b/include/proto/frontend.h
@@ -25,6 +25,8 @@
 #include <common/config.h>
 #include <types/session.h>
 
+int fe_lastactv(const struct proxy *fe);
+int fe_lastsession(const struct proxy *fe);
 int frontend_accept(struct session *s);
 
 
diff --git a/include/proto/listener.h b/include/proto/listener.h
index 1473bfd..a8bbfff 100644
--- a/include/proto/listener.h
+++ b/include/proto/listener.h
@@ -26,6 +26,12 @@
 
 #include <types/listener.h>
 
+/* time since last activity/byte */
+int li_lastactv(const struct listener *li);
+
+/* time since last accepted connection */
+int li_lastsession(const struct listener *li);
+
 /* This function adds the specified listener's file descriptor to the polling
  * lists if it is in the LI_LISTEN state. The listener enters LI_READY or
  * LI_FULL state depending on its number of connections.
diff --git a/include/proto/proxy.h b/include/proto/proxy.h
index 4a4b285..5eb5f0f 100644
--- a/include/proto/proxy.h
+++ b/include/proto/proxy.h
@@ -85,6 +85,14 @@ static void inline proxy_inc_fe_conn_ctr(struct listener *l, struct proxy *fe)
 		fe->fe_counters.cps_max = fe->fe_conn_per_sec.curr_ctr;
 }
 
+/* set the last accept time on the designated frontend */
+static void inline proxy_set_fe_conn_last(struct listener *l, struct proxy *fe)
+{
+	fe->fe_counters.last_sess = now.tv_sec;
+	if (l->counters)
+		l->counters->last_sess = now.tv_sec;
+}
+
 /* increase the number of cumulated connections accepted by the designated frontend */
 static void inline proxy_inc_fe_sess_ctr(struct listener *l, struct proxy *fe)
 {
diff --git a/include/proto/server.h b/include/proto/server.h
index 750d8d5..d7ae9d4 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -35,6 +35,7 @@
 #include <proto/freq_ctr.h>
 
 int srv_downtime(const struct server *s);
+int srv_lastactv(const struct server *s);
 int srv_lastsession(const struct server *s);
 int srv_getinter(const struct check *check);
 
diff --git a/include/types/counters.h b/include/types/counters.h
index ecdc7cb..6a517c2 100644
--- a/include/types/counters.h
+++ b/include/types/counters.h
@@ -23,13 +23,14 @@
 #ifndef _TYPES_COUNTERS_H
 #define _TYPES_COUNTERS_H
 
-/* maybe later we might thing about having a different struct for FE and BE */
+/* maybe later we might think about having a different struct for FE and BE */
 struct pxcounters {
 	unsigned int conn_max;                  /* max # of active sessions */
 	long long    cum_conn;                  /* cumulated number of received connections */
 	long long    cum_sess;                  /* cumulated number of accepted connections */
 	long long  cum_lbconn;                  /* cumulated number of sessions processed by load balancing (BE only) */
-	unsigned long last_sess;                /* last session time */
+	unsigned long last_sess;                /* last session time (on BE tracks the last LB connection) */
+											/* (on FE tracks the last accepted connection) */
 
 	unsigned int cps_max;                   /* maximum of new connections received per second */
 	unsigned int sps_max;                   /* maximum of new connections accepted per second (sessions) */
@@ -37,6 +38,7 @@ struct pxcounters {
 
 	long long bytes_in;                     /* number of bytes transferred from the client to the server */
 	long long bytes_out;                    /* number of bytes transferred from the server to the client */
+	unsigned long last_actv;				/* last activity - tracked as last time of bytes transferred */
 
 	long long comp_in;                      /* input bytes fed to the compressor */
 	long long comp_out;                     /* output bytes emitted by the compressor */
@@ -70,9 +72,11 @@ struct licounters {
 
 	long long cum_conn;			/* cumulated number of received connections */
 	long long cum_sess;			/* cumulated number of accepted sessions */
+	unsigned long last_sess;	/* last accepted connection time on the listener */
 
 	long long bytes_in;			/* number of bytes transferred from the client to the server */
 	long long bytes_out;			/* number of bytes transferred from the server to the client */
+	unsigned long last_actv;		/* last activity - tracked as last time of bytes transferred */
 
 	long long denied_req, denied_resp;	/* blocked requests/responses because of security concerns */
 	long long failed_req;			/* failed requests (eg: invalid or timeout) */
@@ -90,6 +94,7 @@ struct srvcounters {
 
 	long long bytes_in;			/* number of bytes transferred from the client to the server */
 	long long bytes_out;			/* number of bytes transferred from the server to the client */
+	unsigned long last_actv;		/* last activity - tracked as last time of bytes transferred */
 
 	long long failed_conns, failed_resp;	/* failed connect() and responses */
 	long long cli_aborts, srv_aborts;	/* aborted responses during DATA phase due to client or server */
diff --git a/src/backend.c b/src/backend.c
index e561919..45e78f2 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -52,6 +52,16 @@
 #include <proto/stream_interface.h>
 #include <proto/task.h>
 
+/* time since last activity/byte */
+int be_lastactv(const struct proxy *be)
+{
+	if (be->be_counters.last_actv)
+		return now.tv_sec - be->be_counters.last_actv;
+
+	return -1;
+}
+
+/* time since last lb connection */
 int be_lastsession(const struct proxy *be)
 {
 	if (be->be_counters.last_sess)
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 0b7dc08..c701c97 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -39,6 +39,7 @@
 #include <types/global.h>
 
 #include <proto/backend.h>
+#include <proto/frontend.h>
 #include <proto/channel.h>
 #include <proto/checks.h>
 #include <proto/compression.h>
@@ -434,7 +435,7 @@ static void stats_dump_csv_header()
 	              "# pxname,svname,"
 	              "qcur,qmax,"
 	              "scur,smax,slim,stot,lastsess,"
-	              "bin,bout,"
+	              "bin,bout,lastactv,"
 	              "dreq,dresp,"
 	              "ereq,econ,eresp,"
 	              "wretr,wredis,"
@@ -2468,10 +2469,11 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
 		chunk_appendf(&trash,
 		              "</table></div></u></td>"
 		              /* sessions: lbtot, lastsess */
-		              "<td></td><td></td>"
+		              "<td></td><td>%s</td>"
 		              /* bytes : in */
 		              "<td>%s</td>"
 		              "",
+		              human_time(fe_lastsession(px), 1),
 		              U2H(px->fe_counters.bytes_in));
 
 		chunk_appendf(&trash,
@@ -2485,6 +2487,8 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
 		              (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "</u>":"");
 
 		chunk_appendf(&trash,
+				      /* last byte */
+				      "<td>%s</td>"
 		              /* denied: req, resp */
 		              "<td>%s</td><td>%s</td>"
 		              /* errors : request, connect, response */
@@ -2496,6 +2500,7 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
 		              /* rest of server: nothing */
 		              "<td class=ac colspan=8></td></tr>"
 		              "",
+		              human_time(fe_lastactv(px), 1),
 		              U2H(px->fe_counters.denied_req), U2H(px->fe_counters.denied_resp),
 		              U2H(px->fe_counters.failed_req),
 		              px->state == PR_STREADY ? "OPEN" :
@@ -2506,9 +2511,9 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
 		              /* pxid, name, queue cur, queue max, */
 		              "%s,FRONTEND,,,"
 		              /* sessions : current, max, limit, total, lastsess */
-		              "%d,%d,%d,%lld,,"
-		              /* bytes : in, out */
-		              "%lld,%lld,"
+		              "%d,%d,%d,%lld,%d,"
+		              /* bytes : in, out, lastactv */
+		              "%lld,%lld,%d,"
 		              /* denied: req, resp */
 		              "%lld,%lld,"
 		              /* errors : request, connect, response */
@@ -2526,8 +2531,8 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
 		              /* check_status, check_code, check_duration */
 		              ",,,",
 		              px->id,
-		              px->feconn, px->fe_counters.conn_max, px->maxconn, px->fe_counters.cum_sess,
-		              px->fe_counters.bytes_in, px->fe_counters.bytes_out,
+		              px->feconn, px->fe_counters.conn_max, px->maxconn, px->fe_counters.cum_sess, fe_lastsession(px),
+		              px->fe_counters.bytes_in, px->fe_counters.bytes_out, fe_lastactv(px),
 		              px->fe_counters.denied_req, px->fe_counters.denied_resp,
 		              px->fe_counters.failed_req,
 		              px->state == PR_STREADY ? "OPEN" :
@@ -2627,13 +2632,14 @@ static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, st
 		              "<td colspan=3>&nbsp;</td>"
 		              /* sessions: current, max, limit, total, lbtot, lastsess */
 		              "<td>%s</td><td>%s</td><td>%s</td>"
-		              "<td>%s</td><td>&nbsp;</td><td>&nbsp;</td>"
-		              /* bytes: in, out */
-		              "<td>%s</td><td>%s</td>"
+		              "<td>%s</td><td>&nbsp;</td><td>%s</td>"
+		              /* bytes: in, out, last */
+		              "<td>%s</td><td>%s</td><td>%s</td>"
 		              "",
 		              (flags & ST_SHLGNDS)?"</u>":"",
 		              U2H(l->nbconn), U2H(l->counters->conn_max), U2H(l->maxconn),
-		              U2H(l->counters->cum_conn), U2H(l->counters->bytes_in), U2H(l->counters->bytes_out));
+		              U2H(l->counters->cum_conn), human_time(li_lastsession(l), 1),
+		              U2H(l->counters->bytes_in), U2H(l->counters->bytes_out), human_time(li_lastactv(l), 1));
 
 		chunk_appendf(&trash,
 		              /* denied: req, resp */
@@ -2656,9 +2662,9 @@ static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, st
 		              /* pxid, name, queue cur, queue max, */
 		              "%s,%s,,,"
 		              /* sessions: current, max, limit, total, lastsess */
-		              "%d,%d,%d,%lld,,"
-		              /* bytes: in, out */
-		              "%lld,%lld,"
+		              "%d,%d,%d,%lld,%d,"
+		              /* bytes: in, out, last */
+		              "%lld,%lld,%d,"
 		              /* denied: req, resp */
 		              "%lld,%lld,"
 		              /* errors: request, connect, response */
@@ -2688,8 +2694,8 @@ static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, st
 		              "\n",
 		              px->id, l->name,
 		              l->nbconn, l->counters->conn_max,
-		              l->maxconn, l->counters->cum_conn,
-		              l->counters->bytes_in, l->counters->bytes_out,
+		              l->maxconn, l->counters->cum_conn, li_lastsession(l),
+		              l->counters->bytes_in, l->counters->bytes_out, li_lastactv(l),
 		              l->counters->denied_req, l->counters->denied_resp,
 		              l->counters->failed_req,
 		              (l->nbconn < l->maxconn) ? "OPEN" : "FULL",
@@ -2835,8 +2841,8 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in
 		              human_time(srv_lastsession(sv), 1));
 
 		chunk_appendf(&trash,
-		              /* bytes : in, out */
-		              "<td>%s</td><td>%s</td>"
+		              /* bytes : in, out, last */
+		              "<td>%s</td><td>%s</td><td>%s</td>"
 		              /* denied: req, resp */
 		              "<td></td><td>%s</td>"
 		              /* errors : request, connect */
@@ -2846,7 +2852,7 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in
 		              /* warnings: retries, redispatches */
 		              "<td>%lld</td><td>%lld</td>"
 		              "",
-		              U2H(sv->counters.bytes_in), U2H(sv->counters.bytes_out),
+		              U2H(sv->counters.bytes_in), U2H(sv->counters.bytes_out), human_time(srv_lastactv(sv), 1),
 		              U2H(sv->counters.failed_secu),
 		              U2H(sv->counters.failed_conns),
 		              U2H(sv->counters.failed_resp),
@@ -2960,8 +2966,8 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in
 		              "%d,%d,"
 		              /* sessions : current, max, limit, total, lastsess */
 		              "%d,%d,%s,%lld,%d,"
-		              /* bytes : in, out */
-		              "%lld,%lld,"
+		              /* bytes : in, out, last */
+		              "%lld,%lld,%d,"
 		              /* denied: req, resp */
 		              ",%lld,"
 		              /* errors : request, connect, response */
@@ -2973,7 +2979,7 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in
 		              sv->nbpend, sv->counters.nbpend_max,
 		              sv->cur_sess, sv->counters.cur_sess_max, LIM2A(sv->maxconn, ""), sv->counters.cum_sess,
 		              srv_lastsession(sv),
-		              sv->counters.bytes_in, sv->counters.bytes_out,
+		              sv->counters.bytes_in, sv->counters.bytes_out, srv_lastactv(sv),
 		              sv->counters.failed_secu,
 		              sv->counters.failed_conns, sv->counters.failed_resp,
 		              sv->counters.retries, sv->counters.redispatches);
@@ -3199,6 +3205,8 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in
 		              (px->be_counters.comp_in || px->be_counters.comp_byp) ? "</u>":"");
 
 		chunk_appendf(&trash,
+		              /* last activity/byte */
+		              "<td>%s</td>"
 		              /* denied: req, resp */
 		              "<td>%s</td><td>%s</td>"
 		              /* errors : request, connect */
@@ -3214,6 +3222,7 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in
 		              "<td class=ac>%s %s</td><td class=ac>&nbsp;</td><td class=ac>%d</td>"
 		              "<td class=ac>%d</td><td class=ac>%d</td>"
 		              "",
+		              human_time(be_lastactv(px), 1),
 		              U2H(px->be_counters.denied_req), U2H(px->be_counters.denied_resp),
 		              U2H(px->be_counters.failed_conns),
 		              U2H(px->be_counters.failed_resp),
@@ -3243,8 +3252,8 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in
 		              "%d,%d,"
 		              /* sessions : current, max, limit, total, lastsess */
 		              "%d,%d,%d,%lld,%d,"
-		              /* bytes : in, out */
-		              "%lld,%lld,"
+		              /* bytes : in, out, last */
+		              "%lld,%lld,%d,"
 		              /* denied: req, resp */
 		              "%lld,%lld,"
 		              /* errors : request, connect, response */
@@ -3269,7 +3278,7 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in
 		              px->nbpend /* or px->totpend ? */, px->be_counters.nbpend_max,
 		              px->beconn, px->be_counters.conn_max, px->fullconn, px->be_counters.cum_conn,
 		              be_lastsession(px),
-		              px->be_counters.bytes_in, px->be_counters.bytes_out,
+		              px->be_counters.bytes_in, px->be_counters.bytes_out, be_lastactv(px),
 		              px->be_counters.denied_req, px->be_counters.denied_resp,
 		              px->be_counters.failed_conns, px->be_counters.failed_resp,
 		              px->be_counters.retries, px->be_counters.redispatches,
@@ -3383,14 +3392,14 @@ static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px
 	              "<th rowspan=2></th>"
 	              "<th colspan=3>Queue</th>"
 	              "<th colspan=3>Session rate</th><th colspan=6>Sessions</th>"
-	              "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
+	              "<th colspan=3>Bytes</th><th colspan=2>Denied</th>"
 	              "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
 	              "<th colspan=9>Server</th>"
 	              "</tr>\n"
 	              "<tr class=\"titre\">"
 	              "<th>Cur</th><th>Max</th><th>Limit</th>"
 	              "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
-	              "<th>Limit</th><th>Total</th><th>LbTot</th><th>Last</th><th>In</th><th>Out</th>"
+	              "<th>Limit</th><th>Total</th><th>LbTot</th><th>Last</th><th>In</th><th>Out</th><th>Last</th>"
 	              "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
 	              "<th>Resp</th><th>Retr</th><th>Redis</th>"
 	              "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
diff --git a/src/frontend.c b/src/frontend.c
index f94c6ab..bee7f7a 100644
--- a/src/frontend.c
+++ b/src/frontend.c
@@ -46,6 +46,25 @@
 #include <proto/stream_interface.h>
 #include <proto/task.h>
 
+/* time since last byte */
+int fe_lastactv(const struct proxy *fe)
+{
+	if (fe->fe_counters.last_actv)
+		return now.tv_sec - fe->fe_counters.last_actv;
+
+	return -1;
+
+}
+
+/* time since last accepted connection */
+int fe_lastsession(const struct proxy *fe)
+{
+	if (fe->fe_counters.last_sess)
+		return now.tv_sec - fe->fe_counters.last_sess;
+
+	return -1;
+}
+
 /* Finish a session accept() for a proxy (TCP or HTTP). It returns a negative
  * value in case of a critical failure which must cause the listener to be
  * disabled, a positive value in case of success, or zero if it is a success
diff --git a/src/listener.c b/src/listener.c
index 7ab1a87..288636f 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -39,6 +39,24 @@ static struct bind_kw_list bind_keywords = {
 	.list = LIST_HEAD_INIT(bind_keywords.list)
 };
 
+/* last activity/byte time */
+int li_lastactv(const struct listener *li)
+{
+	if (li->counters->last_actv)
+		return now.tv_sec - li->counters->last_actv;
+
+	return -1;
+}
+
+/* time since last accepted connection */
+int li_lastsession(const struct listener *li)
+{
+	if (li->counters->last_sess)
+		return now.tv_sec - li->counters->last_sess;
+
+	return -1;
+}
+
 /* This function adds the specified listener's file descriptor to the polling
  * lists if it is in the LI_LISTEN state. The listener enters LI_READY or
  * LI_FULL state depending on its number of connections.
diff --git a/src/server.c b/src/server.c
index ccae58b..3c4f101 100644
--- a/src/server.c
+++ b/src/server.c
@@ -30,6 +30,15 @@ int srv_downtime(const struct server *s)
 	return now.tv_sec - s->last_change + s->down_time;
 }
 
+/* time since last activity/byte */
+int srv_lastactv(const struct server *s)
+{
+	if (s->counters.last_actv)
+		return now.tv_sec - s->counters.last_actv;
+
+	return -1;
+}
+
 int srv_lastsession(const struct server *s)
 {
 	if (s->counters.last_sess)
diff --git a/src/session.c b/src/session.c
index 4768fd3..634d06b 100644
--- a/src/session.c
+++ b/src/session.c
@@ -126,6 +126,7 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
 		p->fe_counters.conn_max = p->feconn;
 
 	proxy_inc_fe_conn_ctr(l, p);
+	proxy_set_fe_conn_last(l, p);
 
 	/* Add the minimum callbacks to prepare the connection's control layer.
 	 * We need this so that we can safely execute the ACLs used by the
@@ -689,14 +690,20 @@ void session_process_counters(struct session *s)
 		s->logs.bytes_in = s->req->total;
 		if (bytes) {
 			s->fe->fe_counters.bytes_in += bytes;
+			s->fe->fe_counters.last_actv += now.tv_sec;
 
 			s->be->be_counters.bytes_in += bytes;
+			s->be->be_counters.last_actv = now.tv_sec;
 
-			if (objt_server(s->target))
+			if (objt_server(s->target)) {
 				objt_server(s->target)->counters.bytes_in += bytes;
+				objt_server(s->target)->counters.last_actv = now.tv_sec;
+			}
 
-			if (s->listener->counters)
+			if (s->listener->counters) {
 				s->listener->counters->bytes_in += bytes;
+				s->listener->counters->last_actv += now.tv_sec;
+			}
 
 			for (i = 0; i < MAX_SESS_STKCTR; i++) {
 				if (!stkctr_entry(&s->stkctr[i]))
@@ -723,14 +730,20 @@ void session_process_counters(struct session *s)
 		s->logs.bytes_out = s->rep->total;
 		if (bytes) {
 			s->fe->fe_counters.bytes_out += bytes;
+			s->fe->fe_counters.last_actv = now.tv_sec;
 
 			s->be->be_counters.bytes_out += bytes;
+			s->be->be_counters.last_actv = now.tv_sec;
 
-			if (objt_server(s->target))
+			if (objt_server(s->target)) {
 				objt_server(s->target)->counters.bytes_out += bytes;
+				objt_server(s->target)->counters.last_actv = now.tv_sec;
+			}
 
-			if (s->listener->counters)
+			if (s->listener->counters) {
 				s->listener->counters->bytes_out += bytes;
+				s->listener->counters->last_actv = now.tv_sec;
+			}
 
 			for (i = 0; i < MAX_SESS_STKCTR; i++) {
 				if (!stkctr_entry(&s->stkctr[i]))
-- 
1.7.10.4

Reply via email to