blob: 0634406ac9bae4c3b37fb5855e6ec45f55c177ee [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
Michal Vasko30aa3822015-11-25 10:41:54 +01002 * \file netopeerguid.c
3 * \brief NetopeerGUI daemon
Radek Krejci469aab82012-07-22 18:42:20 +02004 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskoa53ef882015-11-24 11:02:01 +01006 * \author Michal Vasko <mvasko@cesnet.cz>
Radek Krejci469aab82012-07-22 18:42:20 +02007 * \date 2011
8 * \date 2012
Tomas Cejka94da2c52013-01-08 18:20:30 +01009 * \date 2013
Michal Vaskoa53ef882015-11-24 11:02:01 +010010 * \date 2015
Radek Krejci469aab82012-07-22 18:42:20 +020011 */
12/*
Michal Vaskoa53ef882015-11-24 11:02:01 +010013 * Copyright (C) 2011-2015 CESNET
Radek Krejci469aab82012-07-22 18:42:20 +020014 *
15 * LICENSE TERMS
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in
24 * the documentation and/or other materials provided with the
25 * distribution.
26 * 3. Neither the name of the Company nor the names of its contributors
27 * may be used to endorse or promote products derived from this
28 * software without specific prior written permission.
29 *
30 * ALTERNATIVELY, provided that this notice is retained in full, this
31 * product may be distributed under the terms of the GNU General Public
32 * License (GPL) version 2 or later, in which case the provisions
33 * of the GPL apply INSTEAD OF those given above.
34 *
35 * This software is provided ``as is'', and any express or implied
36 * warranties, including, but not limited to, the implied warranties of
37 * merchantability and fitness for a particular purpose are disclaimed.
38 * In no event shall the company or contributors be liable for any
39 * direct, indirect, incidental, special, exemplary, or consequential
40 * damages (including, but not limited to, procurement of substitute
41 * goods or services; loss of use, data, or profits; or business
42 * interruption) however caused and on any theory of liability, whether
43 * in contract, strict liability, or tort (including negligence or
44 * otherwise) arising in any way out of the use of this software, even
45 * if advised of the possibility of such damage.
46 *
47 */
Michal Vaskoc3146782015-11-04 14:46:41 +010048#define _GNU_SOURCE
Radek Krejci469aab82012-07-22 18:42:20 +020049
Radek Krejci7b4ddd02012-07-30 08:09:58 +020050#include <unistd.h>
51#include <poll.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010052#include <time.h>
Michal Vaskob69dde22016-03-09 11:42:20 +010053#include <fcntl.h>
Radek Krejci469aab82012-07-22 18:42:20 +020054#include <sys/types.h>
55#include <sys/socket.h>
56#include <sys/un.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010057#include <sys/stat.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010058#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010059#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010060#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010061#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010062#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020063#include <pthread.h>
64#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020065
Michal Vaskof35ea502016-02-24 10:44:54 +010066#include <nc_client.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020067
Tomas Cejka04e08f42014-03-27 19:52:34 +010068#include "../config.h"
69
Tomas Cejkad340dbf2013-03-24 20:36:57 +010070#ifdef WITH_NOTIFICATIONS
Michal Vaskoa53ef882015-11-24 11:02:01 +010071#include "notification_server.h"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010072#endif
73
Tomas Cejka94da2c52013-01-08 18:20:30 +010074#include "message_type.h"
Michal Vaskoa53ef882015-11-24 11:02:01 +010075#include "netopeerguid.h"
Radek Krejci469aab82012-07-22 18:42:20 +020076
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010077#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020078#define MAX_PROCS 5
Michal Vaskoa53ef882015-11-24 11:02:01 +010079#define SOCKET_FILENAME "/var/run/netopeerguid.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020080#define MAX_SOCKET_CL 10
81#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010082#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
83#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020084
Michal Vasko7732dee2015-11-05 10:22:15 +010085/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020086#define SLEEP_TIME 200
87
88#ifndef offsetof
89#define offsetof(type, member) ((size_t) ((type *) 0)->member)
90#endif
91
Tomas Cejka027f3bc2012-11-10 20:28:36 +010092/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020093struct timeval timeout = { 1, 0 };
94
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010095#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010096
Radek Krejci469aab82012-07-22 18:42:20 +020097#define MSG_OK 0
98#define MSG_OPEN 1
99#define MSG_DATA 2
100#define MSG_CLOSE 3
101#define MSG_ERROR 4
102#define MSG_UNKNOWN 5
103
Tomas Cejka47387fd2013-06-10 20:37:46 +0200104pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200105pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100106pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100107pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
108
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100109unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100110struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100111static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200112static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200113pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200114volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200115static char* password;
116
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100117json_object *create_ok_reply(void);
118json_object *create_data_reply(const char *data);
119static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
120 const char *format, json_object **err);
Michal Vaskof35ea502016-02-24 10:44:54 +0100121static void node_add_metadata_recursive(struct lyd_node *data_tree, const struct lys_module *module,
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100122 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100123static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100124
125static void
126signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200127{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100128 switch (sign) {
129 case SIGINT:
130 case SIGTERM:
131 isterminated = 1;
132 break;
133 }
Radek Krejci469aab82012-07-22 18:42:20 +0200134}
135
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100136int
137netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200138{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100139 /* always approve */
140 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200141}
142
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100143char *
Michal Vaskof35ea502016-02-24 10:44:54 +0100144netconf_callback_sshauth_passphrase(const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200145{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100146 char *buf;
147 buf = strdup(password);
148 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200149}
150
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100151char *
152netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200153{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100154 char *buf;
155 buf = strdup(password);
156 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200157}
158
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100159char *
160netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
161 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200162{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100163 char *buf;
164 buf = strdup(password);
165 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200166}
167
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100168void
169netconf_callback_error_process(const char *UNUSED(tag),
170 const char *UNUSED(type),
171 const char *UNUSED(severity),
172 const char *UNUSED(apptag),
173 const char *UNUSED(path),
174 const char *message,
175 const char *UNUSED(attribute),
176 const char *UNUSED(element),
177 const char *UNUSED(ns),
178 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200179{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100180 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
181 if (err_reply_p == NULL) {
182 ERROR("Error message was not allocated. %s", __func__);
183 return;
184 }
185 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100186
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100187 json_object *array = NULL;
188 if (err_reply == NULL) {
189 ERROR("error calback: empty error list");
190 pthread_mutex_lock(&json_lock);
191 err_reply = json_object_new_object();
192 array = json_object_new_array();
193 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
194 json_object_object_add(err_reply, "errors", array);
195 if (message != NULL) {
196 json_object_array_add(array, json_object_new_string(message));
197 }
198 pthread_mutex_unlock(&json_lock);
199 (*err_reply_p) = err_reply;
200 } else {
201 ERROR("error calback: nonempty error list");
202 pthread_mutex_lock(&json_lock);
203 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
204 if (message != NULL) {
205 json_object_array_add(array, json_object_new_string(message));
206 }
207 }
208 pthread_mutex_unlock(&json_lock);
209 }
210 pthread_setspecific(err_reply_key, err_reply_p);
211 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200212}
213
Tomas Cejka47387fd2013-06-10 20:37:46 +0200214/**
215 * should be used in locked area
216 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100217void
218prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200219{
Michal Vaskof35ea502016-02-24 10:44:54 +0100220 int i;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100221 json_object *json_obj = NULL;
222 json_object *js_tmp = NULL;
223 char *old_sid = NULL;
224 const char *j_old_sid = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +0100225 char str_port[6];
226 const char **cpblts;
227 struct lyd_node *yanglib, *module, *node;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200228
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100229 if (s == NULL) {
230 ERROR("No session given.");
231 return;
232 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200233
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100234 pthread_mutex_lock(&json_lock);
235 if (s->hello_message != NULL) {
236 ERROR("clean previous hello message");
237 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
238 j_old_sid = json_object_get_string(js_tmp);
239 if (j_old_sid != NULL) {
240 old_sid = strdup(j_old_sid);
241 }
242 }
243 json_object_put(s->hello_message);
244 s->hello_message = NULL;
245 }
246 s->hello_message = json_object_new_object();
247 if (session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100248 if (!old_sid) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100249 /* we don't have old sid */
Michal Vaskof35ea502016-02-24 10:44:54 +0100250 asprintf(&old_sid, "%u", nc_session_get_id(session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100251 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100252 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
253 free(old_sid);
254 old_sid = NULL;
255
256 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) ? "1.1":"1.0")));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100257 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
Michal Vaskof35ea502016-02-24 10:44:54 +0100258 sprintf(str_port, "%u", nc_session_get_port(session));
259 json_object_object_add(s->hello_message, "port", json_object_new_string(str_port));
260 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_username(session)));
261 cpblts = nc_session_get_cpblts(session);
262 if (cpblts) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100263 json_obj = json_object_new_array();
Michal Vaskof35ea502016-02-24 10:44:54 +0100264 for (i = 0; cpblts[i]; ++i) {
265 json_object_array_add(json_obj, json_object_new_string(cpblts[i]));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100266 }
267 json_object_object_add(s->hello_message, "capabilities", json_obj);
268 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100269
270 yanglib = ly_ctx_info(nc_session_get_ctx(session));
271 if (yanglib) {
272 json_obj = json_object_new_array();
273 LY_TREE_FOR(yanglib->child, module) {
274 if (!strcmp(module->schema->name, "module")) {
275 LY_TREE_FOR(module->child, node) {
276 if (!strcmp(node->schema->name, "name")) {
277 json_object_array_add(json_obj, json_object_new_string(((struct lyd_node_leaf_list *)node)->value_str));
278 break;
279 }
280 }
281 }
282 }
283 json_object_object_add(s->hello_message, "models", json_obj);
284
Michal Vaskoc04900a2016-03-15 16:20:24 +0100285 lyd_free(yanglib);
Michal Vaskof35ea502016-02-24 10:44:54 +0100286 }
287
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100288 DEBUG("%s", json_object_to_json_string(s->hello_message));
289 } else {
290 ERROR("Session was not given.");
291 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
292 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
293 }
294 DEBUG("Status info from hello message prepared");
295 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200296}
297
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100298void
299create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200300{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100301 json_object **err_reply = calloc(1, sizeof(json_object **));
302 if (err_reply == NULL) {
303 ERROR("Allocation of err_reply storage failed!");
304 return;
305 }
306 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
307 ERROR("cannot set thread-specific value.");
308 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200309}
310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100311void
312clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200313{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100314 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
315 if (err_reply != NULL) {
316 if (*err_reply != NULL) {
317 pthread_mutex_lock(&json_lock);
318 json_object_put(*err_reply);
319 pthread_mutex_unlock(&json_lock);
320 }
321 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
322 ERROR("Cannot set thread-specific hash value.");
323 }
324 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200325}
326
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100327void
328free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200329{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100330 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
331 if (err_reply != NULL) {
332 if (*err_reply != NULL) {
333 pthread_mutex_lock(&json_lock);
334 json_object_put(*err_reply);
335 pthread_mutex_unlock(&json_lock);
336 }
337 free(err_reply);
338 err_reply = NULL;
339 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
340 ERROR("Cannot set thread-specific hash value.");
341 }
342 }
343}
344
345static struct session_with_mutex *
346session_get_locked(unsigned int session_key, json_object **err)
347{
348 struct session_with_mutex *locked_session;
349
350 /* get non-exclusive (read) access to sessions_list (conns) */
351 DEBUG("LOCK wrlock %s", __func__);
352 if (pthread_rwlock_rdlock(&session_lock) != 0) {
353 if (*err) {
354 *err = create_error_reply("Locking failed.");
355 }
356 return NULL;
357 }
358 /* get session where send the RPC */
359 for (locked_session = netconf_sessions_list;
360 locked_session && (locked_session->session_key != session_key);
361 locked_session = locked_session->next);
362 if (!locked_session) {
363 if (*err) {
364 *err = create_error_reply("Session not found.");
365 }
366 return NULL;
367 }
368
369 /* get exclusive access to session */
370 DEBUG("LOCK mutex %s", __func__);
371 if (pthread_mutex_lock(&locked_session->lock) != 0) {
372 if (*err) {
373 *err = create_error_reply("Locking failed.");
374 }
375 goto wrlock_fail;
376 }
377 return locked_session;
378
379wrlock_fail:
380 DEBUG("UNLOCK wrlock %s", __func__);
381 pthread_rwlock_unlock(&session_lock);
382 return NULL;
383}
384
385static void
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100386session_user_activity(const char *username)
387{
388 struct session_with_mutex *sess;
389
390 for (sess = netconf_sessions_list; sess; sess = sess->next) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100391 if (!strcmp(nc_session_get_username(sess->session), username)) {
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100392 sess->last_activity = time(NULL);
393 }
394 }
395}
396
397static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100398session_unlock(struct session_with_mutex *locked_session)
399{
400 DEBUG("UNLOCK mutex %s", __func__);
401 pthread_mutex_unlock(&locked_session->lock);
402 DEBUG("UNLOCK wrlock %s", __func__);
403 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200404}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200405
Michal Vasko3fda9a92015-11-23 10:10:57 +0100406static void
407node_metadata_text(const char *text, const char *name, json_object *parent)
408{
409 json_object *obj;
410
411 if (!text) {
412 return;
413 }
414
415 obj = json_object_new_string(text);
416 json_object_object_add(parent, name, obj);
417}
418
419static void
420node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
421{
422 json_object *obj;
423
424 if (!restr) {
425 return;
426 }
427
428 obj = json_object_new_string(restr->expr);
429 json_object_object_add(parent, name, obj);
430}
431
432static void
433node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
434{
435 uint8_t i;
436 json_object *array, *obj;
437
438 if (!must_size || !must) {
439 return;
440 }
441
442 array = json_object_new_array();
443
444 for (i = 0; i < must_size; ++i) {
445 obj = json_object_new_string(must[i].expr);
446 json_object_array_add(array, obj);
447 }
448
449 json_object_object_add(parent, "must", array);
450}
451
452static void
453node_metadata_basic(struct lys_node *node, json_object *parent)
454{
455 json_object *obj;
456
457 /* description */
458 node_metadata_text(node->dsc, "description", parent);
459
460 /* reference */
461 node_metadata_text(node->ref, "reference", parent);
462
463 /* config */
464 if (node->flags & LYS_CONFIG_R) {
465 obj = json_object_new_boolean(0);
466 } else {
467 obj = json_object_new_boolean(1);
468 }
469 json_object_object_add(parent, "config", obj);
470
471 /* status */
472 if (node->flags & LYS_STATUS_DEPRC) {
473 obj = json_object_new_string("deprecated");
474 } else if (node->flags & LYS_STATUS_OBSLT) {
475 obj = json_object_new_string("obsolete");
476 } else {
477 obj = json_object_new_string("current");
478 }
479 json_object_object_add(parent, "status", obj);
480
481 /* mandatory */
482 if (node->flags & LYS_MAND_TRUE) {
483 obj = json_object_new_boolean(1);
484 } else {
485 obj = json_object_new_boolean(0);
486 }
487 json_object_object_add(parent, "mandatory", obj);
488
489 /* NACM extensions */
490 if (node->nacm) {
491 if (node->nacm & LYS_NACM_DENYW) {
492 obj = json_object_new_string("default-deny-write");
493 } else {
494 obj = json_object_new_string("default-deny-all");
495 }
496 json_object_object_add(parent, "ext", obj);
497 }
498}
499
500static void
501node_metadata_when(struct lys_when *when, json_object *parent)
502{
503 json_object *obj;
504
505 if (!when) {
506 return;
507 }
508
509 obj = json_object_new_string(when->cond);
510 json_object_object_add(parent, "when", obj);
511}
512
513static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100514node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100515{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100516 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100517 struct lys_node *child;
518
519 if (!node->child) {
520 return;
521 }
522
523 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100524 if (child->nodetype == LYS_USES) {
525 node_metadata_children_recursive(child, child_array, choice_array);
526 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100527 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100528 if (!*child_array) {
529 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100530 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100531 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100532 } else if (child->nodetype == LYS_CHOICE) {
533 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100534 if (!*choice_array) {
535 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100536 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100537 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100538 }
539 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100540}
541
542static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100543node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100544{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100545 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100546 struct lys_node *child;
547
548 if (!choice->child) {
549 return;
550 }
551
Michal Vasko3fda9a92015-11-23 10:10:57 +0100552 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100553 if (child->nodetype == LYS_USES) {
554 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
555 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100556 obj = json_object_new_string(child->name);
557 json_object_array_add(array, obj);
558 }
559 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100560}
561
562static void
563node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
564{
565 json_object *obj;
566
567 if (min) {
568 obj = json_object_new_int(min);
569 json_object_object_add(parent, "min-elements", obj);
570 }
571
572 if (max) {
573 obj = json_object_new_int(max);
574 json_object_object_add(parent, "max-elements", obj);
575 }
576}
577
578static void
579node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
580{
581 struct lys_ident_der *cur;
582 json_object *obj;
583
584 if (!ident) {
585 return;
586 }
587
588 obj = json_object_new_string(ident->name);
589 json_object_array_add(array, obj);
590
591 for (cur = ident->der; cur; cur = cur->next) {
592 node_metadata_ident_recursive(cur->ident, array);
593 }
594}
595
596static void
597node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
598{
599 json_object *obj, *array, *item;
600 char *str;
601 int i;
602
603 /* built-in YANG type */
604 if (!type->der->module) {
605 switch (type->base) {
606 case LY_TYPE_BINARY:
607 node_metadata_text("binary", "type", parent);
608 node_metadata_restr(type->info.binary.length, "length", parent);
609 break;
610 case LY_TYPE_BITS:
611 node_metadata_text("bits", "type", parent);
612
613 array = json_object_new_array();
614 for (i = 0; i < type->info.bits.count; ++i) {
615 item = json_object_new_object();
616 obj = json_object_new_string(type->info.bits.bit[i].name);
617 json_object_object_add(item, "name", obj);
618 obj = json_object_new_int(type->info.bits.bit[i].pos);
619 json_object_object_add(item, "position", obj);
620 json_object_array_add(array, item);
621 }
622 json_object_object_add(parent, "bits", array);
623 break;
624 case LY_TYPE_BOOL:
625 node_metadata_text("bool", "type", parent);
626 break;
627 case LY_TYPE_DEC64:
628 node_metadata_text("decimal64", "type", parent);
629 node_metadata_restr(type->info.dec64.range, "range", parent);
630 obj = json_object_new_int(type->info.dec64.dig);
631 json_object_object_add(parent, "fraction-digits", obj);
632 break;
633 case LY_TYPE_EMPTY:
634 node_metadata_text("empty", "type", parent);
635 break;
636 case LY_TYPE_ENUM:
637 node_metadata_text("enumeration", "type", parent);
638
639 array = json_object_new_array();
640 for (i = 0; i < type->info.enums.count; ++i) {
641 obj = json_object_new_string(type->info.enums.enm[i].name);
642 json_object_array_add(array, obj);
643 }
644 json_object_object_add(parent, "enumval", array);
645 break;
646 case LY_TYPE_IDENT:
647 node_metadata_text("identityref", "type", parent);
648
649 array = json_object_new_array();
650 node_metadata_ident_recursive(type->info.ident.ref, array);
651 json_object_object_add(parent, "identityval", array);
652 break;
653 case LY_TYPE_INST:
654 node_metadata_text("instance-identifier", "type", parent);
655 if (type->info.inst.req == -1) {
656 obj = json_object_new_boolean(0);
657 } else {
658 obj = json_object_new_boolean(1);
659 }
660 json_object_object_add(parent, "require-instance", obj);
661 break;
662 case LY_TYPE_LEAFREF:
663 node_metadata_text("leafref", "type", parent);
664 node_metadata_text(type->info.lref.path, "path", parent);
665 break;
666 case LY_TYPE_STRING:
667 node_metadata_text("string", "type", parent);
668 node_metadata_restr(type->info.str.length, "length", parent);
669 if (type->info.str.pat_count) {
670 array = json_object_new_array();
671 for (i = 0; i < type->info.str.pat_count; ++i) {
672 obj = json_object_new_string(type->info.str.patterns[i].expr);
673 json_object_array_add(array, obj);
674 }
675 json_object_object_add(parent, "pattern", array);
676 }
677 break;
678 case LY_TYPE_UNION:
679 node_metadata_text("union", "type", parent);
680 array = json_object_new_array();
681 for (i = 0; i < type->info.uni.count; ++i) {
682 obj = json_object_new_object();
683 node_metadata_type(&type->info.uni.types[i], module, obj);
684 json_object_array_add(array, obj);
685 }
686 json_object_object_add(parent, "types", array);
687 break;
688 case LY_TYPE_INT8:
689 node_metadata_text("int8", "type", parent);
690 node_metadata_restr(type->info.num.range, "range", parent);
691 break;
692 case LY_TYPE_UINT8:
693 node_metadata_text("uint8", "type", parent);
694 node_metadata_restr(type->info.num.range, "range", parent);
695 break;
696 case LY_TYPE_INT16:
697 node_metadata_text("int16", "type", parent);
698 node_metadata_restr(type->info.num.range, "range", parent);
699 break;
700 case LY_TYPE_UINT16:
701 node_metadata_text("uint16", "type", parent);
702 node_metadata_restr(type->info.num.range, "range", parent);
703 break;
704 case LY_TYPE_INT32:
705 node_metadata_text("int32", "type", parent);
706 node_metadata_restr(type->info.num.range, "range", parent);
707 break;
708 case LY_TYPE_UINT32:
709 node_metadata_text("uint32", "type", parent);
710 node_metadata_restr(type->info.num.range, "range", parent);
711 break;
712 case LY_TYPE_INT64:
713 node_metadata_text("int64", "type", parent);
714 node_metadata_restr(type->info.num.range, "range", parent);
715 break;
716 case LY_TYPE_UINT64:
717 node_metadata_text("uint64", "type", parent);
718 node_metadata_restr(type->info.num.range, "range", parent);
719 break;
720 default:
721 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
722 break;
723 }
724
725 /* typedef */
726 } else {
727 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
728 node_metadata_text(type->der->name, "type", parent);
729 } else {
730 asprintf(&str, "%s:%s", type->module_name, type->der->name);
731 node_metadata_text(str, "type", parent);
732 free(str);
733 }
734 obj = json_object_new_object();
735 node_metadata_typedef(type->der, obj);
736 json_object_object_add(parent, "typedef", obj);
737 }
738}
739
740static void
741node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
742{
743 json_object *obj;
744
745 /* description */
746 node_metadata_text(tpdf->dsc, "description", parent);
747
748 /* reference */
749 node_metadata_text(tpdf->ref, "reference", parent);
750
751 /* status */
752 if (tpdf->flags & LYS_STATUS_DEPRC) {
753 obj = json_object_new_string("deprecated");
754 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
755 obj = json_object_new_string("obsolete");
756 } else {
757 obj = json_object_new_string("current");
758 }
759 json_object_object_add(parent, "status", obj);
760
761 /* type */
762 node_metadata_type(&tpdf->type, tpdf->module, parent);
763
764 /* units */
765 node_metadata_text(tpdf->units, "units", parent);
766
767 /* default */
768 node_metadata_text(tpdf->dflt, "default", parent);
769}
770
771static void
772node_metadata_container(struct lys_node_container *cont, json_object *parent)
773{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100774 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100775
776 /* element type */
777 obj = json_object_new_string("container");
778 json_object_object_add(parent, "eltype", obj);
779
780 /* shared info */
781 node_metadata_basic((struct lys_node *)cont, parent);
782
783 /* must */
784 node_metadata_must(cont->must_size, cont->must, parent);
785
786 /* presence */
787 node_metadata_text(cont->presence, "presence", parent);
788
789 /* when */
790 node_metadata_when(cont->when, parent);
791
792 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100793 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
794 if (child_array) {
795 json_object_object_add(parent, "children", child_array);
796 }
797 if (choice_array) {
798 json_object_object_add(parent, "choice", choice_array);
799 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100800}
801
802static void
803node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
804{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100805 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100806
807 /* element type */
808 obj = json_object_new_string("choice");
809 json_object_object_add(parent, "eltype", obj);
810
811 /* shared info */
812 node_metadata_basic((struct lys_node *)choice, parent);
813
814 /* default */
Michal Vasko5e1c6052016-03-17 15:43:33 +0100815 if (choice->dflt) {
816 node_metadata_text(choice->dflt->name, "default", parent);
817 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100818
819 /* when */
820 node_metadata_when(choice->when, parent);
821
822 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100823 if (choice->child) {
824 array = json_object_new_array();
825 node_metadata_cases_recursive(choice, array);
826 json_object_object_add(parent, "cases", array);
827 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100828}
829
830static void
831node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
832{
833 json_object *obj;
834 struct lys_node_list *list;
835 int is_key, i;
836
837 /* element type */
838 obj = json_object_new_string("leaf");
839 json_object_object_add(parent, "eltype", obj);
840
841 /* shared info */
842 node_metadata_basic((struct lys_node *)leaf, parent);
843
844 /* type */
845 node_metadata_type(&leaf->type, leaf->module, parent);
846
847 /* units */
848 node_metadata_text(leaf->units, "units", parent);
849
850 /* default */
851 node_metadata_text(leaf->dflt, "default", parent);
852
853 /* must */
854 node_metadata_must(leaf->must_size, leaf->must, parent);
855
856 /* when */
857 node_metadata_when(leaf->when, parent);
858
859 /* iskey */
860 is_key = 0;
861 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
862 if (list && (list->nodetype == LYS_LIST)) {
863 for (i = 0; i < list->keys_size; ++i) {
864 if (list->keys[i] == leaf) {
865 is_key = 1;
866 break;
867 }
868 }
869 }
870 obj = json_object_new_boolean(is_key);
871 json_object_object_add(parent, "iskey", obj);
872}
873
874static void
875node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
876{
877 json_object *obj;
878
879 /* element type */
880 obj = json_object_new_string("leaf-list");
881 json_object_object_add(parent, "eltype", obj);
882
883 /* shared info */
884 node_metadata_basic((struct lys_node *)llist, parent);
885
886 /* type */
887 node_metadata_type(&llist->type, llist->module, parent);
888
889 /* units */
890 node_metadata_text(llist->units, "units", parent);
891
892 /* must */
893 node_metadata_must(llist->must_size, llist->must, parent);
894
895 /* when */
896 node_metadata_when(llist->when, parent);
897
898 /* min/max-elements */
899 node_metadata_min_max(llist->min, llist->max, parent);
900}
901
902static void
903node_metadata_list(struct lys_node_list *list, json_object *parent)
904{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100905 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100906 int i;
907 unsigned int j;
908
909 /* element type */
910 obj = json_object_new_string("list");
911 json_object_object_add(parent, "eltype", obj);
912
913 /* shared info */
914 node_metadata_basic((struct lys_node *)list, parent);
915
916 /* must */
917 node_metadata_must(list->must_size, list->must, parent);
918
919 /* when */
920 node_metadata_when(list->when, parent);
921
922 /* min/max-elements */
923 node_metadata_min_max(list->min, list->max, parent);
924
925 /* keys */
926 if (list->keys_size) {
927 array = json_object_new_array();
928 for (i = 0; i < list->keys_size; ++i) {
929 obj = json_object_new_string(list->keys[i]->name);
930 json_object_array_add(array, obj);
931 }
932 json_object_object_add(parent, "keys", array);
933 }
934
935 /* unique */
936 if (list->unique_size) {
937 array = json_object_new_array();
938 for (i = 0; i < list->unique_size; ++i) {
939 for (j = 0; j < list->unique[i].expr_size; ++j) {
940 obj = json_object_new_string(list->unique[i].expr[j]);
941 json_object_array_add(array, obj);
942 }
943 }
944 json_object_object_add(parent, "unique", array);
945 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100946
947 /* children & choice */
948 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
949 if (child_array) {
950 json_object_object_add(parent, "children", child_array);
951 }
952 if (choice_array) {
953 json_object_object_add(parent, "choice", choice_array);
954 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100955}
956
957static void
958node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
959{
960 json_object *obj;
961
962 /* element type */
963 obj = json_object_new_string("anyxml");
964 json_object_object_add(parent, "eltype", obj);
965
966 /* shared info */
967 node_metadata_basic((struct lys_node *)anyxml, parent);
968
969 /* must */
970 node_metadata_must(anyxml->must_size, anyxml->must, parent);
971
972 /* when */
973 node_metadata_when(anyxml->when, parent);
974
975}
976
977static void
978node_metadata_case(struct lys_node_case *cas, json_object *parent)
979{
980 json_object *obj;
981
982 /* element type */
983 obj = json_object_new_string("case");
984 json_object_object_add(parent, "eltype", obj);
985
986 /* shared info */
987 node_metadata_basic((struct lys_node *)cas, parent);
988
989 /* when */
990 node_metadata_when(cas->when, parent);
991}
992
Michal Vaskoa45770b2015-11-23 15:49:41 +0100993static void
994node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
995{
996 json_object *obj;
997
998 /* element type */
999 obj = json_object_new_string("rpc");
1000 json_object_object_add(parent, "eltype", obj);
1001
1002 /* description */
1003 node_metadata_text(rpc->dsc, "description", parent);
1004
1005 /* reference */
1006 node_metadata_text(rpc->ref, "reference", parent);
1007
1008 /* status */
1009 if (rpc->flags & LYS_STATUS_DEPRC) {
1010 obj = json_object_new_string("deprecated");
1011 } else if (rpc->flags & LYS_STATUS_OBSLT) {
1012 obj = json_object_new_string("obsolete");
1013 } else {
1014 obj = json_object_new_string("current");
1015 }
1016 json_object_object_add(parent, "status", obj);
1017}
1018
1019static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001020node_metadata_model(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001021{
1022 json_object *obj, *array, *item;
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001023 const struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001024 int i;
1025
1026 /* yang-version */
1027 if (module->version == 2) {
1028 obj = json_object_new_string("1.1");
1029 } else {
1030 obj = json_object_new_string("1.0");
1031 }
1032 json_object_object_add(parent, "yang-version", obj);
1033
1034 /* namespace */
1035 node_metadata_text(module->ns, "namespace", parent);
1036
1037 /* prefix */
1038 node_metadata_text(module->prefix, "prefix", parent);
1039
1040 /* contact */
1041 node_metadata_text(module->contact, "contact", parent);
1042
1043 /* organization */
1044 node_metadata_text(module->org, "organization", parent);
1045
1046 /* revision */
1047 if (module->rev_size) {
1048 node_metadata_text(module->rev[0].date, "revision", parent);
1049 }
1050
1051 /* description */
1052 node_metadata_text(module->dsc, "description", parent);
1053
1054 /* import */
1055 if (module->imp_size) {
1056 array = json_object_new_array();
1057 for (i = 0; i < module->imp_size; ++i) {
1058 item = json_object_new_object();
1059
1060 node_metadata_text(module->imp[i].module->name, "name", item);
1061 node_metadata_text(module->imp[i].prefix, "prefix", item);
1062 if (module->imp[i].rev && module->imp[i].rev[0]) {
1063 node_metadata_text(module->imp[i].rev, "revision", item);
1064 }
1065
1066 json_object_array_add(array, item);
1067 }
1068 json_object_object_add(parent, "imports", array);
1069 }
1070
1071 /* include */
1072 if (module->inc_size) {
1073 array = json_object_new_array();
1074 for (i = 0; i < module->inc_size; ++i) {
1075 item = json_object_new_object();
1076
1077 node_metadata_text(module->inc[i].submodule->name, "name", item);
1078 if (module->inc[i].rev && module->inc[i].rev[0]) {
1079 node_metadata_text(module->inc[i].rev, "revision", item);
1080 }
1081
1082 json_object_array_add(array, item);
1083 }
1084 json_object_object_add(parent, "includes", array);
1085 }
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001086
1087 /* top-nodes */
1088 node = NULL;
1089 array = NULL;
1090 while ((node = lys_getnext(node, NULL, module, LYS_GETNEXT_WITHCHOICE))) {
1091 if (node->nodetype & (LYS_RPC | LYS_NOTIF)) {
1092 continue;
1093 }
1094 if (!array) {
1095 array = json_object_new_array();
1096 }
1097 item = json_object_new_string(node->name);
1098 json_object_array_add(array, item);
1099 }
1100 if (array) {
1101 json_object_object_add(parent, "top-nodes", array);
1102 }
Michal Vaskoa45770b2015-11-23 15:49:41 +01001103}
1104
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001105/**
1106 * \defgroup netconf_operations NETCONF operations
1107 * The list of NETCONF operations that mod_netconf supports.
1108 * @{
1109 */
1110
1111/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001112 * \brief Send RPC and wait for reply with timeout.
1113 *
1114 * \param[in] session libnetconf session
1115 * \param[in] rpc prepared RPC message
1116 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1117 * \param[out] reply reply from the server
Michal Vaskof35ea502016-02-24 10:44:54 +01001118 * \return NC_MSG_WOULDBLOCK or NC_MSG_ERROR.
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001119 * On success, it returns NC_MSG_REPLY.
1120 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001121NC_MSG_TYPE
Michal Vaskof35ea502016-02-24 10:44:54 +01001122netconf_send_recv_timed(struct nc_session *session, struct nc_rpc *rpc, int timeout, int strict, struct nc_reply **reply)
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001123{
Michal Vaskof35ea502016-02-24 10:44:54 +01001124 uint64_t msgid;
1125 NC_MSG_TYPE ret;
1126 ret = nc_send_rpc(session, rpc, timeout, &msgid);
1127 if (ret != NC_MSG_RPC) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001128 return ret;
1129 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001130
1131 while ((ret = nc_recv_reply(session, rpc, msgid, timeout, (strict ? LYD_OPT_STRICT : 0), reply)) == NC_MSG_NOTIF);
1132
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001133 return ret;
1134}
1135
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001136/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001137 * \brief Connect to NETCONF server
1138 *
1139 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1140 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001141static unsigned int
Michal Vaskof35ea502016-02-24 10:44:54 +01001142netconf_connect(const char *host, const char *port, const char *user, const char *pass, const char *privkey)
Radek Krejci469aab82012-07-22 18:42:20 +02001143{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001144 struct nc_session* session = NULL;
1145 struct session_with_mutex *locked_session, *last_session;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001146 char *pubkey;
Radek Krejci469aab82012-07-22 18:42:20 +02001147
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001148 /* connect to the requested NETCONF server */
1149 password = (char*)pass;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001150 if (privkey) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001151 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 3);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001152 asprintf(&pubkey, "%s.pub", privkey);
Michal Vaskof35ea502016-02-24 10:44:54 +01001153 nc_client_ssh_add_keypair(pubkey, privkey);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001154 free(pubkey);
1155 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001156 nc_client_ssh_set_username(user);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001157 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Michal Vaskof35ea502016-02-24 10:44:54 +01001158 session = nc_connect_ssh(host, (unsigned short)atoi(port), NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001159 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001160
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001161 /* if connected successful, add session to the list */
1162 if (session != NULL) {
1163 if ((locked_session = calloc(1, sizeof(struct session_with_mutex))) == NULL || pthread_mutex_init (&locked_session->lock, NULL) != 0) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001164 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001165 session = NULL;
1166 free(locked_session);
1167 locked_session = NULL;
1168 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1169 return 0;
1170 }
1171 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001172 locked_session->hello_message = NULL;
1173 locked_session->closed = 0;
1174 pthread_mutex_init(&locked_session->lock, NULL);
1175 DEBUG("Before session_lock");
1176 /* get exclusive access to sessions_list (conns) */
1177 DEBUG("LOCK wrlock %s", __func__);
1178 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001179 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001180 free(locked_session);
1181 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1182 return 0;
1183 }
1184 locked_session->ntfc_subscribed = 0;
1185 DEBUG("Add connection to the list");
1186 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001187 netconf_sessions_list = locked_session;
1188 } else {
1189 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1190 last_session->next = locked_session;
1191 locked_session->prev = last_session;
1192 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001193 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001194
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001195 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001196
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001197 /* store information about session from hello message for future usage */
1198 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001199
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001200 DEBUG("NETCONF session established");
1201 locked_session->session_key = session_key_generator;
1202 ++session_key_generator;
1203 if (session_key_generator == UINT_MAX) {
1204 session_key_generator = 1;
1205 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001206
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001207 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001208 /* unlock session list */
1209 DEBUG("UNLOCK wrlock %s", __func__);
1210 if (pthread_rwlock_unlock(&session_lock) != 0) {
1211 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1212 }
Radek Krejci469aab82012-07-22 18:42:20 +02001213
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001214 return locked_session->session_key;
1215 }
1216
1217 ERROR("Connection could not be established");
1218 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001219}
1220
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001221static int
1222close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001223{
Michal Vaskoa53ef882015-11-24 11:02:01 +01001224 int i;
1225
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001226 DEBUG("lock private lock.");
1227 DEBUG("LOCK mutex %s", __func__);
1228 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1229 ERROR("Error while locking rwlock");
1230 }
1231 locked_session->ntfc_subscribed = 0;
1232 locked_session->closed = 1;
1233 if (locked_session->session != NULL) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001234 nc_session_free(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001235 locked_session->session = NULL;
1236 }
1237 DEBUG("session closed.");
1238 DEBUG("unlock private lock.");
1239 DEBUG("UNLOCK mutex %s", __func__);
1240 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1241 ERROR("Error while locking rwlock");
1242 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001243
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001244 DEBUG("unlock session lock.");
1245 DEBUG("closed session, disabled notif(?), wait 0.5s");
1246 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001247
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001248 /* session shouldn't be used by now */
Michal Vaskoa53ef882015-11-24 11:02:01 +01001249 for (i = 0; i < locked_session->notif_count; ++i) {
1250 free(locked_session->notifications[i].content);
1251 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001252 free(locked_session->notifications);
1253 pthread_mutex_destroy(&locked_session->lock);
1254 if (locked_session->hello_message != NULL) {
1255 json_object_put(locked_session->hello_message);
1256 locked_session->hello_message = NULL;
1257 }
1258 locked_session->session = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001259 free(locked_session);
1260 locked_session = NULL;
1261 DEBUG("NETCONF session closed, everything cleared.");
1262 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001263}
1264
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001265static int
1266netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001267{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001268 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001269
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001270 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001271
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001272 /* get exclusive (write) access to sessions_list (conns) */
1273 DEBUG("lock session lock.");
1274 DEBUG("LOCK wrlock %s", __func__);
1275 if (pthread_rwlock_wrlock (&session_lock) != 0) {
1276 ERROR("Error while locking rwlock");
1277 (*reply) = create_error_reply("Internal: Error while locking.");
1278 return EXIT_FAILURE;
1279 }
1280 /* remove session from the active sessions list -> nobody new can now work with session */
1281 for (locked_session = netconf_sessions_list;
1282 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001283 locked_session = locked_session->next);
1284
1285 if (!locked_session) {
Michal Vasko642cad02016-03-17 12:31:18 +01001286 pthread_rwlock_unlock(&session_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001287 ERROR("Could not find the session %u to close.", session_key);
1288 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001289 return EXIT_FAILURE;
1290 }
1291
1292 if (!locked_session->prev) {
1293 netconf_sessions_list = netconf_sessions_list->next;
Michal Vaskof0b6fbb2016-03-08 12:04:34 +01001294 if (netconf_sessions_list) {
1295 netconf_sessions_list->prev = NULL;
1296 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001297 } else {
1298 locked_session->prev->next = locked_session->next;
1299 if (locked_session->next) {
1300 locked_session->next->prev = locked_session->prev;
1301 }
1302 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001303
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001304 DEBUG("UNLOCK wrlock %s", __func__);
1305 if (pthread_rwlock_unlock (&session_lock) != 0) {
1306 ERROR("Error while unlocking rwlock");
1307 (*reply) = create_error_reply("Internal: Error while unlocking.");
1308 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001309
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001310 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1311 return close_and_free_session(locked_session);
1312 } else {
1313 ERROR("Unknown session to close");
1314 (*reply) = create_error_reply("Internal: Unkown session to close.");
1315 return (EXIT_FAILURE);
1316 }
1317 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001318}
1319
Tomas Cejkac7929632013-10-24 19:25:15 +02001320/**
1321 * Test reply message type and return error message.
1322 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001323 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001324 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001325 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001326 * \param[out] data
1327 * \return NULL on success
1328 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001329json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001330netconf_test_reply(struct nc_session *session, unsigned int session_key, NC_MSG_TYPE msgt, struct nc_reply *reply, struct lyd_node **data)
Tomas Cejkac7929632013-10-24 19:25:15 +02001331{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001332 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001333
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001334 /* process the result of the operation */
1335 switch (msgt) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001336 case NC_MSG_ERROR:
1337 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001338 ERROR("mod_netconf: receiving rpc-reply failed");
1339 if (session_key) {
1340 netconf_close(session_key, &err);
1341 }
1342 if (err != NULL) {
1343 return err;
1344 }
1345 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1346 }
1347 case NC_MSG_NONE:
1348 /* there is error handled by callback */
1349 if (data != NULL) {
1350 free(*data);
1351 (*data) = NULL;
1352 }
1353 return NULL;
1354 case NC_MSG_REPLY:
Michal Vaskof35ea502016-02-24 10:44:54 +01001355 switch (reply->type) {
1356 case NC_RPL_OK:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001357 if ((data != NULL) && (*data != NULL)) {
1358 free(*data);
1359 (*data) = NULL;
1360 }
1361 return create_ok_reply();
Michal Vaskof35ea502016-02-24 10:44:54 +01001362 case NC_RPL_DATA:
1363 if (((*data) = ((struct nc_reply_data *)reply)->data) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001364 ERROR("mod_netconf: no data from reply");
1365 return create_error_reply("Internal: No data from reply received.");
1366 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001367 ((struct nc_reply_data *)reply)->data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001368 return NULL;
1369 }
1370 break;
Michal Vaskof35ea502016-02-24 10:44:54 +01001371 case NC_RPL_ERROR:
1372 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001373 if (data != NULL) {
1374 free(*data);
1375 (*data) = NULL;
1376 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001377 return create_error_reply(((struct nc_reply_error *)reply)->err[0].message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001378 default:
Michal Vaskof35ea502016-02-24 10:44:54 +01001379 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001380 if (data != NULL) {
1381 free(*data);
1382 (*data) = NULL;
1383 }
1384 return create_error_reply("Unknown type of NETCONF reply.");
1385 }
1386 break;
1387 default:
1388 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1389 if (data != NULL) {
1390 free(*data);
1391 (*data) = NULL;
1392 }
1393 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1394 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001395}
1396
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001397json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001398netconf_unlocked_op(struct nc_session *session, struct nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001399{
Michal Vaskof35ea502016-02-24 10:44:54 +01001400 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001401 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001402
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001403 /* check requests */
1404 if (rpc == NULL) {
1405 ERROR("mod_netconf: rpc is not created");
1406 return create_error_reply("Internal error: RPC is not created");
1407 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001408
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001409 if (session != NULL) {
1410 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001411 msgt = netconf_send_recv_timed(session, rpc, 50000, 0, &reply);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001412 /* process the result of the operation */
1413 return netconf_test_reply(session, 0, msgt, reply, NULL);
1414 } else {
1415 ERROR("Unknown session to process.");
1416 return create_error_reply("Internal error: Unknown session to process.");
1417 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001418}
1419
Tomas Cejkac7929632013-10-24 19:25:15 +02001420/**
1421 * Perform RPC method that returns data.
1422 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001423 * \param[in] session_id session identifier
1424 * \param[in] rpc RPC message to perform
1425 * \param[out] received_data received data string, can be NULL when no data expected, value can be set to NULL if no data received
Tomas Cejkac7929632013-10-24 19:25:15 +02001426 * \return NULL on success, json object with error otherwise
1427 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001428static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001429netconf_op(unsigned int session_key, struct nc_rpc *rpc, int strict, struct lyd_node **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001430{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001431 struct session_with_mutex * locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001432 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001433 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001434 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001435 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001436
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001437 /* check requests */
1438 if (rpc == NULL) {
1439 ERROR("mod_netconf: rpc is not created");
1440 res = create_error_reply("Internal: RPC could not be created.");
1441 data = NULL;
1442 goto finished;
1443 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001444
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001445 locked_session = session_get_locked(session_key, &res);
1446 if (!locked_session) {
1447 ERROR("Unknown session or locking failed.");
1448 goto finished;
1449 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001450
Michal Vaskof35ea502016-02-24 10:44:54 +01001451 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001452
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001453 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001454 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, strict, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001455
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001456 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001457
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001458 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001459
Tomas Cejkac7929632013-10-24 19:25:15 +02001460finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001461 nc_reply_free(reply);
1462 if (received_data != NULL) {
1463 (*received_data) = data;
1464 } else {
1465 if (data != NULL) {
1466 free(data);
1467 data = NULL;
1468 }
1469 }
1470 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001471}
1472
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001473static char *
1474netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001475{
Michal Vaskof35ea502016-02-24 10:44:54 +01001476 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001477 struct session_with_mutex *locked_session;
Michal Vasko04245ee2016-03-16 09:32:59 +01001478 json_object *res = NULL, *data_cjson;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001479 enum json_tokener_error tok_err;
Michal Vasko04245ee2016-03-16 09:32:59 +01001480 char *data_json = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001481 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001482
Michal Vaskof35ea502016-02-24 10:44:54 +01001483 /* tell server to show all elements even if they have default values */
1484#ifdef HAVE_WITHDEFAULTS_TAGGED
1485 rpc = nc_rpc_getconfig(source, filter, NC_WD_MODE_ALL_TAG, NC_PARAMTYPE_CONST);
1486#else
1487 rpc = nc_rpc_getconfig(source, filter, 0, NC_PARAMTYPE_CONST);
1488#endif
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001489 if (rpc == NULL) {
1490 ERROR("mod_netconf: creating rpc request failed");
1491 return (NULL);
1492 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001493
Michal Vaskof35ea502016-02-24 10:44:54 +01001494 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001495 nc_rpc_free(rpc);
1496 if (res != NULL) {
1497 (*err) = res;
1498 } else {
1499 (*err) = NULL;
1500 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001501
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001502 if (data) {
1503 for (locked_session = netconf_sessions_list;
1504 locked_session && (locked_session->session_key != session_key);
1505 locked_session = locked_session->next);
1506 /* won't fail */
1507
Michal Vaskof35ea502016-02-24 10:44:54 +01001508 /* print data into JSON */
1509 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001510 ERROR("Printing JSON <get-config> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001511 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001512 return NULL;
1513 }
1514
1515 /* parse JSON data into cjson */
1516 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001517 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001518 if (!data_cjson) {
1519 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1520 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001521 lyd_free_withsiblings(data);
1522 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001523 return NULL;
1524 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001525 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001526
1527 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001528 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001529 node_add_metadata_recursive(sibling, NULL, data_cjson);
1530 lyd_free(sibling);
1531 }
1532
Michal Vaskof35ea502016-02-24 10:44:54 +01001533 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001534 json_object_put(data_cjson);
1535 pthread_mutex_unlock(&json_lock);
1536 }
1537
Michal Vaskof35ea502016-02-24 10:44:54 +01001538 return (data_json);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001539}
1540
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001541static char *
1542netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001543{
Michal Vaskof35ea502016-02-24 10:44:54 +01001544 struct nc_rpc *rpc;
1545 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001546 json_object *res = NULL;
Michal Vasko56ec5952016-04-05 14:30:23 +02001547 char *model_data = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001548
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001549 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001550 rpc = nc_rpc_getschema(identifier, version, format, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001551 if (rpc == NULL) {
1552 ERROR("mod_netconf: creating rpc request failed");
1553 return (NULL);
1554 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001555
Michal Vaskof35ea502016-02-24 10:44:54 +01001556 res = netconf_op(session_key, rpc, 0, &data);
1557 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001558 if (res != NULL) {
1559 (*err) = res;
1560 } else {
1561 (*err) = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001562
1563 if (data) {
Michal Vasko56ec5952016-04-05 14:30:23 +02001564 if (((struct lyd_node_anyxml *)data)->xml_struct) {
1565 lyxml_print_mem(&model_data, ((struct lyd_node_anyxml *)data)->value.xml, 0);
1566 } else {
1567 model_data = strdup(((struct lyd_node_anyxml *)data)->value.str);
1568 }
1569 if (!model_data) {
1570 ERROR("memory allocation fail (%s:%d)", __FILE__, __LINE__);
Michal Vaskof35ea502016-02-24 10:44:54 +01001571 }
1572 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001573 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001574
Michal Vaskof35ea502016-02-24 10:44:54 +01001575 return (model_data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001576}
1577
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001578static char *
1579netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001580{
Michal Vaskof35ea502016-02-24 10:44:54 +01001581 struct nc_rpc* rpc;
1582 char* data_json = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001583 json_object *res = NULL, *data_cjson;
1584 enum json_tokener_error tok_err;
1585 struct session_with_mutex *locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001586 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001587
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001588 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001589 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001590 if (rpc == NULL) {
1591 ERROR("mod_netconf: creating rpc request failed");
1592 return (NULL);
1593 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001594
Michal Vaskof35ea502016-02-24 10:44:54 +01001595 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001596 nc_rpc_free(rpc);
1597 if (res != NULL) {
1598 (*err) = res;
1599 } else {
1600 (*err) = NULL;
1601 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001602
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001603 if (data) {
1604 for (locked_session = netconf_sessions_list;
1605 locked_session && (locked_session->session_key != session_key);
1606 locked_session = locked_session->next);
1607 /* won't fail */
1608
Michal Vaskof35ea502016-02-24 10:44:54 +01001609 /* print JSON data */
1610 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001611 ERROR("Printing JSON <get> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001612 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001613 return NULL;
1614 }
1615
1616 /* parse JSON data into cjson */
1617 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001618 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001619 if (!data_cjson) {
1620 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1621 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001622 lyd_free_withsiblings(data);
1623 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001624 return NULL;
1625 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001626 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001627
1628 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001629 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001630 node_add_metadata_recursive(sibling, NULL, data_cjson);
1631 lyd_free(sibling);
1632 }
1633
Michal Vaskof35ea502016-02-24 10:44:54 +01001634 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001635 json_object_put(data_cjson);
1636 pthread_mutex_unlock(&json_lock);
1637 }
1638
Michal Vaskof35ea502016-02-24 10:44:54 +01001639 return data_json;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001640}
1641
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001642static json_object *
1643netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1644 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001645{
Michal Vaskof35ea502016-02-24 10:44:54 +01001646 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001647 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001648
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001649 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001650 rpc = nc_rpc_copy(target, uri_trg, source, (config ? config : uri_src), 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001651 if (rpc == NULL) {
1652 ERROR("mod_netconf: creating rpc request failed");
1653 return create_error_reply("Internal: Creating rpc request failed");
1654 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001655
Michal Vaskof35ea502016-02-24 10:44:54 +01001656 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001657 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001658
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001659 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001660}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001661
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001662static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001663netconf_editconfig(unsigned int session_key, NC_DATASTORE target, NC_RPC_EDIT_DFLTOP defop,
1664 NC_RPC_EDIT_ERROPT erropt, NC_RPC_EDIT_TESTOPT testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001665{
Michal Vaskof35ea502016-02-24 10:44:54 +01001666 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001667 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001668
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001669 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001670 rpc = nc_rpc_edit(target, defop, testopt, erropt, config_or_url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001671 if (rpc == NULL) {
1672 ERROR("mod_netconf: creating rpc request failed");
1673 return create_error_reply("Internal: Creating rpc request failed");
1674 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001675
Michal Vaskof35ea502016-02-24 10:44:54 +01001676 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001677 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001678
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001679 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001680}
1681
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001682static json_object *
1683netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001684{
Michal Vaskof35ea502016-02-24 10:44:54 +01001685 struct nc_rpc *rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001686 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001687
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001688 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001689 rpc = nc_rpc_kill(atoi(sid));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001690 if (rpc == NULL) {
1691 ERROR("mod_netconf: creating rpc request failed");
1692 return create_error_reply("Internal: Creating rpc request failed");
1693 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001694
Michal Vaskof35ea502016-02-24 10:44:54 +01001695 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001696 nc_rpc_free(rpc);
1697 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001698}
1699
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001700static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001701netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, struct nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001702{
Michal Vaskof35ea502016-02-24 10:44:54 +01001703 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001704 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001705
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001706 /* create requests */
1707 rpc = op_func(target);
1708 if (rpc == NULL) {
1709 ERROR("mod_netconf: creating rpc request failed");
1710 return create_error_reply("Internal: Creating rpc request failed");
1711 }
Radek Krejci2f318372012-07-26 14:22:35 +02001712
Michal Vaskof35ea502016-02-24 10:44:54 +01001713 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001714 nc_rpc_free (rpc);
1715 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001716}
1717
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001718static json_object *
1719netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001720{
Michal Vaskof35ea502016-02-24 10:44:54 +01001721 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001722 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001723 rpc = nc_rpc_delete(target, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001724 if (rpc == NULL) {
1725 ERROR("mod_netconf: creating rpc request failed");
1726 return create_error_reply("Internal: Creating rpc request failed");
1727 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001728
Michal Vaskof35ea502016-02-24 10:44:54 +01001729 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001730 nc_rpc_free (rpc);
1731 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001732}
1733
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001734static json_object *
1735netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001736{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001737 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001738}
1739
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001740static json_object *
1741netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001742{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001743 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001744}
1745
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001746static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001747netconf_generic(unsigned int session_key, const char *xml_content, struct lyd_node **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001748{
Michal Vaskof35ea502016-02-24 10:44:54 +01001749 struct nc_rpc* rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001750 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001751
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001752 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001753 rpc = nc_rpc_generic_xml(xml_content, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001754 if (rpc == NULL) {
1755 ERROR("mod_netconf: creating rpc request failed");
1756 return create_error_reply("Internal: Creating rpc request failed");
1757 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001758
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001759 /* get session where send the RPC */
Michal Vaskof35ea502016-02-24 10:44:54 +01001760 res = netconf_op(session_key, rpc, 0, data);
1761 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001762 return res;
1763}
1764
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001765static int
Michal Vaskof35ea502016-02-24 10:44:54 +01001766node_add_metadata(const struct lys_node *node, const struct lys_module *module, json_object *parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001767{
1768 struct lys_module *cur_module;
1769 json_object *meta_obj;
1770 char *obj_name;
1771
Michal Vaskoa45770b2015-11-23 15:49:41 +01001772 if (node->nodetype == LYS_INPUT) {
1773 /* silently skipped */
1774 return 0;
1775 }
1776
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001777 cur_module = node->module;
1778 if (cur_module->type) {
1779 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1780 }
1781 if (cur_module == module) {
1782 asprintf(&obj_name, "$@%s", node->name);
1783 } else {
1784 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1785 }
1786
1787 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01001788 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001789 free(obj_name);
1790 return 1;
1791 }
1792
1793 meta_obj = json_object_new_object();
1794
1795 switch (node->nodetype) {
1796 case LYS_CONTAINER:
1797 node_metadata_container((struct lys_node_container *)node, meta_obj);
1798 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01001799 case LYS_CHOICE:
1800 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
1801 break;
1802 case LYS_LEAF:
1803 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
1804 break;
1805 case LYS_LEAFLIST:
1806 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
1807 break;
1808 case LYS_LIST:
1809 node_metadata_list((struct lys_node_list *)node, meta_obj);
1810 break;
1811 case LYS_ANYXML:
1812 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
1813 break;
1814 case LYS_CASE:
1815 node_metadata_case((struct lys_node_case *)node, meta_obj);
1816 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001817 case LYS_RPC:
1818 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
1819 break;
1820 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01001821 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
1822 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001823 }
1824
1825 /* just a precaution */
1826 if (json_object_get_type(parent) != json_type_object) {
1827 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1828 free(obj_name);
1829 return 1;
1830 }
1831
1832 json_object_object_add(parent, obj_name, meta_obj);
1833 free(obj_name);
1834 return 0;
1835}
1836
1837static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001838node_add_metadata_recursive(struct lyd_node *data_tree, const struct lys_module *module, json_object *data_json_parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001839{
1840 struct lys_module *cur_module;
1841 struct lys_node *list_schema;
1842 struct lyd_node *child, *list_item;
1843 json_object *child_json, *list_child_json;
1844 char *child_name;
1845 int list_idx;
1846
Michal Vaskoa45770b2015-11-23 15:49:41 +01001847 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1848 return;
1849 }
1850
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001851 /* add data_tree metadata */
1852 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
1853 return;
1854 }
1855
1856 /* get data_tree module */
1857 cur_module = data_tree->schema->module;
1858 if (cur_module->type) {
1859 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1860 }
1861
1862 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1863 /* print correct data_tree JSON name */
1864 if (cur_module == module) {
1865 asprintf(&child_name, "%s", data_tree->schema->name);
1866 } else {
1867 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
1868 }
1869
1870 /* go down in JSON object */
1871 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
1872 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
1873 free(child_name);
1874 return;
1875 }
1876 free(child_name);
1877
1878 if (data_tree->schema->nodetype == LYS_LIST) {
1879 if (json_object_get_type(child_json) != json_type_array) {
1880 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1881 return;
1882 }
1883 /* go down in data tree for every item, we process them all now, skip later
1884 * (metadata duplicate will be detected at the beginning of this function) */
1885 list_idx = 0;
1886 list_schema = data_tree->schema;
1887
1888 LY_TREE_FOR(data_tree, list_item) {
1889 /* another list member */
1890 if (list_item->schema == list_schema) {
1891 list_child_json = json_object_array_get_idx(child_json, list_idx);
1892 if (!list_child_json) {
1893 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
1894 return;
1895 }
1896 LY_TREE_FOR(list_item->child, child) {
1897 node_add_metadata_recursive(child, cur_module, list_child_json);
1898 }
1899
1900 ++list_idx;
1901 }
1902 }
1903 } else {
1904 if (json_object_get_type(child_json) != json_type_object) {
1905 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1906 return;
1907 }
1908 /* go down in data tree */
1909 LY_TREE_FOR(data_tree->child, child) {
1910 node_add_metadata_recursive(child, cur_module, child_json);
1911 }
1912 }
1913 }
1914}
1915
1916static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001917node_add_model_metadata(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001918{
1919 json_object *obj;
1920 char *str;
1921
1922 obj = json_object_new_object();
1923 node_metadata_model(module, obj);
1924 asprintf(&str, "$@@%s", module->name);
1925 json_object_object_add(parent, str, obj);
1926 free(str);
1927}
1928
1929static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001930node_add_children_with_metadata_recursive(const struct lys_node *node, const struct lys_module *module, json_object *parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001931{
Michal Vaskof35ea502016-02-24 10:44:54 +01001932 const struct lys_module *cur_module;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001933 struct lys_node *child;
1934 json_object *node_json;
1935 char *json_name;
1936
Michal Vaskoa45770b2015-11-23 15:49:41 +01001937 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1938 return;
1939 }
1940
1941 if (node->nodetype & LYS_USES) {
1942 cur_module = module;
1943 node_json = parent;
1944 goto children;
1945 }
1946
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001947 /* add node metadata */
1948 if (node_add_metadata(node, module, parent)) {
1949 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
1950 return;
1951 }
1952
Michal Vaskoa45770b2015-11-23 15:49:41 +01001953 /* no other metadata */
1954 if (!node->child) {
1955 return;
1956 }
1957
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001958 /* get node module */
1959 cur_module = node->module;
1960 if (cur_module->type) {
1961 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1962 }
1963
1964 /* create JSON object for child metadata */
1965 node_json = json_object_new_object();
1966 if (cur_module == module) {
1967 json_object_object_add(parent, node->name, node_json);
1968 } else {
1969 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
1970 json_object_object_add(parent, json_name, node_json);
1971 free(json_name);
1972 }
1973
Michal Vaskoa45770b2015-11-23 15:49:41 +01001974children:
Michal Vaskoea2ddd92016-03-17 15:43:58 +01001975 if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1976 LY_TREE_FOR(node->child, child) {
1977 node_add_children_with_metadata_recursive(child, cur_module, node_json);
1978 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001979 }
1980}
1981
1982static json_object *
1983libyang_query(unsigned int session_key, const char *filter, int load_children)
1984{
Michal Vaskof35ea502016-02-24 10:44:54 +01001985 const struct lys_node *node;
1986 const struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001987 struct session_with_mutex *locked_session;
1988 json_object *ret = NULL, *data;
1989
1990 locked_session = session_get_locked(session_key, &ret);
1991 if (!locked_session) {
1992 ERROR("Locking failed or session not found.");
1993 goto finish;
1994 }
1995
Michal Vaskof35ea502016-02-24 10:44:54 +01001996 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001997
Michal Vaskoa45770b2015-11-23 15:49:41 +01001998 if (filter[0] == '/') {
Michal Vasko56ec5952016-04-05 14:30:23 +02001999 node = ly_ctx_get_node(nc_session_get_ctx(locked_session->session), NULL, filter);
Michal Vaskoa45770b2015-11-23 15:49:41 +01002000 if (!node) {
2001 ret = create_error_reply("Failed to resolve XPath filter node.");
2002 goto finish;
2003 }
2004 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002005 module = ly_ctx_get_module(nc_session_get_ctx(locked_session->session), filter, NULL);
Michal Vaskoa45770b2015-11-23 15:49:41 +01002006 if (!module) {
2007 ret = create_error_reply("Failed to find model.");
2008 goto finish;
2009 }
2010 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002011
Michal Vaskoa45770b2015-11-23 15:49:41 +01002012 pthread_mutex_lock(&json_lock);
2013 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002014
Michal Vaskoa45770b2015-11-23 15:49:41 +01002015 if (module) {
2016 node_add_model_metadata(module, data);
2017 if (load_children) {
2018 LY_TREE_FOR(module->data, node) {
2019 node_add_children_with_metadata_recursive(node, NULL, data);
2020 }
2021 }
2022 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002023 if (load_children) {
2024 node_add_children_with_metadata_recursive(node, NULL, data);
2025 } else {
2026 node_add_metadata(node, NULL, data);
2027 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002028 }
2029
Michal Vaskoa45770b2015-11-23 15:49:41 +01002030 pthread_mutex_unlock(&json_lock);
2031 ret = create_data_reply(json_object_to_json_string(data));
2032 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002033
2034finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002035 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002036 return ret;
2037}
2038
2039static json_object *
2040libyang_merge(unsigned int session_key, const char *config)
2041{
2042 struct lyd_node *data_tree = NULL, *sibling;
2043 struct session_with_mutex *locked_session;
2044 json_object *ret = NULL, *data_json = NULL;
2045 enum json_tokener_error err = 0;
2046
2047 locked_session = session_get_locked(session_key, &ret);
2048 if (!locked_session) {
2049 ERROR("Locking failed or session not found.");
2050 goto finish;
2051 }
2052
Michal Vaskof35ea502016-02-24 10:44:54 +01002053 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002054
Michal Vaskof35ea502016-02-24 10:44:54 +01002055 data_tree = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_STRICT);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002056 if (!data_tree) {
2057 ERROR("Creating data tree failed.");
2058 ret = create_error_reply("Failed to create data tree from JSON config.");
2059 session_unlock(locked_session);
2060 goto finish;
2061 }
2062
2063 session_unlock(locked_session);
2064
2065 pthread_mutex_lock(&json_lock);
2066 data_json = json_tokener_parse_verbose(config, &err);
2067 if (!data_json) {
2068 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2069 pthread_mutex_unlock(&json_lock);
2070 ret = create_error_reply(json_tokener_error_desc(err));
2071 goto finish;
2072 }
2073
2074 /* go simultaneously through both trees and add metadata */
2075 LY_TREE_FOR(data_tree, sibling) {
2076 node_add_metadata_recursive(sibling, NULL, data_json);
2077 }
2078 pthread_mutex_unlock(&json_lock);
2079 ret = create_data_reply(json_object_to_json_string(data_json));
2080
2081finish:
2082 LY_TREE_FOR(data_tree, sibling) {
2083 lyd_free(sibling);
2084 }
2085 json_object_put(data_json);
2086 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002087}
2088
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002089/**
2090 * @}
2091 *//* netconf_operations */
2092
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002093void
2094clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002095{
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002096#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +02002097 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
2098
2099#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002100 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002101
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002102 switch (level) {
2103 FOREACH(CASE);
2104 case NC_VERB_VERBOSE:
2105 case NC_VERB_DEBUG:
2106 DEBUG("DEBUG: %s", msg);
2107 break;
2108 }
2109 if (level == NC_VERB_ERROR) {
2110 /* return global error */
2111 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
2112 NULL /* severity */, NULL /* apptag */,
2113 NULL /* path */, msg, NULL /* attribute */,
2114 NULL /* element */, NULL /* ns */, NULL /* sid */);
2115 }
Radek Krejci469aab82012-07-22 18:42:20 +02002116}
2117
Tomas Cejka64b87482013-06-03 16:30:53 +02002118/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002119 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002120 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002121 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002122 * \return pointer to message
2123 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002124char *
2125get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002126{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002127 /* read json in chunked framing */
2128 unsigned int buffer_size = 0;
2129 ssize_t buffer_len = 0;
2130 char *buffer = NULL;
2131 char c;
2132 ssize_t ret;
2133 int i, chunk_len;
2134 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002135
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002136 while (1) {
2137 /* read chunk length */
2138 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2139 if (buffer != NULL) {
2140 free (buffer);
2141 buffer = NULL;
2142 }
2143 break;
2144 }
2145 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2146 if (buffer != NULL) {
2147 free (buffer);
2148 buffer = NULL;
2149 }
2150 break;
2151 }
2152 i=0;
2153 memset (chunk_len_str, 0, 12);
2154 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2155 if (i==0 && c == '#') {
2156 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2157 /* end but invalid */
2158 if (buffer != NULL) {
2159 free (buffer);
2160 buffer = NULL;
2161 }
2162 }
2163 /* end of message, double-loop break */
2164 goto msg_complete;
2165 }
2166 chunk_len_str[i++] = c;
2167 if (i==11) {
2168 ERROR("Message is too long, buffer for length is not big enought!!!!");
2169 break;
2170 }
2171 }
2172 if (c != '\n') {
2173 if (buffer != NULL) {
2174 free (buffer);
2175 buffer = NULL;
2176 }
2177 break;
2178 }
2179 chunk_len_str[i] = 0;
2180 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2181 if (buffer != NULL) {
2182 free (buffer);
2183 buffer = NULL;
2184 }
2185 break;
2186 }
2187 buffer_size += chunk_len+1;
2188 buffer = realloc (buffer, sizeof(char)*buffer_size);
2189 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2190 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2191 if (buffer != NULL) {
2192 free (buffer);
2193 buffer = NULL;
2194 }
2195 break;
2196 }
2197 buffer_len += ret;
2198 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002199msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002200 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002201}
2202
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002203NC_DATASTORE
2204parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002205{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002206 if (strcmp(ds, "running") == 0) {
2207 return NC_DATASTORE_RUNNING;
2208 } else if (strcmp(ds, "startup") == 0) {
2209 return NC_DATASTORE_STARTUP;
2210 } else if (strcmp(ds, "candidate") == 0) {
2211 return NC_DATASTORE_CANDIDATE;
2212 } else if (strcmp(ds, "url") == 0) {
2213 return NC_DATASTORE_URL;
2214 } else if (strcmp(ds, "config") == 0) {
2215 return NC_DATASTORE_CONFIG;
2216 }
2217 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002218}
2219
Michal Vaskof35ea502016-02-24 10:44:54 +01002220NC_RPC_EDIT_TESTOPT
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002221parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002222{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002223 if (strcmp(t, "notset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002224 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002225 } else if (strcmp(t, "testset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002226 return NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002227 } else if (strcmp(t, "set") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002228 return NC_RPC_EDIT_TESTOPT_SET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002229 } else if (strcmp(t, "test") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002230 return NC_RPC_EDIT_TESTOPT_TEST;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002231 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002232 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002233}
2234
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002235json_object *
2236create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002237{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002238 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002239
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002240 pthread_mutex_lock(&json_lock);
2241 reply = json_object_new_object();
2242 array = json_object_new_array();
2243 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2244 json_object_array_add(array, json_object_new_string(errmess));
2245 json_object_object_add(reply, "errors", array);
2246 pthread_mutex_unlock(&json_lock);
2247
2248 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002249}
2250
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002251json_object *
2252create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002253{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002254 pthread_mutex_lock(&json_lock);
2255 json_object *reply = json_object_new_object();
2256 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2257 json_object_object_add(reply, "data", json_object_new_string(data));
2258 pthread_mutex_unlock(&json_lock);
2259 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002260}
2261
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002262json_object *
2263create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002264{
Michal Vasko365dc4c2016-03-17 10:03:58 +01002265 json_object *reply;
2266
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002267 pthread_mutex_lock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002268 reply = json_object_new_object();
2269 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2270 pthread_mutex_unlock(&json_lock);
2271 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002272}
2273
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002274json_object *
2275create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002276{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002277 json_object *replies;
2278
2279 pthread_mutex_lock(&json_lock);
2280 replies = json_object_new_object();
2281 pthread_mutex_unlock(&json_lock);
2282
2283 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002284}
2285
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002286void
2287add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002288{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002289 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002291 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002292
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002293 pthread_mutex_lock(&json_lock);
2294 json_object_object_add(replies, str, reply);
2295 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002296
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002297 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002298}
2299
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002300char *
2301get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002302{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002303 json_object *js_tmp = NULL;
2304 char *res = NULL;
2305 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2306 res = strdup(json_object_get_string(js_tmp));
2307 }
2308 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002309}
2310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002311json_object *
2312handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002313{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002314 char *host = NULL;
2315 char *port = NULL;
2316 char *user = NULL;
2317 char *pass = NULL;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002318 char *privkey = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002319 json_object *reply = NULL;
2320 unsigned int session_key = 0;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002321
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002322 DEBUG("Request: connect");
2323 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002324
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002325 host = get_param_string(request, "host");
2326 port = get_param_string(request, "port");
2327 user = get_param_string(request, "user");
2328 pass = get_param_string(request, "pass");
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002329 privkey = get_param_string(request, "privatekey");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002330
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002331 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002332
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002333 if (host == NULL) {
2334 host = "localhost";
2335 }
2336
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002337 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002338 if (user == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002339 ERROR("Cannot connect - insufficient input.");
2340 session_key = 0;
2341 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002342 session_key = netconf_connect(host, port, user, pass, privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002343 DEBUG("Session key: %u", session_key);
2344 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002345
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002346 GETSPEC_ERR_REPLY
2347
2348 pthread_mutex_lock(&json_lock);
2349 if (session_key == 0) {
2350 /* negative reply */
2351 if (err_reply == NULL) {
2352 reply = json_object_new_object();
2353 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2354 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2355 ERROR("Connection failed.");
2356 } else {
2357 /* use filled err_reply from libnetconf's callback */
2358 reply = err_reply;
2359 ERROR("Connect - error from libnetconf's callback.");
2360 }
2361 } else {
2362 /* positive reply */
2363 reply = json_object_new_object();
2364 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2365 json_object_object_add(reply, "session", json_object_new_int(session_key));
2366 }
2367 memset(pass, 0, strlen(pass));
2368 pthread_mutex_unlock(&json_lock);
2369 CHECK_AND_FREE(host);
2370 CHECK_AND_FREE(user);
2371 CHECK_AND_FREE(port);
2372 CHECK_AND_FREE(pass);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002373 CHECK_AND_FREE(privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002374 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002375}
2376
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002377json_object *
2378handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002379{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002380 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002381
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002382 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002383
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002384 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2385 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2386 } else {
2387 reply = create_ok_reply();
2388 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002389
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002390 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002391}
2392
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002393json_object *
2394handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002395{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002396 char *filter = NULL;
2397 char *data = NULL;
2398 json_object *reply = NULL, *obj;
2399 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002400
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002401 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002402
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002403 pthread_mutex_lock(&json_lock);
2404 filter = get_param_string(request, "filter");
2405 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2406 pthread_mutex_unlock(&json_lock);
2407 reply = create_error_reply("Missing strict parameter.");
2408 return reply;
2409 }
2410 strict = json_object_get_boolean(obj);
2411 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002412
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002413 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2414 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2415 } else {
2416 reply = create_data_reply(data);
2417 free(data);
2418 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002419
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002420 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002421}
2422
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002423json_object *
2424handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002425{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002426 NC_DATASTORE ds_type_s = -1;
2427 char *filter = NULL;
2428 char *data = NULL;
2429 char *source = NULL;
2430 json_object *reply = NULL, *obj;
2431 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002432
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002433 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002434
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002435 pthread_mutex_lock(&json_lock);
2436 filter = get_param_string(request, "filter");
2437 source = get_param_string(request, "source");
2438 if (source != NULL) {
2439 ds_type_s = parse_datastore(source);
2440 }
2441 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2442 pthread_mutex_unlock(&json_lock);
2443 reply = create_error_reply("Missing strict parameter.");
2444 return reply;
2445 }
2446 strict = json_object_get_boolean(obj);
2447 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002448
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002449 if ((int)ds_type_s == -1) {
2450 reply = create_error_reply("Invalid source repository type requested.");
2451 goto finalize;
2452 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002453
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002454 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2455 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2456 } else {
2457 reply = create_data_reply(data);
2458 free(data);
2459 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002460
Tomas Cejka09629492014-07-10 15:58:06 +02002461finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002462 CHECK_AND_FREE(filter);
2463 CHECK_AND_FREE(source);
2464 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002465}
2466
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002467json_object *
2468handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002469{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002470 NC_DATASTORE ds_type_t = -1;
Michal Vaskof35ea502016-02-24 10:44:54 +01002471 NC_RPC_EDIT_DFLTOP defop_type = 0;
2472 NC_RPC_EDIT_ERROPT erropt_type = 0;
2473 NC_RPC_EDIT_TESTOPT testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002474 char *defop = NULL;
2475 char *erropt = NULL;
2476 char *config = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002477 char *target = NULL;
2478 char *testopt = NULL;
2479 char *urisource = NULL;
2480 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002481 struct lyd_node *content;
2482 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002483
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002484 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002485
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002486 pthread_mutex_lock(&json_lock);
2487 /* get parameters */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002488 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2489 pthread_mutex_unlock(&json_lock);
2490 reply = create_error_reply("Missing configs parameter.");
2491 goto finalize;
2492 }
2493 obj = json_object_array_get_idx(configs, idx);
2494 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002495
Michal Vaskof35ea502016-02-24 10:44:54 +01002496 target = get_param_string(request, "target");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002497 defop = get_param_string(request, "default-operation");
2498 erropt = get_param_string(request, "error-option");
2499 urisource = get_param_string(request, "uri-source");
2500 testopt = get_param_string(request, "test-option");
2501 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002502
Michal Vaskob8f5d442016-04-05 14:48:37 +02002503 if (!target) {
2504 ERROR("Missing the target parameter.");
2505 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002506 }
Michal Vaskob8f5d442016-04-05 14:48:37 +02002507 ds_type_t = parse_datastore(target);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002508
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002509 if (defop != NULL) {
2510 if (strcmp(defop, "merge") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002511 defop_type = NC_RPC_EDIT_DFLTOP_MERGE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002512 } else if (strcmp(defop, "replace") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002513 defop_type = NC_RPC_EDIT_DFLTOP_REPLACE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002514 } else if (strcmp(defop, "none") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002515 defop_type = NC_RPC_EDIT_DFLTOP_NONE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002516 } else {
2517 reply = create_error_reply("Invalid default-operation parameter.");
2518 goto finalize;
2519 }
2520 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002521 defop_type = NC_RPC_EDIT_DFLTOP_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002522 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002523
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002524 if (erropt != NULL) {
2525 if (strcmp(erropt, "continue-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002526 erropt_type = NC_RPC_EDIT_ERROPT_CONTINUE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002527 } else if (strcmp(erropt, "stop-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002528 erropt_type = NC_RPC_EDIT_ERROPT_STOP;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002529 } else if (strcmp(erropt, "rollback-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002530 erropt_type = NC_RPC_EDIT_ERROPT_ROLLBACK;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002531 } else {
2532 reply = create_error_reply("Invalid error-option parameter.");
2533 goto finalize;
2534 }
2535 } else {
2536 erropt_type = 0;
2537 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002538
Michal Vaskof35ea502016-02-24 10:44:54 +01002539 if ((config && urisource) || (!config && !urisource)) {
2540 reply = create_error_reply("Invalid config and uri-source data parameters.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002541 goto finalize;
2542 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002543
2544 if (config) {
2545 locked_session = session_get_locked(session_key, NULL);
2546 if (!locked_session) {
2547 ERROR("Unknown session or locking failed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002548 goto finalize;
2549 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002550
2551 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_EDIT);
2552 session_unlock(locked_session);
2553
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002554 if (!content) {
2555 ERROR("Failed to parse edit-config content.");
2556 goto finalize;
2557 }
2558
Michal Vaskof35ea502016-02-24 10:44:54 +01002559 free(config);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002560 config = NULL;
2561
Michal Vaskof35ea502016-02-24 10:44:54 +01002562 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2563 lyd_free_withsiblings(content);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002564 if (!config) {
2565 ERROR("Failed to print edit-config content.");
2566 goto finalize;
2567 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002568 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002569 config = urisource;
2570 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002571
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002572 if (testopt != NULL) {
2573 testopt_type = parse_testopt(testopt);
2574 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002575 testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002576 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002577
Michal Vaskof35ea502016-02-24 10:44:54 +01002578 reply = netconf_editconfig(session_key, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002579
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002580 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002581
Tomas Cejka09629492014-07-10 15:58:06 +02002582finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002583 CHECK_AND_FREE(defop);
2584 CHECK_AND_FREE(erropt);
2585 CHECK_AND_FREE(config);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002586 CHECK_AND_FREE(urisource);
2587 CHECK_AND_FREE(target);
2588 CHECK_AND_FREE(testopt);
2589
2590 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002591}
2592
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002593json_object *
2594handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002595{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002596 NC_DATASTORE ds_type_s = -1;
2597 NC_DATASTORE ds_type_t = -1;
2598 char *config = NULL;
2599 char *target = NULL;
2600 char *source = NULL;
2601 char *uri_src = NULL;
2602 char *uri_trg = NULL;
2603 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002604 struct lyd_node *content;
2605 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002606
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002607 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002608
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002609 /* get parameters */
2610 pthread_mutex_lock(&json_lock);
2611 target = get_param_string(request, "target");
2612 source = get_param_string(request, "source");
2613 uri_src = get_param_string(request, "uri-source");
2614 uri_trg = get_param_string(request, "uri-target");
2615 if (!strcmp(source, "config")) {
2616 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2617 pthread_mutex_unlock(&json_lock);
2618 reply = create_error_reply("Missing configs parameter.");
2619 goto finalize;
2620 }
2621 obj = json_object_array_get_idx(configs, idx);
2622 if (!obj) {
2623 pthread_mutex_unlock(&json_lock);
2624 reply = create_error_reply("Configs array parameter shorter than sessions.");
2625 goto finalize;
2626 }
2627 config = strdup(json_object_get_string(obj));
2628 }
2629 pthread_mutex_unlock(&json_lock);
2630
2631 if (target != NULL) {
2632 ds_type_t = parse_datastore(target);
2633 }
2634 if (source != NULL) {
2635 ds_type_s = parse_datastore(source);
2636 }
2637
2638 if ((int)ds_type_s == -1) {
2639 /* invalid source datastore specified */
2640 reply = create_error_reply("Invalid source repository type requested.");
2641 goto finalize;
2642 }
2643
2644 if ((int)ds_type_t == -1) {
2645 /* invalid target datastore specified */
2646 reply = create_error_reply("Invalid target repository type requested.");
2647 goto finalize;
2648 }
2649
2650 if (ds_type_s == NC_DATASTORE_URL) {
2651 if (uri_src == NULL) {
2652 uri_src = "";
2653 }
2654 }
2655 if (ds_type_t == NC_DATASTORE_URL) {
2656 if (uri_trg == NULL) {
2657 uri_trg = "";
2658 }
2659 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002660
2661 if (config) {
2662 locked_session = session_get_locked(session_key, NULL);
2663 if (!locked_session) {
2664 ERROR("Unknown session or locking failed.");
2665 goto finalize;
2666 }
2667
2668 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_CONFIG);
2669 session_unlock(locked_session);
2670
2671 free(config);
2672 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2673 lyd_free_withsiblings(content);
2674 }
2675
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002676 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2677
2678 CHECK_ERR_SET_REPLY
2679
2680finalize:
2681 CHECK_AND_FREE(config);
2682 CHECK_AND_FREE(target);
2683 CHECK_AND_FREE(source);
2684 CHECK_AND_FREE(uri_src);
2685 CHECK_AND_FREE(uri_trg);
2686
2687 return reply;
2688}
2689
2690json_object *
2691handle_op_deleteconfig(json_object *request, unsigned int session_key)
2692{
2693 json_object *reply;
2694 NC_DATASTORE ds_type = -1;
2695 char *target, *url;
2696
2697 DEBUG("Request: delete-config (session %u)", session_key);
2698
2699 pthread_mutex_lock(&json_lock);
2700 target = get_param_string(request, "target");
2701 url = get_param_string(request, "url");
2702 pthread_mutex_unlock(&json_lock);
2703
2704 if (target != NULL) {
2705 ds_type = parse_datastore(target);
2706 }
2707 if ((int)ds_type == -1) {
2708 reply = create_error_reply("Invalid target repository type requested.");
2709 goto finalize;
2710 }
2711 if (ds_type == NC_DATASTORE_URL) {
2712 if (!url) {
2713 url = "";
2714 }
2715 }
2716
2717 reply = netconf_deleteconfig(session_key, ds_type, url);
2718
2719 CHECK_ERR_SET_REPLY
2720 if (reply == NULL) {
2721 reply = create_ok_reply();
2722 }
2723
2724finalize:
2725 CHECK_AND_FREE(target);
2726 CHECK_AND_FREE(url);
2727 return reply;
2728}
2729
2730json_object *
2731handle_op_lock(json_object *request, unsigned int session_key)
2732{
2733 json_object *reply;
2734 NC_DATASTORE ds_type = -1;
2735 char *target;
2736
2737 DEBUG("Request: lock (session %u)", session_key);
2738
2739 pthread_mutex_lock(&json_lock);
2740 target = get_param_string(request, "target");
2741 pthread_mutex_unlock(&json_lock);
2742
2743 if (target != NULL) {
2744 ds_type = parse_datastore(target);
2745 }
2746 if ((int)ds_type == -1) {
2747 reply = create_error_reply("Invalid target repository type requested.");
2748 goto finalize;
2749 }
2750
2751 reply = netconf_lock(session_key, ds_type);
2752
2753 CHECK_ERR_SET_REPLY
2754 if (reply == NULL) {
2755 reply = create_ok_reply();
2756 }
2757
2758finalize:
2759 CHECK_AND_FREE(target);
2760 return reply;
2761}
2762
2763json_object *
2764handle_op_unlock(json_object *request, unsigned int session_key)
2765{
2766 json_object *reply;
2767 NC_DATASTORE ds_type = -1;
2768 char *target;
2769
2770 DEBUG("Request: unlock (session %u)", session_key);
2771
2772 pthread_mutex_lock(&json_lock);
2773 target = get_param_string(request, "target");
2774 pthread_mutex_unlock(&json_lock);
2775
2776 if (target != NULL) {
2777 ds_type = parse_datastore(target);
2778 }
2779 if ((int)ds_type == -1) {
2780 reply = create_error_reply("Invalid target repository type requested.");
2781 goto finalize;
2782 }
2783
2784 reply = netconf_unlock(session_key, ds_type);
2785
2786 CHECK_ERR_SET_REPLY
2787 if (reply == NULL) {
2788 reply = create_ok_reply();
2789 }
2790
2791finalize:
2792 CHECK_AND_FREE(target);
2793 return reply;
2794}
2795
2796json_object *
2797handle_op_kill(json_object *request, unsigned int session_key)
2798{
2799 json_object *reply = NULL;
2800 char *sid = NULL;
2801
2802 DEBUG("Request: kill-session (session %u)", session_key);
2803
2804 pthread_mutex_lock(&json_lock);
2805 sid = get_param_string(request, "session-id");
2806 pthread_mutex_unlock(&json_lock);
2807
2808 if (sid == NULL) {
2809 reply = create_error_reply("Missing session-id parameter.");
2810 goto finalize;
2811 }
2812
2813 reply = netconf_killsession(session_key, sid);
2814
2815 CHECK_ERR_SET_REPLY
2816
2817finalize:
2818 CHECK_AND_FREE(sid);
2819 return reply;
2820}
2821
2822json_object *
2823handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2824{
2825 json_object *reply = NULL;
2826 struct session_with_mutex *locked_session = NULL;
2827 DEBUG("Request: get info about session %u", session_key);
2828
2829 DEBUG("LOCK wrlock %s", __func__);
2830 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2831 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2832 }
2833
2834 for (locked_session = netconf_sessions_list;
2835 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002836 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002837 if (locked_session != NULL) {
2838 DEBUG("LOCK mutex %s", __func__);
2839 pthread_mutex_lock(&locked_session->lock);
2840 DEBUG("UNLOCK wrlock %s", __func__);
2841 if (pthread_rwlock_unlock(&session_lock) != 0) {
2842 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2843 }
2844 if (locked_session->hello_message != NULL) {
2845 reply = locked_session->hello_message;
2846 } else {
2847 reply = create_error_reply("Invalid session identifier.");
2848 }
2849 DEBUG("UNLOCK mutex %s", __func__);
2850 pthread_mutex_unlock(&locked_session->lock);
2851 } else {
2852 DEBUG("UNLOCK wrlock %s", __func__);
2853 if (pthread_rwlock_unlock(&session_lock) != 0) {
2854 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2855 }
2856 reply = create_error_reply("Invalid session identifier.");
2857 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002858
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002859 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002860}
2861
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002862json_object *
2863handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002864{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002865 json_object *reply = NULL, *contents, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002866 char *content = NULL, *str;
2867 struct lyd_node *data = NULL, *node_content;
2868 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002869
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002870 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002871
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002872 pthread_mutex_lock(&json_lock);
2873 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2874 pthread_mutex_unlock(&json_lock);
2875 reply = create_error_reply("Missing contents parameter.");
2876 goto finalize;
2877 }
2878 obj = json_object_array_get_idx(contents, idx);
2879 if (!obj) {
2880 pthread_mutex_unlock(&json_lock);
2881 reply = create_error_reply("Contents array parameter shorter than sessions.");
2882 goto finalize;
2883 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002884 content = strdup(json_object_get_string(obj));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002885 pthread_mutex_unlock(&json_lock);
2886
Michal Vaskof35ea502016-02-24 10:44:54 +01002887 locked_session = session_get_locked(session_key, NULL);
2888 if (!locked_session) {
2889 ERROR("Unknown session or locking failed.");
2890 goto finalize;
2891 }
2892
2893 node_content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), content, LYD_JSON, LYD_OPT_RPC);
2894 session_unlock(locked_session);
2895
2896 free(content);
2897 lyd_print_mem(&content, node_content, LYD_XML, LYP_WITHSIBLINGS);
2898 lyd_free_withsiblings(node_content);
2899
2900 reply = netconf_generic(session_key, content, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002901 if (reply == NULL) {
2902 GETSPEC_ERR_REPLY
2903 if (err_reply != NULL) {
2904 /* use filled err_reply from libnetconf's callback */
2905 reply = err_reply;
2906 }
2907 } else {
2908 if (data == NULL) {
2909 pthread_mutex_lock(&json_lock);
2910 reply = json_object_new_object();
2911 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2912 pthread_mutex_unlock(&json_lock);
2913 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002914 lyd_print_mem(&str, data, LYD_JSON, LYP_WITHSIBLINGS);
2915 lyd_free_withsiblings(data);
2916 reply = create_data_reply(str);
2917 free(str);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002918 }
2919 }
2920
2921finalize:
Michal Vaskof35ea502016-02-24 10:44:54 +01002922 CHECK_AND_FREE(content);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002923 return reply;
2924}
2925
2926json_object *
2927handle_op_getschema(json_object *request, unsigned int session_key)
2928{
2929 char *data = NULL;
2930 char *identifier = NULL;
2931 char *version = NULL;
2932 char *format = NULL;
2933 json_object *reply = NULL;
2934
2935 DEBUG("Request: get-schema (session %u)", session_key);
2936
2937 pthread_mutex_lock(&json_lock);
2938 identifier = get_param_string(request, "identifier");
2939 version = get_param_string(request, "version");
2940 format = get_param_string(request, "format");
2941 pthread_mutex_unlock(&json_lock);
2942
2943 if (identifier == NULL) {
2944 reply = create_error_reply("No identifier for get-schema supplied.");
2945 goto finalize;
2946 }
2947
2948 DEBUG("get-schema(version: %s, format: %s)", version, format);
2949 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
2950 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
2951 } else {
2952 reply = create_data_reply(data);
2953 free(data);
2954 }
2955
2956finalize:
2957 CHECK_AND_FREE(identifier);
2958 CHECK_AND_FREE(version);
2959 CHECK_AND_FREE(format);
2960 return reply;
2961}
2962
2963json_object *
2964handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
2965{
2966 struct nc_session *temp_session = NULL;
2967 struct session_with_mutex * locked_session = NULL;
2968 json_object *reply = NULL;
2969
2970 DEBUG("Request: reload hello (session %u)", session_key);
2971
2972 DEBUG("LOCK wrlock %s", __func__);
2973 if (pthread_rwlock_wrlock(&session_lock) != 0) {
2974 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2975 return NULL;
2976 }
2977
2978 for (locked_session = netconf_sessions_list;
2979 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002980 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002981 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
2982 DEBUG("LOCK mutex %s", __func__);
2983 pthread_mutex_lock(&locked_session->lock);
2984 DEBUG("creating temporary NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01002985 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002986 if (temp_session != NULL) {
2987 prepare_status_message(locked_session, temp_session);
2988 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01002989 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002990 temp_session = NULL;
2991 } else {
2992 DEBUG("Reload hello failed due to channel establishment");
2993 reply = create_error_reply("Reload was unsuccessful, connection failed.");
2994 }
2995 DEBUG("UNLOCK mutex %s", __func__);
2996 pthread_mutex_unlock(&locked_session->lock);
2997 DEBUG("UNLOCK wrlock %s", __func__);
2998 if (pthread_rwlock_unlock(&session_lock) != 0) {
2999 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3000 }
3001 } else {
3002 DEBUG("UNLOCK wrlock %s", __func__);
3003 if (pthread_rwlock_unlock(&session_lock) != 0) {
3004 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3005 }
3006 reply = create_error_reply("Invalid session identifier.");
3007 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003008
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003009 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
3010 reply = locked_session->hello_message;
3011 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003012
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003013 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003014}
3015
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003016void
Michal Vaskof35ea502016-02-24 10:44:54 +01003017notification_history(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003018{
Michal Vaskof35ea502016-02-24 10:44:54 +01003019 time_t eventtime;
3020 char *content;
3021 (void)session;
3022
3023 eventtime = nc_datetime2time(notif->datetime);
3024
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003025 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3026 if (notif_history_array == NULL) {
3027 ERROR("No list of notification history found.");
3028 return;
3029 }
3030 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3031 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01003032 json_object *notif_obj = json_object_new_object();
3033 if (notif_obj == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003034 ERROR("Could not allocate memory for notification (json).");
3035 goto failed;
3036 }
Michal Vaskof35ea502016-02-24 10:44:54 +01003037 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
3038
3039 json_object_object_add(notif_obj, "eventtime", json_object_new_int64(eventtime));
3040 json_object_object_add(notif_obj, "content", json_object_new_string(content));
3041
3042 free(content);
3043
3044 json_object_array_add(notif_history_array, notif_obj);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003045failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003046 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003047}
3048
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003049json_object *
3050handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003051{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003052 json_object *reply = NULL;
3053 json_object *js_tmp = NULL;
3054 struct session_with_mutex *locked_session = NULL;
3055 struct nc_session *temp_session = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003056 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003057 time_t start = 0;
3058 time_t stop = 0;
3059 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003060
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003061 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003062
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003063 pthread_mutex_lock(&json_lock);
3064 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3065 from = json_object_get_int64(js_tmp);
3066 }
3067 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3068 to = json_object_get_int64(js_tmp);
3069 }
3070 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003071
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003072 start = time(NULL) + from;
3073 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003074
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003075 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003076
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003077 DEBUG("LOCK wrlock %s", __func__);
3078 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3079 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3080 reply = create_error_reply("Internal lock failed.");
3081 goto finalize;
3082 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003083
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003084 for (locked_session = netconf_sessions_list;
3085 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003086 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003087 if (locked_session != NULL) {
3088 DEBUG("LOCK mutex %s", __func__);
3089 pthread_mutex_lock(&locked_session->lock);
3090 DEBUG("UNLOCK wrlock %s", __func__);
3091 if (pthread_rwlock_unlock(&session_lock) != 0) {
3092 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3093 }
3094 DEBUG("creating temporal NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01003095 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003096 if (temp_session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003097 rpc = nc_rpc_subscribe(NULL, NULL, nc_time2datetime(start, NULL), nc_time2datetime(stop, NULL), NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003098 if (rpc == NULL) {
3099 DEBUG("UNLOCK mutex %s", __func__);
3100 pthread_mutex_unlock(&locked_session->lock);
3101 DEBUG("notifications: creating an rpc request failed.");
3102 reply = create_error_reply("notifications: creating an rpc request failed.");
3103 goto finalize;
3104 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003105
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003106 DEBUG("Send NC subscribe.");
3107 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3108 json_object *res = netconf_unlocked_op(temp_session, rpc);
3109 if (res != NULL) {
3110 DEBUG("UNLOCK mutex %s", __func__);
3111 pthread_mutex_unlock(&locked_session->lock);
3112 DEBUG("Subscription RPC failed.");
3113 reply = res;
3114 goto finalize;
3115 }
3116 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003117
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003118 DEBUG("UNLOCK mutex %s", __func__);
3119 pthread_mutex_unlock(&locked_session->lock);
3120 DEBUG("LOCK mutex %s", __func__);
3121 pthread_mutex_lock(&ntf_history_lock);
3122 pthread_mutex_lock(&json_lock);
3123 json_object *notif_history_array = json_object_new_array();
3124 pthread_mutex_unlock(&json_lock);
3125 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3126 ERROR("notif_history: cannot set thread-specific hash value.");
3127 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003128
Michal Vaskof35ea502016-02-24 10:44:54 +01003129 nc_recv_notif_dispatch(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003130
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003131 pthread_mutex_lock(&json_lock);
3132 reply = json_object_new_object();
3133 json_object_object_add(reply, "notifications", notif_history_array);
3134 //json_object_put(notif_history_array);
3135 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003136
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003137 DEBUG("UNLOCK mutex %s", __func__);
3138 pthread_mutex_unlock(&ntf_history_lock);
3139 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01003140 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003141 temp_session = NULL;
3142 } else {
3143 DEBUG("UNLOCK mutex %s", __func__);
3144 pthread_mutex_unlock(&locked_session->lock);
3145 DEBUG("Get history of notification failed due to channel establishment");
3146 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3147 }
3148 } else {
3149 DEBUG("UNLOCK wrlock %s", __func__);
3150 if (pthread_rwlock_unlock(&session_lock) != 0) {
3151 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3152 }
3153 reply = create_error_reply("Invalid session identifier.");
3154 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003155
Tomas Cejka09629492014-07-10 15:58:06 +02003156finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003157 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003158}
3159
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003160json_object *
3161handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003162{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003163 json_object *reply = NULL;
3164 char *target = NULL;
3165 char *url = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003166 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003167 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003168
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003169 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003170
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003171 pthread_mutex_lock(&json_lock);
3172 target = get_param_string(request, "target");
3173 url = get_param_string(request, "url");
3174 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003175
3176
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003177 if (target == NULL) {
3178 reply = create_error_reply("Missing target parameter.");
3179 goto finalize;
3180 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003181
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003182 /* validation */
3183 target_ds = parse_datastore(target);
Michal Vaskof35ea502016-02-24 10:44:54 +01003184 rpc = nc_rpc_validate(target_ds, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003185 if (rpc == NULL) {
3186 DEBUG("mod_netconf: creating rpc request failed");
3187 reply = create_error_reply("Creation of RPC request failed.");
3188 goto finalize;
3189 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003190
Michal Vaskof35ea502016-02-24 10:44:54 +01003191 if ((reply = netconf_op(session_key, rpc, 0, NULL)) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003192 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003193
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003194 if (reply == NULL) {
3195 DEBUG("Request: validation ok.");
3196 reply = create_ok_reply();
3197 }
3198 }
3199 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003200
Tomas Cejka09629492014-07-10 15:58:06 +02003201finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003202 CHECK_AND_FREE(target);
3203 CHECK_AND_FREE(url);
3204 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003205}
3206
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003207json_object *
3208handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003209{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003210 json_object *reply = NULL, *filters, *obj;
3211 char *filter = NULL;
3212 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003213
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003214 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003215
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003216 pthread_mutex_lock(&json_lock);
3217 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3218 pthread_mutex_unlock(&json_lock);
3219 reply = create_error_reply("Missing filters parameter.");
3220 goto finalize;
3221 }
3222 obj = json_object_array_get_idx(filters, idx);
3223 if (!obj) {
3224 pthread_mutex_unlock(&json_lock);
3225 reply = create_error_reply("Filters array parameter shorter than sessions.");
3226 goto finalize;
3227 }
3228 filter = strdup(json_object_get_string(obj));
3229 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3230 load_children = json_object_get_boolean(obj);
3231 }
3232 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003233
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003234 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003235
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003236 CHECK_ERR_SET_REPLY
3237 if (!reply) {
3238 reply = create_error_reply("Query failed.");
3239 }
David Kupka8e60a372012-09-04 09:15:20 +02003240
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003241finalize:
3242 CHECK_AND_FREE(filter);
3243 return reply;
3244}
David Kupka8e60a372012-09-04 09:15:20 +02003245
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003246json_object *
3247handle_op_merge(json_object *request, unsigned int session_key, int idx)
3248{
3249 json_object *reply = NULL, *configs, *obj;
3250 char *config = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003251 struct lyd_node *content;
3252 struct session_with_mutex *locked_session;
David Kupka8e60a372012-09-04 09:15:20 +02003253
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003254 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003255
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003256 pthread_mutex_lock(&json_lock);
3257 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3258 pthread_mutex_unlock(&json_lock);
3259 reply = create_error_reply("Missing configurations parameter.");
3260 goto finalize;
3261 }
3262 obj = json_object_array_get_idx(configs, idx);
3263 if (!obj) {
3264 pthread_mutex_unlock(&json_lock);
3265 reply = create_error_reply("Filters array parameter shorter than sessions.");
3266 goto finalize;
3267 }
3268 config = strdup(json_object_get_string(obj));
3269 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003270
Michal Vaskof35ea502016-02-24 10:44:54 +01003271 locked_session = session_get_locked(session_key, NULL);
3272 if (!locked_session) {
3273 ERROR("Unknown session or locking failed.");
3274 goto finalize;
3275 }
3276
3277 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_DATA);
3278 session_unlock(locked_session);
3279
3280 free(config);
3281 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
3282 lyd_free_withsiblings(content);
3283
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003284 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003285
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003286 CHECK_ERR_SET_REPLY
3287 if (!reply) {
3288 reply = create_error_reply("Merge failed.");
3289 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003291finalize:
3292 CHECK_AND_FREE(config);
3293 return reply;
3294}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003295
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003296void *
3297thread_routine(void *arg)
3298{
3299 void *retval = NULL;
3300 struct pollfd fds;
3301 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3302 json_object *js_tmp = NULL;
Michal Vaskodf280a22016-03-17 09:19:53 +01003303 int operation = (-1), count, i, sent;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003304 int status = 0;
3305 const char *msgtext;
3306 unsigned int session_key = 0;
3307 char *chunked_out_msg = NULL;
3308 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003309
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003310 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003311
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003312 /* init thread specific err_reply memory */
3313 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003314
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003315 while (!isterminated) {
3316 fds.fd = client;
3317 fds.events = POLLIN;
3318 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003319
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003320 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003321
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003322 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3323 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3324 continue;
3325 } else if (status < 0) {
3326 /* 0: poll time outed
3327 * close socket and ignore this request from the client, it can try it again
3328 * -1: poll failed
3329 * something wrong happend, close this socket and wait for another request
3330 */
3331 close(client);
3332 break;
3333 }
3334 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003335
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003336 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003337
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003338 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3339 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3340 /* close client's socket (it's probably already closed by client */
3341 close(client);
3342 break;
3343 }
Tomas Cejka09629492014-07-10 15:58:06 +02003344
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003345 DEBUG("Get framed message...");
3346 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02003347
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003348 DEBUG("Check read buffer.");
3349 if (buffer != NULL) {
Michal Vaskoe7d1bbc2016-04-05 16:09:29 +02003350 DEBUG("Received message:\n%s\n", buffer);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003351 enum json_tokener_error jerr;
3352 pthread_mutex_lock(&json_lock);
3353 request = json_tokener_parse_verbose(buffer, &jerr);
3354 if (jerr != json_tokener_success) {
3355 ERROR("JSON parsing error");
3356 pthread_mutex_unlock(&json_lock);
3357 continue;
3358 }
3359
3360 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3361 operation = json_object_get_int(js_tmp);
3362 }
3363 pthread_mutex_unlock(&json_lock);
3364 if (operation == -1) {
3365 replies = create_replies();
3366 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3367 goto send_reply;
3368 }
3369
3370 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3371 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3372 replies = create_replies();
3373 add_reply(replies, create_error_reply("Operation not supported."), 0);
3374 goto send_reply;
3375 }
3376
3377 DEBUG("operation %d", operation);
3378
3379 /* null global JSON error-reply */
3380 clean_err_reply();
3381
3382 /* clean replies envelope */
3383 if (replies != NULL) {
3384 pthread_mutex_lock(&json_lock);
3385 json_object_put(replies);
3386 pthread_mutex_unlock(&json_lock);
3387 }
3388 replies = create_replies();
3389
3390 if (operation == MSG_CONNECT) {
3391 count = 1;
3392 } else {
3393 pthread_mutex_lock(&json_lock);
3394 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
Michal Vasko642cad02016-03-17 12:31:18 +01003395 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003396 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3397 goto send_reply;
3398 }
3399 count = json_object_array_length(sessions);
3400 pthread_mutex_unlock(&json_lock);
3401 }
3402
3403 for (i = 0; i < count; ++i) {
3404 if (operation != MSG_CONNECT) {
3405 js_tmp = json_object_array_get_idx(sessions, i);
3406 session_key = json_object_get_int(js_tmp);
3407 }
3408
3409 /* process required operation */
Michal Vasko977bad92016-03-15 16:20:51 +01003410 reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003411 switch (operation) {
3412 case MSG_CONNECT:
3413 reply = handle_op_connect(request);
3414 break;
3415 case MSG_DISCONNECT:
3416 reply = handle_op_disconnect(request, session_key);
3417 break;
3418 case MSG_GET:
3419 reply = handle_op_get(request, session_key);
3420 break;
3421 case MSG_GETCONFIG:
3422 reply = handle_op_getconfig(request, session_key);
3423 break;
3424 case MSG_EDITCONFIG:
3425 reply = handle_op_editconfig(request, session_key, i);
3426 break;
3427 case MSG_COPYCONFIG:
3428 reply = handle_op_copyconfig(request, session_key, i);
3429 break;
3430 case MSG_DELETECONFIG:
3431 reply = handle_op_deleteconfig(request, session_key);
3432 break;
3433 case MSG_LOCK:
3434 reply = handle_op_lock(request, session_key);
3435 break;
3436 case MSG_UNLOCK:
3437 reply = handle_op_unlock(request, session_key);
3438 break;
3439 case MSG_KILL:
3440 reply = handle_op_kill(request, session_key);
3441 break;
3442 case MSG_INFO:
3443 reply = handle_op_info(request, session_key);
3444 break;
3445 case MSG_GENERIC:
3446 reply = handle_op_generic(request, session_key, i);
3447 break;
3448 case MSG_GETSCHEMA:
3449 reply = handle_op_getschema(request, session_key);
3450 break;
3451 case MSG_RELOADHELLO:
3452 reply = handle_op_reloadhello(request, session_key);
3453 break;
3454 case MSG_NTF_GETHISTORY:
3455 reply = handle_op_ntfgethistory(request, session_key);
3456 break;
3457 case MSG_VALIDATE:
3458 reply = handle_op_validate(request, session_key);
3459 break;
3460 case SCH_QUERY:
3461 reply = handle_op_query(request, session_key, i);
3462 break;
3463 case SCH_MERGE:
3464 reply = handle_op_merge(request, session_key, i);
3465 break;
3466 }
3467
3468 add_reply(replies, reply, session_key);
3469 }
3470
3471 /* free parameters */
3472 operation = (-1);
3473
3474 DEBUG("Clean request json object.");
3475 if (request != NULL) {
3476 pthread_mutex_lock(&json_lock);
3477 json_object_put(request);
3478 pthread_mutex_unlock(&json_lock);
Michal Vasko365dc4c2016-03-17 10:03:58 +01003479 request = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003480 }
3481 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02003482
3483send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003484 /* send reply to caller */
3485 if (replies) {
3486 pthread_mutex_lock(&json_lock);
3487 msgtext = json_object_to_json_string(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003488 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003489 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3490 if (buffer != NULL) {
3491 free(buffer);
3492 buffer = NULL;
3493 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003494 break;
3495 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003496
Michal Vaskoe7d1bbc2016-04-05 16:09:29 +02003497 DEBUG("Sending message:\n%s\n", chunked_out_msg);
Michal Vaskodf280a22016-03-17 09:19:53 +01003498 i = 0;
3499 sent = 0;
3500 count = strlen(chunked_out_msg) + 1;
3501 while (count && ((i = send(client, chunked_out_msg + sent, count, 0)) != -1)) {
3502 sent += i;
3503 count -= i;
3504 }
3505 if (i == -1) {
3506 ERROR("Sending message failed (%s).", strerror(errno));
3507 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003508 DEBUG("Clean reply json object.");
3509 pthread_mutex_lock(&json_lock);
3510 json_object_put(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003511 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003512 replies = NULL;
3513 DEBUG("Clean message buffer.");
3514 CHECK_AND_FREE(chunked_out_msg);
3515 chunked_out_msg = NULL;
3516 if (buffer) {
3517 free(buffer);
3518 buffer = NULL;
3519 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003520 clean_err_reply();
3521 } else {
3522 ERROR("Reply is NULL, shouldn't be...");
3523 continue;
3524 }
3525 }
3526 }
3527 free(arg);
3528 free_err_reply();
Michal Vaskod0205992016-03-17 10:04:49 +01003529 nc_thread_destroy();
David Kupka8e60a372012-09-04 09:15:20 +02003530
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003531 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003532}
3533
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003534/**
3535 * \brief Close all open NETCONF sessions.
3536 *
3537 * During termination of mod_netconf, it is useful to close all remaining
3538 * sessions. This function iterates over the list of sessions and close them
3539 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003540 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003541static void
3542close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003543{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003544 struct session_with_mutex *locked_session, *next_session;
3545 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003546
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003547 /* get exclusive access to sessions_list (conns) */
3548 DEBUG("LOCK wrlock %s", __func__);
3549 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3550 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3551 return;
3552 }
3553 for (next_session = netconf_sessions_list; next_session;) {
3554 locked_session = next_session;
3555 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003556
Michal Vaskoc3146782015-11-04 14:46:41 +01003557 /* close_and_free_session handles locking on its own */
Michal Vaskof35ea502016-02-24 10:44:54 +01003558 DEBUG("Closing NETCONF session %u (SID %u).", locked_session->session_key, nc_session_get_id(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003559 close_and_free_session(locked_session);
3560 }
3561 netconf_sessions_list = NULL;
3562
3563 /* get exclusive access to sessions_list (conns) */
3564 DEBUG("UNLOCK wrlock %s", __func__);
3565 if (pthread_rwlock_unlock (&session_lock) != 0) {
3566 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3567 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003568}
3569
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003570static void
3571check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003572{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003573 struct nc_session *ns = NULL;
3574 struct session_with_mutex *locked_session = NULL;
3575 time_t current_time = time(NULL);
3576 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003577
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003578 /* get exclusive access to sessions_list (conns) */
3579 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3580 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3581 return;
3582 }
3583 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3584 ns = locked_session->session;
3585 if (ns == NULL) {
3586 continue;
3587 }
3588 pthread_mutex_lock(&locked_session->lock);
3589 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003590 DEBUG("Closing NETCONF session %u (SID %u).", locked_session->session_key, nc_session_get_id(locked_session->session));
Tomas Cejka47387fd2013-06-10 20:37:46 +02003591
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003592 /* close_and_free_session handles locking on its own */
3593 close_and_free_session(locked_session);
3594 } else {
3595 pthread_mutex_unlock(&locked_session->lock);
3596 }
3597 }
3598 /* get exclusive access to sessions_list (conns) */
3599 if (pthread_rwlock_unlock(&session_lock) != 0) {
3600 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3601 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003602}
3603
3604
3605/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003606 * This is actually implementation of NETCONF client
3607 * - requests are received from UNIX socket in the predefined format
3608 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003609 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003610 *
3611 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003612static void
3613forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003614{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003615 struct timeval tv;
3616 struct sockaddr_un local, remote;
3617 int lsock, client, ret, i, pthread_count = 0;
3618 unsigned int olds = 0, timediff = 0;
3619 socklen_t len;
3620 struct pass_to_thread *arg;
3621 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3622 struct timespec maxtime;
3623 pthread_rwlockattr_t lock_attrs;
3624 #ifdef WITH_NOTIFICATIONS
3625 char use_notifications = 0;
3626 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003627
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003628 /* wait at most 5 seconds for every thread to terminate */
3629 maxtime.tv_sec = 5;
3630 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003631
Tomas Cejka04e08f42014-03-27 19:52:34 +01003632#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003633 /* change uid and gid of process for security reasons */
3634 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003635#else
3636# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003637 if (strlen(SU_GROUP) > 0) {
3638 struct group *g = getgrnam(SU_GROUP);
3639 if (g == NULL) {
3640 ERROR("GID (%s) was not found.", SU_GROUP);
3641 return;
3642 }
3643 if (setgid(g->gr_gid) != 0) {
3644 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3645 return;
3646 }
3647 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003648# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003649 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003650# endif
3651# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003652 if (strlen(SU_USER) > 0) {
3653 struct passwd *p = getpwnam(SU_USER);
3654 if (p == NULL) {
3655 ERROR("UID (%s) was not found.", SU_USER);
3656 return;
3657 }
3658 if (setuid(p->pw_uid) != 0) {
3659 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3660 return;
3661 }
3662 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003663# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003664 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003665# endif
3666#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003667
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003668 /* try to remove if exists */
3669 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003670
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003671 /* create listening UNIX socket to accept incoming connections */
3672 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3673 ERROR("Creating socket failed (%s)", strerror(errno));
3674 goto error_exit;
3675 }
Radek Krejci469aab82012-07-22 18:42:20 +02003676
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003677 local.sun_family = AF_UNIX;
3678 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3679 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003680
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003681 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3682 if (errno == EADDRINUSE) {
3683 ERROR("mod_netconf socket address already in use");
3684 goto error_exit;
3685 }
3686 ERROR("Binding socket failed (%s)", strerror(errno));
3687 goto error_exit;
3688 }
Radek Krejci469aab82012-07-22 18:42:20 +02003689
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003690 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3691 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3692 goto error_exit;
3693 }
3694 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003695
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003696 uid_t user = -1;
3697 if (strlen(CHOWN_USER) > 0) {
3698 struct passwd *p = getpwnam(CHOWN_USER);
3699 if (p != NULL) {
3700 user = p->pw_uid;
3701 }
3702 }
3703 gid_t group = -1;
3704 if (strlen(CHOWN_GROUP) > 0) {
3705 struct group *g = getgrnam(CHOWN_GROUP);
3706 if (g != NULL) {
3707 group = g->gr_gid;
3708 }
3709 }
3710 if (chown(sockname, user, group) == -1) {
3711 ERROR("Chown on socket file failed (%s).", strerror(errno));
3712 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003713
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003714 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003715
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003716 #ifdef WITH_NOTIFICATIONS
3717 if (notification_init() == -1) {
3718 ERROR("libwebsockets initialization failed");
3719 use_notifications = 0;
3720 } else {
3721 use_notifications = 1;
3722 }
3723 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003724
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003725 /* setup libnetconf's callbacks */
Michal Vaskod0205992016-03-17 10:04:49 +01003726 nc_client_init();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003727 nc_verbosity(NC_VERB_DEBUG);
Michal Vaskof35ea502016-02-24 10:44:54 +01003728 nc_set_print_clb(clb_print);
3729 nc_client_ssh_set_auth_hostkey_check_clb(netconf_callback_ssh_hostkey_check);
3730 nc_client_ssh_set_auth_interactive_clb(netconf_callback_sshauth_interactive);
3731 nc_client_ssh_set_auth_password_clb(netconf_callback_sshauth_password);
3732 nc_client_ssh_set_auth_privkey_passphrase_clb(netconf_callback_sshauth_passphrase);
Radek Krejci469aab82012-07-22 18:42:20 +02003733
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003734 /* disable publickey authentication */
Michal Vaskof35ea502016-02-24 10:44:54 +01003735 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003736
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003737 /* create mutex protecting session list */
3738 pthread_rwlockattr_init(&lock_attrs);
3739 /* rwlock is shared only with threads in this process */
3740 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3741 /* create rw lock */
3742 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3743 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3744 goto error_exit;
3745 }
3746 pthread_mutex_init(&ntf_history_lock, NULL);
3747 pthread_mutex_init(&json_lock, NULL);
3748 DEBUG("Initialization of notification history.");
3749 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3750 ERROR("Initialization of notification history failed.");
3751 }
3752 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3753 ERROR("Initialization of reply key failed.");
3754 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003755
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003756 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3757 while (isterminated == 0) {
3758 gettimeofday(&tv, NULL);
3759 timediff = (unsigned int)tv.tv_sec - olds;
3760 #ifdef WITH_NOTIFICATIONS
3761 if (use_notifications == 1) {
3762 notification_handle();
3763 }
3764 #endif
3765 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3766 check_timeout_and_close();
3767 }
Radek Krejci469aab82012-07-22 18:42:20 +02003768
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003769 /* open incoming connection if any */
3770 len = sizeof(remote);
3771 client = accept(lsock, (struct sockaddr *) &remote, &len);
3772 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3773 usleep(SLEEP_TIME * 1000);
3774 continue;
3775 } else if (client == -1 && (errno == EINTR)) {
3776 continue;
3777 } else if (client == -1) {
3778 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3779 continue;
3780 }
Radek Krejci469aab82012-07-22 18:42:20 +02003781
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003782 /* set client's socket as non-blocking */
3783 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003784
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003785 arg = malloc(sizeof(struct pass_to_thread));
3786 arg->client = client;
3787 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003788
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003789 /* start new thread. It will serve this particular request and then terminate */
3790 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3791 ERROR("Creating POSIX thread failed: %d\n", ret);
3792 } else {
3793 DEBUG("Thread %lu created", ptids[pthread_count]);
3794 pthread_count++;
3795 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3796 ptids[pthread_count] = 0;
3797 }
Radek Krejci469aab82012-07-22 18:42:20 +02003798
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003799 /* check if some thread already terminated, free some resources by joining it */
3800 for (i = 0; i < pthread_count; i++) {
3801 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3802 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3803 pthread_count--;
3804 if (pthread_count > 0) {
3805 /* place last Thread ID on the place of joined one */
3806 ptids[i] = ptids[pthread_count];
3807 }
3808 }
3809 }
3810 DEBUG("Running %d threads", pthread_count);
3811 }
Radek Krejci469aab82012-07-22 18:42:20 +02003812
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003813 DEBUG("mod_netconf terminating...");
3814 /* join all threads */
3815 for (i = 0; i < pthread_count; i++) {
3816 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3817 }
Radek Krejci469aab82012-07-22 18:42:20 +02003818
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003819 #ifdef WITH_NOTIFICATIONS
3820 notification_close();
3821 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003822
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003823 /* close all NETCONF sessions */
3824 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003825
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003826 /* destroy rwlock */
3827 pthread_rwlock_destroy(&session_lock);
3828 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003829
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003830 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003831
Michal Vaskod0205992016-03-17 10:04:49 +01003832 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003833 free(ptids);
3834 close(lsock);
3835 exit(0);
3836 return;
3837
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003838error_exit:
Michal Vaskod0205992016-03-17 10:04:49 +01003839 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003840 close(lsock);
3841 free(ptids);
3842 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003843}
3844
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003845int
3846main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003847{
Michal Vaskoc3146782015-11-04 14:46:41 +01003848 struct sigaction action;
3849 sigset_t block_mask;
Michal Vaskoa53ef882015-11-24 11:02:01 +01003850 int daemonize = 0, i;
Michal Vaskoc3146782015-11-04 14:46:41 +01003851
Michal Vaskoa53ef882015-11-24 11:02:01 +01003852 if (argc > 3) {
3853 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3854 return 1;
3855 }
3856
3857 sockname = SOCKET_FILENAME;
3858 for (i = 1; i < argc; ++i) {
3859 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
3860 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3861 return 0;
3862 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon")) {
3863 daemonize = 1;
3864 } else {
3865 sockname = argv[i];
3866 }
3867 }
3868
3869 if (daemonize && (daemon(0, 0) == -1)) {
3870 ERROR("daemon() failed (%s)", strerror(errno));
3871 return 1;
Michal Vaskoc3146782015-11-04 14:46:41 +01003872 }
3873
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003874 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003875 action.sa_handler = signal_handler;
3876 action.sa_mask = block_mask;
3877 action.sa_flags = 0;
3878 sigaction(SIGINT, &action, NULL);
3879 sigaction(SIGTERM, &action, NULL);
3880
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003881 forked_proc();
3882 DEBUG("Terminated");
3883 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003884}