2025-02-18 11:33:23 +01:00
/**
* @ file client . c
* @ author Roman Janota < xjanot04 @ fit . vutbr . cz >
* @ brief libnetconf2 client example
*
* @ copyright
* Copyright ( c ) 2022 CESNET , z . s . p . o .
*
* This source code is licensed under BSD 3 - Clause License ( the " License " ) .
* You may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* https : //opensource.org/licenses/BSD-3-Clause
*/
# include "example.h"
# include <getopt.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <libyang/libyang.h>
# include "log.h"
# include "messages_client.h"
# include "netconf.h"
# include "session_client.h"
# include "session_client_ch.h"
static void
help_print ( )
{
printf ( " Example usage: \n "
" client get \n "
" \n "
" Available options: \n "
" -h, --help \t \t Print usage help. \n "
" -p, --port \t \t <port> \t Specify the port to connect to. \n "
" -u, --unix-path \t <path> \t Connect to a UNIX socket located at <path>. \n "
" -P, --ssh-pubkey \t <path> \t Set the path to an SSH Public key. \n "
" -i, --ssh-privkey \t <path> \t Set the path to an SSH Private key. \n \n "
" Available RPCs: \n "
" get [xpath-filter] \t \t \t \t \t send a <get> RPC with optional XPath filter \n "
" get-config [datastore] [xpath-filter] \t \t send a <get-config> RPC with optional XPath filter and datastore, the default datastore is \" running \" \n \n " ) ;
}
2025-05-02 04:10:16 +02:00
static NC_DATASTORE
2025-02-18 11:33:23 +01:00
string2datastore ( const char * str )
{
if ( ! str ) {
return NC_DATASTORE_RUNNING ;
}
if ( ! strcmp ( str , " candidate " ) ) {
return NC_DATASTORE_CANDIDATE ;
} else if ( ! strcmp ( str , " running " ) ) {
return NC_DATASTORE_RUNNING ;
} else if ( ! strcmp ( str , " startup " ) ) {
return NC_DATASTORE_STARTUP ;
} else {
return 0 ;
}
}
static int
send_rpc ( struct nc_session * session , NC_RPC_TYPE rpc_type , const char * param1 , const char * param2 )
{
2025-05-02 04:10:16 +02:00
NC_DATASTORE datastore ;
2025-02-18 11:33:23 +01:00
int r = 0 , rc = 0 ;
uint64_t msg_id = 0 ;
struct lyd_node * envp = NULL , * op = NULL ;
struct nc_rpc * rpc = NULL ;
/* decide which type of RPC to send */
switch ( rpc_type ) {
case NC_RPC_GET :
/* create get RPC with an optional filter */
rpc = nc_rpc_get ( param1 , NC_WD_UNKNOWN , NC_PARAMTYPE_CONST ) ;
break ;
case NC_RPC_GETCONFIG :
/* create get-config RPC with a source datastore and an optional filter */
datastore = string2datastore ( param1 ) ;
if ( ! datastore ) {
ERR_MSG_CLEANUP ( " Invalid name of a datastore. Use candidate, running, startup or neither. \n " ) ;
}
rpc = nc_rpc_getconfig ( datastore , param2 , NC_WD_UNKNOWN , NC_PARAMTYPE_CONST ) ;
break ;
default :
break ;
}
if ( ! rpc ) {
ERR_MSG_CLEANUP ( " Error while creating a RPC \n " ) ;
}
/* send the RPC on the session and remember NETCONF message ID */
r = nc_send_rpc ( session , rpc , 100 , & msg_id ) ;
if ( r ! = NC_MSG_RPC ) {
ERR_MSG_CLEANUP ( " Couldn't send a RPC \n " ) ;
}
/* receive the server's reply with the expected message ID
* as separate rpc - reply NETCONF envelopes and the parsed YANG output itself , if any */
r = nc_recv_reply ( session , rpc , msg_id , 100 , & envp , & op ) ;
if ( r ! = NC_MSG_REPLY ) {
ERR_MSG_CLEANUP ( " Couldn't receive a reply from the server \n " ) ;
}
/* print the whole reply */
if ( ! op ) {
r = lyd_print_file ( stdout , envp , LYD_XML , 0 ) ;
} else {
r = lyd_print_file ( stdout , op , LYD_XML , 0 ) ;
if ( r ) {
ERR_MSG_CLEANUP ( " Couldn't print the RPC to stdout \n " ) ;
}
r = lyd_print_file ( stdout , envp , LYD_XML , 0 ) ;
}
if ( r ) {
ERR_MSG_CLEANUP ( " Couldn't print the RPC to stdout \n " ) ;
}
cleanup :
lyd_free_all ( envp ) ;
lyd_free_all ( op ) ;
nc_rpc_free ( rpc ) ;
return rc ;
}
int
main ( int argc , char * * argv )
{
int rc = 0 , opt , port = 0 ;
struct nc_session * session = NULL ;
const char * unix_socket_path = NULL , * rpc_parameter_1 = NULL , * rpc_parameter_2 = NULL ;
const char * ssh_pubkey_path = NULL , * ssh_privkey_path = NULL ;
struct option options [ ] = {
{ " help " , no_argument , NULL , ' h ' } ,
{ " port " , required_argument , NULL , ' p ' } ,
{ " unix-path " , required_argument , NULL , ' u ' } ,
{ " ssh-pubkey " , required_argument , NULL , ' P ' } ,
{ " ssh-privkey " , required_argument , NULL , ' i ' } ,
{ " debug " , no_argument , NULL , ' d ' } ,
{ NULL , 0 , NULL , 0 }
} ;
if ( argc = = 1 ) {
help_print ( ) ;
goto cleanup ;
}
/* set the path to search for schemas */
nc_client_set_schema_searchpath ( MODULES_DIR ) ;
opterr = 0 ;
while ( ( opt = getopt_long ( argc , argv , " hp:u:P:i:d " , options , NULL ) ) ! = - 1 ) {
switch ( opt ) {
case ' h ' :
help_print ( ) ;
goto cleanup ;
case ' p ' :
port = strtoul ( optarg , NULL , 10 ) ;
break ;
case ' u ' :
unix_socket_path = optarg ;
break ;
case ' P ' :
ssh_pubkey_path = optarg ;
break ;
case ' i ' :
ssh_privkey_path = optarg ;
break ;
case ' d ' :
nc_verbosity ( NC_VERB_DEBUG ) ;
nc_libssh_thread_verbosity ( 2 ) ;
break ;
default :
ERR_MSG_CLEANUP ( " Invalid option or missing argument \n " ) ;
}
}
if ( optind = = argc ) {
ERR_MSG_CLEANUP ( " Expected the name of RPC after options \n " ) ;
}
/* check invalid args combinations */
if ( unix_socket_path & & port ) {
ERR_MSG_CLEANUP ( " Both UNIX socket path and port specified. Please choose either SSH or UNIX. \n " ) ;
} else if ( unix_socket_path & & ( ssh_pubkey_path | | ssh_privkey_path ) ) {
ERR_MSG_CLEANUP ( " Both UNIX socket path and a path to key(s) specified. Please choose either SSH or UNIX. \n " ) ;
} else if ( ( port = = 10001 ) & & ( ! ssh_pubkey_path | | ! ssh_privkey_path ) ) {
ERR_MSG_CLEANUP ( " You need to specify both paths to private and public keys, if you want to connect to a publickey endpoint. \n " ) ;
} else if ( ( port = = 10000 ) & & ( ssh_pubkey_path | | ssh_privkey_path ) ) {
ERR_MSG_CLEANUP ( " Public or private key specified, when connecting to the password endpoint. \n " ) ;
} else if ( ! unix_socket_path & & ! port ) {
ERR_MSG_CLEANUP ( " Neither UNIX socket or SSH specified. \n " ) ;
}
/* connect to the server using the specified transport protocol */
if ( unix_socket_path ) {
/* it's UNIX socket */
session = nc_connect_unix ( unix_socket_path , NULL ) ;
} else {
/* it must be SSH, so set the client SSH username to always be used when connecting to the server */
if ( nc_client_ssh_set_username ( SSH_USERNAME ) ) {
ERR_MSG_CLEANUP ( " Couldn't set the SSH username \n " ) ;
}
if ( ssh_pubkey_path & & ssh_privkey_path ) {
/* set the client's SSH keypair to be used for authentication if necessary */
if ( nc_client_ssh_add_keypair ( ssh_pubkey_path , ssh_privkey_path ) ) {
ERR_MSG_CLEANUP ( " Couldn't set client's SSH keypair. \n " ) ;
}
}
/* try to connect via SSH */
session = nc_connect_ssh ( SSH_ADDRESS , port , NULL ) ;
}
if ( ! session ) {
ERR_MSG_CLEANUP ( " Couldn't connect to the server \n " ) ;
}
/* sending a get RPC */
if ( ! strcmp ( argv [ optind ] , " get " ) ) {
if ( optind + 1 < argc ) {
/* use the specified XPath filter */
rpc_parameter_1 = argv [ optind + 1 ] ;
}
if ( send_rpc ( session , NC_RPC_GET , rpc_parameter_1 , rpc_parameter_2 ) ) {
rc = 1 ;
goto cleanup ;
}
/* sending a get-config RPC */
} else if ( ! strcmp ( argv [ optind ] , " get-config " ) ) {
/* use the specified datastore and optional XPath filter */
if ( optind + 2 < argc ) {
rpc_parameter_1 = argv [ optind + 1 ] ;
rpc_parameter_2 = argv [ optind + 2 ] ;
} else if ( optind + 1 < argc ) {
rpc_parameter_1 = argv [ optind + 1 ] ;
}
if ( send_rpc ( session , NC_RPC_GETCONFIG , rpc_parameter_1 , rpc_parameter_2 ) ) {
rc = 1 ;
goto cleanup ;
}
} else {
ERR_MSG_CLEANUP ( " Invalid name of a RPC \n " ) ;
}
cleanup :
nc_session_free ( session , NULL ) ;
nc_client_destroy ( ) ;
return rc ;
}