blob: 8c8ffdcbead7c0792424232de7784bf7ea8d0e28 [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 Vaskoefdd49f2016-04-06 11:13:27 +020059#include <syslog.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010060#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010061#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010062#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010063#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020064#include <pthread.h>
65#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020066
Michal Vaskof35ea502016-02-24 10:44:54 +010067#include <nc_client.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020068
Tomas Cejka04e08f42014-03-27 19:52:34 +010069#include "../config.h"
70
Tomas Cejkad340dbf2013-03-24 20:36:57 +010071#ifdef WITH_NOTIFICATIONS
Michal Vaskoa53ef882015-11-24 11:02:01 +010072#include "notification_server.h"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010073#endif
74
Tomas Cejka94da2c52013-01-08 18:20:30 +010075#include "message_type.h"
Michal Vaskoa53ef882015-11-24 11:02:01 +010076#include "netopeerguid.h"
Radek Krejci469aab82012-07-22 18:42:20 +020077
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010078#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020079#define MAX_PROCS 5
Michal Vaskoa53ef882015-11-24 11:02:01 +010080#define SOCKET_FILENAME "/var/run/netopeerguid.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020081#define MAX_SOCKET_CL 10
82#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010083#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
84#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020085
Michal Vasko7732dee2015-11-05 10:22:15 +010086/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020087#define SLEEP_TIME 200
88
89#ifndef offsetof
90#define offsetof(type, member) ((size_t) ((type *) 0)->member)
91#endif
92
Tomas Cejka027f3bc2012-11-10 20:28:36 +010093/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020094struct timeval timeout = { 1, 0 };
95
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010096#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010097
Radek Krejci469aab82012-07-22 18:42:20 +020098#define MSG_OK 0
99#define MSG_OPEN 1
100#define MSG_DATA 2
101#define MSG_CLOSE 3
102#define MSG_ERROR 4
103#define MSG_UNKNOWN 5
104
Tomas Cejka47387fd2013-06-10 20:37:46 +0200105pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200106pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100107pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100108pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
109
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100110unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100111struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100112static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200113static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200114pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200115volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200116static char* password;
Michal Vaskoefdd49f2016-04-06 11:13:27 +0200117int daemonize;
Radek Krejci469aab82012-07-22 18:42:20 +0200118
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100119json_object *create_ok_reply(void);
120json_object *create_data_reply(const char *data);
121static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
122 const char *format, json_object **err);
Michal Vaskof35ea502016-02-24 10:44:54 +0100123static void node_add_metadata_recursive(struct lyd_node *data_tree, const struct lys_module *module,
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100124 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100125static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100126
127static void
128signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200129{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100130 switch (sign) {
131 case SIGINT:
132 case SIGTERM:
133 isterminated = 1;
134 break;
135 }
Radek Krejci469aab82012-07-22 18:42:20 +0200136}
137
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100138int
139netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200140{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100141 /* always approve */
142 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200143}
144
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100145char *
Michal Vaskof35ea502016-02-24 10:44:54 +0100146netconf_callback_sshauth_passphrase(const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200147{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100148 char *buf;
149 buf = strdup(password);
150 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200151}
152
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100153char *
154netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200155{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100156 char *buf;
157 buf = strdup(password);
158 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200159}
160
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100161char *
162netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
163 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200164{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100165 char *buf;
166 buf = strdup(password);
167 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200168}
169
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100170void
Michal Vasko56c02b22016-04-06 09:59:09 +0200171netconf_callback_error_process(const char *message)
Radek Krejcic11fd862012-07-26 12:41:21 +0200172{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100173 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
174 if (err_reply_p == NULL) {
175 ERROR("Error message was not allocated. %s", __func__);
176 return;
177 }
178 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100179
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100180 json_object *array = NULL;
181 if (err_reply == NULL) {
182 ERROR("error calback: empty error list");
183 pthread_mutex_lock(&json_lock);
184 err_reply = json_object_new_object();
185 array = json_object_new_array();
186 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
187 json_object_object_add(err_reply, "errors", array);
188 if (message != NULL) {
189 json_object_array_add(array, json_object_new_string(message));
190 }
191 pthread_mutex_unlock(&json_lock);
192 (*err_reply_p) = err_reply;
193 } else {
194 ERROR("error calback: nonempty error list");
195 pthread_mutex_lock(&json_lock);
196 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
197 if (message != NULL) {
198 json_object_array_add(array, json_object_new_string(message));
199 }
200 }
201 pthread_mutex_unlock(&json_lock);
202 }
203 pthread_setspecific(err_reply_key, err_reply_p);
204 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200205}
206
Tomas Cejka47387fd2013-06-10 20:37:46 +0200207/**
208 * should be used in locked area
209 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100210void
211prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200212{
Michal Vaskof35ea502016-02-24 10:44:54 +0100213 int i;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100214 json_object *json_obj = NULL;
215 json_object *js_tmp = NULL;
216 char *old_sid = NULL;
217 const char *j_old_sid = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +0100218 char str_port[6];
219 const char **cpblts;
220 struct lyd_node *yanglib, *module, *node;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200221
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100222 if (s == NULL) {
223 ERROR("No session given.");
224 return;
225 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200226
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100227 pthread_mutex_lock(&json_lock);
228 if (s->hello_message != NULL) {
229 ERROR("clean previous hello message");
230 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
231 j_old_sid = json_object_get_string(js_tmp);
232 if (j_old_sid != NULL) {
233 old_sid = strdup(j_old_sid);
234 }
235 }
236 json_object_put(s->hello_message);
237 s->hello_message = NULL;
238 }
239 s->hello_message = json_object_new_object();
240 if (session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100241 if (!old_sid) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100242 /* we don't have old sid */
Michal Vaskof35ea502016-02-24 10:44:54 +0100243 asprintf(&old_sid, "%u", nc_session_get_id(session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100244 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100245 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
246 free(old_sid);
247 old_sid = NULL;
248
249 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 +0100250 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
Michal Vaskof35ea502016-02-24 10:44:54 +0100251 sprintf(str_port, "%u", nc_session_get_port(session));
252 json_object_object_add(s->hello_message, "port", json_object_new_string(str_port));
253 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_username(session)));
254 cpblts = nc_session_get_cpblts(session);
255 if (cpblts) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100256 json_obj = json_object_new_array();
Michal Vaskof35ea502016-02-24 10:44:54 +0100257 for (i = 0; cpblts[i]; ++i) {
258 json_object_array_add(json_obj, json_object_new_string(cpblts[i]));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100259 }
260 json_object_object_add(s->hello_message, "capabilities", json_obj);
261 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100262
263 yanglib = ly_ctx_info(nc_session_get_ctx(session));
264 if (yanglib) {
265 json_obj = json_object_new_array();
266 LY_TREE_FOR(yanglib->child, module) {
267 if (!strcmp(module->schema->name, "module")) {
268 LY_TREE_FOR(module->child, node) {
269 if (!strcmp(node->schema->name, "name")) {
270 json_object_array_add(json_obj, json_object_new_string(((struct lyd_node_leaf_list *)node)->value_str));
271 break;
272 }
273 }
274 }
275 }
276 json_object_object_add(s->hello_message, "models", json_obj);
277
Michal Vaskoc04900a2016-03-15 16:20:24 +0100278 lyd_free(yanglib);
Michal Vaskof35ea502016-02-24 10:44:54 +0100279 }
280
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100281 DEBUG("%s", json_object_to_json_string(s->hello_message));
282 } else {
283 ERROR("Session was not given.");
284 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
285 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
286 }
287 DEBUG("Status info from hello message prepared");
288 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200289}
290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100291void
292create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200293{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100294 json_object **err_reply = calloc(1, sizeof(json_object **));
295 if (err_reply == NULL) {
296 ERROR("Allocation of err_reply storage failed!");
297 return;
298 }
299 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
300 ERROR("cannot set thread-specific value.");
301 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200302}
303
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100304void
305clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200306{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100307 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
308 if (err_reply != NULL) {
309 if (*err_reply != NULL) {
310 pthread_mutex_lock(&json_lock);
311 json_object_put(*err_reply);
312 pthread_mutex_unlock(&json_lock);
313 }
314 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
315 ERROR("Cannot set thread-specific hash value.");
316 }
317 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200318}
319
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100320void
321free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200322{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100323 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
324 if (err_reply != NULL) {
325 if (*err_reply != NULL) {
326 pthread_mutex_lock(&json_lock);
327 json_object_put(*err_reply);
328 pthread_mutex_unlock(&json_lock);
329 }
330 free(err_reply);
331 err_reply = NULL;
332 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
333 ERROR("Cannot set thread-specific hash value.");
334 }
335 }
336}
337
338static struct session_with_mutex *
339session_get_locked(unsigned int session_key, json_object **err)
340{
341 struct session_with_mutex *locked_session;
342
343 /* get non-exclusive (read) access to sessions_list (conns) */
344 DEBUG("LOCK wrlock %s", __func__);
345 if (pthread_rwlock_rdlock(&session_lock) != 0) {
346 if (*err) {
347 *err = create_error_reply("Locking failed.");
348 }
349 return NULL;
350 }
351 /* get session where send the RPC */
352 for (locked_session = netconf_sessions_list;
353 locked_session && (locked_session->session_key != session_key);
354 locked_session = locked_session->next);
355 if (!locked_session) {
356 if (*err) {
357 *err = create_error_reply("Session not found.");
358 }
359 return NULL;
360 }
361
362 /* get exclusive access to session */
363 DEBUG("LOCK mutex %s", __func__);
364 if (pthread_mutex_lock(&locked_session->lock) != 0) {
365 if (*err) {
366 *err = create_error_reply("Locking failed.");
367 }
368 goto wrlock_fail;
369 }
370 return locked_session;
371
372wrlock_fail:
373 DEBUG("UNLOCK wrlock %s", __func__);
374 pthread_rwlock_unlock(&session_lock);
375 return NULL;
376}
377
378static void
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100379session_user_activity(const char *username)
380{
381 struct session_with_mutex *sess;
382
383 for (sess = netconf_sessions_list; sess; sess = sess->next) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100384 if (!strcmp(nc_session_get_username(sess->session), username)) {
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100385 sess->last_activity = time(NULL);
386 }
387 }
388}
389
390static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100391session_unlock(struct session_with_mutex *locked_session)
392{
393 DEBUG("UNLOCK mutex %s", __func__);
394 pthread_mutex_unlock(&locked_session->lock);
395 DEBUG("UNLOCK wrlock %s", __func__);
396 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200397}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200398
Michal Vasko3fda9a92015-11-23 10:10:57 +0100399static void
400node_metadata_text(const char *text, const char *name, json_object *parent)
401{
402 json_object *obj;
403
404 if (!text) {
405 return;
406 }
407
408 obj = json_object_new_string(text);
409 json_object_object_add(parent, name, obj);
410}
411
412static void
413node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
414{
415 json_object *obj;
416
417 if (!restr) {
418 return;
419 }
420
421 obj = json_object_new_string(restr->expr);
422 json_object_object_add(parent, name, obj);
423}
424
425static void
426node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
427{
428 uint8_t i;
429 json_object *array, *obj;
430
431 if (!must_size || !must) {
432 return;
433 }
434
435 array = json_object_new_array();
436
437 for (i = 0; i < must_size; ++i) {
438 obj = json_object_new_string(must[i].expr);
439 json_object_array_add(array, obj);
440 }
441
442 json_object_object_add(parent, "must", array);
443}
444
445static void
446node_metadata_basic(struct lys_node *node, json_object *parent)
447{
448 json_object *obj;
449
450 /* description */
451 node_metadata_text(node->dsc, "description", parent);
452
453 /* reference */
454 node_metadata_text(node->ref, "reference", parent);
455
456 /* config */
457 if (node->flags & LYS_CONFIG_R) {
458 obj = json_object_new_boolean(0);
459 } else {
460 obj = json_object_new_boolean(1);
461 }
462 json_object_object_add(parent, "config", obj);
463
464 /* status */
465 if (node->flags & LYS_STATUS_DEPRC) {
466 obj = json_object_new_string("deprecated");
467 } else if (node->flags & LYS_STATUS_OBSLT) {
468 obj = json_object_new_string("obsolete");
469 } else {
470 obj = json_object_new_string("current");
471 }
472 json_object_object_add(parent, "status", obj);
473
474 /* mandatory */
475 if (node->flags & LYS_MAND_TRUE) {
476 obj = json_object_new_boolean(1);
477 } else {
478 obj = json_object_new_boolean(0);
479 }
480 json_object_object_add(parent, "mandatory", obj);
481
482 /* NACM extensions */
483 if (node->nacm) {
484 if (node->nacm & LYS_NACM_DENYW) {
485 obj = json_object_new_string("default-deny-write");
486 } else {
487 obj = json_object_new_string("default-deny-all");
488 }
489 json_object_object_add(parent, "ext", obj);
490 }
491}
492
493static void
494node_metadata_when(struct lys_when *when, json_object *parent)
495{
496 json_object *obj;
497
498 if (!when) {
499 return;
500 }
501
502 obj = json_object_new_string(when->cond);
503 json_object_object_add(parent, "when", obj);
504}
505
506static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100507node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100508{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100509 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100510 struct lys_node *child;
511
512 if (!node->child) {
513 return;
514 }
515
516 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100517 if (child->nodetype == LYS_USES) {
518 node_metadata_children_recursive(child, child_array, choice_array);
519 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100520 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100521 if (!*child_array) {
522 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100523 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100524 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100525 } else if (child->nodetype == LYS_CHOICE) {
526 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100527 if (!*choice_array) {
528 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100529 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100530 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100531 }
532 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100533}
534
535static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100536node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100537{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100538 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100539 struct lys_node *child;
540
541 if (!choice->child) {
542 return;
543 }
544
Michal Vasko3fda9a92015-11-23 10:10:57 +0100545 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100546 if (child->nodetype == LYS_USES) {
547 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
548 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100549 obj = json_object_new_string(child->name);
550 json_object_array_add(array, obj);
551 }
552 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100553}
554
555static void
556node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
557{
558 json_object *obj;
559
560 if (min) {
561 obj = json_object_new_int(min);
562 json_object_object_add(parent, "min-elements", obj);
563 }
564
565 if (max) {
566 obj = json_object_new_int(max);
567 json_object_object_add(parent, "max-elements", obj);
568 }
569}
570
571static void
572node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
573{
574 struct lys_ident_der *cur;
575 json_object *obj;
576
577 if (!ident) {
578 return;
579 }
580
581 obj = json_object_new_string(ident->name);
582 json_object_array_add(array, obj);
583
584 for (cur = ident->der; cur; cur = cur->next) {
585 node_metadata_ident_recursive(cur->ident, array);
586 }
587}
588
589static void
590node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
591{
592 json_object *obj, *array, *item;
593 char *str;
594 int i;
595
596 /* built-in YANG type */
597 if (!type->der->module) {
598 switch (type->base) {
599 case LY_TYPE_BINARY:
600 node_metadata_text("binary", "type", parent);
601 node_metadata_restr(type->info.binary.length, "length", parent);
602 break;
603 case LY_TYPE_BITS:
604 node_metadata_text("bits", "type", parent);
605
606 array = json_object_new_array();
607 for (i = 0; i < type->info.bits.count; ++i) {
608 item = json_object_new_object();
609 obj = json_object_new_string(type->info.bits.bit[i].name);
610 json_object_object_add(item, "name", obj);
611 obj = json_object_new_int(type->info.bits.bit[i].pos);
612 json_object_object_add(item, "position", obj);
613 json_object_array_add(array, item);
614 }
615 json_object_object_add(parent, "bits", array);
616 break;
617 case LY_TYPE_BOOL:
618 node_metadata_text("bool", "type", parent);
619 break;
620 case LY_TYPE_DEC64:
621 node_metadata_text("decimal64", "type", parent);
622 node_metadata_restr(type->info.dec64.range, "range", parent);
623 obj = json_object_new_int(type->info.dec64.dig);
624 json_object_object_add(parent, "fraction-digits", obj);
625 break;
626 case LY_TYPE_EMPTY:
627 node_metadata_text("empty", "type", parent);
628 break;
629 case LY_TYPE_ENUM:
630 node_metadata_text("enumeration", "type", parent);
631
632 array = json_object_new_array();
633 for (i = 0; i < type->info.enums.count; ++i) {
634 obj = json_object_new_string(type->info.enums.enm[i].name);
635 json_object_array_add(array, obj);
636 }
637 json_object_object_add(parent, "enumval", array);
638 break;
639 case LY_TYPE_IDENT:
640 node_metadata_text("identityref", "type", parent);
641
642 array = json_object_new_array();
643 node_metadata_ident_recursive(type->info.ident.ref, array);
644 json_object_object_add(parent, "identityval", array);
645 break;
646 case LY_TYPE_INST:
647 node_metadata_text("instance-identifier", "type", parent);
648 if (type->info.inst.req == -1) {
649 obj = json_object_new_boolean(0);
650 } else {
651 obj = json_object_new_boolean(1);
652 }
653 json_object_object_add(parent, "require-instance", obj);
654 break;
655 case LY_TYPE_LEAFREF:
656 node_metadata_text("leafref", "type", parent);
657 node_metadata_text(type->info.lref.path, "path", parent);
658 break;
659 case LY_TYPE_STRING:
660 node_metadata_text("string", "type", parent);
661 node_metadata_restr(type->info.str.length, "length", parent);
662 if (type->info.str.pat_count) {
663 array = json_object_new_array();
664 for (i = 0; i < type->info.str.pat_count; ++i) {
665 obj = json_object_new_string(type->info.str.patterns[i].expr);
666 json_object_array_add(array, obj);
667 }
668 json_object_object_add(parent, "pattern", array);
669 }
670 break;
671 case LY_TYPE_UNION:
672 node_metadata_text("union", "type", parent);
673 array = json_object_new_array();
674 for (i = 0; i < type->info.uni.count; ++i) {
675 obj = json_object_new_object();
676 node_metadata_type(&type->info.uni.types[i], module, obj);
677 json_object_array_add(array, obj);
678 }
679 json_object_object_add(parent, "types", array);
680 break;
681 case LY_TYPE_INT8:
682 node_metadata_text("int8", "type", parent);
683 node_metadata_restr(type->info.num.range, "range", parent);
684 break;
685 case LY_TYPE_UINT8:
686 node_metadata_text("uint8", "type", parent);
687 node_metadata_restr(type->info.num.range, "range", parent);
688 break;
689 case LY_TYPE_INT16:
690 node_metadata_text("int16", "type", parent);
691 node_metadata_restr(type->info.num.range, "range", parent);
692 break;
693 case LY_TYPE_UINT16:
694 node_metadata_text("uint16", "type", parent);
695 node_metadata_restr(type->info.num.range, "range", parent);
696 break;
697 case LY_TYPE_INT32:
698 node_metadata_text("int32", "type", parent);
699 node_metadata_restr(type->info.num.range, "range", parent);
700 break;
701 case LY_TYPE_UINT32:
702 node_metadata_text("uint32", "type", parent);
703 node_metadata_restr(type->info.num.range, "range", parent);
704 break;
705 case LY_TYPE_INT64:
706 node_metadata_text("int64", "type", parent);
707 node_metadata_restr(type->info.num.range, "range", parent);
708 break;
709 case LY_TYPE_UINT64:
710 node_metadata_text("uint64", "type", parent);
711 node_metadata_restr(type->info.num.range, "range", parent);
712 break;
713 default:
714 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
715 break;
716 }
717
718 /* typedef */
719 } else {
720 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
721 node_metadata_text(type->der->name, "type", parent);
722 } else {
723 asprintf(&str, "%s:%s", type->module_name, type->der->name);
724 node_metadata_text(str, "type", parent);
725 free(str);
726 }
727 obj = json_object_new_object();
728 node_metadata_typedef(type->der, obj);
729 json_object_object_add(parent, "typedef", obj);
730 }
731}
732
733static void
734node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
735{
736 json_object *obj;
737
738 /* description */
739 node_metadata_text(tpdf->dsc, "description", parent);
740
741 /* reference */
742 node_metadata_text(tpdf->ref, "reference", parent);
743
744 /* status */
745 if (tpdf->flags & LYS_STATUS_DEPRC) {
746 obj = json_object_new_string("deprecated");
747 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
748 obj = json_object_new_string("obsolete");
749 } else {
750 obj = json_object_new_string("current");
751 }
752 json_object_object_add(parent, "status", obj);
753
754 /* type */
755 node_metadata_type(&tpdf->type, tpdf->module, parent);
756
757 /* units */
758 node_metadata_text(tpdf->units, "units", parent);
759
760 /* default */
761 node_metadata_text(tpdf->dflt, "default", parent);
762}
763
764static void
765node_metadata_container(struct lys_node_container *cont, json_object *parent)
766{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100767 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100768
769 /* element type */
770 obj = json_object_new_string("container");
771 json_object_object_add(parent, "eltype", obj);
772
773 /* shared info */
774 node_metadata_basic((struct lys_node *)cont, parent);
775
776 /* must */
777 node_metadata_must(cont->must_size, cont->must, parent);
778
779 /* presence */
780 node_metadata_text(cont->presence, "presence", parent);
781
782 /* when */
783 node_metadata_when(cont->when, parent);
784
785 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100786 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
787 if (child_array) {
788 json_object_object_add(parent, "children", child_array);
789 }
790 if (choice_array) {
791 json_object_object_add(parent, "choice", choice_array);
792 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100793}
794
795static void
796node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
797{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100798 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100799
800 /* element type */
801 obj = json_object_new_string("choice");
802 json_object_object_add(parent, "eltype", obj);
803
804 /* shared info */
805 node_metadata_basic((struct lys_node *)choice, parent);
806
807 /* default */
Michal Vasko5e1c6052016-03-17 15:43:33 +0100808 if (choice->dflt) {
809 node_metadata_text(choice->dflt->name, "default", parent);
810 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100811
812 /* when */
813 node_metadata_when(choice->when, parent);
814
815 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100816 if (choice->child) {
817 array = json_object_new_array();
818 node_metadata_cases_recursive(choice, array);
819 json_object_object_add(parent, "cases", array);
820 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100821}
822
823static void
824node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
825{
826 json_object *obj;
827 struct lys_node_list *list;
828 int is_key, i;
829
830 /* element type */
831 obj = json_object_new_string("leaf");
832 json_object_object_add(parent, "eltype", obj);
833
834 /* shared info */
835 node_metadata_basic((struct lys_node *)leaf, parent);
836
837 /* type */
838 node_metadata_type(&leaf->type, leaf->module, parent);
839
840 /* units */
841 node_metadata_text(leaf->units, "units", parent);
842
843 /* default */
844 node_metadata_text(leaf->dflt, "default", parent);
845
846 /* must */
847 node_metadata_must(leaf->must_size, leaf->must, parent);
848
849 /* when */
850 node_metadata_when(leaf->when, parent);
851
852 /* iskey */
853 is_key = 0;
854 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
855 if (list && (list->nodetype == LYS_LIST)) {
856 for (i = 0; i < list->keys_size; ++i) {
857 if (list->keys[i] == leaf) {
858 is_key = 1;
859 break;
860 }
861 }
862 }
863 obj = json_object_new_boolean(is_key);
864 json_object_object_add(parent, "iskey", obj);
865}
866
867static void
868node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
869{
870 json_object *obj;
871
872 /* element type */
873 obj = json_object_new_string("leaf-list");
874 json_object_object_add(parent, "eltype", obj);
875
876 /* shared info */
877 node_metadata_basic((struct lys_node *)llist, parent);
878
879 /* type */
880 node_metadata_type(&llist->type, llist->module, parent);
881
882 /* units */
883 node_metadata_text(llist->units, "units", parent);
884
885 /* must */
886 node_metadata_must(llist->must_size, llist->must, parent);
887
888 /* when */
889 node_metadata_when(llist->when, parent);
890
891 /* min/max-elements */
892 node_metadata_min_max(llist->min, llist->max, parent);
893}
894
895static void
896node_metadata_list(struct lys_node_list *list, json_object *parent)
897{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100898 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100899 int i;
900 unsigned int j;
901
902 /* element type */
903 obj = json_object_new_string("list");
904 json_object_object_add(parent, "eltype", obj);
905
906 /* shared info */
907 node_metadata_basic((struct lys_node *)list, parent);
908
909 /* must */
910 node_metadata_must(list->must_size, list->must, parent);
911
912 /* when */
913 node_metadata_when(list->when, parent);
914
915 /* min/max-elements */
916 node_metadata_min_max(list->min, list->max, parent);
917
918 /* keys */
919 if (list->keys_size) {
920 array = json_object_new_array();
921 for (i = 0; i < list->keys_size; ++i) {
922 obj = json_object_new_string(list->keys[i]->name);
923 json_object_array_add(array, obj);
924 }
925 json_object_object_add(parent, "keys", array);
926 }
927
928 /* unique */
929 if (list->unique_size) {
930 array = json_object_new_array();
931 for (i = 0; i < list->unique_size; ++i) {
932 for (j = 0; j < list->unique[i].expr_size; ++j) {
933 obj = json_object_new_string(list->unique[i].expr[j]);
934 json_object_array_add(array, obj);
935 }
936 }
937 json_object_object_add(parent, "unique", array);
938 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100939
940 /* children & choice */
941 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
942 if (child_array) {
943 json_object_object_add(parent, "children", child_array);
944 }
945 if (choice_array) {
946 json_object_object_add(parent, "choice", choice_array);
947 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100948}
949
950static void
951node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
952{
953 json_object *obj;
954
955 /* element type */
956 obj = json_object_new_string("anyxml");
957 json_object_object_add(parent, "eltype", obj);
958
959 /* shared info */
960 node_metadata_basic((struct lys_node *)anyxml, parent);
961
962 /* must */
963 node_metadata_must(anyxml->must_size, anyxml->must, parent);
964
965 /* when */
966 node_metadata_when(anyxml->when, parent);
967
968}
969
970static void
971node_metadata_case(struct lys_node_case *cas, json_object *parent)
972{
973 json_object *obj;
974
975 /* element type */
976 obj = json_object_new_string("case");
977 json_object_object_add(parent, "eltype", obj);
978
979 /* shared info */
980 node_metadata_basic((struct lys_node *)cas, parent);
981
982 /* when */
983 node_metadata_when(cas->when, parent);
984}
985
Michal Vaskoa45770b2015-11-23 15:49:41 +0100986static void
987node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
988{
989 json_object *obj;
990
991 /* element type */
992 obj = json_object_new_string("rpc");
993 json_object_object_add(parent, "eltype", obj);
994
995 /* description */
996 node_metadata_text(rpc->dsc, "description", parent);
997
998 /* reference */
999 node_metadata_text(rpc->ref, "reference", parent);
1000
1001 /* status */
1002 if (rpc->flags & LYS_STATUS_DEPRC) {
1003 obj = json_object_new_string("deprecated");
1004 } else if (rpc->flags & LYS_STATUS_OBSLT) {
1005 obj = json_object_new_string("obsolete");
1006 } else {
1007 obj = json_object_new_string("current");
1008 }
1009 json_object_object_add(parent, "status", obj);
1010}
1011
1012static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001013node_metadata_model(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001014{
1015 json_object *obj, *array, *item;
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001016 const struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001017 int i;
1018
1019 /* yang-version */
1020 if (module->version == 2) {
1021 obj = json_object_new_string("1.1");
1022 } else {
1023 obj = json_object_new_string("1.0");
1024 }
1025 json_object_object_add(parent, "yang-version", obj);
1026
1027 /* namespace */
1028 node_metadata_text(module->ns, "namespace", parent);
1029
1030 /* prefix */
1031 node_metadata_text(module->prefix, "prefix", parent);
1032
1033 /* contact */
1034 node_metadata_text(module->contact, "contact", parent);
1035
1036 /* organization */
1037 node_metadata_text(module->org, "organization", parent);
1038
1039 /* revision */
1040 if (module->rev_size) {
1041 node_metadata_text(module->rev[0].date, "revision", parent);
1042 }
1043
1044 /* description */
1045 node_metadata_text(module->dsc, "description", parent);
1046
1047 /* import */
1048 if (module->imp_size) {
1049 array = json_object_new_array();
1050 for (i = 0; i < module->imp_size; ++i) {
1051 item = json_object_new_object();
1052
1053 node_metadata_text(module->imp[i].module->name, "name", item);
1054 node_metadata_text(module->imp[i].prefix, "prefix", item);
1055 if (module->imp[i].rev && module->imp[i].rev[0]) {
1056 node_metadata_text(module->imp[i].rev, "revision", item);
1057 }
1058
1059 json_object_array_add(array, item);
1060 }
1061 json_object_object_add(parent, "imports", array);
1062 }
1063
1064 /* include */
1065 if (module->inc_size) {
1066 array = json_object_new_array();
1067 for (i = 0; i < module->inc_size; ++i) {
1068 item = json_object_new_object();
1069
1070 node_metadata_text(module->inc[i].submodule->name, "name", item);
1071 if (module->inc[i].rev && module->inc[i].rev[0]) {
1072 node_metadata_text(module->inc[i].rev, "revision", item);
1073 }
1074
1075 json_object_array_add(array, item);
1076 }
1077 json_object_object_add(parent, "includes", array);
1078 }
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001079
1080 /* top-nodes */
1081 node = NULL;
1082 array = NULL;
1083 while ((node = lys_getnext(node, NULL, module, LYS_GETNEXT_WITHCHOICE))) {
1084 if (node->nodetype & (LYS_RPC | LYS_NOTIF)) {
1085 continue;
1086 }
1087 if (!array) {
1088 array = json_object_new_array();
1089 }
1090 item = json_object_new_string(node->name);
1091 json_object_array_add(array, item);
1092 }
1093 if (array) {
1094 json_object_object_add(parent, "top-nodes", array);
1095 }
Michal Vaskoa45770b2015-11-23 15:49:41 +01001096}
1097
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001098/**
1099 * \defgroup netconf_operations NETCONF operations
1100 * The list of NETCONF operations that mod_netconf supports.
1101 * @{
1102 */
1103
1104/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001105 * \brief Send RPC and wait for reply with timeout.
1106 *
1107 * \param[in] session libnetconf session
1108 * \param[in] rpc prepared RPC message
1109 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1110 * \param[out] reply reply from the server
Michal Vaskof35ea502016-02-24 10:44:54 +01001111 * \return NC_MSG_WOULDBLOCK or NC_MSG_ERROR.
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001112 * On success, it returns NC_MSG_REPLY.
1113 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001114NC_MSG_TYPE
Michal Vaskof35ea502016-02-24 10:44:54 +01001115netconf_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 +02001116{
Michal Vaskof35ea502016-02-24 10:44:54 +01001117 uint64_t msgid;
1118 NC_MSG_TYPE ret;
1119 ret = nc_send_rpc(session, rpc, timeout, &msgid);
1120 if (ret != NC_MSG_RPC) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001121 return ret;
1122 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001123
1124 while ((ret = nc_recv_reply(session, rpc, msgid, timeout, (strict ? LYD_OPT_STRICT : 0), reply)) == NC_MSG_NOTIF);
1125
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001126 return ret;
1127}
1128
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001129/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001130 * \brief Connect to NETCONF server
1131 *
1132 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1133 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001134static unsigned int
Michal Vaskof35ea502016-02-24 10:44:54 +01001135netconf_connect(const char *host, const char *port, const char *user, const char *pass, const char *privkey)
Radek Krejci469aab82012-07-22 18:42:20 +02001136{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001137 struct nc_session* session = NULL;
1138 struct session_with_mutex *locked_session, *last_session;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001139 char *pubkey;
Radek Krejci469aab82012-07-22 18:42:20 +02001140
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001141 /* connect to the requested NETCONF server */
1142 password = (char*)pass;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001143 if (privkey) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001144 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 3);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001145 asprintf(&pubkey, "%s.pub", privkey);
Michal Vaskof35ea502016-02-24 10:44:54 +01001146 nc_client_ssh_add_keypair(pubkey, privkey);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001147 free(pubkey);
1148 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001149 nc_client_ssh_set_username(user);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001150 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Michal Vaskof35ea502016-02-24 10:44:54 +01001151 session = nc_connect_ssh(host, (unsigned short)atoi(port), NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001152 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001153
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001154 /* if connected successful, add session to the list */
1155 if (session != NULL) {
1156 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 +01001157 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001158 session = NULL;
1159 free(locked_session);
1160 locked_session = NULL;
1161 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1162 return 0;
1163 }
1164 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001165 locked_session->hello_message = NULL;
1166 locked_session->closed = 0;
1167 pthread_mutex_init(&locked_session->lock, NULL);
1168 DEBUG("Before session_lock");
1169 /* get exclusive access to sessions_list (conns) */
1170 DEBUG("LOCK wrlock %s", __func__);
1171 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001172 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001173 free(locked_session);
1174 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1175 return 0;
1176 }
1177 locked_session->ntfc_subscribed = 0;
1178 DEBUG("Add connection to the list");
1179 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001180 netconf_sessions_list = locked_session;
1181 } else {
1182 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1183 last_session->next = locked_session;
1184 locked_session->prev = last_session;
1185 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001186 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001187
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001188 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001189
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001190 /* store information about session from hello message for future usage */
1191 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001192
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001193 DEBUG("NETCONF session established");
1194 locked_session->session_key = session_key_generator;
1195 ++session_key_generator;
1196 if (session_key_generator == UINT_MAX) {
1197 session_key_generator = 1;
1198 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001199
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001200 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001201 /* unlock session list */
1202 DEBUG("UNLOCK wrlock %s", __func__);
1203 if (pthread_rwlock_unlock(&session_lock) != 0) {
1204 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1205 }
Radek Krejci469aab82012-07-22 18:42:20 +02001206
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001207 return locked_session->session_key;
1208 }
1209
1210 ERROR("Connection could not be established");
1211 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001212}
1213
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001214static int
1215close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001216{
Michal Vaskoa53ef882015-11-24 11:02:01 +01001217 int i;
1218
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001219 DEBUG("LOCK mutex %s", __func__);
1220 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1221 ERROR("Error while locking rwlock");
1222 }
1223 locked_session->ntfc_subscribed = 0;
1224 locked_session->closed = 1;
1225 if (locked_session->session != NULL) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001226 nc_session_free(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001227 locked_session->session = NULL;
1228 }
1229 DEBUG("session closed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001230 DEBUG("UNLOCK mutex %s", __func__);
1231 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1232 ERROR("Error while locking rwlock");
1233 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001234
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001235 DEBUG("closed session, disabled notif(?), wait 0.5s");
1236 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001237
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001238 /* session shouldn't be used by now */
Michal Vaskoa53ef882015-11-24 11:02:01 +01001239 for (i = 0; i < locked_session->notif_count; ++i) {
1240 free(locked_session->notifications[i].content);
1241 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001242 free(locked_session->notifications);
1243 pthread_mutex_destroy(&locked_session->lock);
1244 if (locked_session->hello_message != NULL) {
1245 json_object_put(locked_session->hello_message);
1246 locked_session->hello_message = NULL;
1247 }
1248 locked_session->session = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001249 free(locked_session);
1250 locked_session = NULL;
1251 DEBUG("NETCONF session closed, everything cleared.");
1252 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001253}
1254
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001255static int
1256netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001257{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001258 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001259
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001260 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001261
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001262 /* get exclusive (write) access to sessions_list (conns) */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001263 DEBUG("LOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02001264 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001265 ERROR("Error while locking rwlock");
1266 (*reply) = create_error_reply("Internal: Error while locking.");
1267 return EXIT_FAILURE;
1268 }
1269 /* remove session from the active sessions list -> nobody new can now work with session */
1270 for (locked_session = netconf_sessions_list;
1271 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001272 locked_session = locked_session->next);
1273
1274 if (!locked_session) {
Michal Vaskodc40b3a2016-04-11 14:36:16 +02001275 DEBUG("UNLOCK wrlock %s", __func__);
Michal Vasko642cad02016-03-17 12:31:18 +01001276 pthread_rwlock_unlock(&session_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001277 ERROR("Could not find the session %u to close.", session_key);
1278 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001279 return EXIT_FAILURE;
1280 }
1281
1282 if (!locked_session->prev) {
1283 netconf_sessions_list = netconf_sessions_list->next;
Michal Vaskof0b6fbb2016-03-08 12:04:34 +01001284 if (netconf_sessions_list) {
1285 netconf_sessions_list->prev = NULL;
1286 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001287 } else {
1288 locked_session->prev->next = locked_session->next;
1289 if (locked_session->next) {
1290 locked_session->next->prev = locked_session->prev;
1291 }
1292 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001293
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001294 DEBUG("UNLOCK wrlock %s", __func__);
1295 if (pthread_rwlock_unlock (&session_lock) != 0) {
1296 ERROR("Error while unlocking rwlock");
1297 (*reply) = create_error_reply("Internal: Error while unlocking.");
1298 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001299
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001300 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1301 return close_and_free_session(locked_session);
1302 } else {
1303 ERROR("Unknown session to close");
1304 (*reply) = create_error_reply("Internal: Unkown session to close.");
1305 return (EXIT_FAILURE);
1306 }
1307 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001308}
1309
Tomas Cejkac7929632013-10-24 19:25:15 +02001310/**
1311 * Test reply message type and return error message.
1312 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001313 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001314 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001315 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001316 * \param[out] data
1317 * \return NULL on success
1318 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001319json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001320netconf_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 +02001321{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001322 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001323
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001324 /* process the result of the operation */
1325 switch (msgt) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001326 case NC_MSG_ERROR:
1327 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001328 ERROR("mod_netconf: receiving rpc-reply failed");
1329 if (session_key) {
1330 netconf_close(session_key, &err);
1331 }
1332 if (err != NULL) {
1333 return err;
1334 }
1335 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1336 }
1337 case NC_MSG_NONE:
1338 /* there is error handled by callback */
1339 if (data != NULL) {
1340 free(*data);
1341 (*data) = NULL;
1342 }
1343 return NULL;
1344 case NC_MSG_REPLY:
Michal Vaskof35ea502016-02-24 10:44:54 +01001345 switch (reply->type) {
1346 case NC_RPL_OK:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001347 if ((data != NULL) && (*data != NULL)) {
1348 free(*data);
1349 (*data) = NULL;
1350 }
1351 return create_ok_reply();
Michal Vaskof35ea502016-02-24 10:44:54 +01001352 case NC_RPL_DATA:
1353 if (((*data) = ((struct nc_reply_data *)reply)->data) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001354 ERROR("mod_netconf: no data from reply");
1355 return create_error_reply("Internal: No data from reply received.");
1356 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001357 ((struct nc_reply_data *)reply)->data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001358 return NULL;
1359 }
1360 break;
Michal Vaskof35ea502016-02-24 10:44:54 +01001361 case NC_RPL_ERROR:
1362 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001363 if (data != NULL) {
1364 free(*data);
1365 (*data) = NULL;
1366 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001367 return create_error_reply(((struct nc_reply_error *)reply)->err[0].message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001368 default:
Michal Vaskof35ea502016-02-24 10:44:54 +01001369 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001370 if (data != NULL) {
1371 free(*data);
1372 (*data) = NULL;
1373 }
1374 return create_error_reply("Unknown type of NETCONF reply.");
1375 }
1376 break;
1377 default:
1378 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1379 if (data != NULL) {
1380 free(*data);
1381 (*data) = NULL;
1382 }
1383 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1384 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001385}
1386
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001387json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001388netconf_unlocked_op(struct nc_session *session, struct nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001389{
Michal Vaskof35ea502016-02-24 10:44:54 +01001390 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001391 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001392
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001393 /* check requests */
1394 if (rpc == NULL) {
1395 ERROR("mod_netconf: rpc is not created");
1396 return create_error_reply("Internal error: RPC is not created");
1397 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001398
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001399 if (session != NULL) {
1400 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001401 msgt = netconf_send_recv_timed(session, rpc, 50000, 0, &reply);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001402 /* process the result of the operation */
1403 return netconf_test_reply(session, 0, msgt, reply, NULL);
1404 } else {
1405 ERROR("Unknown session to process.");
1406 return create_error_reply("Internal error: Unknown session to process.");
1407 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001408}
1409
Tomas Cejkac7929632013-10-24 19:25:15 +02001410/**
1411 * Perform RPC method that returns data.
1412 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001413 * \param[in] session_id session identifier
1414 * \param[in] rpc RPC message to perform
1415 * \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 +02001416 * \return NULL on success, json object with error otherwise
1417 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001418static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001419netconf_op(unsigned int session_key, struct nc_rpc *rpc, int strict, struct lyd_node **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001420{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001421 struct session_with_mutex * locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001422 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001423 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001424 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001425 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001426
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001427 /* check requests */
1428 if (rpc == NULL) {
1429 ERROR("mod_netconf: rpc is not created");
1430 res = create_error_reply("Internal: RPC could not be created.");
1431 data = NULL;
1432 goto finished;
1433 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001434
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001435 locked_session = session_get_locked(session_key, &res);
1436 if (!locked_session) {
1437 ERROR("Unknown session or locking failed.");
1438 goto finished;
1439 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001440
Michal Vaskof35ea502016-02-24 10:44:54 +01001441 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001442
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001443 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001444 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, strict, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001445
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001446 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001447
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001448 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001449
Tomas Cejkac7929632013-10-24 19:25:15 +02001450finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001451 nc_reply_free(reply);
1452 if (received_data != NULL) {
1453 (*received_data) = data;
1454 } else {
1455 if (data != NULL) {
1456 free(data);
1457 data = NULL;
1458 }
1459 }
1460 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001461}
1462
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001463static char *
1464netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001465{
Michal Vaskof35ea502016-02-24 10:44:54 +01001466 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001467 struct session_with_mutex *locked_session;
Michal Vasko04245ee2016-03-16 09:32:59 +01001468 json_object *res = NULL, *data_cjson;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001469 enum json_tokener_error tok_err;
Michal Vasko04245ee2016-03-16 09:32:59 +01001470 char *data_json = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001471 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001472
Michal Vaskof35ea502016-02-24 10:44:54 +01001473 /* tell server to show all elements even if they have default values */
1474#ifdef HAVE_WITHDEFAULTS_TAGGED
1475 rpc = nc_rpc_getconfig(source, filter, NC_WD_MODE_ALL_TAG, NC_PARAMTYPE_CONST);
1476#else
1477 rpc = nc_rpc_getconfig(source, filter, 0, NC_PARAMTYPE_CONST);
1478#endif
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001479 if (rpc == NULL) {
1480 ERROR("mod_netconf: creating rpc request failed");
1481 return (NULL);
1482 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001483
Michal Vaskof35ea502016-02-24 10:44:54 +01001484 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001485 nc_rpc_free(rpc);
1486 if (res != NULL) {
1487 (*err) = res;
1488 } else {
1489 (*err) = NULL;
1490 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001491
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001492 if (data) {
1493 for (locked_session = netconf_sessions_list;
1494 locked_session && (locked_session->session_key != session_key);
1495 locked_session = locked_session->next);
1496 /* won't fail */
1497
Michal Vaskof35ea502016-02-24 10:44:54 +01001498 /* print data into JSON */
1499 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001500 ERROR("Printing JSON <get-config> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001501 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001502 return NULL;
1503 }
1504
1505 /* parse JSON data into cjson */
1506 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001507 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001508 if (!data_cjson) {
1509 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1510 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001511 lyd_free_withsiblings(data);
1512 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001513 return NULL;
1514 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001515 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001516
1517 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001518 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001519 node_add_metadata_recursive(sibling, NULL, data_cjson);
1520 lyd_free(sibling);
1521 }
1522
Michal Vaskof35ea502016-02-24 10:44:54 +01001523 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001524 json_object_put(data_cjson);
1525 pthread_mutex_unlock(&json_lock);
1526 }
1527
Michal Vaskof35ea502016-02-24 10:44:54 +01001528 return (data_json);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001529}
1530
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001531static char *
1532netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001533{
Michal Vaskof35ea502016-02-24 10:44:54 +01001534 struct nc_rpc *rpc;
1535 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001536 json_object *res = NULL;
Michal Vasko56ec5952016-04-05 14:30:23 +02001537 char *model_data = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001538
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001539 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001540 rpc = nc_rpc_getschema(identifier, version, format, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001541 if (rpc == NULL) {
1542 ERROR("mod_netconf: creating rpc request failed");
1543 return (NULL);
1544 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001545
Michal Vaskof35ea502016-02-24 10:44:54 +01001546 res = netconf_op(session_key, rpc, 0, &data);
1547 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001548 if (res != NULL) {
1549 (*err) = res;
1550 } else {
1551 (*err) = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001552
1553 if (data) {
Michal Vasko56ec5952016-04-05 14:30:23 +02001554 if (((struct lyd_node_anyxml *)data)->xml_struct) {
1555 lyxml_print_mem(&model_data, ((struct lyd_node_anyxml *)data)->value.xml, 0);
1556 } else {
1557 model_data = strdup(((struct lyd_node_anyxml *)data)->value.str);
1558 }
1559 if (!model_data) {
1560 ERROR("memory allocation fail (%s:%d)", __FILE__, __LINE__);
Michal Vaskof35ea502016-02-24 10:44:54 +01001561 }
1562 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001563 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001564
Michal Vaskof35ea502016-02-24 10:44:54 +01001565 return (model_data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001566}
1567
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001568static char *
1569netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001570{
Michal Vaskof35ea502016-02-24 10:44:54 +01001571 struct nc_rpc* rpc;
1572 char* data_json = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001573 json_object *res = NULL, *data_cjson;
1574 enum json_tokener_error tok_err;
1575 struct session_with_mutex *locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001576 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001577
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001578 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001579 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001580 if (rpc == NULL) {
1581 ERROR("mod_netconf: creating rpc request failed");
1582 return (NULL);
1583 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001584
Michal Vaskof35ea502016-02-24 10:44:54 +01001585 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001586 nc_rpc_free(rpc);
1587 if (res != NULL) {
1588 (*err) = res;
1589 } else {
1590 (*err) = NULL;
1591 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001592
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001593 if (data) {
1594 for (locked_session = netconf_sessions_list;
1595 locked_session && (locked_session->session_key != session_key);
1596 locked_session = locked_session->next);
1597 /* won't fail */
1598
Michal Vaskof35ea502016-02-24 10:44:54 +01001599 /* print JSON data */
1600 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001601 ERROR("Printing JSON <get> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001602 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001603 return NULL;
1604 }
1605
1606 /* parse JSON data into cjson */
1607 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001608 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001609 if (!data_cjson) {
1610 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1611 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001612 lyd_free_withsiblings(data);
1613 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001614 return NULL;
1615 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001616 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001617
1618 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001619 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001620 node_add_metadata_recursive(sibling, NULL, data_cjson);
1621 lyd_free(sibling);
1622 }
1623
Michal Vaskof35ea502016-02-24 10:44:54 +01001624 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001625 json_object_put(data_cjson);
1626 pthread_mutex_unlock(&json_lock);
1627 }
1628
Michal Vaskof35ea502016-02-24 10:44:54 +01001629 return data_json;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001630}
1631
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001632static json_object *
1633netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1634 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001635{
Michal Vaskof35ea502016-02-24 10:44:54 +01001636 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001637 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001638
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001639 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001640 rpc = nc_rpc_copy(target, uri_trg, source, (config ? config : uri_src), 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001641 if (rpc == NULL) {
1642 ERROR("mod_netconf: creating rpc request failed");
1643 return create_error_reply("Internal: Creating rpc request failed");
1644 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001645
Michal Vaskof35ea502016-02-24 10:44:54 +01001646 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001647 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001648
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001649 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001650}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001651
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001652static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001653netconf_editconfig(unsigned int session_key, NC_DATASTORE target, NC_RPC_EDIT_DFLTOP defop,
1654 NC_RPC_EDIT_ERROPT erropt, NC_RPC_EDIT_TESTOPT testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001655{
Michal Vaskof35ea502016-02-24 10:44:54 +01001656 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001657 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001658
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001659 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001660 rpc = nc_rpc_edit(target, defop, testopt, erropt, config_or_url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001661 if (rpc == NULL) {
1662 ERROR("mod_netconf: creating rpc request failed");
1663 return create_error_reply("Internal: Creating rpc request failed");
1664 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001665
Michal Vaskof35ea502016-02-24 10:44:54 +01001666 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001667 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001668
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001669 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001670}
1671
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001672static json_object *
1673netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001674{
Michal Vaskof35ea502016-02-24 10:44:54 +01001675 struct nc_rpc *rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001676 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001677
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001678 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001679 rpc = nc_rpc_kill(atoi(sid));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001680 if (rpc == NULL) {
1681 ERROR("mod_netconf: creating rpc request failed");
1682 return create_error_reply("Internal: Creating rpc request failed");
1683 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001684
Michal Vaskof35ea502016-02-24 10:44:54 +01001685 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001686 nc_rpc_free(rpc);
1687 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001688}
1689
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001690static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001691netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, struct nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001692{
Michal Vaskof35ea502016-02-24 10:44:54 +01001693 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001694 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001695
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001696 /* create requests */
1697 rpc = op_func(target);
1698 if (rpc == NULL) {
1699 ERROR("mod_netconf: creating rpc request failed");
1700 return create_error_reply("Internal: Creating rpc request failed");
1701 }
Radek Krejci2f318372012-07-26 14:22:35 +02001702
Michal Vaskof35ea502016-02-24 10:44:54 +01001703 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001704 nc_rpc_free (rpc);
1705 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001706}
1707
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001708static json_object *
1709netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001710{
Michal Vaskof35ea502016-02-24 10:44:54 +01001711 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001712 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001713 rpc = nc_rpc_delete(target, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001714 if (rpc == NULL) {
1715 ERROR("mod_netconf: creating rpc request failed");
1716 return create_error_reply("Internal: Creating rpc request failed");
1717 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001718
Michal Vaskof35ea502016-02-24 10:44:54 +01001719 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001720 nc_rpc_free (rpc);
1721 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001722}
1723
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001724static json_object *
1725netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001726{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001727 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001728}
1729
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001730static json_object *
1731netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001732{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001733 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001734}
1735
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001736static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001737netconf_generic(unsigned int session_key, const char *xml_content, struct lyd_node **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001738{
Michal Vaskof35ea502016-02-24 10:44:54 +01001739 struct nc_rpc* rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001740 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001741
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001742 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001743 rpc = nc_rpc_generic_xml(xml_content, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001744 if (rpc == NULL) {
1745 ERROR("mod_netconf: creating rpc request failed");
1746 return create_error_reply("Internal: Creating rpc request failed");
1747 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001748
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001749 /* get session where send the RPC */
Michal Vaskof35ea502016-02-24 10:44:54 +01001750 res = netconf_op(session_key, rpc, 0, data);
1751 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001752 return res;
1753}
1754
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001755static int
Michal Vaskof35ea502016-02-24 10:44:54 +01001756node_add_metadata(const struct lys_node *node, const struct lys_module *module, json_object *parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001757{
1758 struct lys_module *cur_module;
1759 json_object *meta_obj;
1760 char *obj_name;
1761
Michal Vaskoa45770b2015-11-23 15:49:41 +01001762 if (node->nodetype == LYS_INPUT) {
1763 /* silently skipped */
1764 return 0;
1765 }
1766
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001767 cur_module = node->module;
1768 if (cur_module->type) {
1769 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1770 }
1771 if (cur_module == module) {
1772 asprintf(&obj_name, "$@%s", node->name);
1773 } else {
1774 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1775 }
1776
1777 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01001778 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001779 free(obj_name);
1780 return 1;
1781 }
1782
1783 meta_obj = json_object_new_object();
1784
1785 switch (node->nodetype) {
1786 case LYS_CONTAINER:
1787 node_metadata_container((struct lys_node_container *)node, meta_obj);
1788 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01001789 case LYS_CHOICE:
1790 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
1791 break;
1792 case LYS_LEAF:
1793 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
1794 break;
1795 case LYS_LEAFLIST:
1796 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
1797 break;
1798 case LYS_LIST:
1799 node_metadata_list((struct lys_node_list *)node, meta_obj);
1800 break;
1801 case LYS_ANYXML:
1802 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
1803 break;
1804 case LYS_CASE:
1805 node_metadata_case((struct lys_node_case *)node, meta_obj);
1806 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001807 case LYS_RPC:
1808 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
1809 break;
1810 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01001811 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
1812 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001813 }
1814
1815 /* just a precaution */
1816 if (json_object_get_type(parent) != json_type_object) {
1817 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1818 free(obj_name);
1819 return 1;
1820 }
1821
1822 json_object_object_add(parent, obj_name, meta_obj);
1823 free(obj_name);
1824 return 0;
1825}
1826
1827static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001828node_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 +01001829{
1830 struct lys_module *cur_module;
1831 struct lys_node *list_schema;
1832 struct lyd_node *child, *list_item;
1833 json_object *child_json, *list_child_json;
1834 char *child_name;
1835 int list_idx;
1836
Michal Vaskoa45770b2015-11-23 15:49:41 +01001837 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1838 return;
1839 }
1840
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001841 /* add data_tree metadata */
1842 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
1843 return;
1844 }
1845
1846 /* get data_tree module */
1847 cur_module = data_tree->schema->module;
1848 if (cur_module->type) {
1849 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1850 }
1851
1852 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1853 /* print correct data_tree JSON name */
1854 if (cur_module == module) {
1855 asprintf(&child_name, "%s", data_tree->schema->name);
1856 } else {
1857 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
1858 }
1859
1860 /* go down in JSON object */
1861 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
1862 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
1863 free(child_name);
1864 return;
1865 }
1866 free(child_name);
1867
1868 if (data_tree->schema->nodetype == LYS_LIST) {
1869 if (json_object_get_type(child_json) != json_type_array) {
1870 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1871 return;
1872 }
1873 /* go down in data tree for every item, we process them all now, skip later
1874 * (metadata duplicate will be detected at the beginning of this function) */
1875 list_idx = 0;
1876 list_schema = data_tree->schema;
1877
1878 LY_TREE_FOR(data_tree, list_item) {
1879 /* another list member */
1880 if (list_item->schema == list_schema) {
1881 list_child_json = json_object_array_get_idx(child_json, list_idx);
1882 if (!list_child_json) {
1883 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
1884 return;
1885 }
1886 LY_TREE_FOR(list_item->child, child) {
1887 node_add_metadata_recursive(child, cur_module, list_child_json);
1888 }
1889
1890 ++list_idx;
1891 }
1892 }
1893 } else {
1894 if (json_object_get_type(child_json) != json_type_object) {
1895 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1896 return;
1897 }
1898 /* go down in data tree */
1899 LY_TREE_FOR(data_tree->child, child) {
1900 node_add_metadata_recursive(child, cur_module, child_json);
1901 }
1902 }
1903 }
1904}
1905
1906static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001907node_add_model_metadata(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001908{
1909 json_object *obj;
1910 char *str;
1911
1912 obj = json_object_new_object();
1913 node_metadata_model(module, obj);
1914 asprintf(&str, "$@@%s", module->name);
1915 json_object_object_add(parent, str, obj);
1916 free(str);
1917}
1918
1919static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001920node_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 +01001921{
Michal Vaskof35ea502016-02-24 10:44:54 +01001922 const struct lys_module *cur_module;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001923 struct lys_node *child;
1924 json_object *node_json;
1925 char *json_name;
1926
Michal Vaskoa45770b2015-11-23 15:49:41 +01001927 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1928 return;
1929 }
1930
1931 if (node->nodetype & LYS_USES) {
1932 cur_module = module;
1933 node_json = parent;
1934 goto children;
1935 }
1936
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001937 /* add node metadata */
1938 if (node_add_metadata(node, module, parent)) {
1939 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
1940 return;
1941 }
1942
Michal Vaskoa45770b2015-11-23 15:49:41 +01001943 /* no other metadata */
1944 if (!node->child) {
1945 return;
1946 }
1947
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001948 /* get node module */
1949 cur_module = node->module;
1950 if (cur_module->type) {
1951 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1952 }
1953
1954 /* create JSON object for child metadata */
1955 node_json = json_object_new_object();
1956 if (cur_module == module) {
1957 json_object_object_add(parent, node->name, node_json);
1958 } else {
1959 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
1960 json_object_object_add(parent, json_name, node_json);
1961 free(json_name);
1962 }
1963
Michal Vaskoa45770b2015-11-23 15:49:41 +01001964children:
Michal Vaskoea2ddd92016-03-17 15:43:58 +01001965 if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1966 LY_TREE_FOR(node->child, child) {
1967 node_add_children_with_metadata_recursive(child, cur_module, node_json);
1968 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001969 }
1970}
1971
1972static json_object *
1973libyang_query(unsigned int session_key, const char *filter, int load_children)
1974{
Michal Vaskof35ea502016-02-24 10:44:54 +01001975 const struct lys_node *node;
1976 const struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001977 struct session_with_mutex *locked_session;
1978 json_object *ret = NULL, *data;
1979
1980 locked_session = session_get_locked(session_key, &ret);
1981 if (!locked_session) {
1982 ERROR("Locking failed or session not found.");
1983 goto finish;
1984 }
1985
Michal Vaskof35ea502016-02-24 10:44:54 +01001986 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001987
Michal Vaskoa45770b2015-11-23 15:49:41 +01001988 if (filter[0] == '/') {
Michal Vasko56ec5952016-04-05 14:30:23 +02001989 node = ly_ctx_get_node(nc_session_get_ctx(locked_session->session), NULL, filter);
Michal Vaskoa45770b2015-11-23 15:49:41 +01001990 if (!node) {
1991 ret = create_error_reply("Failed to resolve XPath filter node.");
1992 goto finish;
1993 }
1994 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001995 module = ly_ctx_get_module(nc_session_get_ctx(locked_session->session), filter, NULL);
Michal Vaskoa45770b2015-11-23 15:49:41 +01001996 if (!module) {
1997 ret = create_error_reply("Failed to find model.");
1998 goto finish;
1999 }
2000 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002001
Michal Vaskoa45770b2015-11-23 15:49:41 +01002002 pthread_mutex_lock(&json_lock);
2003 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002004
Michal Vaskoa45770b2015-11-23 15:49:41 +01002005 if (module) {
2006 node_add_model_metadata(module, data);
2007 if (load_children) {
2008 LY_TREE_FOR(module->data, node) {
2009 node_add_children_with_metadata_recursive(node, NULL, data);
2010 }
2011 }
2012 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002013 if (load_children) {
2014 node_add_children_with_metadata_recursive(node, NULL, data);
2015 } else {
2016 node_add_metadata(node, NULL, data);
2017 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002018 }
2019
Michal Vaskoa45770b2015-11-23 15:49:41 +01002020 pthread_mutex_unlock(&json_lock);
2021 ret = create_data_reply(json_object_to_json_string(data));
2022 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002023
2024finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002025 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002026 return ret;
2027}
2028
2029static json_object *
2030libyang_merge(unsigned int session_key, const char *config)
2031{
2032 struct lyd_node *data_tree = NULL, *sibling;
2033 struct session_with_mutex *locked_session;
2034 json_object *ret = NULL, *data_json = NULL;
2035 enum json_tokener_error err = 0;
2036
2037 locked_session = session_get_locked(session_key, &ret);
2038 if (!locked_session) {
2039 ERROR("Locking failed or session not found.");
2040 goto finish;
2041 }
2042
Michal Vaskof35ea502016-02-24 10:44:54 +01002043 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002044
Michal Vaskof35ea502016-02-24 10:44:54 +01002045 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 +01002046 if (!data_tree) {
2047 ERROR("Creating data tree failed.");
2048 ret = create_error_reply("Failed to create data tree from JSON config.");
2049 session_unlock(locked_session);
2050 goto finish;
2051 }
2052
2053 session_unlock(locked_session);
2054
2055 pthread_mutex_lock(&json_lock);
2056 data_json = json_tokener_parse_verbose(config, &err);
2057 if (!data_json) {
2058 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2059 pthread_mutex_unlock(&json_lock);
2060 ret = create_error_reply(json_tokener_error_desc(err));
2061 goto finish;
2062 }
2063
2064 /* go simultaneously through both trees and add metadata */
2065 LY_TREE_FOR(data_tree, sibling) {
2066 node_add_metadata_recursive(sibling, NULL, data_json);
2067 }
2068 pthread_mutex_unlock(&json_lock);
2069 ret = create_data_reply(json_object_to_json_string(data_json));
2070
2071finish:
2072 LY_TREE_FOR(data_tree, sibling) {
2073 lyd_free(sibling);
2074 }
2075 json_object_put(data_json);
2076 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002077}
2078
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002079/**
2080 * @}
2081 *//* netconf_operations */
2082
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002083void
2084clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002085{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002086 switch (level) {
Michal Vasko56c02b22016-04-06 09:59:09 +02002087 case NC_VERB_ERROR:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002088 ERROR("lib ERROR: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002089 break;
2090 case NC_VERB_WARNING:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002091 ERROR("lib WARNING: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002092 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002093 case NC_VERB_VERBOSE:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002094 ERROR("lib VERBOSE: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002095 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002096 case NC_VERB_DEBUG:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002097 DEBUG("lib DEBUG: %s", msg);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002098 break;
2099 }
Michal Vasko56c02b22016-04-06 09:59:09 +02002100
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002101 if (level == NC_VERB_ERROR) {
2102 /* return global error */
Michal Vasko56c02b22016-04-06 09:59:09 +02002103 netconf_callback_error_process(msg);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002104 }
Radek Krejci469aab82012-07-22 18:42:20 +02002105}
2106
Tomas Cejka64b87482013-06-03 16:30:53 +02002107/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002108 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002109 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002110 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002111 * \return pointer to message
2112 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002113char *
2114get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002115{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002116 /* read json in chunked framing */
2117 unsigned int buffer_size = 0;
2118 ssize_t buffer_len = 0;
2119 char *buffer = NULL;
2120 char c;
2121 ssize_t ret;
2122 int i, chunk_len;
2123 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002124
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002125 while (1) {
2126 /* read chunk length */
2127 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2128 if (buffer != NULL) {
2129 free (buffer);
2130 buffer = NULL;
2131 }
2132 break;
2133 }
2134 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2135 if (buffer != NULL) {
2136 free (buffer);
2137 buffer = NULL;
2138 }
2139 break;
2140 }
2141 i=0;
2142 memset (chunk_len_str, 0, 12);
2143 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2144 if (i==0 && c == '#') {
2145 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2146 /* end but invalid */
2147 if (buffer != NULL) {
2148 free (buffer);
2149 buffer = NULL;
2150 }
2151 }
2152 /* end of message, double-loop break */
2153 goto msg_complete;
2154 }
2155 chunk_len_str[i++] = c;
2156 if (i==11) {
2157 ERROR("Message is too long, buffer for length is not big enought!!!!");
2158 break;
2159 }
2160 }
2161 if (c != '\n') {
2162 if (buffer != NULL) {
2163 free (buffer);
2164 buffer = NULL;
2165 }
2166 break;
2167 }
2168 chunk_len_str[i] = 0;
2169 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2170 if (buffer != NULL) {
2171 free (buffer);
2172 buffer = NULL;
2173 }
2174 break;
2175 }
2176 buffer_size += chunk_len+1;
2177 buffer = realloc (buffer, sizeof(char)*buffer_size);
2178 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2179 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2180 if (buffer != NULL) {
2181 free (buffer);
2182 buffer = NULL;
2183 }
2184 break;
2185 }
2186 buffer_len += ret;
2187 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002188msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002189 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002190}
2191
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002192NC_DATASTORE
2193parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002194{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002195 if (strcmp(ds, "running") == 0) {
2196 return NC_DATASTORE_RUNNING;
2197 } else if (strcmp(ds, "startup") == 0) {
2198 return NC_DATASTORE_STARTUP;
2199 } else if (strcmp(ds, "candidate") == 0) {
2200 return NC_DATASTORE_CANDIDATE;
2201 } else if (strcmp(ds, "url") == 0) {
2202 return NC_DATASTORE_URL;
2203 } else if (strcmp(ds, "config") == 0) {
2204 return NC_DATASTORE_CONFIG;
2205 }
2206 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002207}
2208
Michal Vaskof35ea502016-02-24 10:44:54 +01002209NC_RPC_EDIT_TESTOPT
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002210parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002211{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002212 if (strcmp(t, "notset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002213 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002214 } else if (strcmp(t, "testset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002215 return NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002216 } else if (strcmp(t, "set") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002217 return NC_RPC_EDIT_TESTOPT_SET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002218 } else if (strcmp(t, "test") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002219 return NC_RPC_EDIT_TESTOPT_TEST;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002220 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002221 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002222}
2223
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002224json_object *
2225create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002226{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002227 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002228
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002229 pthread_mutex_lock(&json_lock);
2230 reply = json_object_new_object();
2231 array = json_object_new_array();
2232 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2233 json_object_array_add(array, json_object_new_string(errmess));
2234 json_object_object_add(reply, "errors", array);
2235 pthread_mutex_unlock(&json_lock);
2236
2237 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002238}
2239
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002240json_object *
2241create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002242{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002243 pthread_mutex_lock(&json_lock);
2244 json_object *reply = json_object_new_object();
2245 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2246 json_object_object_add(reply, "data", json_object_new_string(data));
2247 pthread_mutex_unlock(&json_lock);
2248 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002249}
2250
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002251json_object *
2252create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002253{
Michal Vasko365dc4c2016-03-17 10:03:58 +01002254 json_object *reply;
2255
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002256 pthread_mutex_lock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002257 reply = json_object_new_object();
2258 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2259 pthread_mutex_unlock(&json_lock);
2260 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002261}
2262
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002263json_object *
2264create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002265{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002266 json_object *replies;
2267
2268 pthread_mutex_lock(&json_lock);
2269 replies = json_object_new_object();
2270 pthread_mutex_unlock(&json_lock);
2271
2272 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002273}
2274
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002275void
2276add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002277{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002278 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002279
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002280 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002281
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002282 pthread_mutex_lock(&json_lock);
2283 json_object_object_add(replies, str, reply);
2284 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002285
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002286 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002287}
2288
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002289char *
2290get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002291{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002292 json_object *js_tmp = NULL;
2293 char *res = NULL;
2294 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2295 res = strdup(json_object_get_string(js_tmp));
2296 }
2297 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002298}
2299
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002300json_object *
2301handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002302{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002303 char *host = NULL;
2304 char *port = NULL;
2305 char *user = NULL;
2306 char *pass = NULL;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002307 char *privkey = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002308 json_object *reply = NULL;
2309 unsigned int session_key = 0;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002311 DEBUG("Request: connect");
2312 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002313
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002314 host = get_param_string(request, "host");
2315 port = get_param_string(request, "port");
2316 user = get_param_string(request, "user");
2317 pass = get_param_string(request, "pass");
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002318 privkey = get_param_string(request, "privatekey");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002319
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002320 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002321
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002322 if (host == NULL) {
2323 host = "localhost";
2324 }
2325
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002326 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002327 if (user == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002328 ERROR("Cannot connect - insufficient input.");
2329 session_key = 0;
2330 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002331 session_key = netconf_connect(host, port, user, pass, privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002332 DEBUG("Session key: %u", session_key);
2333 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002334
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002335 GETSPEC_ERR_REPLY
2336
2337 pthread_mutex_lock(&json_lock);
2338 if (session_key == 0) {
2339 /* negative reply */
2340 if (err_reply == NULL) {
2341 reply = json_object_new_object();
2342 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2343 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2344 ERROR("Connection failed.");
2345 } else {
2346 /* use filled err_reply from libnetconf's callback */
2347 reply = err_reply;
2348 ERROR("Connect - error from libnetconf's callback.");
2349 }
2350 } else {
2351 /* positive reply */
2352 reply = json_object_new_object();
2353 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2354 json_object_object_add(reply, "session", json_object_new_int(session_key));
2355 }
2356 memset(pass, 0, strlen(pass));
2357 pthread_mutex_unlock(&json_lock);
2358 CHECK_AND_FREE(host);
2359 CHECK_AND_FREE(user);
2360 CHECK_AND_FREE(port);
2361 CHECK_AND_FREE(pass);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002362 CHECK_AND_FREE(privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002363 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002364}
2365
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002366json_object *
2367handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002368{
Michal Vasko53260242016-04-06 11:18:18 +02002369 json_object *reply = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002370
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002371 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002372
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002373 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2374 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2375 } else {
2376 reply = create_ok_reply();
2377 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002378
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002379 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002380}
2381
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002382json_object *
2383handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002384{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002385 char *filter = NULL;
2386 char *data = NULL;
2387 json_object *reply = NULL, *obj;
2388 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002389
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002390 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002391
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002392 pthread_mutex_lock(&json_lock);
2393 filter = get_param_string(request, "filter");
2394 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2395 pthread_mutex_unlock(&json_lock);
2396 reply = create_error_reply("Missing strict parameter.");
2397 return reply;
2398 }
2399 strict = json_object_get_boolean(obj);
2400 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002401
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002402 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2403 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2404 } else {
2405 reply = create_data_reply(data);
2406 free(data);
2407 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002408
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002409 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002410}
2411
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002412json_object *
2413handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002414{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002415 NC_DATASTORE ds_type_s = -1;
2416 char *filter = NULL;
2417 char *data = NULL;
2418 char *source = NULL;
2419 json_object *reply = NULL, *obj;
2420 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002421
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002422 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002423
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002424 pthread_mutex_lock(&json_lock);
2425 filter = get_param_string(request, "filter");
2426 source = get_param_string(request, "source");
2427 if (source != NULL) {
2428 ds_type_s = parse_datastore(source);
2429 }
2430 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2431 pthread_mutex_unlock(&json_lock);
2432 reply = create_error_reply("Missing strict parameter.");
2433 return reply;
2434 }
2435 strict = json_object_get_boolean(obj);
2436 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002437
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002438 if ((int)ds_type_s == -1) {
2439 reply = create_error_reply("Invalid source repository type requested.");
2440 goto finalize;
2441 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002442
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002443 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2444 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2445 } else {
2446 reply = create_data_reply(data);
2447 free(data);
2448 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002449
Tomas Cejka09629492014-07-10 15:58:06 +02002450finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002451 CHECK_AND_FREE(filter);
2452 CHECK_AND_FREE(source);
2453 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002454}
2455
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002456json_object *
2457handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002458{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002459 NC_DATASTORE ds_type_t = -1;
Michal Vaskof35ea502016-02-24 10:44:54 +01002460 NC_RPC_EDIT_DFLTOP defop_type = 0;
2461 NC_RPC_EDIT_ERROPT erropt_type = 0;
2462 NC_RPC_EDIT_TESTOPT testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002463 char *defop = NULL;
2464 char *erropt = NULL;
2465 char *config = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002466 char *target = NULL;
2467 char *testopt = NULL;
2468 char *urisource = NULL;
2469 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002470 struct lyd_node *content;
2471 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002472
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002473 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002474
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002475 pthread_mutex_lock(&json_lock);
2476 /* get parameters */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002477 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2478 pthread_mutex_unlock(&json_lock);
2479 reply = create_error_reply("Missing configs parameter.");
2480 goto finalize;
2481 }
2482 obj = json_object_array_get_idx(configs, idx);
2483 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002484
Michal Vaskof35ea502016-02-24 10:44:54 +01002485 target = get_param_string(request, "target");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002486 defop = get_param_string(request, "default-operation");
2487 erropt = get_param_string(request, "error-option");
2488 urisource = get_param_string(request, "uri-source");
2489 testopt = get_param_string(request, "test-option");
2490 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002491
Michal Vaskob8f5d442016-04-05 14:48:37 +02002492 if (!target) {
2493 ERROR("Missing the target parameter.");
2494 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002495 }
Michal Vaskob8f5d442016-04-05 14:48:37 +02002496 ds_type_t = parse_datastore(target);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002497
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002498 if (defop != NULL) {
2499 if (strcmp(defop, "merge") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002500 defop_type = NC_RPC_EDIT_DFLTOP_MERGE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002501 } else if (strcmp(defop, "replace") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002502 defop_type = NC_RPC_EDIT_DFLTOP_REPLACE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002503 } else if (strcmp(defop, "none") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002504 defop_type = NC_RPC_EDIT_DFLTOP_NONE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002505 } else {
2506 reply = create_error_reply("Invalid default-operation parameter.");
2507 goto finalize;
2508 }
2509 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002510 defop_type = NC_RPC_EDIT_DFLTOP_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002511 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002512
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002513 if (erropt != NULL) {
2514 if (strcmp(erropt, "continue-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002515 erropt_type = NC_RPC_EDIT_ERROPT_CONTINUE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002516 } else if (strcmp(erropt, "stop-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002517 erropt_type = NC_RPC_EDIT_ERROPT_STOP;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002518 } else if (strcmp(erropt, "rollback-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002519 erropt_type = NC_RPC_EDIT_ERROPT_ROLLBACK;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002520 } else {
2521 reply = create_error_reply("Invalid error-option parameter.");
2522 goto finalize;
2523 }
2524 } else {
2525 erropt_type = 0;
2526 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002527
Michal Vaskof35ea502016-02-24 10:44:54 +01002528 if ((config && urisource) || (!config && !urisource)) {
2529 reply = create_error_reply("Invalid config and uri-source data parameters.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002530 goto finalize;
2531 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002532
2533 if (config) {
2534 locked_session = session_get_locked(session_key, NULL);
2535 if (!locked_session) {
2536 ERROR("Unknown session or locking failed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002537 goto finalize;
2538 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002539
2540 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_EDIT);
2541 session_unlock(locked_session);
2542
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002543 if (!content) {
2544 ERROR("Failed to parse edit-config content.");
2545 goto finalize;
2546 }
2547
Michal Vaskof35ea502016-02-24 10:44:54 +01002548 free(config);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002549 config = NULL;
2550
Michal Vaskof35ea502016-02-24 10:44:54 +01002551 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2552 lyd_free_withsiblings(content);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002553 if (!config) {
2554 ERROR("Failed to print edit-config content.");
2555 goto finalize;
2556 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002557 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002558 config = urisource;
2559 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002560
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002561 if (testopt != NULL) {
2562 testopt_type = parse_testopt(testopt);
2563 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002564 testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002565 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002566
Michal Vaskof35ea502016-02-24 10:44:54 +01002567 reply = netconf_editconfig(session_key, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002568
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002569 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002570
Tomas Cejka09629492014-07-10 15:58:06 +02002571finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002572 CHECK_AND_FREE(defop);
2573 CHECK_AND_FREE(erropt);
2574 CHECK_AND_FREE(config);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002575 CHECK_AND_FREE(urisource);
2576 CHECK_AND_FREE(target);
2577 CHECK_AND_FREE(testopt);
2578
2579 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002580}
2581
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002582json_object *
2583handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002584{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002585 NC_DATASTORE ds_type_s = -1;
2586 NC_DATASTORE ds_type_t = -1;
2587 char *config = NULL;
2588 char *target = NULL;
2589 char *source = NULL;
2590 char *uri_src = NULL;
2591 char *uri_trg = NULL;
2592 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002593 struct lyd_node *content;
2594 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002595
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002596 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002597
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002598 /* get parameters */
2599 pthread_mutex_lock(&json_lock);
2600 target = get_param_string(request, "target");
2601 source = get_param_string(request, "source");
2602 uri_src = get_param_string(request, "uri-source");
2603 uri_trg = get_param_string(request, "uri-target");
2604 if (!strcmp(source, "config")) {
2605 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2606 pthread_mutex_unlock(&json_lock);
2607 reply = create_error_reply("Missing configs parameter.");
2608 goto finalize;
2609 }
2610 obj = json_object_array_get_idx(configs, idx);
2611 if (!obj) {
2612 pthread_mutex_unlock(&json_lock);
2613 reply = create_error_reply("Configs array parameter shorter than sessions.");
2614 goto finalize;
2615 }
2616 config = strdup(json_object_get_string(obj));
2617 }
2618 pthread_mutex_unlock(&json_lock);
2619
2620 if (target != NULL) {
2621 ds_type_t = parse_datastore(target);
2622 }
2623 if (source != NULL) {
2624 ds_type_s = parse_datastore(source);
2625 }
2626
2627 if ((int)ds_type_s == -1) {
2628 /* invalid source datastore specified */
2629 reply = create_error_reply("Invalid source repository type requested.");
2630 goto finalize;
2631 }
2632
2633 if ((int)ds_type_t == -1) {
2634 /* invalid target datastore specified */
2635 reply = create_error_reply("Invalid target repository type requested.");
2636 goto finalize;
2637 }
2638
2639 if (ds_type_s == NC_DATASTORE_URL) {
2640 if (uri_src == NULL) {
2641 uri_src = "";
2642 }
2643 }
2644 if (ds_type_t == NC_DATASTORE_URL) {
2645 if (uri_trg == NULL) {
2646 uri_trg = "";
2647 }
2648 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002649
2650 if (config) {
2651 locked_session = session_get_locked(session_key, NULL);
2652 if (!locked_session) {
2653 ERROR("Unknown session or locking failed.");
2654 goto finalize;
2655 }
2656
2657 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_CONFIG);
2658 session_unlock(locked_session);
2659
2660 free(config);
2661 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2662 lyd_free_withsiblings(content);
2663 }
2664
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002665 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2666
2667 CHECK_ERR_SET_REPLY
2668
2669finalize:
2670 CHECK_AND_FREE(config);
2671 CHECK_AND_FREE(target);
2672 CHECK_AND_FREE(source);
2673 CHECK_AND_FREE(uri_src);
2674 CHECK_AND_FREE(uri_trg);
2675
2676 return reply;
2677}
2678
2679json_object *
2680handle_op_deleteconfig(json_object *request, unsigned int session_key)
2681{
2682 json_object *reply;
2683 NC_DATASTORE ds_type = -1;
2684 char *target, *url;
2685
2686 DEBUG("Request: delete-config (session %u)", session_key);
2687
2688 pthread_mutex_lock(&json_lock);
2689 target = get_param_string(request, "target");
2690 url = get_param_string(request, "url");
2691 pthread_mutex_unlock(&json_lock);
2692
2693 if (target != NULL) {
2694 ds_type = parse_datastore(target);
2695 }
2696 if ((int)ds_type == -1) {
2697 reply = create_error_reply("Invalid target repository type requested.");
2698 goto finalize;
2699 }
2700 if (ds_type == NC_DATASTORE_URL) {
2701 if (!url) {
2702 url = "";
2703 }
2704 }
2705
2706 reply = netconf_deleteconfig(session_key, ds_type, url);
2707
2708 CHECK_ERR_SET_REPLY
2709 if (reply == NULL) {
2710 reply = create_ok_reply();
2711 }
2712
2713finalize:
2714 CHECK_AND_FREE(target);
2715 CHECK_AND_FREE(url);
2716 return reply;
2717}
2718
2719json_object *
2720handle_op_lock(json_object *request, unsigned int session_key)
2721{
2722 json_object *reply;
2723 NC_DATASTORE ds_type = -1;
2724 char *target;
2725
2726 DEBUG("Request: lock (session %u)", session_key);
2727
2728 pthread_mutex_lock(&json_lock);
2729 target = get_param_string(request, "target");
2730 pthread_mutex_unlock(&json_lock);
2731
2732 if (target != NULL) {
2733 ds_type = parse_datastore(target);
2734 }
2735 if ((int)ds_type == -1) {
2736 reply = create_error_reply("Invalid target repository type requested.");
2737 goto finalize;
2738 }
2739
2740 reply = netconf_lock(session_key, ds_type);
2741
2742 CHECK_ERR_SET_REPLY
2743 if (reply == NULL) {
2744 reply = create_ok_reply();
2745 }
2746
2747finalize:
2748 CHECK_AND_FREE(target);
2749 return reply;
2750}
2751
2752json_object *
2753handle_op_unlock(json_object *request, unsigned int session_key)
2754{
2755 json_object *reply;
2756 NC_DATASTORE ds_type = -1;
2757 char *target;
2758
2759 DEBUG("Request: unlock (session %u)", session_key);
2760
2761 pthread_mutex_lock(&json_lock);
2762 target = get_param_string(request, "target");
2763 pthread_mutex_unlock(&json_lock);
2764
2765 if (target != NULL) {
2766 ds_type = parse_datastore(target);
2767 }
2768 if ((int)ds_type == -1) {
2769 reply = create_error_reply("Invalid target repository type requested.");
2770 goto finalize;
2771 }
2772
2773 reply = netconf_unlock(session_key, ds_type);
2774
2775 CHECK_ERR_SET_REPLY
2776 if (reply == NULL) {
2777 reply = create_ok_reply();
2778 }
2779
2780finalize:
2781 CHECK_AND_FREE(target);
2782 return reply;
2783}
2784
2785json_object *
2786handle_op_kill(json_object *request, unsigned int session_key)
2787{
2788 json_object *reply = NULL;
2789 char *sid = NULL;
2790
2791 DEBUG("Request: kill-session (session %u)", session_key);
2792
2793 pthread_mutex_lock(&json_lock);
2794 sid = get_param_string(request, "session-id");
2795 pthread_mutex_unlock(&json_lock);
2796
2797 if (sid == NULL) {
2798 reply = create_error_reply("Missing session-id parameter.");
2799 goto finalize;
2800 }
2801
2802 reply = netconf_killsession(session_key, sid);
2803
2804 CHECK_ERR_SET_REPLY
2805
2806finalize:
2807 CHECK_AND_FREE(sid);
2808 return reply;
2809}
2810
2811json_object *
2812handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2813{
2814 json_object *reply = NULL;
2815 struct session_with_mutex *locked_session = NULL;
2816 DEBUG("Request: get info about session %u", session_key);
2817
2818 DEBUG("LOCK wrlock %s", __func__);
2819 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2820 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2821 }
2822
2823 for (locked_session = netconf_sessions_list;
2824 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002825 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002826 if (locked_session != NULL) {
2827 DEBUG("LOCK mutex %s", __func__);
2828 pthread_mutex_lock(&locked_session->lock);
2829 DEBUG("UNLOCK wrlock %s", __func__);
2830 if (pthread_rwlock_unlock(&session_lock) != 0) {
2831 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2832 }
2833 if (locked_session->hello_message != NULL) {
2834 reply = locked_session->hello_message;
2835 } else {
2836 reply = create_error_reply("Invalid session identifier.");
2837 }
2838 DEBUG("UNLOCK mutex %s", __func__);
2839 pthread_mutex_unlock(&locked_session->lock);
2840 } else {
2841 DEBUG("UNLOCK wrlock %s", __func__);
2842 if (pthread_rwlock_unlock(&session_lock) != 0) {
2843 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2844 }
2845 reply = create_error_reply("Invalid session identifier.");
2846 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002847
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002848 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002849}
2850
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002851json_object *
2852handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002853{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002854 json_object *reply = NULL, *contents, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002855 char *content = NULL, *str;
2856 struct lyd_node *data = NULL, *node_content;
2857 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002858
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002859 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002860
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002861 pthread_mutex_lock(&json_lock);
2862 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2863 pthread_mutex_unlock(&json_lock);
2864 reply = create_error_reply("Missing contents parameter.");
2865 goto finalize;
2866 }
2867 obj = json_object_array_get_idx(contents, idx);
2868 if (!obj) {
2869 pthread_mutex_unlock(&json_lock);
2870 reply = create_error_reply("Contents array parameter shorter than sessions.");
2871 goto finalize;
2872 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002873 content = strdup(json_object_get_string(obj));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002874 pthread_mutex_unlock(&json_lock);
2875
Michal Vaskof35ea502016-02-24 10:44:54 +01002876 locked_session = session_get_locked(session_key, NULL);
2877 if (!locked_session) {
2878 ERROR("Unknown session or locking failed.");
2879 goto finalize;
2880 }
2881
2882 node_content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), content, LYD_JSON, LYD_OPT_RPC);
2883 session_unlock(locked_session);
2884
2885 free(content);
2886 lyd_print_mem(&content, node_content, LYD_XML, LYP_WITHSIBLINGS);
2887 lyd_free_withsiblings(node_content);
2888
2889 reply = netconf_generic(session_key, content, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002890 if (reply == NULL) {
2891 GETSPEC_ERR_REPLY
2892 if (err_reply != NULL) {
2893 /* use filled err_reply from libnetconf's callback */
2894 reply = err_reply;
2895 }
2896 } else {
2897 if (data == NULL) {
2898 pthread_mutex_lock(&json_lock);
2899 reply = json_object_new_object();
2900 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2901 pthread_mutex_unlock(&json_lock);
2902 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002903 lyd_print_mem(&str, data, LYD_JSON, LYP_WITHSIBLINGS);
2904 lyd_free_withsiblings(data);
2905 reply = create_data_reply(str);
2906 free(str);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002907 }
2908 }
2909
2910finalize:
Michal Vaskof35ea502016-02-24 10:44:54 +01002911 CHECK_AND_FREE(content);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002912 return reply;
2913}
2914
2915json_object *
2916handle_op_getschema(json_object *request, unsigned int session_key)
2917{
2918 char *data = NULL;
2919 char *identifier = NULL;
2920 char *version = NULL;
2921 char *format = NULL;
2922 json_object *reply = NULL;
2923
2924 DEBUG("Request: get-schema (session %u)", session_key);
2925
2926 pthread_mutex_lock(&json_lock);
2927 identifier = get_param_string(request, "identifier");
2928 version = get_param_string(request, "version");
2929 format = get_param_string(request, "format");
2930 pthread_mutex_unlock(&json_lock);
2931
2932 if (identifier == NULL) {
2933 reply = create_error_reply("No identifier for get-schema supplied.");
2934 goto finalize;
2935 }
2936
2937 DEBUG("get-schema(version: %s, format: %s)", version, format);
2938 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
2939 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
2940 } else {
2941 reply = create_data_reply(data);
2942 free(data);
2943 }
2944
2945finalize:
2946 CHECK_AND_FREE(identifier);
2947 CHECK_AND_FREE(version);
2948 CHECK_AND_FREE(format);
2949 return reply;
2950}
2951
2952json_object *
2953handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
2954{
2955 struct nc_session *temp_session = NULL;
2956 struct session_with_mutex * locked_session = NULL;
2957 json_object *reply = NULL;
2958
2959 DEBUG("Request: reload hello (session %u)", session_key);
2960
2961 DEBUG("LOCK wrlock %s", __func__);
2962 if (pthread_rwlock_wrlock(&session_lock) != 0) {
2963 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2964 return NULL;
2965 }
2966
2967 for (locked_session = netconf_sessions_list;
2968 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002969 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002970 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
2971 DEBUG("LOCK mutex %s", __func__);
2972 pthread_mutex_lock(&locked_session->lock);
2973 DEBUG("creating temporary NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01002974 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002975 if (temp_session != NULL) {
2976 prepare_status_message(locked_session, temp_session);
2977 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01002978 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002979 temp_session = NULL;
2980 } else {
2981 DEBUG("Reload hello failed due to channel establishment");
2982 reply = create_error_reply("Reload was unsuccessful, connection failed.");
2983 }
2984 DEBUG("UNLOCK mutex %s", __func__);
2985 pthread_mutex_unlock(&locked_session->lock);
2986 DEBUG("UNLOCK wrlock %s", __func__);
2987 if (pthread_rwlock_unlock(&session_lock) != 0) {
2988 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2989 }
2990 } else {
2991 DEBUG("UNLOCK wrlock %s", __func__);
2992 if (pthread_rwlock_unlock(&session_lock) != 0) {
2993 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2994 }
2995 reply = create_error_reply("Invalid session identifier.");
2996 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002997
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002998 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
2999 reply = locked_session->hello_message;
3000 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003001
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003002 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003003}
3004
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003005void
Michal Vaskof35ea502016-02-24 10:44:54 +01003006notification_history(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003007{
Michal Vaskof35ea502016-02-24 10:44:54 +01003008 time_t eventtime;
3009 char *content;
3010 (void)session;
3011
3012 eventtime = nc_datetime2time(notif->datetime);
3013
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003014 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3015 if (notif_history_array == NULL) {
3016 ERROR("No list of notification history found.");
3017 return;
3018 }
3019 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3020 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01003021 json_object *notif_obj = json_object_new_object();
3022 if (notif_obj == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003023 ERROR("Could not allocate memory for notification (json).");
3024 goto failed;
3025 }
Michal Vaskof35ea502016-02-24 10:44:54 +01003026 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
3027
3028 json_object_object_add(notif_obj, "eventtime", json_object_new_int64(eventtime));
3029 json_object_object_add(notif_obj, "content", json_object_new_string(content));
3030
3031 free(content);
3032
3033 json_object_array_add(notif_history_array, notif_obj);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003034failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003035 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003036}
3037
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003038json_object *
3039handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003040{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003041 json_object *reply = NULL;
3042 json_object *js_tmp = NULL;
3043 struct session_with_mutex *locked_session = NULL;
3044 struct nc_session *temp_session = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003045 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003046 time_t start = 0;
3047 time_t stop = 0;
3048 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003049
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003050 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003051
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003052 pthread_mutex_lock(&json_lock);
3053 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3054 from = json_object_get_int64(js_tmp);
3055 }
3056 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3057 to = json_object_get_int64(js_tmp);
3058 }
3059 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003060
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003061 start = time(NULL) + from;
3062 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003063
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003064 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003065
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003066 DEBUG("LOCK wrlock %s", __func__);
3067 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3068 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3069 reply = create_error_reply("Internal lock failed.");
3070 goto finalize;
3071 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003072
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003073 for (locked_session = netconf_sessions_list;
3074 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003075 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003076 if (locked_session != NULL) {
3077 DEBUG("LOCK mutex %s", __func__);
3078 pthread_mutex_lock(&locked_session->lock);
3079 DEBUG("UNLOCK wrlock %s", __func__);
3080 if (pthread_rwlock_unlock(&session_lock) != 0) {
3081 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3082 }
3083 DEBUG("creating temporal NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01003084 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003085 if (temp_session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003086 rpc = nc_rpc_subscribe(NULL, NULL, nc_time2datetime(start, NULL), nc_time2datetime(stop, NULL), NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003087 if (rpc == NULL) {
3088 DEBUG("UNLOCK mutex %s", __func__);
3089 pthread_mutex_unlock(&locked_session->lock);
3090 DEBUG("notifications: creating an rpc request failed.");
3091 reply = create_error_reply("notifications: creating an rpc request failed.");
3092 goto finalize;
3093 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003094
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003095 DEBUG("Send NC subscribe.");
3096 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3097 json_object *res = netconf_unlocked_op(temp_session, rpc);
3098 if (res != NULL) {
3099 DEBUG("UNLOCK mutex %s", __func__);
3100 pthread_mutex_unlock(&locked_session->lock);
3101 DEBUG("Subscription RPC failed.");
3102 reply = res;
3103 goto finalize;
3104 }
3105 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003106
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003107 DEBUG("UNLOCK mutex %s", __func__);
3108 pthread_mutex_unlock(&locked_session->lock);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003109 DEBUG("LOCK ntf mutex %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003110 pthread_mutex_lock(&ntf_history_lock);
3111 pthread_mutex_lock(&json_lock);
3112 json_object *notif_history_array = json_object_new_array();
3113 pthread_mutex_unlock(&json_lock);
3114 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3115 ERROR("notif_history: cannot set thread-specific hash value.");
3116 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003117
Michal Vaskof35ea502016-02-24 10:44:54 +01003118 nc_recv_notif_dispatch(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003119
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003120 pthread_mutex_lock(&json_lock);
3121 reply = json_object_new_object();
3122 json_object_object_add(reply, "notifications", notif_history_array);
3123 //json_object_put(notif_history_array);
3124 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003125
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003126 DEBUG("UNLOCK ntf mutex %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003127 pthread_mutex_unlock(&ntf_history_lock);
3128 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01003129 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003130 temp_session = NULL;
3131 } else {
3132 DEBUG("UNLOCK mutex %s", __func__);
3133 pthread_mutex_unlock(&locked_session->lock);
3134 DEBUG("Get history of notification failed due to channel establishment");
3135 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3136 }
3137 } else {
3138 DEBUG("UNLOCK wrlock %s", __func__);
3139 if (pthread_rwlock_unlock(&session_lock) != 0) {
3140 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3141 }
3142 reply = create_error_reply("Invalid session identifier.");
3143 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003144
Tomas Cejka09629492014-07-10 15:58:06 +02003145finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003146 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003147}
3148
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003149json_object *
3150handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003151{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003152 json_object *reply = NULL;
3153 char *target = NULL;
3154 char *url = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003155 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003156 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003157
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003158 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003159
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003160 pthread_mutex_lock(&json_lock);
3161 target = get_param_string(request, "target");
3162 url = get_param_string(request, "url");
3163 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003164
3165
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003166 if (target == NULL) {
3167 reply = create_error_reply("Missing target parameter.");
3168 goto finalize;
3169 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003170
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003171 /* validation */
3172 target_ds = parse_datastore(target);
Michal Vaskof35ea502016-02-24 10:44:54 +01003173 rpc = nc_rpc_validate(target_ds, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003174 if (rpc == NULL) {
3175 DEBUG("mod_netconf: creating rpc request failed");
3176 reply = create_error_reply("Creation of RPC request failed.");
3177 goto finalize;
3178 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003179
Michal Vaskof35ea502016-02-24 10:44:54 +01003180 if ((reply = netconf_op(session_key, rpc, 0, NULL)) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003181 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003182
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003183 if (reply == NULL) {
3184 DEBUG("Request: validation ok.");
3185 reply = create_ok_reply();
3186 }
3187 }
3188 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003189
Tomas Cejka09629492014-07-10 15:58:06 +02003190finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003191 CHECK_AND_FREE(target);
3192 CHECK_AND_FREE(url);
3193 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003194}
3195
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003196json_object *
3197handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003198{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003199 json_object *reply = NULL, *filters, *obj;
3200 char *filter = NULL;
3201 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003202
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003203 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003204
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003205 pthread_mutex_lock(&json_lock);
3206 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3207 pthread_mutex_unlock(&json_lock);
3208 reply = create_error_reply("Missing filters parameter.");
3209 goto finalize;
3210 }
3211 obj = json_object_array_get_idx(filters, idx);
3212 if (!obj) {
3213 pthread_mutex_unlock(&json_lock);
3214 reply = create_error_reply("Filters array parameter shorter than sessions.");
3215 goto finalize;
3216 }
3217 filter = strdup(json_object_get_string(obj));
3218 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3219 load_children = json_object_get_boolean(obj);
3220 }
3221 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003222
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003223 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003224
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003225 CHECK_ERR_SET_REPLY
3226 if (!reply) {
3227 reply = create_error_reply("Query failed.");
3228 }
David Kupka8e60a372012-09-04 09:15:20 +02003229
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003230finalize:
3231 CHECK_AND_FREE(filter);
3232 return reply;
3233}
David Kupka8e60a372012-09-04 09:15:20 +02003234
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003235json_object *
3236handle_op_merge(json_object *request, unsigned int session_key, int idx)
3237{
3238 json_object *reply = NULL, *configs, *obj;
3239 char *config = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003240 struct lyd_node *content;
3241 struct session_with_mutex *locked_session;
David Kupka8e60a372012-09-04 09:15:20 +02003242
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003243 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003244
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003245 pthread_mutex_lock(&json_lock);
3246 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3247 pthread_mutex_unlock(&json_lock);
3248 reply = create_error_reply("Missing configurations parameter.");
3249 goto finalize;
3250 }
3251 obj = json_object_array_get_idx(configs, idx);
3252 if (!obj) {
3253 pthread_mutex_unlock(&json_lock);
3254 reply = create_error_reply("Filters array parameter shorter than sessions.");
3255 goto finalize;
3256 }
3257 config = strdup(json_object_get_string(obj));
3258 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003259
Michal Vaskof35ea502016-02-24 10:44:54 +01003260 locked_session = session_get_locked(session_key, NULL);
3261 if (!locked_session) {
3262 ERROR("Unknown session or locking failed.");
3263 goto finalize;
3264 }
3265
3266 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_DATA);
3267 session_unlock(locked_session);
3268
3269 free(config);
3270 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
3271 lyd_free_withsiblings(content);
3272
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003273 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003274
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003275 CHECK_ERR_SET_REPLY
3276 if (!reply) {
3277 reply = create_error_reply("Merge failed.");
3278 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003279
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003280finalize:
3281 CHECK_AND_FREE(config);
3282 return reply;
3283}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003284
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003285void *
3286thread_routine(void *arg)
3287{
3288 void *retval = NULL;
3289 struct pollfd fds;
3290 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3291 json_object *js_tmp = NULL;
Michal Vaskodf280a22016-03-17 09:19:53 +01003292 int operation = (-1), count, i, sent;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003293 int status = 0;
3294 const char *msgtext;
3295 unsigned int session_key = 0;
3296 char *chunked_out_msg = NULL;
3297 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003298
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003299 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003300
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003301 /* init thread specific err_reply memory */
3302 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003303
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003304 while (!isterminated) {
3305 fds.fd = client;
3306 fds.events = POLLIN;
3307 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003308
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003309 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003311 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3312 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3313 continue;
3314 } else if (status < 0) {
3315 /* 0: poll time outed
3316 * close socket and ignore this request from the client, it can try it again
3317 * -1: poll failed
3318 * something wrong happend, close this socket and wait for another request
3319 */
3320 close(client);
3321 break;
3322 }
3323 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003324
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003325 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003326
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003327 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3328 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3329 /* close client's socket (it's probably already closed by client */
3330 close(client);
3331 break;
3332 }
Tomas Cejka09629492014-07-10 15:58:06 +02003333
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003334 buffer = get_framed_message(client);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003335 if (buffer != NULL) {
Michal Vaskoe7d1bbc2016-04-05 16:09:29 +02003336 DEBUG("Received message:\n%s\n", buffer);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003337 enum json_tokener_error jerr;
3338 pthread_mutex_lock(&json_lock);
3339 request = json_tokener_parse_verbose(buffer, &jerr);
3340 if (jerr != json_tokener_success) {
3341 ERROR("JSON parsing error");
3342 pthread_mutex_unlock(&json_lock);
3343 continue;
3344 }
3345
3346 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3347 operation = json_object_get_int(js_tmp);
3348 }
3349 pthread_mutex_unlock(&json_lock);
3350 if (operation == -1) {
3351 replies = create_replies();
3352 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3353 goto send_reply;
3354 }
3355
3356 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3357 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3358 replies = create_replies();
3359 add_reply(replies, create_error_reply("Operation not supported."), 0);
3360 goto send_reply;
3361 }
3362
3363 DEBUG("operation %d", operation);
3364
3365 /* null global JSON error-reply */
3366 clean_err_reply();
3367
3368 /* clean replies envelope */
3369 if (replies != NULL) {
3370 pthread_mutex_lock(&json_lock);
3371 json_object_put(replies);
3372 pthread_mutex_unlock(&json_lock);
3373 }
3374 replies = create_replies();
3375
3376 if (operation == MSG_CONNECT) {
3377 count = 1;
3378 } else {
3379 pthread_mutex_lock(&json_lock);
3380 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
Michal Vasko642cad02016-03-17 12:31:18 +01003381 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003382 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3383 goto send_reply;
3384 }
3385 count = json_object_array_length(sessions);
3386 pthread_mutex_unlock(&json_lock);
3387 }
3388
3389 for (i = 0; i < count; ++i) {
3390 if (operation != MSG_CONNECT) {
3391 js_tmp = json_object_array_get_idx(sessions, i);
3392 session_key = json_object_get_int(js_tmp);
3393 }
3394
3395 /* process required operation */
Michal Vasko977bad92016-03-15 16:20:51 +01003396 reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003397 switch (operation) {
3398 case MSG_CONNECT:
3399 reply = handle_op_connect(request);
3400 break;
3401 case MSG_DISCONNECT:
3402 reply = handle_op_disconnect(request, session_key);
3403 break;
3404 case MSG_GET:
3405 reply = handle_op_get(request, session_key);
3406 break;
3407 case MSG_GETCONFIG:
3408 reply = handle_op_getconfig(request, session_key);
3409 break;
3410 case MSG_EDITCONFIG:
3411 reply = handle_op_editconfig(request, session_key, i);
3412 break;
3413 case MSG_COPYCONFIG:
3414 reply = handle_op_copyconfig(request, session_key, i);
3415 break;
3416 case MSG_DELETECONFIG:
3417 reply = handle_op_deleteconfig(request, session_key);
3418 break;
3419 case MSG_LOCK:
3420 reply = handle_op_lock(request, session_key);
3421 break;
3422 case MSG_UNLOCK:
3423 reply = handle_op_unlock(request, session_key);
3424 break;
3425 case MSG_KILL:
3426 reply = handle_op_kill(request, session_key);
3427 break;
3428 case MSG_INFO:
3429 reply = handle_op_info(request, session_key);
3430 break;
3431 case MSG_GENERIC:
3432 reply = handle_op_generic(request, session_key, i);
3433 break;
3434 case MSG_GETSCHEMA:
3435 reply = handle_op_getschema(request, session_key);
3436 break;
3437 case MSG_RELOADHELLO:
3438 reply = handle_op_reloadhello(request, session_key);
3439 break;
3440 case MSG_NTF_GETHISTORY:
3441 reply = handle_op_ntfgethistory(request, session_key);
3442 break;
3443 case MSG_VALIDATE:
3444 reply = handle_op_validate(request, session_key);
3445 break;
3446 case SCH_QUERY:
3447 reply = handle_op_query(request, session_key, i);
3448 break;
3449 case SCH_MERGE:
3450 reply = handle_op_merge(request, session_key, i);
3451 break;
3452 }
3453
3454 add_reply(replies, reply, session_key);
3455 }
3456
3457 /* free parameters */
3458 operation = (-1);
3459
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003460 if (request != NULL) {
3461 pthread_mutex_lock(&json_lock);
3462 json_object_put(request);
3463 pthread_mutex_unlock(&json_lock);
Michal Vasko365dc4c2016-03-17 10:03:58 +01003464 request = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003465 }
Tomas Cejka09629492014-07-10 15:58:06 +02003466
3467send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003468 /* send reply to caller */
3469 if (replies) {
3470 pthread_mutex_lock(&json_lock);
3471 msgtext = json_object_to_json_string(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003472 pthread_mutex_unlock(&json_lock);
Michal Vaskof849e4e2016-04-06 10:00:08 +02003473 DEBUG("Sending message:\n%s\n", msgtext);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003474 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3475 if (buffer != NULL) {
3476 free(buffer);
3477 buffer = NULL;
3478 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003479 break;
3480 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003481
Michal Vaskodf280a22016-03-17 09:19:53 +01003482 i = 0;
3483 sent = 0;
3484 count = strlen(chunked_out_msg) + 1;
3485 while (count && ((i = send(client, chunked_out_msg + sent, count, 0)) != -1)) {
3486 sent += i;
3487 count -= i;
3488 }
3489 if (i == -1) {
3490 ERROR("Sending message failed (%s).", strerror(errno));
3491 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003492 pthread_mutex_lock(&json_lock);
3493 json_object_put(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003494 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003495 replies = NULL;
Michal Vasko2314d402016-04-06 13:24:06 +02003496
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003497 CHECK_AND_FREE(chunked_out_msg);
3498 chunked_out_msg = NULL;
3499 if (buffer) {
3500 free(buffer);
3501 buffer = NULL;
3502 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003503 clean_err_reply();
3504 } else {
3505 ERROR("Reply is NULL, shouldn't be...");
3506 continue;
3507 }
3508 }
3509 }
3510 free(arg);
3511 free_err_reply();
Michal Vaskod0205992016-03-17 10:04:49 +01003512 nc_thread_destroy();
David Kupka8e60a372012-09-04 09:15:20 +02003513
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003514 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003515}
3516
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003517/**
3518 * \brief Close all open NETCONF sessions.
3519 *
3520 * During termination of mod_netconf, it is useful to close all remaining
3521 * sessions. This function iterates over the list of sessions and close them
3522 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003523 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003524static void
3525close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003526{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003527 struct session_with_mutex *locked_session, *next_session;
3528 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003529
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003530 /* get exclusive access to sessions_list (conns) */
3531 DEBUG("LOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003532 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003533 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3534 return;
3535 }
3536 for (next_session = netconf_sessions_list; next_session;) {
3537 locked_session = next_session;
3538 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003539
Michal Vaskoc3146782015-11-04 14:46:41 +01003540 /* close_and_free_session handles locking on its own */
Michal Vaskof35ea502016-02-24 10:44:54 +01003541 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 +01003542 close_and_free_session(locked_session);
3543 }
3544 netconf_sessions_list = NULL;
3545
3546 /* get exclusive access to sessions_list (conns) */
3547 DEBUG("UNLOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003548 if (pthread_rwlock_unlock(&session_lock) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003549 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3550 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003551}
3552
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003553static void
3554check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003555{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003556 struct nc_session *ns = NULL;
3557 struct session_with_mutex *locked_session = NULL;
3558 time_t current_time = time(NULL);
3559 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003560
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003561 /* get exclusive access to sessions_list (conns) */
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003562 DEBUG("LOCK wrlock %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003563 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3564 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3565 return;
3566 }
3567 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3568 ns = locked_session->session;
3569 if (ns == NULL) {
3570 continue;
3571 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003572 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003573 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 +02003574
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003575 /* close_and_free_session handles locking on its own */
3576 close_and_free_session(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003577 }
3578 }
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003579 DEBUG("UNLOCK wrlock %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003580 if (pthread_rwlock_unlock(&session_lock) != 0) {
3581 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3582 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003583}
3584
3585
3586/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003587 * This is actually implementation of NETCONF client
3588 * - requests are received from UNIX socket in the predefined format
3589 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003590 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003591 *
3592 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003593static void
3594forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003595{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003596 struct timeval tv;
3597 struct sockaddr_un local, remote;
3598 int lsock, client, ret, i, pthread_count = 0;
3599 unsigned int olds = 0, timediff = 0;
3600 socklen_t len;
3601 struct pass_to_thread *arg;
3602 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3603 struct timespec maxtime;
3604 pthread_rwlockattr_t lock_attrs;
3605 #ifdef WITH_NOTIFICATIONS
3606 char use_notifications = 0;
3607 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003608
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003609 /* wait at most 5 seconds for every thread to terminate */
3610 maxtime.tv_sec = 5;
3611 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003612
Tomas Cejka04e08f42014-03-27 19:52:34 +01003613#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003614 /* change uid and gid of process for security reasons */
3615 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003616#else
3617# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003618 if (strlen(SU_GROUP) > 0) {
3619 struct group *g = getgrnam(SU_GROUP);
3620 if (g == NULL) {
3621 ERROR("GID (%s) was not found.", SU_GROUP);
3622 return;
3623 }
3624 if (setgid(g->gr_gid) != 0) {
3625 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3626 return;
3627 }
3628 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003629# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003630 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003631# endif
3632# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003633 if (strlen(SU_USER) > 0) {
3634 struct passwd *p = getpwnam(SU_USER);
3635 if (p == NULL) {
3636 ERROR("UID (%s) was not found.", SU_USER);
3637 return;
3638 }
3639 if (setuid(p->pw_uid) != 0) {
3640 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3641 return;
3642 }
3643 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003644# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003645 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003646# endif
3647#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003648
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003649 /* try to remove if exists */
3650 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003651
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003652 /* create listening UNIX socket to accept incoming connections */
3653 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3654 ERROR("Creating socket failed (%s)", strerror(errno));
3655 goto error_exit;
3656 }
Radek Krejci469aab82012-07-22 18:42:20 +02003657
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003658 local.sun_family = AF_UNIX;
3659 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3660 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003661
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003662 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3663 if (errno == EADDRINUSE) {
3664 ERROR("mod_netconf socket address already in use");
3665 goto error_exit;
3666 }
3667 ERROR("Binding socket failed (%s)", strerror(errno));
3668 goto error_exit;
3669 }
Radek Krejci469aab82012-07-22 18:42:20 +02003670
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003671 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3672 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3673 goto error_exit;
3674 }
3675 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003676
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003677 uid_t user = -1;
3678 if (strlen(CHOWN_USER) > 0) {
3679 struct passwd *p = getpwnam(CHOWN_USER);
3680 if (p != NULL) {
3681 user = p->pw_uid;
3682 }
3683 }
3684 gid_t group = -1;
3685 if (strlen(CHOWN_GROUP) > 0) {
3686 struct group *g = getgrnam(CHOWN_GROUP);
3687 if (g != NULL) {
3688 group = g->gr_gid;
3689 }
3690 }
3691 if (chown(sockname, user, group) == -1) {
3692 ERROR("Chown on socket file failed (%s).", strerror(errno));
3693 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003694
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003695 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003696
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003697 #ifdef WITH_NOTIFICATIONS
3698 if (notification_init() == -1) {
3699 ERROR("libwebsockets initialization failed");
3700 use_notifications = 0;
3701 } else {
3702 use_notifications = 1;
3703 }
3704 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003705
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003706 /* setup libnetconf's callbacks */
Michal Vaskod0205992016-03-17 10:04:49 +01003707 nc_client_init();
Michal Vaskodedf91b2016-04-06 10:00:31 +02003708 nc_verbosity(NC_VERB_VERBOSE);
Michal Vaskof35ea502016-02-24 10:44:54 +01003709 nc_set_print_clb(clb_print);
3710 nc_client_ssh_set_auth_hostkey_check_clb(netconf_callback_ssh_hostkey_check);
3711 nc_client_ssh_set_auth_interactive_clb(netconf_callback_sshauth_interactive);
3712 nc_client_ssh_set_auth_password_clb(netconf_callback_sshauth_password);
3713 nc_client_ssh_set_auth_privkey_passphrase_clb(netconf_callback_sshauth_passphrase);
Radek Krejci469aab82012-07-22 18:42:20 +02003714
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003715 /* disable publickey authentication */
Michal Vaskof35ea502016-02-24 10:44:54 +01003716 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003717
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003718 /* create mutex protecting session list */
3719 pthread_rwlockattr_init(&lock_attrs);
3720 /* rwlock is shared only with threads in this process */
3721 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3722 /* create rw lock */
3723 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3724 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3725 goto error_exit;
3726 }
3727 pthread_mutex_init(&ntf_history_lock, NULL);
3728 pthread_mutex_init(&json_lock, NULL);
3729 DEBUG("Initialization of notification history.");
3730 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3731 ERROR("Initialization of notification history failed.");
3732 }
3733 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3734 ERROR("Initialization of reply key failed.");
3735 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003736
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003737 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3738 while (isterminated == 0) {
3739 gettimeofday(&tv, NULL);
3740 timediff = (unsigned int)tv.tv_sec - olds;
3741 #ifdef WITH_NOTIFICATIONS
3742 if (use_notifications == 1) {
3743 notification_handle();
3744 }
3745 #endif
3746 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3747 check_timeout_and_close();
3748 }
Radek Krejci469aab82012-07-22 18:42:20 +02003749
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003750 /* open incoming connection if any */
3751 len = sizeof(remote);
3752 client = accept(lsock, (struct sockaddr *) &remote, &len);
3753 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3754 usleep(SLEEP_TIME * 1000);
3755 continue;
3756 } else if (client == -1 && (errno == EINTR)) {
3757 continue;
3758 } else if (client == -1) {
3759 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3760 continue;
3761 }
Radek Krejci469aab82012-07-22 18:42:20 +02003762
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003763 /* set client's socket as non-blocking */
3764 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003765
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003766 arg = malloc(sizeof(struct pass_to_thread));
3767 arg->client = client;
3768 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003769
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003770 /* start new thread. It will serve this particular request and then terminate */
3771 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3772 ERROR("Creating POSIX thread failed: %d\n", ret);
3773 } else {
3774 DEBUG("Thread %lu created", ptids[pthread_count]);
3775 pthread_count++;
3776 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3777 ptids[pthread_count] = 0;
3778 }
Radek Krejci469aab82012-07-22 18:42:20 +02003779
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003780 /* check if some thread already terminated, free some resources by joining it */
3781 for (i = 0; i < pthread_count; i++) {
3782 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3783 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3784 pthread_count--;
3785 if (pthread_count > 0) {
3786 /* place last Thread ID on the place of joined one */
3787 ptids[i] = ptids[pthread_count];
3788 }
3789 }
3790 }
3791 DEBUG("Running %d threads", pthread_count);
3792 }
Radek Krejci469aab82012-07-22 18:42:20 +02003793
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003794 DEBUG("mod_netconf terminating...");
3795 /* join all threads */
3796 for (i = 0; i < pthread_count; i++) {
3797 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3798 }
Radek Krejci469aab82012-07-22 18:42:20 +02003799
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003800 #ifdef WITH_NOTIFICATIONS
3801 notification_close();
3802 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003803
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003804 /* close all NETCONF sessions */
3805 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003806
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003807 /* destroy rwlock */
3808 pthread_rwlock_destroy(&session_lock);
3809 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003810
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003811 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003812
Michal Vaskod0205992016-03-17 10:04:49 +01003813 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003814 free(ptids);
3815 close(lsock);
3816 exit(0);
3817 return;
3818
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003819error_exit:
Michal Vaskod0205992016-03-17 10:04:49 +01003820 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003821 close(lsock);
3822 free(ptids);
3823 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003824}
3825
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003826int
3827main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003828{
Michal Vaskoc3146782015-11-04 14:46:41 +01003829 struct sigaction action;
3830 sigset_t block_mask;
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003831 int i;
Michal Vaskoc3146782015-11-04 14:46:41 +01003832
Michal Vaskoa53ef882015-11-24 11:02:01 +01003833 if (argc > 3) {
3834 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3835 return 1;
3836 }
3837
3838 sockname = SOCKET_FILENAME;
3839 for (i = 1; i < argc; ++i) {
3840 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
3841 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3842 return 0;
3843 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon")) {
3844 daemonize = 1;
3845 } else {
3846 sockname = argv[i];
3847 }
3848 }
3849
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003850 if (daemonize) {
3851 if (daemon(0, 0) == -1) {
3852 ERROR("daemon() failed (%s)", strerror(errno));
3853 return 1;
3854 }
3855 openlog("netopeerguid", LOG_PID, LOG_DAEMON);
Michal Vaskoc3146782015-11-04 14:46:41 +01003856 }
3857
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003858 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003859 action.sa_handler = signal_handler;
3860 action.sa_mask = block_mask;
3861 action.sa_flags = 0;
3862 sigaction(SIGINT, &action, NULL);
3863 sigaction(SIGTERM, &action, NULL);
3864
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003865 forked_proc();
3866 DEBUG("Terminated");
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003867 if (daemonize) {
3868 closelog();
3869 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003870 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003871}