-
Notifications
You must be signed in to change notification settings - Fork 150
/
Copy pathclient.c
266 lines (232 loc) · 8.35 KB
/
client.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/**
* @file client.c
* @author Roman Janota <[email protected]>
* @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 \tPrint usage help.\n"
" -p, --port\t\t<port>\tSpecify the port to connect to.\n"
" -u, --unix-path\t<path>\tConnect to a UNIX socket located at <path>.\n"
" -P, --ssh-pubkey\t<path>\tSet the path to an SSH Public key.\n"
" -i, --ssh-privkey\t<path>\tSet 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");
}
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;
}