/** * @file client.c * @author Roman Janota * @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 #include #include #include #include #include #include #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 \tPrint usage help.\n" " -p, --port\t\t\tSpecify the port to connect to.\n" " -u, --unix-path\t\tConnect to a UNIX socket located at .\n" " -P, --ssh-pubkey\t\tSet the path to an SSH Public key.\n" " -i, --ssh-privkey\t\tSet the path to an SSH Private key.\n\n" " Available RPCs:\n" " get [xpath-filter]\t\t\t\t\t send a RPC with optional XPath filter\n" " get-config [datastore] [xpath-filter]\t\t send a RPC with optional XPath filter and datastore, the default datastore is \"running\" \n\n"); } static enum NC_DATASTORE_TYPE 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) { enum NC_DATASTORE_TYPE datastore; 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; }