blob: 38512b60debde2f896541e5bd0a899a128c736ae [file] [log] [blame]
Roman Janota907cf932022-06-03 12:33:34 +02001/**
2 * @file client.c
3 * @author Roman Janota <xjanot04@fit.vutbr.cz>
4 * @brief libnetconf2 client example
5 *
6 * @copyright
7 * Copyright (c) 2022 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#include "example.h"
17
18#include <getopt.h>
19#include <stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#include <libyang/libyang.h>
26
27#include "log.h"
28#include "messages_client.h"
29#include "netconf.h"
30#include "session_client.h"
31#include "session_client_ch.h"
32
33static void
34help_print()
35{
36 printf("Example usage:\n"
37 " client -u ./unix_socket get\n"
38 "\n"
39 " Available options:\n"
40 " -h, --help\t \tPrint usage help.\n"
41 " -u, --unix\t<path>\tConnect to a UNIX socket at the place specified by <path>.\n"
42 " -s, --ssh\t\tConnect to a SSH server.\n\n"
43 " Available RPCs:\n"
44 " get [xpath-filter]\t\t\t\t\t send a <get> RPC with optional XPath filter\n"
45 " 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");
46}
47
48static enum NC_DATASTORE_TYPE
49string2datastore(const char *str)
50{
51 if (!str) {
52 return NC_DATASTORE_RUNNING;
53 }
54
55 if (!strcmp(str, "candidate")) {
56 return NC_DATASTORE_CANDIDATE;
57 } else if (!strcmp(str, "running")) {
58 return NC_DATASTORE_RUNNING;
59 } else if (!strcmp(str, "startup")) {
60 return NC_DATASTORE_STARTUP;
61 } else {
62 return 0;
63 }
64}
65
66static int
67send_rpc(struct nc_session *session, NC_RPC_TYPE rpc_type, const char *param1, const char *param2)
68{
69 enum NC_DATASTORE_TYPE datastore;
70 int r = 0, rc = 0;
71 uint64_t msg_id = 0;
72 struct lyd_node *envp = NULL, *op = NULL;
73 struct nc_rpc *rpc = NULL;
74
75 /* decide which type of RPC to send */
76 switch (rpc_type) {
77 case NC_RPC_GET:
78 /* create get RPC with an optional filter */
79 rpc = nc_rpc_get(param1, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST);
80 break;
81
82 case NC_RPC_GETCONFIG:
83 /* create get-config RPC with a source datastore and an optional filter */
84 datastore = string2datastore(param1);
85 if (!datastore) {
86 ERR_MSG_CLEANUP("Invalid name of a datastore. Use candidate, running, startup or neither.\n");
87 }
88 rpc = nc_rpc_getconfig(datastore, param2, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST);
89 break;
90
91 default:
92 break;
93 }
94 if (!rpc) {
95 ERR_MSG_CLEANUP("Error while creating a RPC\n");
96 }
97
98 /* send the RPC on the session and remember NETCONF message ID */
99 r = nc_send_rpc(session, rpc, 100, &msg_id);
100 if (r != NC_MSG_RPC) {
101 ERR_MSG_CLEANUP("Couldn't send a RPC\n");
102 }
103
104 /* receive the server's reply with the expected message ID
105 * as separate rpc-reply NETCONF envelopes and the parsed YANG output itself, if any */
106 r = nc_recv_reply(session, rpc, msg_id, 100, &envp, &op);
107 if (r != NC_MSG_REPLY) {
108 ERR_MSG_CLEANUP("Couldn't receive a reply from the server\n");
109 }
110
111 /* print the whole reply */
112 if (!op) {
113 r = lyd_print_file(stdout, envp, LYD_XML, 0);
114 } else {
115 r = lyd_print_file(stdout, op, LYD_XML, 0);
116 if (r) {
117 ERR_MSG_CLEANUP("Couldn't print the RPC to stdout\n");
118 }
119 r = lyd_print_file(stdout, envp, LYD_XML, 0);
120 }
121 if (r) {
122 ERR_MSG_CLEANUP("Couldn't print the RPC to stdout\n");
123 }
124
125cleanup:
126 lyd_free_all(envp);
127 lyd_free_all(op);
128 nc_rpc_free(rpc);
129 return rc;
130}
131
132int
133main(int argc, char **argv)
134{
135 int rc = 0, opt, connection_type = NONE;
136 struct nc_session *session = NULL;
137 const char *unix_socket_path = NULL, *rpc_parameter_1 = NULL, *rpc_parameter_2 = NULL;
138
139 struct option options[] = {
140 {"help", no_argument, NULL, 'h'},
141 {"unix", required_argument, NULL, 'u'},
142 {"ssh", no_argument, NULL, 's'},
143 {"debug", no_argument, NULL, 'd'},
144 {NULL, 0, NULL, 0}
145 };
146
147 if (argc == 1) {
148 help_print();
149 goto cleanup;
150 }
151
152 nc_client_init();
153 /* set the path to search for schemas */
154 nc_client_set_schema_searchpath(MODULES_DIR);
155
156 opterr = 0;
157
158 while ((opt = getopt_long(argc, argv, "hu:sd", options, NULL)) != -1) {
159 switch (opt) {
160 case 'h':
161 help_print();
162 goto cleanup;
163
164 case 'u':
165 unix_socket_path = optarg;
166 connection_type = UNIX;
167 break;
168
169 case 's':
170 connection_type = SSH;
171 /* set the client SSH username to always be used when connecting to the server */
172 if (nc_client_ssh_set_username(SSH_USERNAME)) {
173 ERR_MSG_CLEANUP("Couldn't set the SSH username\n");
174 }
175 break;
176
177 case 'd':
178 nc_verbosity(NC_VERB_DEBUG);
179 break;
180
181 default:
182 ERR_MSG_CLEANUP("Invalid option or missing argument\n");
183 }
184 }
185
186 if (optind == argc) {
187 ERR_MSG_CLEANUP("Expected the name of RPC after options\n");
188 }
189
190 /* connect to the server using the specified transport protocol */
191 switch (connection_type) {
192 case UNIX:
193 session = nc_connect_unix(unix_socket_path, NULL);
194 break;
195
196 case SSH:
197 session = nc_connect_ssh(SSH_ADDRESS, SSH_PORT, NULL);
198 break;
199
200 case NONE:
201 ERR_MSG_CLEANUP("Expected connection type (either SSH or UNIX)!\n");
202 }
203 if (!session) {
204 ERR_MSG_CLEANUP("Couldn't connect to the server\n");
205 }
206
207 /* sending a get RPC */
208 if (!strcmp(argv[optind], "get")) {
209 if (optind + 1 < argc) {
210 /* use the specified XPath filter */
211 rpc_parameter_1 = argv[optind + 1];
212 }
213 if (send_rpc(session, NC_RPC_GET, rpc_parameter_1, rpc_parameter_2)) {
214 rc = 1;
215 goto cleanup;
216 }
217 /* sending a get-config RPC */
218 } else if (!strcmp(argv[optind], "get-config")) {
219 /* use the specified datastore and optional XPath filter */
220 if (optind + 2 < argc) {
221 rpc_parameter_1 = argv[optind + 1];
222 rpc_parameter_2 = argv[optind + 2];
223 } else if (optind + 1 < argc) {
224 rpc_parameter_1 = argv[optind + 1];
225 }
226 if (send_rpc(session, NC_RPC_GETCONFIG, rpc_parameter_1, rpc_parameter_2)) {
227 rc = 1;
228 goto cleanup;
229 }
230 } else {
231 ERR_MSG_CLEANUP("Invalid name of a RPC\n");
232 }
233
234cleanup:
235 nc_session_free(session, NULL);
236 nc_client_destroy();
237 return rc;
238}