blob: f42f1b36e25d4ca33343de6a9b05a55ebb3f3c0b [file] [log] [blame]
Michal Vasko294d4c62016-02-01 10:10:27 +01001/**
Michal Vasko7227f352016-02-01 12:11:40 +01002 * \file test_fd_comm.c
Michal Vasko294d4c62016-02-01 10:10:27 +01003 * \author Michal Vasko <mvasko@cesnet.cz>
Michal Vasko7227f352016-02-01 12:11:40 +01004 * \brief libnetconf2 tests - file descriptor basic RPC communication
Michal Vasko294d4c62016-02-01 10:10:27 +01005 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoad1cc6a2016-02-26 15:06:06 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko294d4c62016-02-01 10:10:27 +010013 */
14
15#include <errno.h>
16#include <fcntl.h>
17#include <pthread.h>
18#include <setjmp.h>
19#include <stdarg.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <sys/socket.h>
28
29#include <cmocka.h>
30#include <libyang/libyang.h>
31
32#include <session_client.h>
33#include <session_server.h>
34#include <session_p.h>
35#include <messages_p.h>
Jan Kundrátcf15d6c2017-10-26 18:07:56 +020036#include "tests/config.h"
Michal Vasko294d4c62016-02-01 10:10:27 +010037
38struct nc_session *server_session;
39struct nc_session *client_session;
Michal Vasko8c5518b2016-03-01 12:35:13 +010040struct ly_ctx *ctx;
Michal Vasko131120a2018-05-29 15:44:02 +020041volatile int glob_state;
Michal Vasko294d4c62016-02-01 10:10:27 +010042
43struct nc_server_reply *
44my_get_rpc_clb(struct lyd_node *rpc, struct nc_session *session)
45{
46 assert_string_equal(rpc->schema->name, "get");
47 assert_ptr_equal(session, server_session);
48
49 return nc_server_reply_ok();
50}
51
52struct nc_server_reply *
53my_getconfig_rpc_clb(struct lyd_node *rpc, struct nc_session *session)
54{
55 struct lyd_node *data;
Michal Vasko294d4c62016-02-01 10:10:27 +010056
57 assert_string_equal(rpc->schema->name, "get-config");
58 assert_ptr_equal(session, server_session);
59
Radek Krejci539efb62016-08-24 15:05:16 +020060 data = lyd_new_path(NULL, session->ctx, "/ietf-netconf:get-config/data", NULL, LYD_ANYDATA_CONSTSTRING,
61 LYD_PATH_OPT_OUTPUT);
Michal Vasko294d4c62016-02-01 10:10:27 +010062 assert_non_null(data);
63
Radek Krejci36dfdb32016-09-01 16:56:35 +020064 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko294d4c62016-02-01 10:10:27 +010065}
66
Michal Vasko131120a2018-05-29 15:44:02 +020067struct nc_server_reply *
68my_commit_rpc_clb(struct lyd_node *rpc, struct nc_session *session)
69{
70 assert_string_equal(rpc->schema->name, "commit");
71 assert_ptr_equal(session, server_session);
72
73 /* update state */
74 glob_state = 1;
75
76 /* wait until the client receives the notification */
77 while (glob_state != 3) {
78 usleep(100000);
79 }
80
81 return nc_server_reply_ok();
82}
83
84static struct nc_session *
85test_new_session(NC_SIDE side)
86{
87 struct nc_session *sess;
88
89 sess = calloc(1, sizeof *sess);
90 if (!sess) {
91 return NULL;
92 }
93
94 sess->side = side;
95
96 if (side == NC_SERVER) {
97 sess->opts.server.rpc_lock = malloc(sizeof *sess->opts.server.rpc_lock);
98 sess->opts.server.rpc_cond = malloc(sizeof *sess->opts.server.rpc_cond);
99 sess->opts.server.rpc_inuse = malloc(sizeof *sess->opts.server.rpc_inuse);
100 if (!sess->opts.server.rpc_lock || !sess->opts.server.rpc_cond || !sess->opts.server.rpc_inuse) {
101 goto error;
102 }
103 pthread_mutex_init(sess->opts.server.rpc_lock, NULL);
104 pthread_cond_init(sess->opts.server.rpc_cond, NULL);
105 *sess->opts.server.rpc_inuse = 0;
106 }
107
108 sess->io_lock = malloc(sizeof *sess->io_lock);
109 if (!sess->io_lock) {
110 goto error;
111 }
112 pthread_mutex_init(sess->io_lock, NULL);
113
114 return sess;
115
116error:
117 if (side == NC_SERVER) {
118 free(sess->opts.server.rpc_lock);
119 free(sess->opts.server.rpc_cond);
120 free((int *)sess->opts.server.rpc_inuse);
121 }
122 free(sess);
123 return NULL;
124}
125
Michal Vasko294d4c62016-02-01 10:10:27 +0100126static int
127setup_sessions(void **state)
128{
129 (void)state;
Michal Vasko294d4c62016-02-01 10:10:27 +0100130 int sock[2];
131
Michal Vasko294d4c62016-02-01 10:10:27 +0100132 /* create communication channel */
133 socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
134
Michal Vasko294d4c62016-02-01 10:10:27 +0100135 /* create server session */
Michal Vasko131120a2018-05-29 15:44:02 +0200136 server_session = test_new_session(NC_SERVER);
Michal Vasko294d4c62016-02-01 10:10:27 +0100137 server_session->status = NC_STATUS_RUNNING;
Michal Vasko294d4c62016-02-01 10:10:27 +0100138 server_session->id = 1;
Michal Vasko294d4c62016-02-01 10:10:27 +0100139 server_session->ti_type = NC_TI_FD;
Michal Vasko294d4c62016-02-01 10:10:27 +0100140 server_session->ti.fd.in = sock[0];
141 server_session->ti.fd.out = sock[0];
142 server_session->ctx = ctx;
143 server_session->flags = NC_SESSION_SHAREDCTX;
144
145 /* create client session */
Michal Vasko131120a2018-05-29 15:44:02 +0200146 client_session = test_new_session(NC_CLIENT);
Michal Vasko294d4c62016-02-01 10:10:27 +0100147 client_session->status = NC_STATUS_RUNNING;
Michal Vasko294d4c62016-02-01 10:10:27 +0100148 client_session->id = 1;
Michal Vasko294d4c62016-02-01 10:10:27 +0100149 client_session->ti_type = NC_TI_FD;
Michal Vasko294d4c62016-02-01 10:10:27 +0100150 client_session->ti.fd.in = sock[1];
151 client_session->ti.fd.out = sock[1];
152 client_session->ctx = ctx;
153 client_session->flags = NC_SESSION_SHAREDCTX;
Michal Vasko338f8472016-10-13 15:01:27 +0200154 client_session->opts.client.msgid = 50;
Michal Vasko294d4c62016-02-01 10:10:27 +0100155
156 return 0;
157}
158
159static int
160teardown_sessions(void **state)
161{
162 (void)state;
163
Michal Vasko294d4c62016-02-01 10:10:27 +0100164 close(server_session->ti.fd.in);
Michal Vasko131120a2018-05-29 15:44:02 +0200165 nc_session_free(server_session, NULL);
Michal Vasko294d4c62016-02-01 10:10:27 +0100166
167 close(client_session->ti.fd.in);
Michal Vasko131120a2018-05-29 15:44:02 +0200168 nc_session_free(client_session, NULL);
Michal Vasko294d4c62016-02-01 10:10:27 +0100169
170 return 0;
171}
172
173static void
Michal Vasko58d152f2016-02-01 11:44:49 +0100174test_send_recv_ok(void)
Michal Vasko294d4c62016-02-01 10:10:27 +0100175{
Michal Vasko294d4c62016-02-01 10:10:27 +0100176 int ret;
177 uint64_t msgid;
178 NC_MSG_TYPE msgtype;
179 struct nc_rpc *rpc;
180 struct nc_reply *reply;
181 struct nc_pollsession *ps;
182
183 /* client RPC */
184 rpc = nc_rpc_get(NULL, 0, 0);
185 assert_non_null(rpc);
186
187 msgtype = nc_send_rpc(client_session, rpc, 0, &msgid);
188 assert_int_equal(msgtype, NC_MSG_RPC);
189
190 /* server RPC, send reply */
191 ps = nc_ps_new();
192 assert_non_null(ps);
193 nc_ps_add_session(ps, server_session);
194
Michal Vasko71090fc2016-05-24 16:37:28 +0200195 ret = nc_ps_poll(ps, 0, NULL);
196 assert_int_equal(ret, NC_PSPOLL_RPC);
Michal Vasko294d4c62016-02-01 10:10:27 +0100197
198 /* server finished */
199 nc_ps_free(ps);
200
201 /* client reply */
Michal Vaskoeb7080e2016-02-18 13:27:05 +0100202 msgtype = nc_recv_reply(client_session, rpc, msgid, 0, 0, &reply);
Michal Vasko294d4c62016-02-01 10:10:27 +0100203 assert_int_equal(msgtype, NC_MSG_REPLY);
204
205 nc_rpc_free(rpc);
206 assert_int_equal(reply->type, NC_RPL_OK);
207 nc_reply_free(reply);
208}
209
210static void
Michal Vasko58d152f2016-02-01 11:44:49 +0100211test_send_recv_ok_10(void **state)
Michal Vasko294d4c62016-02-01 10:10:27 +0100212{
213 (void)state;
Michal Vasko58d152f2016-02-01 11:44:49 +0100214
215 server_session->version = NC_VERSION_10;
216 client_session->version = NC_VERSION_10;
217
218 test_send_recv_ok();
219}
220
221static void
222test_send_recv_ok_11(void **state)
223{
224 (void)state;
225
226 server_session->version = NC_VERSION_11;
227 client_session->version = NC_VERSION_11;
228
229 test_send_recv_ok();
230}
231
232static void
233test_send_recv_error(void)
234{
Michal Vasko294d4c62016-02-01 10:10:27 +0100235 int ret;
236 uint64_t msgid;
237 NC_MSG_TYPE msgtype;
238 struct nc_rpc *rpc;
239 struct nc_reply *reply;
240 struct nc_pollsession *ps;
241
242 /* client RPC */
243 rpc = nc_rpc_kill(1);
244 assert_non_null(rpc);
245
246 msgtype = nc_send_rpc(client_session, rpc, 0, &msgid);
247 assert_int_equal(msgtype, NC_MSG_RPC);
248
249 /* server RPC, send reply */
250 ps = nc_ps_new();
251 assert_non_null(ps);
252 nc_ps_add_session(ps, server_session);
253
Michal Vasko71090fc2016-05-24 16:37:28 +0200254 ret = nc_ps_poll(ps, 0, NULL);
255 assert_int_equal(ret, NC_PSPOLL_RPC | NC_PSPOLL_REPLY_ERROR);
Michal Vasko294d4c62016-02-01 10:10:27 +0100256
257 /* server finished */
258 nc_ps_free(ps);
259
260 /* client reply */
Michal Vaskoeb7080e2016-02-18 13:27:05 +0100261 msgtype = nc_recv_reply(client_session, rpc, msgid, 0, 0, &reply);
Michal Vasko294d4c62016-02-01 10:10:27 +0100262 assert_int_equal(msgtype, NC_MSG_REPLY);
263
264 nc_rpc_free(rpc);
265 assert_int_equal(reply->type, NC_RPL_ERROR);
266 assert_string_equal(((struct nc_reply_error *)reply)->err->tag, "operation-not-supported");
267 nc_reply_free(reply);
268}
269
270static void
Michal Vasko58d152f2016-02-01 11:44:49 +0100271test_send_recv_error_10(void **state)
Michal Vasko294d4c62016-02-01 10:10:27 +0100272{
273 (void)state;
Michal Vasko58d152f2016-02-01 11:44:49 +0100274
275 server_session->version = NC_VERSION_10;
276 client_session->version = NC_VERSION_10;
277
278 test_send_recv_error();
279}
280
281static void
282test_send_recv_error_11(void **state)
283{
284 (void)state;
285
286 server_session->version = NC_VERSION_11;
287 client_session->version = NC_VERSION_11;
288
289 test_send_recv_error();
290}
291
292static void
293test_send_recv_data(void)
294{
Michal Vasko294d4c62016-02-01 10:10:27 +0100295 int ret;
296 uint64_t msgid;
297 NC_MSG_TYPE msgtype;
298 struct nc_rpc *rpc;
299 struct nc_reply *reply;
300 struct nc_pollsession *ps;
301
302 /* client RPC */
303 rpc = nc_rpc_getconfig(NC_DATASTORE_RUNNING, NULL, 0, 0);
304 assert_non_null(rpc);
305
306 msgtype = nc_send_rpc(client_session, rpc, 0, &msgid);
307 assert_int_equal(msgtype, NC_MSG_RPC);
308
309 /* server RPC, send reply */
310 ps = nc_ps_new();
311 assert_non_null(ps);
312 nc_ps_add_session(ps, server_session);
313
Michal Vasko71090fc2016-05-24 16:37:28 +0200314 ret = nc_ps_poll(ps, 0, NULL);
315 assert_int_equal(ret, NC_PSPOLL_RPC);
Michal Vasko294d4c62016-02-01 10:10:27 +0100316
317 /* server finished */
318 nc_ps_free(ps);
319
320 /* client reply */
Radek Krejci8c8e1242016-09-07 16:43:54 +0200321 msgtype = nc_recv_reply(client_session, rpc, msgid, 0, 0, &reply);
Michal Vasko294d4c62016-02-01 10:10:27 +0100322 assert_int_equal(msgtype, NC_MSG_REPLY);
323
324 nc_rpc_free(rpc);
325 assert_int_equal(reply->type, NC_RPL_DATA);
326 nc_reply_free(reply);
327}
328
Michal Vasko58d152f2016-02-01 11:44:49 +0100329static void
330test_send_recv_data_10(void **state)
331{
332 (void)state;
333
334 server_session->version = NC_VERSION_10;
335 client_session->version = NC_VERSION_10;
336
337 test_send_recv_data();
338}
339
340static void
341test_send_recv_data_11(void **state)
342{
343 (void)state;
344
345 server_session->version = NC_VERSION_11;
346 client_session->version = NC_VERSION_11;
347
348 test_send_recv_data();
349}
350
Michal Vasko131120a2018-05-29 15:44:02 +0200351static void
352test_notif_clb(struct nc_session *session, const struct nc_notif *notif)
353{
354 assert_ptr_equal(session, client_session);
355 assert_string_equal(notif->tree->schema->name, "notificationComplete");
356
357 /* client notification received, update state */
358 while (glob_state != 2) {
359 usleep(1000);
360 }
361 glob_state = 3;
362}
363
364static void *
365server_send_notif_thread(void *arg)
366{
367 NC_MSG_TYPE msg_type;
368 struct lyd_node *notif_tree;
369 struct nc_server_notif *notif;
370 char *buf;
371 (void)arg;
372
373 /* wait for the RPC callback to be called */
374 while (glob_state != 1) {
375 usleep(1000);
376 }
377
378 /* create notif */
379 notif_tree = lyd_new_path(NULL, ctx, "/nc-notifications:notificationComplete", NULL, 0, 0);
380 assert_non_null(notif_tree);
381 buf = malloc(64);
382 assert_non_null(buf);
383 notif = nc_server_notif_new(notif_tree, nc_time2datetime(time(NULL), NULL, buf), NC_PARAMTYPE_FREE);
384 assert_non_null(notif);
385
386 /* send notif */
387 nc_session_set_notif_status(server_session, 1);
388 msg_type = nc_server_notif_send(server_session, notif, 100);
389 nc_server_notif_free(notif);
390 assert_int_equal(msg_type, NC_MSG_NOTIF);
391
392 /* update state */
393 glob_state = 2;
394
395 return NULL;
396}
397
Michal Vasko294d4c62016-02-01 10:10:27 +0100398static void
Michal Vasko58d152f2016-02-01 11:44:49 +0100399test_send_recv_notif(void)
Michal Vasko294d4c62016-02-01 10:10:27 +0100400{
Michal Vasko131120a2018-05-29 15:44:02 +0200401 int ret;
402 pthread_t tid;
403 uint64_t msgid;
404 NC_MSG_TYPE msgtype;
405 struct nc_rpc *rpc;
406 struct nc_reply *reply;
407 struct nc_pollsession *ps;
Michal Vasko294d4c62016-02-01 10:10:27 +0100408
Michal Vasko131120a2018-05-29 15:44:02 +0200409 /* client RPC */
410 rpc = nc_rpc_commit(0, 0, NULL, NULL, 0);
411 assert_non_null(rpc);
412
413 msgtype = nc_send_rpc(client_session, rpc, 0, &msgid);
414 assert_int_equal(msgtype, NC_MSG_RPC);
415
416 /* client subscription */
417 ret = nc_recv_notif_dispatch(client_session, test_notif_clb);
418 assert_int_equal(ret, 0);
419
420 /* create server */
421 ps = nc_ps_new();
422 assert_non_null(ps);
423 nc_ps_add_session(ps, server_session);
424
425 /* server will send a notification */
426 glob_state = 0;
427 ret = pthread_create(&tid, NULL, server_send_notif_thread, NULL);
428 assert_int_equal(ret, 0);
429
430 /* server blocked on RPC */
431 ret = nc_ps_poll(ps, 0, NULL);
432 assert_int_equal(ret, NC_PSPOLL_RPC);
433
434 /* RPC, notification finished fine */
435 assert_int_equal(glob_state, 3);
436
437 /* server finished */
438 ret = pthread_join(tid, NULL);
439 assert_int_equal(ret, 0);
440 nc_ps_free(ps);
441
442 /* client reply */
443 msgtype = nc_recv_reply(client_session, rpc, msgid, 0, 0, &reply);
444 assert_int_equal(msgtype, NC_MSG_REPLY);
445
446 nc_rpc_free(rpc);
447 assert_int_equal(reply->type, NC_RPL_OK);
448 nc_reply_free(reply);
449}
450
451static void
452test_send_recv_notif_10(void **state)
453{
454 (void)state;
455
456 server_session->version = NC_VERSION_10;
457 client_session->version = NC_VERSION_10;
458
459 test_send_recv_notif();
460}
461
462static void
463test_send_recv_notif_11(void **state)
464{
465 (void)state;
466
467 server_session->version = NC_VERSION_11;
468 client_session->version = NC_VERSION_11;
469
470 test_send_recv_notif();
471}
Michal Vasko294d4c62016-02-01 10:10:27 +0100472
473int
474main(void)
475{
Michal Vasko8c5518b2016-03-01 12:35:13 +0100476 int ret;
477 const struct lys_module *module;
Michal Vasko88639e92017-08-03 14:38:10 +0200478 const struct lys_node *node;
Michal Vasko8c5518b2016-03-01 12:35:13 +0100479
480 /* create ctx */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200481 ctx = ly_ctx_new(TESTS_DIR"../schemas", 0);
Michal Vasko8c5518b2016-03-01 12:35:13 +0100482 assert_non_null(ctx);
483
484 /* load modules */
485 module = ly_ctx_load_module(ctx, "ietf-netconf-acm", NULL);
486 assert_non_null(module);
487
488 module = ly_ctx_load_module(ctx, "ietf-netconf", NULL);
489 assert_non_null(module);
Michal Vasko131120a2018-05-29 15:44:02 +0200490 ret = lys_features_enable(module, "candidate");
491 assert_int_equal(ret, 0);
492
493 module = ly_ctx_load_module(ctx, "nc-notifications", NULL);
494 assert_non_null(module);
Michal Vasko8c5518b2016-03-01 12:35:13 +0100495
496 /* set RPC callbacks */
Michal Vasko88639e92017-08-03 14:38:10 +0200497 node = ly_ctx_get_node(module->ctx, NULL, "/ietf-netconf:get", 0);
498 assert_non_null(node);
499 lys_set_private(node, my_get_rpc_clb);
500
501 node = ly_ctx_get_node(module->ctx, NULL, "/ietf-netconf:get-config", 0);
502 assert_non_null(node);
503 lys_set_private(node, my_getconfig_rpc_clb);
Michal Vasko8c5518b2016-03-01 12:35:13 +0100504
Michal Vasko131120a2018-05-29 15:44:02 +0200505 node = ly_ctx_get_node(module->ctx, NULL, "/ietf-netconf:commit", 0);
506 assert_non_null(node);
507 lys_set_private(node, my_commit_rpc_clb);
508
Michal Vasko8c5518b2016-03-01 12:35:13 +0100509 nc_server_init(ctx);
510
Michal Vasko294d4c62016-02-01 10:10:27 +0100511 const struct CMUnitTest comm[] = {
Michal Vasko58d152f2016-02-01 11:44:49 +0100512 cmocka_unit_test_setup_teardown(test_send_recv_ok_10, setup_sessions, teardown_sessions),
513 cmocka_unit_test_setup_teardown(test_send_recv_error_10, setup_sessions, teardown_sessions),
514 cmocka_unit_test_setup_teardown(test_send_recv_data_10, setup_sessions, teardown_sessions),
Michal Vasko131120a2018-05-29 15:44:02 +0200515 cmocka_unit_test_setup_teardown(test_send_recv_notif_10, setup_sessions, teardown_sessions),
Michal Vasko58d152f2016-02-01 11:44:49 +0100516 cmocka_unit_test_setup_teardown(test_send_recv_ok_11, setup_sessions, teardown_sessions),
517 cmocka_unit_test_setup_teardown(test_send_recv_error_11, setup_sessions, teardown_sessions),
Michal Vasko131120a2018-05-29 15:44:02 +0200518 cmocka_unit_test_setup_teardown(test_send_recv_data_11, setup_sessions, teardown_sessions),
519 cmocka_unit_test_setup_teardown(test_send_recv_notif_11, setup_sessions, teardown_sessions),
Michal Vasko294d4c62016-02-01 10:10:27 +0100520 };
521
Michal Vasko8c5518b2016-03-01 12:35:13 +0100522 ret = cmocka_run_group_tests(comm, NULL, NULL);
523
524 nc_server_destroy();
525 ly_ctx_destroy(ctx, NULL);
526
527 return ret;
Michal Vasko294d4c62016-02-01 10:10:27 +0100528}