libnetconf CHANGE support for new draft netconf-server nodes
diff --git a/src/session_server.c b/src/session_server.c
index 9f0ac4b..cf697db 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -174,7 +174,7 @@
}
int
-nc_sock_listen_inet(const char *address, uint16_t port)
+nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
{
int opt;
int is_ipv4, sock;
@@ -207,7 +207,7 @@
goto fail;
}
- if (nc_sock_enable_keepalive(sock)) {
+ if (nc_sock_enable_keepalive(sock, ka)) {
goto fail;
}
@@ -1765,8 +1765,12 @@
ret = -1;
goto cleanup;
}
+ memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
+ server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
+ server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
+ server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
if (!server_opts.binds) {
@@ -1775,10 +1779,8 @@
goto cleanup;
}
- server_opts.binds[server_opts.endpt_count - 1].address = NULL;
- server_opts.binds[server_opts.endpt_count - 1].port = 0;
+ memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
server_opts.binds[server_opts.endpt_count - 1].sock = -1;
- server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
switch (ti) {
#ifdef NC_ENABLED_SSH
@@ -1832,145 +1834,6 @@
return ret;
}
-int
-nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
-{
- struct nc_endpt *endpt;
- struct nc_bind *bind = NULL;
- uint16_t i;
- int sock = -1, set_addr, ret = 0;
-
- if (!endpt_name) {
- ERRARG("endpt_name");
- return -1;
- } else if ((!address && !port) || (address && port)) {
- ERRARG("address and port");
- return -1;
- }
-
- if (address) {
- set_addr = 1;
- } else {
- set_addr = 0;
- }
-
- /* BIND LOCK */
- pthread_mutex_lock(&server_opts.bind_lock);
-
- /* ENDPT LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
- if (!endpt) {
- /* BIND UNLOCK */
- pthread_mutex_unlock(&server_opts.bind_lock);
- return -1;
- }
-
- bind = &server_opts.binds[i];
-
- if (set_addr) {
- port = bind->port;
- } else {
- address = bind->address;
- }
-
- if (!set_addr && endpt->ti == NC_TI_UNIX) {
- ret = -1;
- goto cleanup;
- }
-
- /* we have all the information we need to create a listening socket */
- if (address && (port || endpt->ti == NC_TI_UNIX)) {
- /* create new socket, close the old one */
- if (endpt->ti == NC_TI_UNIX)
- sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
- else
- sock = nc_sock_listen_inet(address, port);
- if (sock == -1) {
- ret = -1;
- goto cleanup;
- }
-
- if (bind->sock > -1) {
- close(bind->sock);
- }
- bind->sock = sock;
- } /* else we are just setting address or port */
-
- if (set_addr) {
- lydict_remove(server_opts.ctx, bind->address);
- bind->address = lydict_insert(server_opts.ctx, address, 0);
- } else {
- bind->port = port;
- }
-
- if (sock > -1) {
-#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
- VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
-#elif defined(NC_ENABLED_SSH)
- VRB("Listening on %s:%u for SSH connections.", address, port);
-#else
- VRB("Listening on %s:%u for TLS connections.", address, port);
-#endif
- }
-
-cleanup:
- /* ENDPT UNLOCK */
- pthread_rwlock_unlock(&server_opts.endpt_lock);
-
- /* BIND UNLOCK */
- pthread_mutex_unlock(&server_opts.bind_lock);
-
- return ret;
-}
-
-API int
-nc_server_endpt_set_address(const char *endpt_name, const char *address)
-{
- return nc_server_endpt_set_address_port(endpt_name, address, 0);
-}
-
-API int
-nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
-{
- return nc_server_endpt_set_address_port(endpt_name, NULL, port);
-}
-
-API int
-nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
-{
- struct nc_endpt *endpt;
- uint16_t i;
- int ret = 0;
-
- if (!endpt_name) {
- ERRARG("endpt_name");
- return -1;
- } else if (mode == 0) {
- ERRARG("mode");
- return -1;
- }
-
- /* ENDPT LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
- if (!endpt)
- return -1;
-
- if (endpt->ti != NC_TI_UNIX) {
- ret = -1;
- goto cleanup;
- }
-
- endpt->opts.unixsock->mode = mode;
- endpt->opts.unixsock->uid = uid;
- endpt->opts.unixsock->gid = gid;
-
-cleanup:
- /* ENDPT UNLOCK */
- pthread_rwlock_unlock(&server_opts.endpt_lock);
-
- return ret;
-}
-
API int
nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
{
@@ -2087,6 +1950,209 @@
return ret;
}
+API int
+nc_server_endpt_count(void)
+{
+ return server_opts.endpt_count;
+}
+
+int
+nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
+{
+ struct nc_endpt *endpt;
+ struct nc_bind *bind = NULL;
+ uint16_t i;
+ int sock = -1, set_addr, ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ } else if ((!address && !port) || (address && port)) {
+ ERRARG("address and port");
+ return -1;
+ }
+
+ if (address) {
+ set_addr = 1;
+ } else {
+ set_addr = 0;
+ }
+
+ /* BIND LOCK */
+ pthread_mutex_lock(&server_opts.bind_lock);
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
+ if (!endpt) {
+ /* BIND UNLOCK */
+ pthread_mutex_unlock(&server_opts.bind_lock);
+ return -1;
+ }
+
+ bind = &server_opts.binds[i];
+
+ if (set_addr) {
+ port = bind->port;
+ } else {
+ address = bind->address;
+ }
+
+ if (!set_addr && endpt->ti == NC_TI_UNIX) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* we have all the information we need to create a listening socket */
+ if (address && (port || endpt->ti == NC_TI_UNIX)) {
+ /* create new socket, close the old one */
+ if (endpt->ti == NC_TI_UNIX)
+ sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
+ else
+ sock = nc_sock_listen_inet(address, port, &endpt->ka);
+ if (sock == -1) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (bind->sock > -1) {
+ close(bind->sock);
+ }
+ bind->sock = sock;
+ } /* else we are just setting address or port */
+
+ if (set_addr) {
+ lydict_remove(server_opts.ctx, bind->address);
+ bind->address = lydict_insert(server_opts.ctx, address, 0);
+ } else {
+ bind->port = port;
+ }
+
+ if (sock > -1) {
+#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
+ VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
+#elif defined(NC_ENABLED_SSH)
+ VRB("Listening on %s:%u for SSH connections.", address, port);
+#else
+ VRB("Listening on %s:%u for TLS connections.", address, port);
+#endif
+ }
+
+cleanup:
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ /* BIND UNLOCK */
+ pthread_mutex_unlock(&server_opts.bind_lock);
+
+ return ret;
+}
+
+API int
+nc_server_endpt_set_address(const char *endpt_name, const char *address)
+{
+ return nc_server_endpt_set_address_port(endpt_name, address, 0);
+}
+
+API int
+nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
+{
+ return nc_server_endpt_set_address_port(endpt_name, NULL, port);
+}
+
+API int
+nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
+{
+ struct nc_endpt *endpt;
+ uint16_t i;
+ int ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ } else if (mode == 0) {
+ ERRARG("mode");
+ return -1;
+ }
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
+ if (!endpt)
+ return -1;
+
+ if (endpt->ti != NC_TI_UNIX) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ endpt->opts.unixsock->mode = mode;
+ endpt->opts.unixsock->uid = uid;
+ endpt->opts.unixsock->gid = gid;
+
+cleanup:
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ return ret;
+}
+
+API int
+nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
+{
+ struct nc_endpt *endpt;
+ int ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
+ if (!endpt) {
+ return -1;
+ }
+
+ endpt->ka.enabled = (enable ? 1 : 0);
+
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ return ret;
+}
+
+API int
+nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
+{
+ struct nc_endpt *endpt;
+ int ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
+ if (!endpt) {
+ return -1;
+ }
+
+ if (idle_time > -1) {
+ endpt->ka.idle_time = idle_time;
+ }
+ if (max_probes > -1) {
+ endpt->ka.max_probes = max_probes;
+ }
+ if (probe_interval > -1) {
+ endpt->ka.probe_interval = probe_interval;
+ }
+
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ return ret;
+}
+
API NC_MSG_TYPE
nc_accept(int timeout, struct nc_session **session)
{
@@ -2455,10 +2521,12 @@
return -1;
}
+ memset(&client->ch_endpts[client->ch_endpt_count - 1], 0, sizeof *client->ch_endpts);
client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
- client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
- client->ch_endpts[client->ch_endpt_count - 1].port = 0;
client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
+ client->ch_endpts[client->ch_endpt_count - 1].ka.idle_time = 1;
+ client->ch_endpts[client->ch_endpt_count - 1].ka.max_probes = 10;
+ client->ch_endpts[client->ch_endpt_count - 1].ka.probe_interval = 5;
/* UNLOCK */
nc_server_ch_client_unlock(client);
@@ -2613,6 +2681,95 @@
}
API int
+nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
+{
+ uint16_t i;
+ int ret = -1;
+ struct nc_ch_client *client;
+
+ if (!client_name) {
+ ERRARG("client_name");
+ return -1;
+ } else if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* LOCK */
+ client = nc_server_ch_client_lock(client_name, 0, NULL);
+ if (!client) {
+ return -1;
+ }
+
+ for (i = 0; i < client->ch_endpt_count; ++i) {
+ if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
+ client->ch_endpts[i].ka.enabled = (enable ? 1 : 0);
+
+ ret = 0;
+ break;
+ }
+ }
+
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+
+ if (ret == -1) {
+ ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
+ }
+
+ return ret;
+}
+
+API int
+nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
+ int probe_interval)
+{
+ uint16_t i;
+ int ret = -1;
+ struct nc_ch_client *client;
+
+ if (!client_name) {
+ ERRARG("client_name");
+ return -1;
+ } else if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* LOCK */
+ client = nc_server_ch_client_lock(client_name, 0, NULL);
+ if (!client) {
+ return -1;
+ }
+
+ for (i = 0; i < client->ch_endpt_count; ++i) {
+ if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
+ if (idle_time > -1) {
+ client->ch_endpts[i].ka.idle_time = idle_time;
+ }
+ if (max_probes > -1) {
+ client->ch_endpts[i].ka.max_probes = max_probes;
+ }
+ if (probe_interval > -1) {
+ client->ch_endpts[i].ka.probe_interval = probe_interval;
+ }
+
+ ret = 0;
+ break;
+ }
+ }
+
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+
+ if (ret == -1) {
+ ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
+ }
+
+ return ret;
+}
+
+API int
nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
{
struct nc_ch_client *client;
@@ -2637,13 +2794,12 @@
/* set default options */
switch (conn_type) {
case NC_CH_PERSIST:
- client->conn.persist.idle_timeout = 86400;
- client->conn.persist.ka_max_wait = 30;
- client->conn.persist.ka_max_attempts = 3;
+ /* no options */
break;
case NC_CH_PERIOD:
- client->conn.period.idle_timeout = 300;
- client->conn.period.reconnect_timeout = 60;
+ client->conn.period.period = 60;
+ client->conn.period.anchor_time = 0;
+ client->conn.period.idle_timeout = 120;
break;
default:
ERRINT;
@@ -2658,7 +2814,41 @@
}
API int
-nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
+nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
+{
+ struct nc_ch_client *client;
+
+ if (!client_name) {
+ ERRARG("client_name");
+ return -1;
+ } else if (!period) {
+ ERRARG("period");
+ return -1;
+ }
+
+ /* LOCK */
+ client = nc_server_ch_client_lock(client_name, 0, NULL);
+ if (!client) {
+ return -1;
+ }
+
+ if (client->conn_type != NC_CH_PERIOD) {
+ ERR("Call Home client \"%s\" is not of periodic connection type.");
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+ return -1;
+ }
+
+ client->conn.period.period = period;
+
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+
+ return 0;
+}
+
+API int
+nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
{
struct nc_ch_client *client;
@@ -2673,14 +2863,14 @@
return -1;
}
- if (client->conn_type != NC_CH_PERSIST) {
- ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
+ if (client->conn_type != NC_CH_PERIOD) {
+ ERR("Call Home client \"%s\" is not of periodic connection type.");
/* UNLOCK */
nc_server_ch_client_unlock(client);
return -1;
}
- client->conn.persist.idle_timeout = idle_timeout;
+ client->conn.period.anchor_time = anchor_time;
/* UNLOCK */
nc_server_ch_client_unlock(client);
@@ -2689,72 +2879,7 @@
}
API int
-nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
-{
- struct nc_ch_client *client;
-
- if (!client_name) {
- ERRARG("client_name");
- return -1;
- } else if (!max_wait) {
- ERRARG("max_wait");
- return -1;
- }
-
- /* LOCK */
- client = nc_server_ch_client_lock(client_name, 0, NULL);
- if (!client) {
- return -1;
- }
-
- if (client->conn_type != NC_CH_PERSIST) {
- ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
- return -1;
- }
-
- client->conn.persist.ka_max_wait = max_wait;
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return 0;
-}
-
-API int
-nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
-{
- struct nc_ch_client *client;
-
- if (!client_name) {
- ERRARG("client_name");
- return -1;
- }
-
- /* LOCK */
- client = nc_server_ch_client_lock(client_name, 0, NULL);
- if (!client) {
- return -1;
- }
-
- if (client->conn_type != NC_CH_PERSIST) {
- ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
- return -1;
- }
-
- client->conn.persist.ka_max_attempts = max_attempts;
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return 0;
-}
-
-API int
-nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
+nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
{
struct nc_ch_client *client;
@@ -2785,40 +2910,6 @@
}
API int
-nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
-{
- struct nc_ch_client *client;
-
- if (!client_name) {
- ERRARG("client_name");
- return -1;
- } else if (!reconnect_timeout) {
- ERRARG("reconnect_timeout");
- return -1;
- }
-
- /* LOCK */
- client = nc_server_ch_client_lock(client_name, 0, NULL);
- if (!client) {
- return -1;
- }
-
- if (client->conn_type != NC_CH_PERIOD) {
- ERR("Call Home client \"%s\" is not of periodic connection type.");
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
- return -1;
- }
-
- client->conn.period.reconnect_timeout = reconnect_timeout;
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return 0;
-}
-
-API int
nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
{
struct nc_ch_client *client;
@@ -2878,7 +2969,7 @@
struct timespec ts_cur;
char *ip_host;
- sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending, &ip_host);
+ sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
if (sock < 0) {
return NC_MSG_ERROR;
}
@@ -3043,11 +3134,10 @@
break;
}
- if (client->conn_type == NC_CH_PERSIST) {
- /* TODO keep-alives */
- idle_timeout = client->conn.persist.idle_timeout;
- } else {
+ if (client->conn_type == NC_CH_PERIOD) {
idle_timeout = client->conn.period.idle_timeout;
+ } else {
+ idle_timeout = 0;
}
nc_gettimespec_mono(&ts);
@@ -3085,6 +3175,7 @@
struct nc_session *session;
struct nc_ch_client *client;
uint32_t client_id;
+ time_t reconnect_in;
/* LOCK */
client = nc_server_ch_client_with_endpt_lock(data->client_name);
@@ -3121,13 +3212,14 @@
}
/* session changed status -> it was disconnected for whatever reason,
- * persistent connection immediately tries to reconnect, periodic waits some first */
+ * persistent connection immediately tries to reconnect, periodic connects at specific times */
if (client->conn_type == NC_CH_PERIOD) {
/* UNLOCK */
nc_server_ch_client_unlock(client);
- /* TODO wake up sometimes to check for new notifications */
- usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
+ /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
+ reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
+ sleep(reconnect_in);
/* LOCK */
client = nc_server_ch_client_with_endpt_lock(data->client_name);
@@ -3143,7 +3235,7 @@
/* set next endpoint to try */
if (client->start_with == NC_CH_FIRST_LISTED) {
next_endpt_index = 0;
- } else {
+ } else if (client->start_with == NC_CH_LAST_CONNECTED) {
/* we keep the current one but due to unlock/lock we have to find it again */
for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
@@ -3154,6 +3246,9 @@
/* endpoint was removed, start with the first one */
next_endpt_index = 0;
}
+ } else {
+ /* just get a random index */
+ next_endpt_index = rand() % client->ch_endpt_count;
}
} else {
@@ -3258,12 +3353,6 @@
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
-API int
-nc_server_endpt_count(void)
-{
- return server_opts.endpt_count;
-}
-
API time_t
nc_session_get_start_time(const struct nc_session *session)
{