all CHANGE larger api change to enable server stats collecting
All the information required by ietf-netconf-monitoring
model can now be collected.
diff --git a/src/session_server.c b/src/session_server.c
index 47faab2..478ebfa 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -467,31 +467,33 @@
return server_opts.idle_timeout;
}
-API int
+API NC_MSG_TYPE
nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
{
+ NC_MSG_TYPE msgtype;
+
if (!server_opts.ctx) {
ERRINIT;
- return -1;
+ return NC_MSG_ERROR;
} else if (fdin < 0) {
ERRARG("fdin");
- return -1;
+ return NC_MSG_ERROR;
} else if (fdout < 0) {
ERRARG("fdout");
- return -1;
+ return NC_MSG_ERROR;
} else if (!username) {
ERRARG("username");
- return -1;
+ return NC_MSG_ERROR;
} else if (!session) {
ERRARG("session");
- return -1;
+ return NC_MSG_ERROR;
}
/* prepare session structure */
*session = calloc(1, sizeof **session);
if (!(*session)) {
ERRMEM;
- return -1;
+ return NC_MSG_ERROR;
}
(*session)->status = NC_STATUS_STARTING;
(*session)->side = NC_SERVER;
@@ -511,18 +513,16 @@
pthread_spin_unlock(&server_opts.sid_lock);
/* NETCONF handshake */
- if (nc_handshake(*session)) {
- goto fail;
+ msgtype = nc_handshake(*session);
+ if (msgtype != NC_MSG_HELLO) {
+ nc_session_free(*session, NULL);
+ *session = NULL;
+ return msgtype;
}
(*session)->session_start = (*session)->last_rpc = time(NULL);
(*session)->status = NC_STATUS_RUNNING;
- return 0;
-
-fail:
- nc_session_free(*session, NULL);
- *session = NULL;
- return -1;
+ return msgtype;
}
int
@@ -787,8 +787,13 @@
return count;
}
-/* must be called holding the session lock! */
-static NC_MSG_TYPE
+/* must be called holding the session lock!
+ * returns: NC_PSPOLL_ERROR,
+ * NC_PSPOLL_BAD_RPC,
+ * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
+ * NC_PSPOLL_RPC
+ */
+static int
nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
{
struct lyxml_elem *xml = NULL;
@@ -798,13 +803,13 @@
if (!session) {
ERRARG("session");
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
} else if (!rpc) {
ERRARG("rpc");
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
} else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
ERR("Session %u: invalid session to receive RPCs.", session->id);
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
}
msgtype = nc_read_msg(session, &xml);
@@ -826,49 +831,58 @@
nc_server_reply_free(reply);
if (ret == -1) {
ERR("Session %u: failed to write reply.", session->id);
- msgtype = NC_MSG_ERROR;
- } else {
- msgtype = NC_MSG_NONE;
}
+ ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
+ } else {
+ ret = NC_PSPOLL_RPC;
}
(*rpc)->root = xml;
break;
case NC_MSG_HELLO:
ERR("Session %u: received another <hello> message.", session->id);
+ ret = NC_PSPOLL_BAD_RPC;
goto error;
case NC_MSG_REPLY:
ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
+ ret = NC_PSPOLL_BAD_RPC;
goto error;
case NC_MSG_NOTIF:
ERR("Session %u: received <notification> from a NETCONF client.", session->id);
+ ret = NC_PSPOLL_BAD_RPC;
goto error;
default:
- /* NC_MSG_ERROR - pass it out;
+ /* NC_MSG_ERROR,
* NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
*/
+ ret = NC_PSPOLL_ERROR;
break;
}
- return msgtype;
+ return ret;
error:
/* cleanup */
lyxml_free(server_opts.ctx, xml);
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
}
-/* must be called holding the session lock! */
-static NC_MSG_TYPE
+/* must be called holding the session lock!
+ * returns: NC_PSPOLL_ERROR,
+ * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
+ * NC_PSPOLL_REPLY_ERROR,
+ * 0
+ */
+static int
nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
{
nc_rpc_clb clb;
struct nc_server_reply *reply;
- int ret;
+ int ret = 0, r;
if (!rpc) {
ERRINT;
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
}
/* no callback, reply with a not-implemented error */
@@ -882,50 +896,53 @@
if (!reply) {
reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
}
+ r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
+ if (reply->type == NC_RPL_ERROR) {
+ ret |= NC_PSPOLL_REPLY_ERROR;
+ }
+ nc_server_reply_free(reply);
- ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
+ if (r == -1) {
+ ERR("Session %u: failed to write reply.", session->id);
+ ret |= NC_PSPOLL_ERROR;
+ }
/* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
session->status = NC_STATUS_INVALID;
}
- if (ret == -1) {
- ERR("Session %u: failed to write reply.", session->id);
- nc_server_reply_free(reply);
- return NC_MSG_ERROR;
- }
- nc_server_reply_free(reply);
-
- return NC_MSG_REPLY;
+ return ret;
}
API int
-nc_ps_poll(struct nc_pollsession *ps, int timeout)
+nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
{
int ret;
uint16_t i;
time_t cur_time;
- NC_MSG_TYPE msgtype;
- struct nc_session *session;
+ struct nc_session *cur_session;
struct nc_server_rpc *rpc = NULL;
if (!ps || !ps->session_count) {
ERRARG("ps");
- return -1;
+ return NC_PSPOLL_ERROR;
}
cur_time = time(NULL);
/* LOCK */
if (nc_ps_lock(ps)) {
- return -1;
+ return NC_PSPOLL_ERROR;
}
for (i = 0; i < ps->session_count; ++i) {
if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
ERR("Session %u: session not running.", ps->sessions[i]->id);
- ret = -1;
+ ret = NC_PSPOLL_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
}
@@ -934,7 +951,10 @@
ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
ps->sessions[i]->status = NC_STATUS_INVALID;
ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
- ret = 3;
+ ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
}
@@ -950,7 +970,12 @@
/* no leftover event */
i = 0;
ret = poll(ps->pfds, ps->session_count, timeout);
- if (ret < 1) {
+ if (ret < 0) {
+ ERR("Poll failed (%s).", strerror(errno));
+ ret = NC_PSPOLL_ERROR;
+ goto finish;
+ } else if (!ret) {
+ ret = NC_PSPOLL_TIMEOUT;
goto finish;
}
}
@@ -961,13 +986,19 @@
ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
ps->sessions[i]->status = NC_STATUS_INVALID;
ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
- ret = 3;
+ ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
} else if (ps->pfds[i].revents & POLLERR) {
ERR("Session %u: communication socket error.", ps->sessions[i]->id);
ps->sessions[i]->status = NC_STATUS_INVALID;
ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
- ret = 3;
+ ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
} else if (ps->pfds[i].revents & POLLIN) {
#ifdef NC_ENABLED_SSH
@@ -978,7 +1009,7 @@
ret = nc_ssh_pollin(ps->sessions[i], timeout);
/* clear POLLIN on sessions sharing this session's SSH session */
- if ((ret == 1) || (ret >= 4)) {
+ if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
for (j = i + 1; j < ps->session_count; ++j) {
if (ps->pfds[j].fd == ps->pfds[i].fd) {
ps->pfds[j].revents = 0;
@@ -986,19 +1017,22 @@
}
}
- /* actual event happened */
- if ((ret <= 0) || (ret >= 3)) {
+ /* SSH message only */
+ if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
ps->pfds[i].revents = 0;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
/* event occurred on some other channel */
- } else if (ret == 2) {
+ } else if (ret & NC_PSPOLL_PENDING) {
ps->pfds[i].revents = 0;
if (i == ps->session_count - 1) {
/* last session and it is not the right channel, ... */
if (!timeout) {
/* ... timeout is 0, so that is it */
- ret = 0;
+ ret = NC_PSPOLL_TIMEOUT;
goto finish;
}
/* ... retry polling reasonable time apart ... */
@@ -1023,69 +1057,58 @@
if (i == ps->session_count) {
ERRINT;
- ret = -1;
+ ret = NC_PSPOLL_ERROR;
goto finish;
}
/* this is the session with some data available for reading */
- session = ps->sessions[i];
+ cur_session = ps->sessions[i];
+ if (session) {
+ *session = cur_session;
+ }
/* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
- ret = nc_timedlock(session->ti_lock, timeout);
- if (ret != 1) {
- /* error or timeout */
+ ret = nc_timedlock(cur_session->ti_lock, timeout);
+ if (ret < 0) {
+ ret = NC_PSPOLL_ERROR;
+ goto finish;
+ } else if (!ret) {
+ ret = NC_PSPOLL_TIMEOUT;
goto finish;
}
- msgtype = nc_recv_rpc(session, &rpc);
- if (msgtype == NC_MSG_ERROR) {
- pthread_mutex_unlock(session->ti_lock);
- if (session->status != NC_STATUS_RUNNING) {
- ret = 3;
- goto finish;
+ ret = nc_recv_rpc(cur_session, &rpc);
+ if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
+ pthread_mutex_unlock(cur_session->ti_lock);
+ if (cur_session->status != NC_STATUS_RUNNING) {
+ ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
}
- ret = -1;
goto finish;
- } else if (msgtype == NC_MSG_NONE) {
- /* already processed, just stop further processing */
- pthread_mutex_unlock(session->ti_lock);
- goto done;
}
- if (msgtype == NC_MSG_RPC) {
- session->last_rpc = time(NULL);
- }
+ cur_session->last_rpc = time(NULL);
/* process RPC */
- msgtype = nc_send_reply(session, rpc);
+ ret |= nc_send_reply(cur_session, rpc);
- pthread_mutex_unlock(session->ti_lock);
-
- if (msgtype == NC_MSG_ERROR) {
- nc_server_rpc_free(rpc, server_opts.ctx);
- ret = -1;
- goto finish;
+ pthread_mutex_unlock(cur_session->ti_lock);
+ if (cur_session->status != NC_STATUS_RUNNING) {
+ ret |= NC_PSPOLL_SESSION_TERM;
+ if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
+ ret |= NC_PSPOLL_SESSION_ERROR;
+ }
}
-done:
nc_server_rpc_free(rpc, server_opts.ctx);
- /* status change takes precedence over leftover events (return 2) */
- if (session->status != NC_STATUS_RUNNING) {
- ret = 3;
- goto finish;
- }
-
/* is there some other socket waiting? */
for (++i; i < ps->session_count; ++i) {
if (ps->pfds[i].revents) {
- ret = 2;
- goto finish;
+ ret |= NC_PSPOLL_PENDING;
+ break;
}
}
- ret = 1;
-
finish:
/* UNLOCK */
nc_ps_unlock(ps);
@@ -1395,19 +1418,20 @@
return ret;
}
-API int
+API NC_MSG_TYPE
nc_accept(int timeout, struct nc_session **session)
{
+ NC_MSG_TYPE msgtype;
int sock, ret;
char *host = NULL;
uint16_t port, idx;
if (!server_opts.ctx) {
ERRINIT;
- return -1;
+ return NC_MSG_ERROR;
} else if (!session) {
ERRARG("session");
- return -1;
+ return NC_MSG_ERROR;
}
/* we have to hold WRITE for the whole time, since there is not
@@ -1419,7 +1443,7 @@
ERRINIT;
/* WRITE UNLOCK */
pthread_rwlock_unlock(&server_opts.endpt_array_lock);
- return -1;
+ return NC_MSG_ERROR;
}
ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
@@ -1428,7 +1452,7 @@
/* WRITE UNLOCK */
pthread_rwlock_unlock(&server_opts.endpt_array_lock);
free(host);
- return ret;
+ return NC_MSG_ERROR;
}
sock = ret;
@@ -1437,8 +1461,8 @@
ERRMEM;
close(sock);
free(host);
- ret = -1;
- goto fail;
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
}
(*session)->status = NC_STATUS_STARTING;
(*session)->side = NC_SERVER;
@@ -1452,8 +1476,8 @@
if (!(*session)->ti_lock) {
ERRMEM;
close(sock);
- ret = -1;
- goto fail;
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
}
pthread_mutex_init((*session)->ti_lock, NULL);
@@ -1463,24 +1487,32 @@
#ifdef NC_ENABLED_SSH
if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
ret = nc_accept_ssh_session(*session, sock, timeout);
- if (ret < 1) {
- goto fail;
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
+ goto cleanup;
}
} else
#endif
#ifdef NC_ENABLED_TLS
if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
ret = nc_accept_tls_session(*session, sock, timeout);
- if (ret < 1) {
- goto fail;
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
+ goto cleanup;
}
} else
#endif
{
ERRINT;
close(sock);
- ret = -1;
- goto fail;
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
}
(*session)->data = NULL;
@@ -1496,54 +1528,56 @@
pthread_spin_unlock(&server_opts.sid_lock);
/* NETCONF handshake */
- if (nc_handshake(*session)) {
+ msgtype = nc_handshake(*session);
+ if (msgtype != NC_MSG_HELLO) {
nc_session_free(*session, NULL);
*session = NULL;
- return -1;
+ return msgtype;
}
(*session)->session_start = time(NULL);
(*session)->status = NC_STATUS_RUNNING;
- return 1;
+ return msgtype;
-fail:
+cleanup:
/* WRITE UNLOCK */
pthread_rwlock_unlock(&server_opts.endpt_array_lock);
nc_session_free(*session, NULL);
*session = NULL;
- return ret;
+ return msgtype;
}
-int
+NC_MSG_TYPE
nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
{
+ NC_MSG_TYPE msgtype;
int sock, ret;
if (!host) {
ERRARG("host");
- return -1;
+ return NC_MSG_ERROR;
} else if (!port) {
ERRARG("port");
- return -1;
+ return NC_MSG_ERROR;
} else if (!ti) {
ERRARG("ti");
- return -1;
+ return NC_MSG_ERROR;
} else if (!session) {
ERRARG("session");
- return -1;
+ return NC_MSG_ERROR;
}
sock = nc_sock_connect(host, port);
if (sock < 0) {
- return -1;
+ return NC_MSG_ERROR;
}
*session = calloc(1, sizeof **session);
if (!(*session)) {
ERRMEM;
close(sock);
- return -1;
+ return NC_MSG_ERROR;
}
(*session)->status = NC_STATUS_STARTING;
(*session)->side = NC_SERVER;
@@ -1557,7 +1591,7 @@
if (!(*session)->ti_lock) {
ERRMEM;
close(sock);
- ret = -1;
+ msgtype = NC_MSG_ERROR;
goto fail;
}
pthread_mutex_init((*session)->ti_lock, NULL);
@@ -1575,7 +1609,11 @@
/* OPTS UNLOCK */
pthread_mutex_unlock(&ssh_ch_opts_lock);
- if (ret < 1) {
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto fail;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
goto fail;
}
} else
@@ -1592,7 +1630,11 @@
/* OPTS UNLOCK */
pthread_mutex_unlock(&tls_ch_opts_lock);
- if (ret < 1) {
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto fail;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
goto fail;
}
} else
@@ -1600,7 +1642,7 @@
{
ERRINT;
close(sock);
- ret = -1;
+ msgtype = NC_MSG_ERROR;
goto fail;
}
@@ -1612,19 +1654,19 @@
pthread_spin_unlock(&server_opts.sid_lock);
/* NETCONF handshake */
- if (nc_handshake(*session)) {
- ret = -1;
+ msgtype = nc_handshake(*session);
+ if (msgtype != NC_MSG_HELLO) {
goto fail;
}
(*session)->session_start = time(NULL);
(*session)->status = NC_STATUS_RUNNING;
- return 1;
+ return msgtype;
fail:
nc_session_free(*session, NULL);
*session = NULL;
- return ret;
+ return msgtype;
}
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */