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/netconf.h b/src/netconf.h
index 201dc56..4b13cd1 100644
--- a/src/netconf.h
+++ b/src/netconf.h
@@ -60,6 +60,7 @@
     NC_MSG_WOULDBLOCK,      /**< timeout return value */
     NC_MSG_NONE,            /**< no message at input or message was processed internally */
     NC_MSG_HELLO,           /**< \<hello\> message */
+    NC_MSG_BAD_HELLO,       /**< \<hello\> message parsing failed */
     NC_MSG_RPC,             /**< \<rpc\> message */
     NC_MSG_REPLY,           /**< \<rpc-reply\> message */
     NC_MSG_REPLY_ERR_MSGID, /**< \<rpc-reply\> message with missing or wrong message-id attribute value */
diff --git a/src/session.c b/src/session.c
index 92df1d2..80ec526 100644
--- a/src/session.c
+++ b/src/session.c
@@ -789,12 +789,16 @@
             goto error;
         }
         break;
+    case NC_MSG_WOULDBLOCK:
+        ERR("Server's <hello> timeout elapsed.");
+        break;
     case NC_MSG_ERROR:
         /* nothing special, just pass it out */
         break;
     default:
         ERR("Unexpected message received instead of <hello>.");
         msgtype = NC_MSG_ERROR;
+        break;
     }
 
     /* cleanup */
@@ -813,7 +817,7 @@
 nc_recv_server_hello(struct nc_session *session)
 {
     struct lyxml_elem *xml = NULL, *node;
-    NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
+    NC_MSG_TYPE msgtype;
     int ver = -1;
     int flag = 0;
 
@@ -827,20 +831,20 @@
                 continue;
             } else if (strcmp(node->name, "capabilities")) {
                 ERR("Unexpected <%s> element in client's <hello>.", node->name);
-                msgtype = NC_MSG_ERROR;
+                msgtype = NC_MSG_BAD_HELLO;
                 goto cleanup;
             }
 
             if (flag) {
                 /* multiple capabilities elements */
                 ERR("Invalid <hello> message (multiple <capabilities> elements).");
-                msgtype = NC_MSG_ERROR;
+                msgtype = NC_MSG_BAD_HELLO;
                 goto cleanup;
             }
             flag = 1;
 
             if ((ver = parse_cpblts(node, NULL)) < 0) {
-                msgtype = NC_MSG_ERROR;
+                msgtype = NC_MSG_BAD_HELLO;
                 goto cleanup;
             }
             session->version = ver;
@@ -851,11 +855,11 @@
         break;
     case NC_MSG_WOULDBLOCK:
         ERR("Client's <hello> timeout elapsed.");
-        msgtype = NC_MSG_ERROR;
         break;
     default:
         ERR("Unexpected message received instead of <hello>.");
         msgtype = NC_MSG_ERROR;
+        break;
     }
 
 cleanup:
@@ -864,7 +868,7 @@
     return msgtype;
 }
 
-int
+NC_MSG_TYPE
 nc_handshake(struct nc_session *session)
 {
     NC_MSG_TYPE type;
@@ -876,7 +880,7 @@
     }
 
     if (type != NC_MSG_HELLO) {
-        return 1;
+        return type;
     }
 
     if (session->side == NC_CLIENT) {
@@ -885,11 +889,7 @@
         type = nc_recv_server_hello(session);
     }
 
-    if (type != NC_MSG_HELLO) {
-        return 1;
-    }
-
-    return 0;
+    return type;
 }
 
 #ifdef NC_ENABLED_SSH
diff --git a/src/session_client.c b/src/session_client.c
index d675240..3991fab 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -372,7 +372,7 @@
     session->ctx = ctx;
 
     /* NETCONF handshake */
-    if (nc_handshake(session)) {
+    if (nc_handshake(session) != NC_MSG_HELLO) {
         goto fail;
     }
     session->status = NC_STATUS_RUNNING;
diff --git a/src/session_client_ssh.c b/src/session_client_ssh.c
index 8d810ab..3d93ff0 100644
--- a/src/session_client_ssh.c
+++ b/src/session_client_ssh.c
@@ -1380,7 +1380,7 @@
     session->ctx = ctx;
 
     /* NETCONF handshake */
-    if (nc_handshake(session)) {
+    if (nc_handshake(session) != NC_MSG_HELLO) {
         goto fail;
     }
     session->status = NC_STATUS_RUNNING;
@@ -1510,7 +1510,7 @@
     session->ctx = ctx;
 
     /* NETCONF handshake */
-    if (nc_handshake(session)) {
+    if (nc_handshake(session) != NC_MSG_HELLO) {
         goto fail;
     }
     session->status = NC_STATUS_RUNNING;
@@ -1582,7 +1582,7 @@
     new_session->ctx = ctx;
 
     /* NETCONF handshake */
-    if (nc_handshake(new_session)) {
+    if (nc_handshake(new_session) != NC_MSG_HELLO) {
         goto fail;
     }
     new_session->status = NC_STATUS_RUNNING;
diff --git a/src/session_client_tls.c b/src/session_client_tls.c
index 25ddd85..649614b 100644
--- a/src/session_client_tls.c
+++ b/src/session_client_tls.c
@@ -572,7 +572,7 @@
     session->ctx = ctx;
 
     /* NETCONF handshake */
-    if (nc_handshake(session)) {
+    if (nc_handshake(session) != NC_MSG_HELLO) {
         goto fail;
     }
     session->status = NC_STATUS_RUNNING;
@@ -644,7 +644,7 @@
     session->ctx = ctx;
 
     /* NETCONF handshake */
-    if (nc_handshake(session)) {
+    if (nc_handshake(session) != NC_MSG_HELLO) {
         goto fail;
     }
     session->status = NC_STATUS_RUNNING;
diff --git a/src/session_p.h b/src/session_p.h
index 8464222..6ca7ef3 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -315,9 +315,10 @@
  * @brief Perform NETCONF handshake on \p session.
  *
  * @param[in] session NETCONF session to use.
- * @return 0 on success, non-zero on failure.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message parsing fail
+ * (server-side only), NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other error.
  */
-int nc_handshake(struct nc_session *session);
+NC_MSG_TYPE nc_handshake(struct nc_session *session);
 
 /**
  * @brief Create a socket connection.
@@ -437,9 +438,10 @@
  * @param[in] port Port to connect to.
  * @param[in] ti Transport fo the connection.
  * @param[out] session New Call Home session.
- * @return 0 on success, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
  */
-int nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session);
+NC_MSG_TYPE nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session);
 
 void nc_init(void);
 
@@ -485,13 +487,13 @@
  *
  * @param[in] session NETCONF session communicating on the socket.
  * @param[in,out] timeout Timeout for locking ti_lock.
- * @return 0 - timeout,
- *         1 if \p session channel has data,
- *         2 if some other channel has data,
- *         3 on \p session status change,
- *         4 on new SSH message,
- *         5 on new NETCONF SSH channel,
- *        -1 on error.
+ * @return NC_PSPOLL_TIMEOUT,
+ *         NC_PSPOLL_RPC (has new data),
+ *         NC_PSPOLL_PENDING (other channel has data),
+ *         NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR,
+ *         NC_PSPOLL_SSH_MSG,
+ *         NC_PSPOLL_SSH_CHANNEL,
+ *         NC_PSPOLL_ERROR.
  */
 int nc_ssh_pollin(struct nc_session *session, int timeout);
 
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 */
diff --git a/src/session_server.h b/src/session_server.h
index c445304..5707e19 100644
--- a/src/session_server.h
+++ b/src/session_server.h
@@ -172,9 +172,10 @@
  * @param[in] fdout File descriptor to write (unencrypted) XML data to.
  * @param[in] username NETCONF username as provided by the transport protocol.
  * @param[out] session New session on success.
- * @return 0 on success, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
  */
-int nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session);
+NC_MSG_TYPE nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session);
 
 /**
  * @brief Create an empty structure for polling sessions.
@@ -219,29 +220,34 @@
  */
 uint16_t nc_ps_session_count(struct nc_pollsession *ps);
 
+#define NC_PSPOLL_TIMEOUT 0x0001       /**< Timeout elapsed. */
+#define NC_PSPOLL_RPC 0x0002           /**< RPC was correctly parsed and processed. */
+#define NC_PSPOLL_BAD_RPC 0x0004       /**< RPC was received, but failed to be parsed. */
+#define NC_PSPOLL_REPLY_ERROR 0x0008   /**< Response to the RPC was a \<rpc-reply\> of type error. */
+#define NC_PSPOLL_SESSION_TERM 0x0010  /**< Some session was terminated. */
+#define NC_PSPOLL_SESSION_ERROR 0x0020 /**< Some session was terminated incorrectly (not by \<close-session\> or \<kill-session\> RPCs. */
+#define NC_PSPOLL_PENDING 0x0040       /**< Unhandled pending events on other session. */
+#define NC_PSPOLL_ERROR 0x0080         /**< Other fatal errors (they are printed). */
+
+#ifdef NC_ENABLED_SSH
+#   define NC_PSPOLL_SSH_MSG 0x0100       /**< SSH message received (and processed, if relevant, only with SSH support). */
+#   define NC_PSPOLL_SSH_CHANNEL 0x0200   /**< New SSH channel opened on an existing session (only with SSH support). */
+#endif
+
 /**
  * @brief Poll sessions and process any received RPCs.
  *
- * All the sessions must be running. If a session fails causing it to change its
- * status, it can be learnt from the return value. Only one event on one session
+ * All the sessions must be running. Only one event on one session
  * is handled in one function call.
  *
  * @param[in] ps Pollsession structure to use.
  * @param[in] timeout Poll timeout in milliseconds. 0 for non-blocking call, -1 for
  *                    infinite waiting.
- * @return 0 on elapsed timeout,
- *         1 if an RPC was processed (even if it was not known - it failed to be
- *           parsed into session ctx),
- *         2 if an RPC was processed and there are unhandled events on other sessions,
- *         3 if a session from \p ps changed its status (was invalidated),
- *         -1 on error (a session likely changed its status as well).
- *
- *         Only with SSH support:
- *         4 if an SSH message was processed,
- *         5 if a new NETCONF SSH channel was created; call nc_ps_accept_ssh_channel()
- *           to establish a new NETCONF session.
+ * @param[in] session Session that was processed and that specific return bits concern.
+ *                    Can be NULL.
+ * @return Bitfield of NC_PSPOLL_* macros, almost any combination can be returned.
  */
-int nc_ps_poll(struct nc_pollsession *ps, int timeout);
+int nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session);
 
 /**
  * @brief Remove sessions from a pollsession structure and
@@ -261,26 +267,39 @@
  * @brief Accept new sessions on all the listening endpoints.
  *
  * @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
- * non-blocking call, -1 for infinite waiting.
+ *                    non-blocking call, -1 for infinite waiting.
  * @param[out] session New session.
- * @return 1 on success, 0 on timeout, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
  */
-int nc_accept(int timeout, struct nc_session **session);
+NC_MSG_TYPE nc_accept(int timeout, struct nc_session **session);
 
 #endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
 
 #ifdef NC_ENABLED_SSH
 
 /**
+ * @brief Accept a new NETCONF session on an SSH session of a running NETCONF \p orig_session.
+ *        Call this function only when nc_ps_poll() returns NC_PSPOLL_SSH_CHANNEL on \p orig_session.
+ *
+ * @param[in] orig_session Session that has a new SSH channel ready.
+ * @param[out] session New session.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
+ */
+NC_MSG_TYPE nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session);
+
+/**
  * @brief Accept a new NETCONF session on an SSH session of a running NETCONF session
- *        that was polled in \p ps. Call this function only when nc_ps_poll() on \p ps returns 5.
+ *        that was polled in \p ps. Call this function only when nc_ps_poll() on \p ps returns NC_PSPOLL_SSH_CHANNEL.
  *        The new session is only returned in \p session, it is not added to \p ps.
  *
  * @param[in] ps Unmodified pollsession structure from the previous nc_ps_poll() call.
  * @param[out] session New session.
- * @return 0 on success, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
  */
-int nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session);
+NC_MSG_TYPE nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session);
 
 /**
  * @brief Add a new SSH endpoint and start listening on it.
diff --git a/src/session_server_ch.h b/src/session_server_ch.h
index 161a44f..79eca8c 100644
--- a/src/session_server_ch.h
+++ b/src/session_server_ch.h
@@ -29,9 +29,10 @@
  * @param[in] host Host the client is listening on.
  * @param[in] port Port the client is listening on.
  * @param[out] session New Call Home session.
- * @return 1 on success, 0 on timeout, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
  */
-int nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session);
+NC_MSG_TYPE nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session);
 
 /**
  * @brief Set Call Home SSH host keys the server will identify itself with. Each of RSA, DSA, and
@@ -110,9 +111,10 @@
  * @param[in] host Host the client is listening on.
  * @param[in] port Port the client is listening on.
  * @param[out] session New Call Home session.
- * @return 1 on success, 0 on timeout, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
  */
-int nc_connect_callhome_tls(const char *host, uint16_t port, struct nc_session **session);
+NC_MSG_TYPE nc_connect_callhome_tls(const char *host, uint16_t port, struct nc_session **session);
 
 /**
  * @brief Set server Call Home TLS certificate. Alternative to nc_tls_server_set_cert_path().
diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c
index d59e87a..409a83b 100644
--- a/src/session_server_ssh.c
+++ b/src/session_server_ssh.c
@@ -1005,8 +1005,6 @@
     return 0;
 }
 
-/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
- * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
 int
 nc_ssh_pollin(struct nc_session *session, int timeout)
 {
@@ -1015,8 +1013,10 @@
 
     ret = nc_timedlock(session->ti_lock, timeout);
 
-    if (ret != 1) {
-        return ret;
+    if (ret < 0) {
+        return NC_PSPOLL_ERROR;
+    } else if (!ret) {
+        return NC_PSPOLL_TIMEOUT;
     }
 
     ret = ssh_execute_message_callbacks(session->ti.libssh.session);
@@ -1027,7 +1027,7 @@
             ssh_get_error(session->ti.libssh.session));
         session->status = NC_STATUS_INVALID;
         session->term_reason = NC_SESSION_TERM_OTHER;
-        return 3;
+        return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
     }
 
     /* new SSH message */
@@ -1038,38 +1038,38 @@
                 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
                         && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
                     /* new NETCONF SSH channel */
-                    return 5;
+                    return NC_PSPOLL_SSH_CHANNEL;
                 }
             }
         }
 
         /* just some SSH message */
-        return 4;
+        return NC_PSPOLL_SSH_MSG;
     }
 
     /* no new SSH message, maybe NETCONF data? */
     ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
     /* not this one */
     if (!ret) {
-        return 2;
+        return NC_PSPOLL_PENDING;
     } else if (ret == SSH_ERROR) {
         ERR("Session %u: SSH channel error (%s).", session->id,
             ssh_get_error(session->ti.libssh.session));
         session->status = NC_STATUS_INVALID;
         session->term_reason = NC_SESSION_TERM_OTHER;
-        return 3;
+        return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
     } else if (ret == SSH_EOF) {
         ERR("Session %u: communication channel unexpectedly closed (libssh).",
             session->id);
         session->status = NC_STATUS_INVALID;
         session->term_reason = NC_SESSION_TERM_DROPPED;
-        return 3;
+        return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
     }
 
-    return 1;
+    return NC_PSPOLL_RPC;
 }
 
-API int
+API NC_MSG_TYPE
 nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
 {
     return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
@@ -1170,23 +1170,77 @@
     return 1;
 }
 
-API int
+API NC_MSG_TYPE
+nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
+{
+    NC_MSG_TYPE msgtype;
+    struct nc_session *new_session = NULL;
+
+    if (!orig_session) {
+        ERRARG("orig_session");
+        return NC_MSG_ERROR;
+    } else if (!session) {
+        ERRARG("session");
+        return NC_MSG_ERROR;
+    }
+
+    if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
+            && orig_session->ti.libssh.next) {
+        for (new_session = orig_session->ti.libssh.next;
+                new_session != orig_session;
+                new_session = new_session->ti.libssh.next) {
+            if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
+                    && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
+                /* we found our session */
+                break;
+            }
+        }
+        if (new_session == orig_session) {
+            new_session = NULL;
+        }
+    }
+
+    if (!new_session) {
+        ERR("Session does not have a NETCONF SSH channel ready.");
+        return NC_MSG_ERROR;
+    }
+
+    /* assign new SID atomically */
+    pthread_spin_lock(&server_opts.sid_lock);
+    new_session->id = server_opts.new_session_id++;
+    pthread_spin_unlock(&server_opts.sid_lock);
+
+    /* NETCONF handshake */
+    msgtype = nc_handshake(new_session);
+    if (msgtype != NC_MSG_HELLO) {
+        return msgtype;
+    }
+
+    new_session->session_start = time(NULL);
+    new_session->status = NC_STATUS_RUNNING;
+    *session = new_session;
+
+    return msgtype;
+}
+
+API NC_MSG_TYPE
 nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
 {
+    NC_MSG_TYPE msgtype;
     struct nc_session *new_session = NULL;
     uint16_t i;
 
     if (!ps) {
         ERRARG("ps");
-        return -1;
+        return NC_MSG_ERROR;
     } else if (!session) {
         ERRARG("session");
-        return -1;
+        return NC_MSG_ERROR;
     }
 
     /* LOCK */
     if (nc_ps_lock(ps)) {
-        return -1;
+        return NC_MSG_ERROR;
     }
 
     for (i = 0; i < ps->session_count; ++i) {
@@ -1215,7 +1269,7 @@
 
     if (!new_session) {
         ERR("No session with a NETCONF SSH channel ready was found.");
-        return -1;
+        return NC_MSG_ERROR;
     }
 
     /* assign new SID atomically */
@@ -1224,13 +1278,14 @@
     pthread_spin_unlock(&server_opts.sid_lock);
 
     /* NETCONF handshake */
-    if (nc_handshake(new_session)) {
-        return -1;
+    msgtype = nc_handshake(new_session);
+    if (msgtype != NC_MSG_HELLO) {
+        return msgtype;
     }
 
     new_session->session_start = time(NULL);
     new_session->status = NC_STATUS_RUNNING;
     *session = new_session;
 
-    return 0;
+    return msgtype;
 }
diff --git a/src/session_server_tls.c b/src/session_server_tls.c
index 42bb445..cbcd3d5 100644
--- a/src/session_server_tls.c
+++ b/src/session_server_tls.c
@@ -1515,7 +1515,7 @@
     pthread_key_create(&verify_key, NULL);
 }
 
-API int
+API NC_MSG_TYPE
 nc_connect_callhome_tls(const char *host, uint16_t port, struct nc_session **session)
 {
     return nc_connect_callhome(host, port, NC_TI_OPENSSL, session);
diff --git a/tests/test_fd_comm.c b/tests/test_fd_comm.c
index 67602b7..7a63760 100644
--- a/tests/test_fd_comm.c
+++ b/tests/test_fd_comm.c
@@ -141,8 +141,8 @@
     assert_non_null(ps);
     nc_ps_add_session(ps, server_session);
 
-    ret = nc_ps_poll(ps, 0);
-    assert_int_equal(ret, 1);
+    ret = nc_ps_poll(ps, 0, NULL);
+    assert_int_equal(ret, NC_PSPOLL_RPC);
 
     /* server finished */
     nc_ps_free(ps);
@@ -200,8 +200,8 @@
     assert_non_null(ps);
     nc_ps_add_session(ps, server_session);
 
-    ret = nc_ps_poll(ps, 0);
-    assert_int_equal(ret, 1);
+    ret = nc_ps_poll(ps, 0, NULL);
+    assert_int_equal(ret, NC_PSPOLL_RPC | NC_PSPOLL_REPLY_ERROR);
 
     /* server finished */
     nc_ps_free(ps);
@@ -260,8 +260,8 @@
     assert_non_null(ps);
     nc_ps_add_session(ps, server_session);
 
-    ret = nc_ps_poll(ps, 0);
-    assert_int_equal(ret, 1);
+    ret = nc_ps_poll(ps, 0, NULL);
+    assert_int_equal(ret, NC_PSPOLL_RPC);
 
     /* server finished */
     nc_ps_free(ps);
diff --git a/tests/test_server_thread.c b/tests/test_server_thread.c
index 5d7d08d..5ba9881 100644
--- a/tests/test_server_thread.c
+++ b/tests/test_server_thread.c
@@ -44,6 +44,7 @@
 server_thread(void *arg)
 {
     (void)arg;
+    NC_MSG_TYPE msgtype;
     int ret;
     struct nc_pollsession *ps;
     struct nc_session *session;
@@ -54,21 +55,21 @@
     pthread_barrier_wait(&barrier);
 
 #if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
-    ret = nc_accept(NC_ACCEPT_TIMEOUT, &session);
-    nc_assert(ret == 1);
+    msgtype = nc_accept(NC_ACCEPT_TIMEOUT, &session);
+    nc_assert(msgtype == NC_MSG_HELLO);
 
     nc_ps_add_session(ps, session);
-    ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT);
-    nc_assert(ret == 3);
+    ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
+    nc_assert(ret & NC_PSPOLL_RPC);
     nc_ps_clear(ps, 0, NULL);
 #endif
 
-    ret = nc_accept(NC_ACCEPT_TIMEOUT, &session);
-    nc_assert(ret == 1);
+    msgtype = nc_accept(NC_ACCEPT_TIMEOUT, &session);
+    nc_assert(msgtype == NC_MSG_HELLO);
 
     nc_ps_add_session(ps, session);
-    ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT);
-    nc_assert(ret == 3);
+    ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
+    nc_assert(ret & NC_PSPOLL_RPC);
     nc_ps_clear(ps, 0, NULL);
 
     nc_ps_free(ps);