client CHANGE make client options thread specific

Allow different client settings (schema searchpath, call home bindings,
SSH/TLS authentication settings) in different threads. This change should
improve multithreading support for the client applications.
diff --git a/src/netconf.h b/src/netconf.h
index ef69a74..73152db 100644
--- a/src/netconf.h
+++ b/src/netconf.h
@@ -125,20 +125,6 @@
     NC_PARAMTYPE_DUP_AND_FREE /**< make a copy of the argument, free afterwards */
 } NC_PARAMTYPE;
 
-#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
-
-/**
- * @brief Free all the dynamically allocated thread-specific libssl/libcrypto
- * resources.
- *
- * This function should be called only if init (nc_client_init(), respectively nc_server_init()) was called.
- * Call it in every thread your application creates just before the thread exits. In the last thread
- * (usually the main one) call nc_client_destroy(), respectively nc_server_destroy().
- */
-void nc_thread_destroy(void);
-
-#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
-
 /**
  * @brief Transform given time_t (seconds since the epoch) into the RFC 3339 format
  * accepted by NETCONF functions.
diff --git a/src/session.h b/src/session.h
index 314ec12..2d35243 100644
--- a/src/session.h
+++ b/src/session.h
@@ -201,4 +201,18 @@
  */
 void nc_session_free(struct nc_session *session, void (*data_free)(void *));
 
+#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
+
+/**
+ * @brief Free all the dynamically allocated thread-specific libssl/libcrypto
+ * resources.
+ *
+ * This function should be called only if init (nc_client_init(), respectively nc_server_init()) was called.
+ * Call it in every thread your application creates just before the thread exits. In the last thread
+ * (usually the main one) call nc_client_destroy(), respectively nc_server_destroy().
+ */
+void nc_thread_destroy(void);
+
+#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
+
 #endif /* NC_SESSION_H_ */
diff --git a/src/session_client.c b/src/session_client.c
index 2614bf9..165f336 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <arpa/inet.h>
@@ -35,7 +36,119 @@
 
 static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
 
-struct nc_client_opts client_opts;
+#ifdef NC_ENABLED_SSH
+int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
+char *sshauth_password(const char *username, const char *hostname, void *priv);
+char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
+char *sshauth_privkey_passphrase(const char* privkey_path, void *priv);
+#endif /* NC_ENABLED_SSH */
+
+static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
+static pthread_key_t nc_client_context_key;
+#ifdef __linux__
+static struct nc_client_context context_main = {
+    /* .opts zeroed */
+#ifdef NC_ENABLED_SSH
+    .ssh_opts = {
+        .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
+        .auth_hostkey_check = sshauth_hostkey_check,
+        .auth_password = sshauth_password,
+        .auth_interactive = sshauth_interactive,
+        .auth_privkey_passphrase = sshauth_privkey_passphrase
+    },
+    .ssh_ch_opts = {
+        .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
+        .auth_hostkey_check = sshauth_hostkey_check,
+        .auth_password = sshauth_password,
+        .auth_interactive = sshauth_interactive,
+        .auth_privkey_passphrase = sshauth_privkey_passphrase
+    }
+#endif /* NC_ENABLED_SSH */
+    /* .tls_ structures zeroed */
+};
+#endif
+
+static void
+nc_client_context_free(void *ptr)
+{
+    struct nc_client_context *c = (struct nc_client_context *)ptr;
+
+#ifdef __linux__
+    /* in __linux__ we use static memory in the main thread,
+     * so this check is for programs terminating the main()
+     * function by pthread_exit() :)
+     */
+    if (c != &context_main)
+#endif
+    {
+        nc_client_set_schema_searchpath(NULL);
+#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
+        nc_client_ch_del_bind(NULL, 0, 0);
+#endif
+#ifdef NC_ENABLED_SSH
+        nc_client_ssh_destroy_opts();
+#endif
+#ifdef NC_ENABLED_TLS
+        nc_client_tls_destroy_opts();
+#endif
+        free(c);
+    }
+}
+
+static void
+nc_client_context_createkey(void)
+{
+    int r;
+
+    /* initiate */
+    while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN);
+    pthread_setspecific(nc_client_context_key, NULL);
+}
+
+struct nc_client_context *
+nc_client_context_location(void)
+{
+    struct nc_client_context *e;
+
+    pthread_once(&nc_client_context_once, nc_client_context_createkey);
+    e = pthread_getspecific(nc_client_context_key);
+    if (!e) {
+        /* prepare ly_err storage */
+#ifdef __linux__
+        if (getpid() == syscall(SYS_gettid)) {
+            /* main thread - use global variable instead of thread-specific variable. */
+            e = &context_main;
+        } else
+#endif /* __linux__ */
+        {
+            e = calloc(1, sizeof *e);
+            /* set default values */
+#ifdef NC_ENABLED_SSH
+            e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
+            e->ssh_opts.auth_pref[0].value = 3;
+            e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
+            e->ssh_opts.auth_pref[1].value = 2;
+            e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
+            e->ssh_opts.auth_pref[2].value = 1;
+            e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
+            e->ssh_opts.auth_password = sshauth_password;
+            e->ssh_opts.auth_interactive = sshauth_interactive;
+            e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
+
+            /* callhome settings are the same except the inverted auth methods preferences */
+            memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
+            e->ssh_ch_opts.auth_pref[0].value = 1;
+            e->ssh_ch_opts.auth_pref[1].value = 2;
+            e->ssh_ch_opts.auth_pref[2].value = 3;
+#endif /* NC_ENABLED_SSH */
+        }
+        pthread_setspecific(nc_client_context_key, e);
+    }
+
+    return e;
+}
+
+#define client_opts nc_client_context_location()->opts
 
 API int
 nc_client_set_schema_searchpath(const char *path)
@@ -1117,16 +1230,6 @@
 API void
 nc_client_destroy(void)
 {
-    nc_client_set_schema_searchpath(NULL);
-#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
-    nc_client_ch_del_bind(NULL, 0, 0);
-#endif
-#ifdef NC_ENABLED_SSH
-    nc_client_ssh_destroy_opts();
-#endif
-#ifdef NC_ENABLED_TLS
-    nc_client_tls_destroy_opts();
-#endif
     nc_destroy();
 }
 
diff --git a/src/session_client.h b/src/session_client.h
index bb2a700..b136d45 100644
--- a/src/session_client.h
+++ b/src/session_client.h
@@ -247,8 +247,13 @@
 /**
  * @brief Set SSH authentication method preference.
  *
- * @param[in] auth_type Authentication method to modify the prefrence of.
- * @param[in] pref Preference of \p auth_type. Negative values disable the method.
+ * The default preference is as follows:
+ * - interactive authentication (3)
+ * - password authentication (2)
+ * - public key authentication (1)
+ *
+ * @param[in] auth_type Authentication method to modify the preference of.
+ * @param[in] pref Preference of \p auth_type. Higher number increases priority, negative values disable the method.
  */
 void nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref);
 
diff --git a/src/session_client_ch.h b/src/session_client_ch.h
index d31edc9..c94b42d 100644
--- a/src/session_client_ch.h
+++ b/src/session_client_ch.h
@@ -211,8 +211,13 @@
 /**
  * @brief Set SSH Call Home authentication method preference.
  *
- * @param[in] auth_type Authentication method to modify the prefrence of.
- * @param[in] pref Preference of \p auth_type. Negative values disable the method.
+ * The default preference is as follows:
+ * - public key authentication (3)
+ * - password authentication (2)
+ * - interactive authentication (1)
+ *
+ * @param[in] auth_type Authentication method to modify the preference of.
+ * @param[in] pref Preference of \p auth_type. Higher number increases priority, negative values disable the method.
  */
 void nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref);
 
diff --git a/src/session_client_ssh.c b/src/session_client_ssh.c
index cf4ad95..b30409a 100644
--- a/src/session_client_ssh.c
+++ b/src/session_client_ssh.c
@@ -44,28 +44,11 @@
 #include "session_client_ch.h"
 #include "libnetconf.h"
 
-static int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
-static char *sshauth_password(const char *username, const char *hostname, void *priv);
-static char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
-static char *sshauth_privkey_passphrase(const char* privkey_path, void *priv);
+struct nc_client_context *nc_client_context_location(void);
 
-extern struct nc_client_opts client_opts;
-
-static struct nc_client_ssh_opts ssh_opts = {
-    .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
-    .auth_hostkey_check = sshauth_hostkey_check,
-    .auth_password = sshauth_password,
-    .auth_interactive = sshauth_interactive,
-    .auth_privkey_passphrase = sshauth_privkey_passphrase
-};
-
-static struct nc_client_ssh_opts ssh_ch_opts = {
-    .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
-    .auth_hostkey_check = sshauth_hostkey_check,
-    .auth_password = sshauth_password,
-    .auth_interactive = sshauth_interactive,
-    .auth_privkey_passphrase = sshauth_privkey_passphrase
-};
+#define client_opts nc_client_context_location()->opts
+#define ssh_opts nc_client_context_location()->ssh_opts
+#define ssh_ch_opts nc_client_context_location()->ssh_ch_opts
 
 static FILE *
 open_tty_noecho(const char *path, struct termios *oldterm)
@@ -221,7 +204,7 @@
 
 #endif /* ENABLE_DNSSEC */
 
-static int
+int
 sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(priv))
 {
     char *hexa;
@@ -342,7 +325,7 @@
     return -1;
 }
 
-static char *
+char *
 sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
 {
     char *buf;
@@ -391,7 +374,7 @@
     return buf;
 }
 
-static char *
+char *
 sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
 {
     unsigned int buflen = 64, cur_len;
@@ -468,7 +451,7 @@
     return NULL;
 }
 
-static char *
+char *
 sshauth_privkey_passphrase(const char* privkey_path, void *UNUSED(priv))
 {
     char c, *buf;
diff --git a/src/session_client_tls.c b/src/session_client_tls.c
index f6435b1..4ce2a8e 100644
--- a/src/session_client_tls.c
+++ b/src/session_client_tls.c
@@ -29,9 +29,11 @@
 #include "session_client_ch.h"
 #include "libnetconf.h"
 
-extern struct nc_client_opts client_opts;
-static struct nc_client_tls_opts tls_opts;
-static struct nc_client_tls_opts tls_ch_opts;
+struct nc_client_context *nc_client_context_location(void);
+
+#define client_opts nc_client_context_location()->opts
+#define tls_opts nc_client_context_location()->tls_opts
+#define tls_ch_opts nc_client_context_location()->tls_ch_opts
 
 static int tlsauth_ch;
 
diff --git a/src/session_p.h b/src/session_p.h
index 13982cf..6a69573 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -136,6 +136,19 @@
     uint16_t ch_bind_count;
 };
 
+/* ACCESS unlocked */
+struct nc_client_context {
+    struct nc_client_opts opts;
+#ifdef NC_ENABLED_SSH
+    struct nc_client_ssh_opts ssh_opts;
+    struct nc_client_ssh_opts ssh_ch_opts;
+#endif /* NC_ENABLED_SSH */
+#ifdef NC_ENABLED_TLS
+    struct nc_client_tls_opts tls_opts;
+    struct nc_client_tls_opts tls_ch_opts;
+#endif /* NC_ENABLED_TLS */
+};
+
 struct nc_server_opts {
     /* ACCESS unlocked (dictionary locked internally in libyang) */
     struct ly_ctx *ctx;