server session FEATURE accepting NETCONF sessions on more SSH channels
Also some other enhancements and refactoring.
diff --git a/src/session.c b/src/session.c
index 9554add..f53e105 100644
--- a/src/session.c
+++ b/src/session.c
@@ -49,6 +49,67 @@
extern struct nc_server_opts server_opts;
+/*
+ * @return 1 - success
+ * 0 - timeout
+ * -1 - error
+ */
+int
+nc_timedlock(pthread_mutex_t *lock, int timeout, int *elapsed)
+{
+ int ret;
+ struct timespec ts_timeout, ts_old, ts_new;
+
+ if (timeout > 0) {
+ clock_gettime(CLOCK_REALTIME, &ts_timeout);
+
+ if (elapsed) {
+ ts_old = ts_timeout;
+ }
+
+ ts_timeout.tv_sec += timeout / 1000;
+ ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
+
+ ret = pthread_mutex_timedlock(lock, &ts_timeout);
+
+ if (elapsed) {
+ clock_gettime(CLOCK_REALTIME, &ts_new);
+
+ *elapsed += (ts_new.tv_sec - ts_old.tv_sec) * 1000;
+ *elapsed += (ts_new.tv_nsec - ts_old.tv_nsec) / 1000000;
+ }
+ } else if (!timeout) {
+ ret = pthread_mutex_trylock(lock);
+ } else { /* timeout == -1 */
+ ret = pthread_mutex_lock(lock);
+ }
+
+ if (ret == ETIMEDOUT) {
+ /* timeout */
+ return 0;
+ } else if (ret) {
+ /* error */
+ ERR("Mutex lock failed (%s).", strerror(errno));
+ return -1;
+ }
+
+ /* ok */
+ return 1;
+}
+
+void
+nc_subtract_elapsed(int *timeout, struct timespec *old_ts)
+{
+ struct timespec new_ts;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
+
+ *timeout -= (new_ts.tv_sec - old_ts->tv_sec) * 1000;
+ *timeout -= (new_ts.tv_nsec - old_ts->tv_nsec) / 1000000;
+
+ *old_ts = new_ts;
+}
+
API NC_STATUS
nc_session_get_status(const struct nc_session *session)
{
@@ -166,54 +227,6 @@
return NC_MSG_RPC;
}
-/*
- * @return 1 - success
- * 0 - timeout
- * -1 - error
- */
-int
-nc_timedlock(pthread_mutex_t *lock, int timeout, int *elapsed)
-{
- int ret;
- struct timespec ts_timeout, ts_old, ts_new;
-
- if (timeout > 0) {
- clock_gettime(CLOCK_REALTIME, &ts_timeout);
-
- if (elapsed) {
- ts_old = ts_timeout;
- }
-
- ts_timeout.tv_sec += timeout / 1000;
- ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
-
- ret = pthread_mutex_timedlock(lock, &ts_timeout);
-
- if (elapsed) {
- clock_gettime(CLOCK_REALTIME, &ts_new);
-
- *elapsed += (ts_new.tv_sec - ts_old.tv_sec) * 1000;
- *elapsed += (ts_new.tv_nsec - ts_old.tv_nsec) / 1000000;
- }
- } else if (!timeout) {
- ret = pthread_mutex_trylock(lock);
- } else { /* timeout == -1 */
- ret = pthread_mutex_lock(lock);
- }
-
- if (ret == ETIMEDOUT) {
- /* timeout */
- return 0;
- } else if (ret) {
- /* error */
- ERR("Mutex lock failed (%s).", strerror(errno));
- return -1;
- }
-
- /* ok */
- return 1;
-}
-
API void
nc_session_free(struct nc_session *session)
{
@@ -329,14 +342,44 @@
* SSH channel). So destroy the SSH session only if there is no other NETCONF session using
* it.
*/
- if (!session->ti.libssh.next) {
+ multisession = 0;
+ if (session->ti.libssh.next) {
+ for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
+ if (siter->status != NC_STATUS_STARTING) {
+ multisession = 1;
+ break;
+ }
+ }
+ }
+
+ if (!multisession) {
+ /* it's not multisession yet, but we still need to free the starting sessions */
+ if (session->ti.libssh.next) {
+ do {
+ siter = session->ti.libssh.next;
+ session->ti.libssh.next = siter->ti.libssh.next;
+
+ /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
+ if (session->side == NC_SERVER) {
+ nc_ctx_lock(-1, NULL);
+ }
+ lydict_remove(session->ctx, session->username);
+ lydict_remove(session->ctx, session->host);
+ if (session->side == NC_SERVER) {
+ nc_ctx_unlock();
+ }
+ if (!(session->flags & NC_SESSION_SHAREDCTX)) {
+ ly_ctx_destroy(session->ctx);
+ }
+
+ free(siter);
+ } while (session->ti.libssh.next != session);
+ }
if (connected) {
ssh_disconnect(session->ti.libssh.session);
}
ssh_free(session->ti.libssh.session);
} else {
- /* multiple NETCONF sessions on a single SSH session */
- multisession = 1;
/* remove the session from the list */
for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
if (session->ti.libssh.next == siter) {
@@ -346,6 +389,17 @@
/* there are still multiple sessions, keep the ring list */
siter->ti.libssh.next = session->ti.libssh.next;
}
+ /* change nc_sshcb_msg() argument, we need a RUNNING session and this one will be freed */
+ if (session->flags & NC_SESSION_SSH_MSG_CB) {
+ for (siter = session->ti.libssh.next; siter->status != NC_STATUS_RUNNING; siter = siter->ti.libssh.next) {
+ if (siter->ti.libssh.next == session) {
+ ERRINT;
+ break;
+ }
+ }
+ ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, siter);
+ siter->flags |= NC_SESSION_SSH_MSG_CB;
+ }
}
break;
#endif
diff --git a/src/session_p.h b/src/session_p.h
index ddf7d56..87429cc 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -224,9 +224,14 @@
/* server side only data */
time_t last_rpc;
#ifdef ENABLE_SSH
- /* additional flags */
+ /* SSH session authenticated */
# define NC_SESSION_SSH_AUTHENTICATED 0x02
+ /* netconf subsystem requested */
# define NC_SESSION_SSH_SUBSYS_NETCONF 0x04
+ /* new SSH message arrived */
+# define NC_SESSION_SSH_NEW_MSG 0x08
+ /* this session is passed to nc_sshcb_msg() */
+# define NC_SESSION_SSH_MSG_CB 0x10
uint16_t ssh_auth_attempts;
#endif
@@ -245,6 +250,8 @@
int nc_timedlock(pthread_mutex_t *lock, int timeout, int *elapsed);
+void nc_subtract_elapsed(int *timeout, struct timespec *old_ts);
+
/**
* @brief Fill libyang context in \p session. Context models are based on the stored session
* capabilities. If the server does not support \<get-schema\>, the models are searched
@@ -317,6 +324,32 @@
*/
int nc_accept_ssh_session(struct nc_session *session, int sock, int timeout);
+/**
+ * @brief Callback called when a new SSH message is received.
+ *
+ * @param[in] sshsession SSH session the message arrived on.
+ * @param[in] msg SSH message itself.
+ * @param[in] data NETCONF session running on \p sshsession.
+ * @return 0 if the message was handled, 1 if it is left up to libssh.
+ */
+int nc_sshcb_msg(ssh_session sshsession, ssh_message msg, void *data);
+
+/**
+ * @brief Inspect what exactly happened if a SSH session socket poll
+ * returned POLLIN.
+ *
+ * @param[in] session NETCONF session communicating on the socket.
+ * @param[in,out] timeout Timeout for locking ti_lock, gets updated.
+ * @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.
+ */
+int nc_ssh_pollin(struct nc_session *session, int *timeout);
+
#endif
#ifdef ENABLE_TLS
diff --git a/src/session_server.c b/src/session_server.c
index 4a5ce44..89b52ad 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -587,12 +587,12 @@
nc_ps_poll(struct nc_pollsession *ps, int timeout)
{
int ret;
- uint16_t i;
+ uint16_t i, j;
time_t cur_time;
NC_MSG_TYPE msgtype;
struct nc_session *session;
struct nc_server_rpc *rpc;
- struct timespec old_ts, new_ts;
+ struct timespec old_ts;
if (!ps || !ps->session_count) {
ERRARG;
@@ -648,25 +648,35 @@
return 3;
} else if (ps->pfds[i].revents & POLLIN) {
#ifdef ENABLE_SSH
- if (ps->sessions[i].session->ti_type == NC_TI_LIBSSH) {
- /* things are not that simple with SSH, we need to check the channel */
- ret = ssh_channel_poll_timeout(ps->sessions[i].session->ti.libssh.channel, 0, 0);
- /* not this one */
- if (!ret) {
+ if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
+ /* things are not that simple with SSH... */
+ ret = nc_ssh_pollin(ps->sessions[i], &timeout);
+
+ /* clear POLLIN on sessions sharing this session's SSH session */
+ if ((ret == 1) || (ret >= 4)) {
+ for (j = i + 1; j < ps->session_count; ++j) {
+ if (ps->pfds[j].fd == ps->pfds[i].fd) {
+ ps->pfds[j].revents = 0;
+ }
+ }
+ }
+
+ /* actual event happened */
+ if ((ret <= 0) || (ret >= 3)) {
+ ps->pfds[i].revents = 0;
+ return ret;
+
+ /* event occurred on some other channel */
+ } else if (ret == 2) {
+ ps->pfds[i].revents = 0;
if (i == ps->session_count - 1) {
/* last session and it is not the right channel, ... */
if (timeout > 0) {
/* ... decrease timeout, wait it all out and try again, last time */
- clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
-
- timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
- timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
- if (timeout < 0) {
- ERRINT;
- return -1;
- }
-
- old_ts = new_ts;
+ nc_subtract_elapsed(&timeout, &old_ts);
+ usleep(timeout * 1000);
+ timeout = 0;
+ goto retry_poll;
} else if (!timeout) {
/* ... timeout is 0, so that is it */
return 0;
@@ -677,20 +687,7 @@
}
}
/* check other sessions */
- ps->sessions[i].revents = 0;
continue;
- } else if (ret == SSH_ERROR) {
- ERR("Session %u: SSH channel error (%s).", ps->sessions[i].session->id,
- ssh_get_error(ps->sessions[i].session->ti.libssh.session));
- ps->sessions[i].session->status = NC_STATUS_INVALID;
- ps->sessions[i].session->term_reason = NC_SESSION_TERM_OTHER;
- return 3;
- } else if (ret == SSH_EOF) {
- ERR("Session %u: communication channel unexpectedly closed (libssh).",
- ps->sessions[i].session->id);
- ps->sessions[i].session->status = NC_STATUS_INVALID;
- ps->sessions[i].session->term_reason = NC_SESSION_TERM_DROPPED;
- return 3;
}
}
#endif /* ENABLE_SSH */
@@ -710,15 +707,7 @@
session = ps->sessions[i];
if (timeout > 0) {
- clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
-
- /* subtract elapsed time */
- timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
- timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
- if (timeout < 0) {
- ERRINT;
- return -1;
- }
+ nc_subtract_elapsed(&timeout, &old_ts);
}
/* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
diff --git a/src/session_server.h b/src/session_server.h
index a9f18c3..a1bfb2a 100644
--- a/src/session_server.h
+++ b/src/session_server.h
@@ -175,8 +175,8 @@
* @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 (new RPC, TODO
- * new SSH channel request) on one session is handled in one function call.
+ * status, it can be learnt from the return value. 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
@@ -186,6 +186,11 @@
* 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.
+ *
+ * Only with SSH support:
+ * 4 if an SSH message was processed,
+ * 5 if a new NETCONF SSH channel was created; call nc_ssh_ps_accept_channel()
+ * to establish a new NETCONF session.
*/
int nc_ps_poll(struct nc_pollsession *ps, int timeout);
@@ -234,7 +239,7 @@
*
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
* non-blocking call, -1 for infinite waiting.
- * @param[out] session New session on success.
+ * @param[out] session New session.
* @return 1 on success, 0 on timeout, -1 or error.
*/
int nc_accept(int timeout, struct nc_session **session);
@@ -244,6 +249,16 @@
#ifdef ENABLE_SSH
/**
+ * @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.
+ *
+ * @param[in] ps Unmodified pollsession structure from the previous nc_ps_poll() call.
+ * @param[out] session New session.
+ * @return 1 on success, -1 on error.
+ */
+int nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session);
+
+/**
* @brief Set SSH host keys the server will identify itself with. Each of RSA, DSA, and
* ECDSA key can be set. If the particular type was already set, it is replaced.
*
diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c
index 4c1d6fb..3824c52 100644
--- a/src/session_server_ssh.c
+++ b/src/session_server_ssh.c
@@ -418,53 +418,87 @@
}
static int
-nc_sshcb_channel_open(struct nc_session *session, ssh_channel channel)
+nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
{
- while (session->ti.libssh.next) {
- if (session->status == NC_STATUS_STARTING) {
+ ssh_channel chan;
+
+ /* first channel request */
+ if (!session->ti.libssh.channel) {
+ if (session->status != NC_STATUS_STARTING) {
ERRINT;
return -1;
}
- session = session->ti.libssh.next;
- }
+ chan = ssh_message_channel_request_open_reply_accept(msg);
+ if (!chan) {
+ ERR("Failed to create a new SSH channel.");
+ return -1;
+ }
+ session->ti.libssh.channel = chan;
- if ((session->status != NC_STATUS_STARTING) || session->ti.libssh.channel) {
- ERRINT;
- return -1;
+ /* additional channel request */
+ } else {
+ chan = ssh_message_channel_request_open_reply_accept(msg);
+ if (!chan) {
+ ERR("Session %u: failed to create a new SSH channel.", session->id);
+ return -1;
+ }
+ /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
}
- session->ti.libssh.channel = channel;
-
return 0;
}
static int
nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
{
- while (session && (session->ti.libssh.channel != channel)) {
- session = session->ti.libssh.next;
- }
+ struct nc_session *new_session;
- if (!session) {
- ERRINT;
+ if (strcmp(subsystem, "netconf")) {
+ WRN("Received an unknown subsystem \"%s\" request.", subsystem);
return -1;
}
- if (!strcmp(subsystem, "netconf")) {
- if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
- WRN("Client \"%s\" requested subsystem \"netconf\" for the second time.", session->username);
- } else {
- session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
+ if (session->ti.libssh.channel == channel) {
+ /* first channel requested */
+ if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
+ ERRINT;
+ return -1;
}
+ if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
+ ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
+ return -1;
+ }
+
+ session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
} else {
- WRN("Client \"%s\" requested an unknown subsystem \"%s\".", session->username, subsystem);
- return -1;
+ /* additional channel subsystem request, new session is ready as far as SSH is concerned */
+ new_session = calloc(1, sizeof *new_session);
+
+ /* insert the new session */
+ if (!session->ti.libssh.next) {
+ new_session->ti.libssh.next = session;
+ } else {
+ new_session->ti.libssh.next = session->ti.libssh.next;
+ }
+ session->ti.libssh.next = new_session;
+
+ new_session->status = NC_STATUS_STARTING;
+ new_session->side = NC_SERVER;
+ new_session->ti_type = NC_TI_LIBSSH;
+ new_session->ti_lock = session->ti_lock;
+ new_session->ti.libssh.channel = channel;
+ new_session->ti.libssh.session = session->ti.libssh.session;
+ new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
+ new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
+ new_session->port = session->port;
+ new_session->ctx = server_opts.ctx;
+ new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
}
return 0;
}
-static int
+int
nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
{
const char *str_type, *str_subtype = NULL, *username;
@@ -583,6 +617,7 @@
}
VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
+ session->flags |= NC_SESSION_SSH_NEW_MSG;
/*
* process known messages
@@ -633,19 +668,17 @@
}
} else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
if ((type == SSH_REQUEST_CHANNEL_OPEN) && (subtype == (int)SSH_CHANNEL_SESSION)) {
- ssh_channel chan;
- if ((chan = ssh_message_channel_request_open_reply_accept(msg)) == NULL) {
+ if (nc_sshcb_channel_open(session, msg)) {
ssh_message_reply_default(msg);
- return 0;
}
- nc_sshcb_channel_open(session, chan);
return 0;
+
} else if ((type == SSH_REQUEST_CHANNEL) && (subtype == (int)SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
- if (!nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
- ssh_message_channel_request_subsystem(msg))) {
- ssh_message_channel_request_reply_success(msg);
- } else {
+ if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
+ ssh_message_channel_request_subsystem(msg))) {
ssh_message_reply_default(msg);
+ } else {
+ ssh_message_channel_request_reply_success(msg);
}
return 0;
}
@@ -742,6 +775,73 @@
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)
+{
+ int ret, elapsed = 0;
+ struct nc_session *new;
+
+ ret = nc_timedlock(session->ti_lock, *timeout, &elapsed);
+ if (*timeout > 0) {
+ *timeout -= elapsed;
+ }
+
+ if (ret != 1) {
+ return ret;
+ }
+
+ ret = ssh_execute_message_callbacks(session->ti.libssh.session);
+ pthread_mutex_unlock(session->ti_lock);
+
+ if (ret != SSH_OK) {
+ ERR("Session %u: failed to receive SSH messages (%s).", session->id,
+ ssh_get_error(session->ti.libssh.session));
+ session->status = NC_STATUS_INVALID;
+ session->term_reason = NC_SESSION_TERM_OTHER;
+ return 3;
+ }
+
+ /* new SSH message */
+ if (session->flags & NC_SESSION_SSH_NEW_MSG) {
+ session->flags &= ~NC_SESSION_SSH_NEW_MSG;
+ if (session->ti.libssh.next) {
+ for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
+ if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
+ && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
+ /* new NETCONF SSH channel */
+ return 5;
+ }
+ }
+ }
+
+ /* just some SSH message */
+ return 4;
+ }
+
+ /* 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;
+ } 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;
+ } 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 1;
+}
+
int
nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
{
@@ -768,6 +868,7 @@
ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
+ session->flags |= NC_SESSION_SSH_MSG_CB;
/* LOCK */
ret = nc_timedlock(&ssh_opts.sshbind_lock, timeout, &elapsed);
@@ -827,48 +928,59 @@
return ret;
}
+ session->flags &= ~NC_SESSION_SSH_NEW_MSG;
+
return 1;
}
-/* TODO remove */
-struct nc_session *
-nc_accept_ssh_channel(struct nc_session *session, int timeout)
+API int
+nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
{
- struct nc_session *new_session;
- int ret;
+ uint16_t i;
+ struct nc_session *new_session = NULL;
- new_session = calloc(1, sizeof *new_session);
- new_session->ti.libssh.session = session->ti.libssh.session;
- ret = nc_open_netconf_channel(new_session, timeout);
- if (ret) {
- goto fail;
+ if (!ps || !session) {
+ ERRARG;
+ return -1;
}
- /* new channel was requested and opened, fill in the whole session now */
- for (; session->ti.libssh.next; session = session->ti.libssh.next);
- session->ti.libssh.next = new_session;
+ for (i = 0; i < ps->session_count; ++i) {
+ if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
+ && ps->sessions[i]->ti.libssh.next) {
+ /* an SSH session with more channels */
+ for (new_session = ps->sessions[i]->ti.libssh.next;
+ new_session != ps->sessions[i];
+ 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 != ps->sessions[i]) {
+ break;
+ }
- new_session->status = NC_STATUS_STARTING;
- new_session->side = NC_SERVER;
- new_session->ti_type = NC_TI_LIBSSH;
- new_session->ti_lock = session->ti_lock;
- new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SHAREDCTX;
- new_session->ctx = server_opts.ctx;
+ new_session = NULL;
+ }
+ }
- new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
- new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
- new_session->port = session->port;
+ if (!new_session) {
+ ERR("No session with a NETCONF SSH channel ready was found.");
+ return -1;
+ }
+
+ /* 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 */
if (nc_handshake(new_session)) {
- goto fail;
+ return -1;
}
new_session->status = NC_STATUS_RUNNING;
+ *session = new_session;
- return new_session;
-
-fail:
- nc_session_free(new_session);
-
- return NULL;
+ return 0;
}