Besides ID, libvirt should provide several parameters to help the user
distinguish two clients from each other. One of them is the connection
timestamp. This patch also adds a testcase for proper JSON formatting of the
new attribute too (proper formatting of older clients that did not support
this attribute yet is included in the existing tests) - in order to
testGenerateJSON to work, a mock of time_t time(time_t *timer) needed to be
created.
Signed-off-by: Erik Skultety <eskultet(a)redhat.com>
---
src/rpc/virnetserverclient.c | 46 +++++++++++++-
src/rpc/virnetserverclient.h | 1 +
tests/Makefile.am | 7 +++
.../input-data-client-timestamp.json | 70 ++++++++++++++++++++++
.../output-data-client-timestamp.json | 70 ++++++++++++++++++++++
tests/virnetdaemonmock.c | 34 +++++++++++
tests/virnetdaemontest.c | 4 +-
7 files changed, 228 insertions(+), 4 deletions(-)
create mode 100644 tests/virnetdaemondata/input-data-client-timestamp.json
create mode 100644 tests/virnetdaemondata/output-data-client-timestamp.json
create mode 100644 tests/virnetdaemonmock.c
diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c
index 7233773..a1543b9 100644
--- a/src/rpc/virnetserverclient.c
+++ b/src/rpc/virnetserverclient.c
@@ -85,6 +85,13 @@ struct _virNetServerClient
virIdentityPtr identity;
+ /* Connection timestamp, i.e. when a client connected to the daemon (UTC).
+ * For old clients restored by post-exec-restart, which did not have this
+ * attribute, value of 0 (epoch time) is used to indicate we have no
+ * information about their connection time.
+ */
+ time_t conn_time;
+
/* Count of messages in the 'tx' queue,
* and the server worker pool queue
* ie RPC calls in progress. Does not count
@@ -355,7 +362,8 @@ virNetServerClientNewInternal(unsigned long long id,
virNetTLSContextPtr tls,
#endif
bool readonly,
- size_t nrequests_max)
+ size_t nrequests_max,
+ time_t timestamp)
{
virNetServerClientPtr client;
@@ -373,6 +381,7 @@ virNetServerClientNewInternal(unsigned long long id,
client->tlsCtxt = virObjectRef(tls);
#endif
client->nrequests_max = nrequests_max;
+ client->conn_time = timestamp;
client->sockTimer = virEventAddTimeout(-1, virNetServerClientSockTimerFunc,
client, NULL);
@@ -413,6 +422,7 @@ virNetServerClientPtr virNetServerClientNew(unsigned long long id,
void *privOpaque)
{
virNetServerClientPtr client;
+ time_t now;
VIR_DEBUG("sock=%p auth=%d tls=%p", sock, auth,
#ifdef WITH_GNUTLS
@@ -422,11 +432,17 @@ virNetServerClientPtr virNetServerClientNew(unsigned long long id,
#endif
);
+ if ((now = time(NULL)) == (time_t) - 1) {
+ virReportSystemError(errno, "%s", _("failed to get current
time"));
+ return NULL;
+ }
+
if (!(client = virNetServerClientNewInternal(id, sock, auth,
#ifdef WITH_GNUTLS
tls,
#endif
- readonly, nrequests_max)))
+ readonly, nrequests_max,
+ now)))
return NULL;
if (privNew) {
@@ -456,6 +472,7 @@ virNetServerClientPtr
virNetServerClientNewPostExecRestart(virJSONValuePtr objec
bool readonly;
unsigned int nrequests_max;
unsigned long long id;
+ time_t timestamp;
if (virJSONValueObjectGetNumberInt(object, "auth", &auth) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -492,6 +509,18 @@ virNetServerClientPtr
virNetServerClientNewPostExecRestart(virJSONValuePtr objec
}
}
+ if (!virJSONValueObjectHasKey(object, "conn_time")) {
+ timestamp = 0;
+ } else {
+ if (virJSONValueObjectGetNumberLong(object, "conn_time",
+ (long long *) ×tamp) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Malformed conn_time field in JSON "
+ "state document"));
+ return NULL;
+ }
+ }
+
if (!(sock = virNetSocketNewPostExecRestart(child))) {
virObjectUnref(sock);
return NULL;
@@ -504,7 +533,8 @@ virNetServerClientPtr
virNetServerClientNewPostExecRestart(virJSONValuePtr objec
NULL,
#endif
readonly,
- nrequests_max))) {
+ nrequests_max,
+ timestamp))) {
virObjectUnref(sock);
return NULL;
}
@@ -552,6 +582,11 @@ virJSONValuePtr
virNetServerClientPreExecRestart(virNetServerClientPtr client)
if (virJSONValueObjectAppendNumberUint(object, "nrequests_max",
client->nrequests_max) < 0)
goto error;
+ if (client->conn_time &&
+ virJSONValueObjectAppendNumberLong(object, "conn_time",
+ client->conn_time) < 0)
+ goto error;
+
if (!(child = virNetSocketPreExecRestart(client->sock)))
goto error;
@@ -610,6 +645,11 @@ unsigned long long virNetServerClientGetID(virNetServerClientPtr
client)
return client->id;
}
+long long virNetServerClientGetTimestamp(virNetServerClientPtr client)
+{
+ return client->conn_time;
+}
+
#ifdef WITH_GNUTLS
bool virNetServerClientHasTLSSession(virNetServerClientPtr client)
{
diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h
index 95edb06..e68ef76 100644
--- a/src/rpc/virnetserverclient.h
+++ b/src/rpc/virnetserverclient.h
@@ -82,6 +82,7 @@ int virNetServerClientGetAuth(virNetServerClientPtr client);
void virNetServerClientSetAuth(virNetServerClientPtr client, int auth);
bool virNetServerClientGetReadonly(virNetServerClientPtr client);
unsigned long long virNetServerClientGetID(virNetServerClientPtr client);
+long long virNetServerClientGetTimestamp(virNetServerClientPtr client);
# ifdef WITH_GNUTLS
bool virNetServerClientHasTLSSession(virNetServerClientPtr client);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8cf53bf..6d8fa00 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -408,6 +408,7 @@ EXTRA_DIST += $(test_scripts)
test_libraries = libshunload.la \
virportallocatormock.la \
+ virnetdaemonmock.la \
virnetserverclientmock.la \
vircgroupmock.la \
virpcimock.la \
@@ -927,6 +928,12 @@ virnetdaemontest_SOURCES = \
virnetdaemontest_CFLAGS = $(XDR_CFLAGS) $(AM_CFLAGS)
virnetdaemontest_LDADD = $(LDADDS)
+virnetdaemonmock_la_SOURCES = \
+ virnetdaemonmock.c
+virnetdaemonmock_la_CFLAGS = $(AM_CFLAGS)
+virnetdaemonmock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS)
+virnetdaemonmock_la_LIBADD = $(MOCKLIBS_LIBS)
+
virnetserverclienttest_SOURCES = \
virnetserverclienttest.c \
testutils.h testutils.c
diff --git a/tests/virnetdaemondata/input-data-client-timestamp.json
b/tests/virnetdaemondata/input-data-client-timestamp.json
new file mode 100644
index 0000000..d069997
--- /dev/null
+++ b/tests/virnetdaemondata/input-data-client-timestamp.json
@@ -0,0 +1,70 @@
+{
+ "servers": {
+ "testServer0": {
+ "min_workers": 10,
+ "max_workers": 50,
+ "priority_workers": 5,
+ "max_clients": 100,
+ "max_anonymous_clients": 10,
+ "keepaliveInterval": 120,
+ "keepaliveCount": 5,
+ "next_client_id": 3,
+ "services": [
+ {
+ "auth": 0,
+ "readonly": true,
+ "nrequests_client_max": 2,
+ "socks": [
+ {
+ "fd": 100,
+ "errfd": -1,
+ "pid": 0,
+ "isClient": false
+ }
+ ]
+ },
+ {
+ "auth": 2,
+ "readonly": false,
+ "nrequests_client_max": 5,
+ "socks": [
+ {
+ "fd": 101,
+ "errfd": -1,
+ "pid": 0,
+ "isClient": false
+ }
+ ]
+ }
+ ],
+ "clients": [
+ {
+ "id": 1,
+ "auth": 1,
+ "readonly": true,
+ "nrequests_max": 15,
+ "conn_time": 1234567890,
+ "sock": {
+ "fd": 102,
+ "errfd": -1,
+ "pid": -1,
+ "isClient": true
+ }
+ },
+ {
+ "id": 2,
+ "auth": 2,
+ "readonly": true,
+ "nrequests_max": 66,
+ "conn_time": 1234567890,
+ "sock": {
+ "fd": 103,
+ "errfd": -1,
+ "pid": -1,
+ "isClient": true
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/virnetdaemondata/output-data-client-timestamp.json
b/tests/virnetdaemondata/output-data-client-timestamp.json
new file mode 100644
index 0000000..d069997
--- /dev/null
+++ b/tests/virnetdaemondata/output-data-client-timestamp.json
@@ -0,0 +1,70 @@
+{
+ "servers": {
+ "testServer0": {
+ "min_workers": 10,
+ "max_workers": 50,
+ "priority_workers": 5,
+ "max_clients": 100,
+ "max_anonymous_clients": 10,
+ "keepaliveInterval": 120,
+ "keepaliveCount": 5,
+ "next_client_id": 3,
+ "services": [
+ {
+ "auth": 0,
+ "readonly": true,
+ "nrequests_client_max": 2,
+ "socks": [
+ {
+ "fd": 100,
+ "errfd": -1,
+ "pid": 0,
+ "isClient": false
+ }
+ ]
+ },
+ {
+ "auth": 2,
+ "readonly": false,
+ "nrequests_client_max": 5,
+ "socks": [
+ {
+ "fd": 101,
+ "errfd": -1,
+ "pid": 0,
+ "isClient": false
+ }
+ ]
+ }
+ ],
+ "clients": [
+ {
+ "id": 1,
+ "auth": 1,
+ "readonly": true,
+ "nrequests_max": 15,
+ "conn_time": 1234567890,
+ "sock": {
+ "fd": 102,
+ "errfd": -1,
+ "pid": -1,
+ "isClient": true
+ }
+ },
+ {
+ "id": 2,
+ "auth": 2,
+ "readonly": true,
+ "nrequests_max": 66,
+ "conn_time": 1234567890,
+ "sock": {
+ "fd": 103,
+ "errfd": -1,
+ "pid": -1,
+ "isClient": true
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/virnetdaemonmock.c b/tests/virnetdaemonmock.c
new file mode 100644
index 0000000..6d807a5
--- /dev/null
+++ b/tests/virnetdaemonmock.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Erik Skultety <eskultet(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include "internal.h"
+#include <time.h>
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+time_t time(time_t *t)
+{
+ const time_t ret = 1234567890;
+ if (t)
+ *t = ret;
+ return ret;
+}
diff --git a/tests/virnetdaemontest.c b/tests/virnetdaemontest.c
index b98c148..714f8d7 100644
--- a/tests/virnetdaemontest.c
+++ b/tests/virnetdaemontest.c
@@ -339,15 +339,17 @@ mymain(void)
EXEC_RESTART_TEST("admin-server-names", 2);
EXEC_RESTART_TEST("no-keepalive-required", 2);
EXEC_RESTART_TEST("client-ids", 1);
+ EXEC_RESTART_TEST("client-timestamp", 1);
EXEC_RESTART_TEST_FAIL("anon-clients", 2);
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
+VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virnetdaemonmock.so")
#else
static int
mymain(void)
{
return EXIT_AM_SKIP;
}
-#endif
VIRT_TEST_MAIN(mymain);
+#endif
--
2.4.11