session CHANGE new timeout introduced
Also described with other options in README.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index de1b049..e9d4293 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,7 +36,9 @@
option(ENABLE_SSH "Enable NETCONF over SSH support (via libssh)" ON)
option(ENABLE_TLS "Enable NETCONF over TLS support (via OpenSSL)" ON)
option(ENABLE_DNSSEC "Enable support for SSHFP retrieval using DNSSEC for SSH (requires OpenSSL and libval)" OFF)
-set(READ_TIMEOUT 30 CACHE STRING "Maximum number of seconds waiting for some expected data")
+set(READ_INACTIVE_TIMEOUT 20 CACHE STRING "Maximum number of seconds waiting for new data once some data have arrived")
+set(READ_ACTIVE_TIMEOUT 300 CACHE STRING "Maximum number of seconds for receiving a full message")
+set(MAX_PSPOLL_THREAD_COUNT 6 CACHE STRING "Maximum number of threads that could simultaneously access a ps_poll structure")
if(ENABLE_DNSSEC AND NOT ENABLE_SSH)
message(WARNING "DNSSEC SSHFP retrieval cannot be used without SSH support.")
diff --git a/README.md b/README.md
index a0d48e1..f7fd08f 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@
datastore implementation are not available in **libnetconf2**. In the case of
the Netopeer2 server, all these features (and much more) are implemented as
part of the server itself or its datastore implementation -
-[**sysrepo**](https://github.com/sysrepo/sysrepo).
+[**sysrepo**](https://github.com/sysrepo/sysrepo).
### Notifications
@@ -221,6 +221,37 @@
```
$ cmake -D CMAKE_BUILD_TYPE:String="Release" ..
```
+
+### Inactive Read Timeout
+
+It is possible to adjust inactive read timeout. It is used when a new message is
+being read and no new data had arrived for this amount of seconds. 20 is the default value.
+
+```
+$ cmake -D READ_INACTIVE_TIMEOUT:String="20" ..
+```
+
+### Active Read Timeout
+
+Active read timeout is used to limit the maximum number of seconds a message is given
+to arrive in its entirety once a beginning is read. The default is 300 (5 minutes).
+
+```
+$ cmake -D READ_ACTIVE_TIMEOUT:String="300" ..
+```
+
+### PSPoll Thread Count
+
+This value limits the maximum number of threads that can concurrently access
+(wait for access) a single pspoll structure. To simplify, how many threads could
+simultaneously call a function whose parameter is one and the same pspoll structure.
+If using **netopeer2-server**, it will warn that this value needs to be adjusted if
+too small.
+
+```
+$ cmake -D MAX_PSPOLL_THREAD_COUNT:String="6" ..
+```
+
### CMake Notes
Note that, with CMake, if you want to change the compiler or its options after
diff --git a/nc_server.h.in b/nc_server.h.in
index a2b6add..f01fc93 100644
--- a/nc_server.h.in
+++ b/nc_server.h.in
@@ -3,12 +3,12 @@
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2's main public header for NETCONF servers.
*
- * Copyright (c) 2015 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* https://opensource.org/licenses/BSD-3-Clause
*/
@@ -18,6 +18,8 @@
@SSH_MACRO@
@TLS_MACRO@
+#define NC_PS_QUEUE_SIZE @MAX_PSPOLL_THREAD_COUNT@
+
#include <libnetconf2/netconf.h>
#include <libnetconf2/log.h>
#include <libnetconf2/messages_server.h>
diff --git a/src/config.h.in b/src/config.h.in
index 0179821..4af2bb9 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -3,7 +3,7 @@
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2 various configuration settings.
*
- * Copyright (c) 2015 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -50,9 +50,14 @@
#define SCHEMAS_DIR "@CMAKE_INSTALL_PREFIX@/@DATA_INSTALL_DIR@"
/*
- * Partial message read timeout in seconds
- * (also used as nc_pollsession lock timeout and internal <get-schema> RPC reply timeout)
+ * Inactive read timeout
*/
-#define NC_READ_TIMEOUT @READ_TIMEOUT@
+#define NC_READ_INACT_TIMEOUT @READ_INACTIVE_TIMEOUT@
+
+/*
+ * Active read timeout in seconds
+ * (also used for internal <get-schema> RPC reply timeout)
+ */
+#define NC_READ_ACT_TIMEOUT @READ_ACTIVE_TIMEOUT@
#endif /* NC_CONFIG_H_ */
diff --git a/src/io.c b/src/io.c
index 33c1e08..6e31e87 100644
--- a/src/io.c
+++ b/src/io.c
@@ -35,11 +35,12 @@
#define BUFFERSIZE 512
static ssize_t
-nc_read(struct nc_session *session, char *buf, size_t count, uint16_t *read_timeout)
+nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_timeout, uint32_t *act_timeout)
{
size_t readd = 0;
ssize_t r = -1;
uint16_t sleep_count = 0;
+ uint32_t cur_inact_timeout = inact_timeout;
assert(session);
assert(buf);
@@ -134,24 +135,32 @@
#endif
}
- /* nothing read */
if (r == 0) {
+ /* nothing read */
usleep(NC_TIMEOUT_STEP);
++sleep_count;
- if (1000000 / NC_TIMEOUT_STEP == sleep_count) {
- /* we slept a full second */
- --(*read_timeout);
+ if (1000 / NC_TIMEOUT_STEP == sleep_count) {
+ /* we slept a full millisecond */
+ --cur_inact_timeout;
+ --(*act_timeout);
sleep_count = 0;
}
- if (!*read_timeout) {
- ERR("Session %u: reading a full NETCONF message timeout elapsed.", session->id);
+ if (!cur_inact_timeout || !*act_timeout) {
+ if (!inact_timeout) {
+ ERR("Session %u: inactive read timeout elapsed.", session->id);
+ } else {
+ ERR("Session %u: active read timeout elapsed.", session->id);
+ }
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_OTHER;
return -1;
}
+ } else {
+ /* something read */
+ readd += r;
+ cur_inact_timeout = inact_timeout;
}
- readd += r;
} while (readd < count);
buf[count] = '\0';
@@ -159,7 +168,7 @@
}
static ssize_t
-nc_read_chunk(struct nc_session *session, size_t len, uint16_t *read_timeout, char **chunk)
+nc_read_chunk(struct nc_session *session, size_t len, uint32_t inact_timeout, uint32_t *act_timeout, char **chunk)
{
ssize_t r;
@@ -176,7 +185,7 @@
return -1;
}
- r = nc_read(session, *chunk, len, read_timeout);
+ r = nc_read(session, *chunk, len, inact_timeout, act_timeout);
if (r <= 0) {
free(*chunk);
return -1;
@@ -189,7 +198,8 @@
}
static ssize_t
-nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint16_t *read_timeout, char **result)
+nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout, uint32_t *act_timeout,
+ char **result)
{
char *chunk = NULL;
size_t size, count = 0, r, len;
@@ -229,7 +239,7 @@
}
/* get another character */
- r = nc_read(session, &(chunk[count]), 1, read_timeout);
+ r = nc_read(session, &(chunk[count]), 1, inact_timeout, act_timeout);
if (r != 1) {
free(chunk);
return -1;
@@ -264,7 +274,8 @@
int ret;
char *msg = NULL, *chunk;
uint64_t chunk_len, len = 0;
- uint16_t read_timeout = NC_READ_TIMEOUT;
+ /* use timeouts in milliseconds instead seconds */
+ uint32_t act_timeout = NC_READ_ACT_TIMEOUT * 1000, inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
struct nc_server_reply *reply;
assert(session && data);
@@ -278,7 +289,7 @@
/* read the message */
switch (session->version) {
case NC_VERSION_10:
- ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &read_timeout, &msg);
+ ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &act_timeout, &msg);
if (ret == -1) {
goto error;
}
@@ -288,11 +299,11 @@
break;
case NC_VERSION_11:
while (1) {
- ret = nc_read_until(session, "\n#", 0, &read_timeout, NULL);
+ ret = nc_read_until(session, "\n#", 0, inact_timeout, &act_timeout, NULL);
if (ret == -1) {
goto error;
}
- ret = nc_read_until(session, "\n", 0, &read_timeout, &chunk);
+ ret = nc_read_until(session, "\n", 0, inact_timeout, &act_timeout, &chunk);
if (ret == -1) {
goto error;
}
@@ -316,7 +327,7 @@
}
/* now we have size of next chunk, so read the chunk */
- ret = nc_read_chunk(session, chunk_len, &read_timeout, &chunk);
+ ret = nc_read_chunk(session, chunk_len, inact_timeout, &act_timeout, &chunk);
if (ret == -1) {
goto error;
}
diff --git a/src/netconf.h b/src/netconf.h
index 1221b08..aea5dbd 100644
--- a/src/netconf.h
+++ b/src/netconf.h
@@ -35,7 +35,7 @@
#define NC_PORT_CH_TLS 4335
/** @brief Microseconds after which tasks are repeated until the full timeout elapses.
- * A second (1000 000) should be divisible by this number without remain.
+ * A millisecond (1000) should be divisible by this number without remain.
*/
#define NC_TIMEOUT_STEP 20
diff --git a/src/session.c b/src/session.c
index afc89f5..b52de4e 100644
--- a/src/session.c
+++ b/src/session.c
@@ -333,7 +333,7 @@
}
if (session->ti_lock) {
- r = nc_timedlock(session->ti_lock, NC_READ_TIMEOUT * 1000, __func__);
+ r = nc_timedlock(session->ti_lock, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
if (r == -1) {
return;
} else if (!r) {
diff --git a/src/session_client.c b/src/session_client.c
index 15fa4a8..5b9902c 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -216,7 +216,7 @@
}
do {
- msg = nc_recv_reply(session, rpc, msgid, NC_READ_TIMEOUT * 1000, 0, &reply);
+ msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, 0, &reply);
} while (msg == NC_MSG_NOTIF);
nc_rpc_free(rpc);
if (msg == NC_MSG_WOULDBLOCK) {
diff --git a/src/session_p.h b/src/session_p.h
index 37c231b..1396473 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -25,6 +25,7 @@
#include "netconf.h"
#include "session.h"
#include "messages_client.h"
+#include "../nc_server.h"
#ifdef NC_ENABLED_SSH
@@ -246,6 +247,16 @@
#define NC_TRANSPORT_TIMEOUT 2000
/**
+ * Timeout in msec for acquiring a lock of a session that is supposed to be freed.
+ */
+#define NC_SESSION_FREE_LOCK_TIMEOUT 5000
+
+/**
+ * Timeout in msec for acquiring a lock of a pollsession structure.
+ */
+#define NC_PS_LOCK_TIMEOUT 500
+
+/**
* Number of sockets kept waiting to be accepted.
*/
#define NC_REVERSE_QUEUE 5
@@ -362,8 +373,6 @@
} opts;
};
-#define NC_PS_QUEUE_SIZE 6
-
/* ACCESS locked */
struct nc_pollsession {
struct nc_session **sessions;
diff --git a/src/session_server.c b/src/session_server.c
index b142dd9..b96432c 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -666,7 +666,11 @@
struct timespec ts;
nc_gettimespec(&ts);
- ts.tv_sec += NC_READ_TIMEOUT;
+ ts.tv_nsec += NC_PS_LOCK_TIMEOUT * 1000000;
+ while (ts.tv_nsec >= 1000000000L) {
+ ts.tv_nsec -= 1000000000L;
+ ++ts.tv_sec;
+ }
/* LOCK */
ret = pthread_mutex_timedlock(&ps->lock, &ts);
@@ -702,7 +706,11 @@
/* is it our turn? */
while (ps->queue[ps->queue_begin] != *id) {
nc_gettimespec(&ts);
- ts.tv_sec += NC_READ_TIMEOUT;
+ ts.tv_nsec += NC_PS_LOCK_TIMEOUT * 1000000;
+ while (ts.tv_nsec >= 1000000000L) {
+ ts.tv_nsec -= 1000000000L;
+ ++ts.tv_sec;
+ }
ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
if (ret) {
@@ -727,7 +735,11 @@
struct timespec ts;
nc_gettimespec(&ts);
- ts.tv_sec += NC_READ_TIMEOUT;
+ ts.tv_nsec += NC_PS_LOCK_TIMEOUT * 1000000;
+ while (ts.tv_nsec >= 1000000000L) {
+ ts.tv_nsec -= 1000000000L;
+ ++ts.tv_sec;
+ }
/* LOCK */
ret = pthread_mutex_timedlock(&ps->lock, &ts);