diff --git a/CHANGELOG.md b/CHANGELOG.md
index e892b8f6..2abf271a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,16 @@
## 6.1.0
Expected: beginning of 2023
+### C/CLI-API changes on existing features
+Developers may need to change their code
+
+* C-API
+ * Added `sock_flags` parameter to `clixon_proc_socket()`
+
+### Minor features
+
+* Added `CLIXON_CLIENT_SSH` to client API to communicate remotely via SSH netconf sub-system
+
### Corrected Bugs
* Fixed [Netconf monitoring](https://github.com/clicon/clixon/issues/370)
diff --git a/configure b/configure
index efbdb17e..6a341253 100755
--- a/configure
+++ b/configure
@@ -4607,7 +4607,7 @@ fi
cat >>confdefs.h <<_ACEOF
-#define SSH_BIN $SSH_BIN
+#define SSH_BIN "$SSH_BIN"
_ACEOF
diff --git a/configure.ac b/configure.ac
index 803eccc3..93b11da0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -149,7 +149,7 @@ fi
# SSH binary path
AC_PATH_PROG(SSH_BIN, ssh)
-AC_DEFINE_UNQUOTED(SSH_BIN, $SSH_BIN, [SSH binary])
+AC_DEFINE_UNQUOTED(SSH_BIN, "$SSH_BIN", [SSH binary])
# Get "bison" from bison -y or other string
if test "$YACC" = "${YACC##bison}" ; then
diff --git a/lib/clixon/clixon_client.h b/lib/clixon/clixon_client.h
index 2b8172a8..0feb3a84 100644
--- a/lib/clixon/clixon_client.h
+++ b/lib/clixon/clixon_client.h
@@ -42,11 +42,25 @@
typedef void *clixon_handle;
typedef void *clixon_client_handle;
-/* Connection type as parameter to connect */
+/* Connection type as parameter to connect
+ */
typedef enum {
- CLIXON_CLIENT_IPC, /* Internal IPC API, only experimental use */
- CLIXON_CLIENT_NETCONF, /* External Netconf */
- CLIXON_CLIENT_SSH /* NYI External Netconf over SSH */
+ /* Internal IPC API, connect directly on local UNIX domain socket to backend
+ * or using IP according to CLICON_SOCK_FAMILY setting
+ * see https://clixon-docs.readthedocs.io/en/latest/netconf.html#ipc
+ * Must be local on device
+ */
+ CLIXON_CLIENT_IPC,
+ /* Regular NETCONF via local netconf binary
+ * Fork clixon_netconf locally which in turn communicates with backend
+ * Must be local on device
+ */
+ CLIXON_CLIENT_NETCONF,
+ /* Regular NETCONF using ssh sub-system via local SSH (openssh) client binary
+ * Fork ssh locally which in turn communicates remotely to device
+ * Must have openssh installed locally and device must have ssh sub-subsystem
+ */
+ CLIXON_CLIENT_SSH
} clixon_client_type;
/*
@@ -59,7 +73,9 @@ extern "C" {
clixon_handle clixon_client_init(const char *config_file);
int clixon_client_terminate(clixon_handle h);
-clixon_client_handle clixon_client_connect(clixon_handle h, clixon_client_type socktype);
+int clixon_client_lock(int sock, const int lock, const char *db);
+int clixon_client_hello(int sock, int version);
+clixon_client_handle clixon_client_connect(clixon_handle h, clixon_client_type socktype, const char *dest);
int clixon_client_disconnect(clixon_client_handle ch);
int clixon_client_get_bool(clixon_client_handle ch, int *rval, const char *xnamespace, const char *xpath);
int clixon_client_get_str(clixon_client_handle ch, char *rval, int n, const char *xnamespace, const char *xpath);
diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h
index 1e6d3cba..ea89dd75 100644
--- a/lib/clixon/clixon_netconf_lib.h
+++ b/lib/clixon/clixon_netconf_lib.h
@@ -124,7 +124,7 @@ enum error_option{ /* edit-config */
/* NETCONF framing
*/
enum framing_type{
- NETCONF_SSH_EOM, /* RFC 4742, RFC 6242 hello msg (end-of-msg: ]]>]]>)*/
+ NETCONF_SSH_EOM=0, /* RFC 4742, RFC 6242 hello msg (end-of-msg: ]]>]]>)*/
NETCONF_SSH_CHUNKED, /* RFC 6242 Chunked framing */
};
typedef enum framing_type netconf_framing_type;
diff --git a/lib/clixon/clixon_proc.h b/lib/clixon/clixon_proc.h
index d2e1c3b7..e57adcf4 100644
--- a/lib/clixon/clixon_proc.h
+++ b/lib/clixon/clixon_proc.h
@@ -58,7 +58,7 @@ typedef int (proc_cb_t)(clicon_handle h, process_entry_t *pe, proc_operation *op
/*
* Prototypes
*/
-int clixon_proc_socket(char **argv, pid_t *pid, int *sock);
+int clixon_proc_socket(char **argv, int sock_flags, pid_t *pid, int *sock);
int clixon_proc_socket_close(pid_t pid, int sock);
int clixon_proc_background(char **argv, const char *netns, pid_t *pid);
int clixon_process_pid(clicon_handle h, const char *name, pid_t *pid);
diff --git a/lib/src/clixon_client.c b/lib/src/clixon_client.c
index e66848eb..ac84f6c1 100644
--- a/lib/src/clixon_client.c
+++ b/lib/src/clixon_client.c
@@ -146,7 +146,7 @@ clixon_client_terminate(clicon_handle h)
* @retval 0 OK
* @retval -1 Error
*/
-static int
+int
clixon_client_lock(int sock,
const int lock,
const char *db)
@@ -204,15 +204,14 @@ clixon_client_lock(int sock,
/*! Internal function to construct the encoding and hello message
*
- * @param[in] sock Socket
- * @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
- * @param[in] xpath XPath
- * @param[out] xdata XML data tree (may or may not include the intended data)
- * @retval 0 OK
- * @retval -1 Error
+ * @param[in] sock Socket to netconf server
+ * @param[in] version Netconf version for capability announcement
+ * @retval 0 OK
+ * @retval -1 Error
*/
-static int
-clixon_client_hello(int sock)
+int
+clixon_client_hello(int sock,
+ int version)
{
int retval = -1;
cbuf *msg = NULL;
@@ -222,9 +221,11 @@ clixon_client_hello(int sock)
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
- cprintf(msg, "");
+ // cprintf(msg, "\n");
cprintf(msg, "", NETCONF_BASE_NAMESPACE);
- cprintf(msg, "%s", NETCONF_BASE_CAPABILITY_1_1);
+ cprintf(msg, "");
+ cprintf(msg, "%s", version==0?NETCONF_BASE_CAPABILITY_1_0:NETCONF_BASE_CAPABILITY_1_1);
+ cprintf(msg, "");
cprintf(msg, "");
cprintf(msg, "]]>]]>");
if (clicon_msg_send1(sock, msg) < 0)
@@ -237,25 +238,112 @@ clixon_client_hello(int sock)
return retval;
}
+/*!
+ */
+static int
+clixon_client_connect_netconf(clicon_handle h,
+ struct clixon_client_handle *cch)
+{
+ int retval = -1;
+ int nr;
+ int i;
+ char **argv = NULL;
+ char *netconf_bin = NULL;
+ struct stat st = {0,};
+ char dbgstr[8];
+
+ nr = 7;
+ if (clicon_debug_get() != 0)
+ nr += 2;
+ if ((argv = calloc(nr, sizeof(char *))) == NULL){
+ clicon_err(OE_UNIX, errno, "calloc");
+ goto done;
+ }
+ i = 0;
+ if ((netconf_bin = getenv("CLIXON_NETCONF_BIN")) == NULL)
+ netconf_bin = CLIXON_NETCONF_BIN;
+ if (stat(netconf_bin, &st) < 0){
+ clicon_err(OE_NETCONF, errno, "netconf binary %s. Set with CLIXON_NETCONF_BIN=",
+ netconf_bin);
+ goto done;
+ }
+ argv[i++] = netconf_bin;
+ argv[i++] = "-q";
+ argv[i++] = "-f";
+ argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
+ argv[i++] = "-l"; /* log to syslog */
+ argv[i++] = "s";
+ if (clicon_debug_get() != 0){
+ argv[i++] = "-D";
+ snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
+ argv[i++] = dbgstr;
+ }
+ argv[i++] = NULL;
+ assert(i==nr);
+ if (clixon_proc_socket(argv, SOCK_DGRAM, &cch->cch_pid, &cch->cch_socket) < 0){
+ goto done;
+ }
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*!
+ */
+static int
+clixon_client_connect_ssh(clicon_handle h,
+ struct clixon_client_handle *cch,
+ const char *dest)
+{
+ int retval = -1;
+ int nr;
+ int i;
+ char **argv = NULL;
+ char *ssh_bin = SSH_BIN;
+ struct stat st = {0,};
+
+ clicon_debug(1, "%s", __FUNCTION__);
+ nr = 5;
+ if ((argv = calloc(nr, sizeof(char *))) == NULL){
+ clicon_err(OE_UNIX, errno, "calloc");
+ goto done;
+ }
+ i = 0;
+ if (stat(ssh_bin, &st) < 0){
+ clicon_err(OE_NETCONF, errno, "ssh binary %s", ssh_bin);
+ goto done;
+ }
+ argv[i++] = ssh_bin;
+ argv[i++] = (char*)dest;
+ argv[i++] = "-s";
+ argv[i++] = "netconf";
+ argv[i++] = NULL;
+ assert(i==nr);
+ for (i=0;icch_pid, &cch->cch_socket) < 0){
+ goto done;
+ }
+ retval = 0;
+ done:
+ return retval;
+}
+
/*! Connect client to clixon backend according to config and return a socket
* @param[in] h Clixon handle
* @param[in] socktype Type of socket, internal/external/netconf/ssh
+ * @param[in] dest Destination for some types
* @retval ch Clixon session handler
* @retval NULL Error
* @see clixon_client_disconnect Close the socket returned here
*/
clixon_client_handle
clixon_client_connect(clicon_handle h,
- clixon_client_type socktype)
+ clixon_client_type socktype,
+ const char *dest)
{
struct clixon_client_handle *cch = NULL;
- char **argv = NULL;
- int nr;
- int i;
- char *netconf_bin = NULL;
- struct stat st = {0,};
size_t sz = sizeof(struct clixon_client_handle);
- char dbgstr[8];
clicon_debug(1, "%s", __FUNCTION__);
if ((cch = malloc(sz)) == NULL){
@@ -271,48 +359,19 @@ clixon_client_connect(clicon_handle h,
goto err;
break;
case CLIXON_CLIENT_NETCONF:
- nr = 7;
- if (clicon_debug_get() != 0)
- nr += 2;
- if ((argv = calloc(nr, sizeof(char *))) == NULL){
- clicon_err(OE_UNIX, errno, "calloc");
- goto err;
- }
- i = 0;
- if ((netconf_bin = getenv("CLIXON_NETCONF_BIN")) == NULL)
- netconf_bin = CLIXON_NETCONF_BIN;
- if (stat(netconf_bin, &st) < 0){
- clicon_err(OE_NETCONF, errno, "netconf binary %s. Set with CLIXON_NETCONF_BIN=",
- netconf_bin);
- goto err;
- }
- argv[i++] = netconf_bin;
- argv[i++] = "-q";
- argv[i++] = "-f";
- argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
- argv[i++] = "-l"; /* log to syslog */
- argv[i++] = "s";
- if (clicon_debug_get() != 0){
- argv[i++] = "-D";
- snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
- argv[i++] = dbgstr;
- }
- argv[i++] = NULL;
- assert(i==nr);
- if (clixon_proc_socket(argv, &cch->cch_pid, &cch->cch_socket) < 0){
- goto err;
- }
- /* Start with encoding and hello message */
- if (clixon_client_hello(cch->cch_socket) < 0)
+ if (clixon_client_connect_netconf(h, cch) < 0)
goto err;
break;
+#ifdef SSH_BIN
case CLIXON_CLIENT_SSH:
+ if (clixon_client_connect_ssh(h, cch, dest) < 0)
+ goto err;
+#else
+ clicon_err(OE_UNIX, 0, "No ssh bin");
+ goto done;
+#endif
break;
} /* switch */
- /* lock */
- if (clixon_client_lock(cch->cch_socket, 1, "running") < 0)
- goto err;
- cch->cch_locked = 1;
done:
clicon_debug(1, "%s retval:%p", __FUNCTION__, cch);
return cch;
@@ -347,13 +406,12 @@ clixon_client_disconnect(clixon_client_handle ch)
case CLIXON_CLIENT_IPC:
close(cch->cch_socket);
break;
+ case CLIXON_CLIENT_SSH:
case CLIXON_CLIENT_NETCONF:
if (clixon_proc_socket_close(cch->cch_pid,
cch->cch_socket) < 0)
goto done;
break;
- case CLIXON_CLIENT_SSH:
- break;
}
free(cch);
retval = 0;
@@ -403,6 +461,7 @@ clixon_xml_bottom(cxobj *xtop,
* @param[out] xdata XML data tree (may or may not include the intended data)
* @retval 0 OK
* @retval -1 Error
+ * @note configurable netconf framing type, now hardwired to 0
*/
static int
clixon_client_get_xdata(int sock,
@@ -445,9 +504,11 @@ clixon_client_get_xdata(int sock,
cprintf(msg, "/>");
}
cprintf(msg, "");
- if (netconf_output_encap(NETCONF_SSH_CHUNKED, msg) < 0)
+ if (netconf_output_encap(0, msg) < 0) // XXX configurable session
goto done;
- if (clicon_rpc1(sock, msg, msgret, &eof) < 0)
+ if (clicon_msg_send1(sock, msg) < 0)
+ goto done;
+ if (clicon_msg_rcv1(sock, msgret, &eof) < 0)
goto done;
if (eof){
close(sock);
diff --git a/lib/src/clixon_proc.c b/lib/src/clixon_proc.c
index b26ae094..421b72ef 100644
--- a/lib/src/clixon_proc.c
+++ b/lib/src/clixon_proc.c
@@ -163,15 +163,18 @@ clixon_proc_sigint(int sig)
}
/*! Fork a child, exec a child and setup socket to child and return to caller
- * @param[in] argv NULL-terminated Argument vector
- * @param[in] doerr If non-zero, stderr will be directed to the pipe as well.
- * @param[out] s Socket
- * @retval O OK
- * @retval -1 Error.
+ * @param[in] argv NULL-terminated Argument vector
+ * @param[in] sock_flags Socket type/flags, typically SOCK_DGRAM or SOCK_STREAM, see
+ * @param[out] pid Process-id of child
+ * @param[out] sock Socket
+ * @retval O OK
+ * @retval -1 Error.
* @see clixon_proc_socket_close close sockets, kill child and wait for child termination
+ * @see for flags usage see man sockerpair(2)
*/
int
clixon_proc_socket(char **argv,
+ int sock_flags,
pid_t *pid,
int *sock)
{
@@ -182,12 +185,6 @@ clixon_proc_socket(char **argv,
sigset_t oset;
int sig = 0;
-#ifdef __APPLE__
- int sock_flags = SOCK_DGRAM;
-#else
- int sock_flags = SOCK_DGRAM | SOCK_CLOEXEC;
-#endif
-
if (argv == NULL){
clicon_err(OE_UNIX, EINVAL, "argv is NULL");
goto done;
@@ -197,18 +194,6 @@ clixon_proc_socket(char **argv,
goto done;
}
-#ifdef __APPLE__
- if (fcntl(sp[0], O_CLOEXEC)) {
- clicon_err(OE_UNIX, errno, "fcntl, sp[0]");
- goto done;
- }
-
- if (fcntl(sp[1], O_CLOEXEC)) {
- clicon_err(OE_UNIX, errno, "fcntl, sp[1]");
- goto done;
- }
-#endif
-
sigprocmask(0, NULL, &oset);
set_signal(SIGINT, clixon_proc_sigint, &oldhandler);
sig++;
diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c
index e0f0276b..e2e257c4 100644
--- a/lib/src/clixon_proto.c
+++ b/lib/src/clixon_proto.c
@@ -433,6 +433,7 @@ clicon_msg_rcv(int s,
* @param[out] eof Set if eof encountered
* @see netconf_input_cb()
* @see clicon_msg_rcv using IPC message struct
+ * @note only NETCONF version 1.0 EOM framing
*/
int
clicon_msg_rcv1(int s,
@@ -502,7 +503,7 @@ clicon_msg_send1(int s,
int retval = -1;
if (atomicio((ssize_t (*)(int, void *, size_t))write,
- s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
+ s, cbuf_get(cb), cbuf_len(cb)) < 0){
clicon_err(OE_CFG, errno, "atomicio");
clicon_log(LOG_WARNING, "%s: write: %s", __FUNCTION__, strerror(errno));
goto done;
diff --git a/test/test_client.sh b/test/test_client.sh
index 1a7dc794..746ce4c6 100755
--- a/test/test_client.sh
+++ b/test/test_client.sh
@@ -85,8 +85,9 @@ main(int argc,
char **argv)
{
int retval = -1;
- clixon_handle h = NULL; /* clixon handle */
+ clixon_handle h = NULL; /* clixon handle */
clixon_client_handle ch = NULL; /* clixon client handle */
+ int s;
clicon_log_init("client", LOG_DEBUG, CLICON_LOG_STDERR); // debug
clicon_debug_init($debug, NULL); // debug
@@ -95,9 +96,11 @@ main(int argc,
if ((h = clixon_client_init("$cfg")) == NULL)
return -1;
/* Make a connection over netconf or ssh/netconf */
- if ((ch = clixon_client_connect(h, CLIXON_CLIENT_NETCONF)) == NULL)
+ if ((ch = clixon_client_connect(h, CLIXON_CLIENT_NETCONF, NULL)) == NULL)
return -1;
-
+ s = clixon_client_socket_get(ch);
+ if (clixon_client_hello(s, 0) < 0)
+ return -1;
/* Here are read functions depending on an example YANG
* (Need an example YANG and XML input to confd)
*/