blob: fc2397f75dbc99bd0f9b5b7a4cb117d255ff3ece [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);
Michal Vasko59a43052016-04-13 10:23:12 +0200313 *err_reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100314 }
315 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
316 ERROR("Cannot set thread-specific hash value.");
317 }
318 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200319}
320
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100321void
322free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200323{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100324 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
325 if (err_reply != NULL) {
326 if (*err_reply != NULL) {
327 pthread_mutex_lock(&json_lock);
328 json_object_put(*err_reply);
329 pthread_mutex_unlock(&json_lock);
330 }
331 free(err_reply);
332 err_reply = NULL;
333 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
334 ERROR("Cannot set thread-specific hash value.");
335 }
336 }
337}
338
339static struct session_with_mutex *
340session_get_locked(unsigned int session_key, json_object **err)
341{
342 struct session_with_mutex *locked_session;
343
344 /* get non-exclusive (read) access to sessions_list (conns) */
345 DEBUG("LOCK wrlock %s", __func__);
346 if (pthread_rwlock_rdlock(&session_lock) != 0) {
347 if (*err) {
348 *err = create_error_reply("Locking failed.");
349 }
350 return NULL;
351 }
352 /* get session where send the RPC */
353 for (locked_session = netconf_sessions_list;
354 locked_session && (locked_session->session_key != session_key);
355 locked_session = locked_session->next);
356 if (!locked_session) {
357 if (*err) {
358 *err = create_error_reply("Session not found.");
359 }
360 return NULL;
361 }
362
363 /* get exclusive access to session */
364 DEBUG("LOCK mutex %s", __func__);
365 if (pthread_mutex_lock(&locked_session->lock) != 0) {
366 if (*err) {
367 *err = create_error_reply("Locking failed.");
368 }
369 goto wrlock_fail;
370 }
371 return locked_session;
372
373wrlock_fail:
374 DEBUG("UNLOCK wrlock %s", __func__);
375 pthread_rwlock_unlock(&session_lock);
376 return NULL;
377}
378
379static void
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100380session_user_activity(const char *username)
381{
382 struct session_with_mutex *sess;
383
384 for (sess = netconf_sessions_list; sess; sess = sess->next) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100385 if (!strcmp(nc_session_get_username(sess->session), username)) {
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100386 sess->last_activity = time(NULL);
387 }
388 }
389}
390
391static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100392session_unlock(struct session_with_mutex *locked_session)
393{
394 DEBUG("UNLOCK mutex %s", __func__);
395 pthread_mutex_unlock(&locked_session->lock);
396 DEBUG("UNLOCK wrlock %s", __func__);
397 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200398}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200399
Michal Vasko3fda9a92015-11-23 10:10:57 +0100400static void
401node_metadata_text(const char *text, const char *name, json_object *parent)
402{
403 json_object *obj;
404
405 if (!text) {
406 return;
407 }
408
409 obj = json_object_new_string(text);
410 json_object_object_add(parent, name, obj);
411}
412
413static void
414node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
415{
416 json_object *obj;
417
418 if (!restr) {
419 return;
420 }
421
422 obj = json_object_new_string(restr->expr);
423 json_object_object_add(parent, name, obj);
424}
425
426static void
427node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
428{
429 uint8_t i;
430 json_object *array, *obj;
431
432 if (!must_size || !must) {
433 return;
434 }
435
436 array = json_object_new_array();
437
438 for (i = 0; i < must_size; ++i) {
439 obj = json_object_new_string(must[i].expr);
440 json_object_array_add(array, obj);
441 }
442
443 json_object_object_add(parent, "must", array);
444}
445
446static void
447node_metadata_basic(struct lys_node *node, json_object *parent)
448{
449 json_object *obj;
450
451 /* description */
452 node_metadata_text(node->dsc, "description", parent);
453
454 /* reference */
455 node_metadata_text(node->ref, "reference", parent);
456
457 /* config */
458 if (node->flags & LYS_CONFIG_R) {
459 obj = json_object_new_boolean(0);
460 } else {
461 obj = json_object_new_boolean(1);
462 }
463 json_object_object_add(parent, "config", obj);
464
465 /* status */
466 if (node->flags & LYS_STATUS_DEPRC) {
467 obj = json_object_new_string("deprecated");
468 } else if (node->flags & LYS_STATUS_OBSLT) {
469 obj = json_object_new_string("obsolete");
470 } else {
471 obj = json_object_new_string("current");
472 }
473 json_object_object_add(parent, "status", obj);
474
475 /* mandatory */
476 if (node->flags & LYS_MAND_TRUE) {
477 obj = json_object_new_boolean(1);
478 } else {
479 obj = json_object_new_boolean(0);
480 }
481 json_object_object_add(parent, "mandatory", obj);
482
483 /* NACM extensions */
484 if (node->nacm) {
485 if (node->nacm & LYS_NACM_DENYW) {
486 obj = json_object_new_string("default-deny-write");
487 } else {
488 obj = json_object_new_string("default-deny-all");
489 }
490 json_object_object_add(parent, "ext", obj);
491 }
492}
493
494static void
495node_metadata_when(struct lys_when *when, json_object *parent)
496{
497 json_object *obj;
498
499 if (!when) {
500 return;
501 }
502
503 obj = json_object_new_string(when->cond);
504 json_object_object_add(parent, "when", obj);
505}
506
507static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100508node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100509{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100510 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100511 struct lys_node *child;
512
513 if (!node->child) {
514 return;
515 }
516
517 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100518 if (child->nodetype == LYS_USES) {
519 node_metadata_children_recursive(child, child_array, choice_array);
520 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100521 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100522 if (!*child_array) {
523 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100524 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100525 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100526 } else if (child->nodetype == LYS_CHOICE) {
527 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100528 if (!*choice_array) {
529 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100530 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100531 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100532 }
533 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100534}
535
536static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100537node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100538{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100539 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100540 struct lys_node *child;
541
542 if (!choice->child) {
543 return;
544 }
545
Michal Vasko3fda9a92015-11-23 10:10:57 +0100546 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100547 if (child->nodetype == LYS_USES) {
548 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
549 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100550 obj = json_object_new_string(child->name);
551 json_object_array_add(array, obj);
552 }
553 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100554}
555
556static void
557node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
558{
559 json_object *obj;
560
561 if (min) {
562 obj = json_object_new_int(min);
563 json_object_object_add(parent, "min-elements", obj);
564 }
565
566 if (max) {
567 obj = json_object_new_int(max);
568 json_object_object_add(parent, "max-elements", obj);
569 }
570}
571
572static void
573node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
574{
Michal Vasko4e7d6ce2016-05-02 14:07:20 +0200575 int i;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100576 json_object *obj;
577
578 if (!ident) {
579 return;
580 }
581
582 obj = json_object_new_string(ident->name);
583 json_object_array_add(array, obj);
584
Michal Vasko4e7d6ce2016-05-02 14:07:20 +0200585 for (i = 0; ident->der[i]; ++i) {
586 node_metadata_ident_recursive(ident->der[i], array);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100587 }
588}
589
590static void
591node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
592{
593 json_object *obj, *array, *item;
594 char *str;
595 int i;
596
597 /* built-in YANG type */
598 if (!type->der->module) {
599 switch (type->base) {
600 case LY_TYPE_BINARY:
601 node_metadata_text("binary", "type", parent);
602 node_metadata_restr(type->info.binary.length, "length", parent);
603 break;
604 case LY_TYPE_BITS:
605 node_metadata_text("bits", "type", parent);
606
607 array = json_object_new_array();
608 for (i = 0; i < type->info.bits.count; ++i) {
609 item = json_object_new_object();
610 obj = json_object_new_string(type->info.bits.bit[i].name);
611 json_object_object_add(item, "name", obj);
612 obj = json_object_new_int(type->info.bits.bit[i].pos);
613 json_object_object_add(item, "position", obj);
614 json_object_array_add(array, item);
615 }
616 json_object_object_add(parent, "bits", array);
617 break;
618 case LY_TYPE_BOOL:
619 node_metadata_text("bool", "type", parent);
620 break;
621 case LY_TYPE_DEC64:
622 node_metadata_text("decimal64", "type", parent);
623 node_metadata_restr(type->info.dec64.range, "range", parent);
624 obj = json_object_new_int(type->info.dec64.dig);
625 json_object_object_add(parent, "fraction-digits", obj);
626 break;
627 case LY_TYPE_EMPTY:
628 node_metadata_text("empty", "type", parent);
629 break;
630 case LY_TYPE_ENUM:
631 node_metadata_text("enumeration", "type", parent);
632
633 array = json_object_new_array();
634 for (i = 0; i < type->info.enums.count; ++i) {
635 obj = json_object_new_string(type->info.enums.enm[i].name);
636 json_object_array_add(array, obj);
637 }
638 json_object_object_add(parent, "enumval", array);
639 break;
640 case LY_TYPE_IDENT:
641 node_metadata_text("identityref", "type", parent);
642
643 array = json_object_new_array();
644 node_metadata_ident_recursive(type->info.ident.ref, array);
645 json_object_object_add(parent, "identityval", array);
646 break;
647 case LY_TYPE_INST:
648 node_metadata_text("instance-identifier", "type", parent);
649 if (type->info.inst.req == -1) {
650 obj = json_object_new_boolean(0);
651 } else {
652 obj = json_object_new_boolean(1);
653 }
654 json_object_object_add(parent, "require-instance", obj);
655 break;
656 case LY_TYPE_LEAFREF:
657 node_metadata_text("leafref", "type", parent);
658 node_metadata_text(type->info.lref.path, "path", parent);
659 break;
660 case LY_TYPE_STRING:
661 node_metadata_text("string", "type", parent);
662 node_metadata_restr(type->info.str.length, "length", parent);
663 if (type->info.str.pat_count) {
664 array = json_object_new_array();
665 for (i = 0; i < type->info.str.pat_count; ++i) {
666 obj = json_object_new_string(type->info.str.patterns[i].expr);
667 json_object_array_add(array, obj);
668 }
669 json_object_object_add(parent, "pattern", array);
670 }
671 break;
672 case LY_TYPE_UNION:
673 node_metadata_text("union", "type", parent);
674 array = json_object_new_array();
675 for (i = 0; i < type->info.uni.count; ++i) {
676 obj = json_object_new_object();
677 node_metadata_type(&type->info.uni.types[i], module, obj);
678 json_object_array_add(array, obj);
679 }
680 json_object_object_add(parent, "types", array);
681 break;
682 case LY_TYPE_INT8:
683 node_metadata_text("int8", "type", parent);
684 node_metadata_restr(type->info.num.range, "range", parent);
685 break;
686 case LY_TYPE_UINT8:
687 node_metadata_text("uint8", "type", parent);
688 node_metadata_restr(type->info.num.range, "range", parent);
689 break;
690 case LY_TYPE_INT16:
691 node_metadata_text("int16", "type", parent);
692 node_metadata_restr(type->info.num.range, "range", parent);
693 break;
694 case LY_TYPE_UINT16:
695 node_metadata_text("uint16", "type", parent);
696 node_metadata_restr(type->info.num.range, "range", parent);
697 break;
698 case LY_TYPE_INT32:
699 node_metadata_text("int32", "type", parent);
700 node_metadata_restr(type->info.num.range, "range", parent);
701 break;
702 case LY_TYPE_UINT32:
703 node_metadata_text("uint32", "type", parent);
704 node_metadata_restr(type->info.num.range, "range", parent);
705 break;
706 case LY_TYPE_INT64:
707 node_metadata_text("int64", "type", parent);
708 node_metadata_restr(type->info.num.range, "range", parent);
709 break;
710 case LY_TYPE_UINT64:
711 node_metadata_text("uint64", "type", parent);
712 node_metadata_restr(type->info.num.range, "range", parent);
713 break;
714 default:
715 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
716 break;
717 }
718
719 /* typedef */
720 } else {
721 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
722 node_metadata_text(type->der->name, "type", parent);
723 } else {
724 asprintf(&str, "%s:%s", type->module_name, type->der->name);
725 node_metadata_text(str, "type", parent);
726 free(str);
727 }
728 obj = json_object_new_object();
729 node_metadata_typedef(type->der, obj);
730 json_object_object_add(parent, "typedef", obj);
731 }
732}
733
734static void
735node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
736{
737 json_object *obj;
738
739 /* description */
740 node_metadata_text(tpdf->dsc, "description", parent);
741
742 /* reference */
743 node_metadata_text(tpdf->ref, "reference", parent);
744
745 /* status */
746 if (tpdf->flags & LYS_STATUS_DEPRC) {
747 obj = json_object_new_string("deprecated");
748 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
749 obj = json_object_new_string("obsolete");
750 } else {
751 obj = json_object_new_string("current");
752 }
753 json_object_object_add(parent, "status", obj);
754
755 /* type */
756 node_metadata_type(&tpdf->type, tpdf->module, parent);
757
758 /* units */
759 node_metadata_text(tpdf->units, "units", parent);
760
761 /* default */
762 node_metadata_text(tpdf->dflt, "default", parent);
763}
764
765static void
766node_metadata_container(struct lys_node_container *cont, json_object *parent)
767{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100768 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100769
770 /* element type */
771 obj = json_object_new_string("container");
772 json_object_object_add(parent, "eltype", obj);
773
774 /* shared info */
775 node_metadata_basic((struct lys_node *)cont, parent);
776
777 /* must */
778 node_metadata_must(cont->must_size, cont->must, parent);
779
780 /* presence */
781 node_metadata_text(cont->presence, "presence", parent);
782
783 /* when */
784 node_metadata_when(cont->when, parent);
785
786 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100787 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
788 if (child_array) {
789 json_object_object_add(parent, "children", child_array);
790 }
791 if (choice_array) {
792 json_object_object_add(parent, "choice", choice_array);
793 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100794}
795
796static void
797node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
798{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100799 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100800
801 /* element type */
802 obj = json_object_new_string("choice");
803 json_object_object_add(parent, "eltype", obj);
804
805 /* shared info */
806 node_metadata_basic((struct lys_node *)choice, parent);
807
808 /* default */
Michal Vasko5e1c6052016-03-17 15:43:33 +0100809 if (choice->dflt) {
810 node_metadata_text(choice->dflt->name, "default", parent);
811 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100812
813 /* when */
814 node_metadata_when(choice->when, parent);
815
816 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100817 if (choice->child) {
818 array = json_object_new_array();
819 node_metadata_cases_recursive(choice, array);
820 json_object_object_add(parent, "cases", array);
821 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100822}
823
824static void
825node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
826{
827 json_object *obj;
828 struct lys_node_list *list;
829 int is_key, i;
830
831 /* element type */
832 obj = json_object_new_string("leaf");
833 json_object_object_add(parent, "eltype", obj);
834
835 /* shared info */
836 node_metadata_basic((struct lys_node *)leaf, parent);
837
838 /* type */
839 node_metadata_type(&leaf->type, leaf->module, parent);
840
841 /* units */
842 node_metadata_text(leaf->units, "units", parent);
843
844 /* default */
845 node_metadata_text(leaf->dflt, "default", parent);
846
847 /* must */
848 node_metadata_must(leaf->must_size, leaf->must, parent);
849
850 /* when */
851 node_metadata_when(leaf->when, parent);
852
853 /* iskey */
854 is_key = 0;
855 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
856 if (list && (list->nodetype == LYS_LIST)) {
857 for (i = 0; i < list->keys_size; ++i) {
858 if (list->keys[i] == leaf) {
859 is_key = 1;
860 break;
861 }
862 }
863 }
864 obj = json_object_new_boolean(is_key);
865 json_object_object_add(parent, "iskey", obj);
866}
867
868static void
869node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
870{
871 json_object *obj;
872
873 /* element type */
874 obj = json_object_new_string("leaf-list");
875 json_object_object_add(parent, "eltype", obj);
876
877 /* shared info */
878 node_metadata_basic((struct lys_node *)llist, parent);
879
880 /* type */
881 node_metadata_type(&llist->type, llist->module, parent);
882
883 /* units */
884 node_metadata_text(llist->units, "units", parent);
885
886 /* must */
887 node_metadata_must(llist->must_size, llist->must, parent);
888
889 /* when */
890 node_metadata_when(llist->when, parent);
891
892 /* min/max-elements */
893 node_metadata_min_max(llist->min, llist->max, parent);
894}
895
896static void
897node_metadata_list(struct lys_node_list *list, json_object *parent)
898{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100899 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100900 int i;
901 unsigned int j;
902
903 /* element type */
904 obj = json_object_new_string("list");
905 json_object_object_add(parent, "eltype", obj);
906
907 /* shared info */
908 node_metadata_basic((struct lys_node *)list, parent);
909
910 /* must */
911 node_metadata_must(list->must_size, list->must, parent);
912
913 /* when */
914 node_metadata_when(list->when, parent);
915
916 /* min/max-elements */
917 node_metadata_min_max(list->min, list->max, parent);
918
919 /* keys */
920 if (list->keys_size) {
921 array = json_object_new_array();
922 for (i = 0; i < list->keys_size; ++i) {
923 obj = json_object_new_string(list->keys[i]->name);
924 json_object_array_add(array, obj);
925 }
926 json_object_object_add(parent, "keys", array);
927 }
928
929 /* unique */
930 if (list->unique_size) {
931 array = json_object_new_array();
932 for (i = 0; i < list->unique_size; ++i) {
933 for (j = 0; j < list->unique[i].expr_size; ++j) {
934 obj = json_object_new_string(list->unique[i].expr[j]);
935 json_object_array_add(array, obj);
936 }
937 }
938 json_object_object_add(parent, "unique", array);
939 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100940
941 /* children & choice */
942 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
943 if (child_array) {
944 json_object_object_add(parent, "children", child_array);
945 }
946 if (choice_array) {
947 json_object_object_add(parent, "choice", choice_array);
948 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100949}
950
951static void
952node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
953{
954 json_object *obj;
955
956 /* element type */
957 obj = json_object_new_string("anyxml");
958 json_object_object_add(parent, "eltype", obj);
959
960 /* shared info */
961 node_metadata_basic((struct lys_node *)anyxml, parent);
962
963 /* must */
964 node_metadata_must(anyxml->must_size, anyxml->must, parent);
965
966 /* when */
967 node_metadata_when(anyxml->when, parent);
968
969}
970
971static void
972node_metadata_case(struct lys_node_case *cas, json_object *parent)
973{
974 json_object *obj;
975
976 /* element type */
977 obj = json_object_new_string("case");
978 json_object_object_add(parent, "eltype", obj);
979
980 /* shared info */
981 node_metadata_basic((struct lys_node *)cas, parent);
982
983 /* when */
984 node_metadata_when(cas->when, parent);
985}
986
Michal Vaskoa45770b2015-11-23 15:49:41 +0100987static void
988node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
989{
990 json_object *obj;
991
992 /* element type */
993 obj = json_object_new_string("rpc");
994 json_object_object_add(parent, "eltype", obj);
995
996 /* description */
997 node_metadata_text(rpc->dsc, "description", parent);
998
999 /* reference */
1000 node_metadata_text(rpc->ref, "reference", parent);
1001
1002 /* status */
1003 if (rpc->flags & LYS_STATUS_DEPRC) {
1004 obj = json_object_new_string("deprecated");
1005 } else if (rpc->flags & LYS_STATUS_OBSLT) {
1006 obj = json_object_new_string("obsolete");
1007 } else {
1008 obj = json_object_new_string("current");
1009 }
1010 json_object_object_add(parent, "status", obj);
1011}
1012
1013static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001014node_metadata_model(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001015{
Michal Vaskob1e28f22016-05-02 14:07:38 +02001016 json_object *obj, *array, *array2, *item;
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001017 const struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001018 int i;
1019
1020 /* yang-version */
1021 if (module->version == 2) {
1022 obj = json_object_new_string("1.1");
1023 } else {
1024 obj = json_object_new_string("1.0");
1025 }
1026 json_object_object_add(parent, "yang-version", obj);
1027
1028 /* namespace */
1029 node_metadata_text(module->ns, "namespace", parent);
1030
1031 /* prefix */
1032 node_metadata_text(module->prefix, "prefix", parent);
1033
1034 /* contact */
1035 node_metadata_text(module->contact, "contact", parent);
1036
1037 /* organization */
1038 node_metadata_text(module->org, "organization", parent);
1039
1040 /* revision */
1041 if (module->rev_size) {
1042 node_metadata_text(module->rev[0].date, "revision", parent);
1043 }
1044
1045 /* description */
1046 node_metadata_text(module->dsc, "description", parent);
1047
1048 /* import */
1049 if (module->imp_size) {
1050 array = json_object_new_array();
1051 for (i = 0; i < module->imp_size; ++i) {
1052 item = json_object_new_object();
1053
1054 node_metadata_text(module->imp[i].module->name, "name", item);
1055 node_metadata_text(module->imp[i].prefix, "prefix", item);
1056 if (module->imp[i].rev && module->imp[i].rev[0]) {
1057 node_metadata_text(module->imp[i].rev, "revision", item);
1058 }
1059
1060 json_object_array_add(array, item);
1061 }
1062 json_object_object_add(parent, "imports", array);
1063 }
1064
1065 /* include */
1066 if (module->inc_size) {
1067 array = json_object_new_array();
1068 for (i = 0; i < module->inc_size; ++i) {
1069 item = json_object_new_object();
1070
1071 node_metadata_text(module->inc[i].submodule->name, "name", item);
1072 if (module->inc[i].rev && module->inc[i].rev[0]) {
1073 node_metadata_text(module->inc[i].rev, "revision", item);
1074 }
1075
1076 json_object_array_add(array, item);
1077 }
1078 json_object_object_add(parent, "includes", array);
1079 }
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001080
Michal Vaskob1e28f22016-05-02 14:07:38 +02001081 /* top-nodes and RPCs */
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001082 node = NULL;
1083 array = NULL;
Michal Vaskob1e28f22016-05-02 14:07:38 +02001084 array2 = NULL;
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001085 while ((node = lys_getnext(node, NULL, module, LYS_GETNEXT_WITHCHOICE))) {
Michal Vaskob1e28f22016-05-02 14:07:38 +02001086 if (node->nodetype == LYS_NOTIF) {
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001087 continue;
1088 }
Michal Vaskob1e28f22016-05-02 14:07:38 +02001089 if (node->nodetype == LYS_RPC) {
1090 if (!array2) {
1091 array2 = json_object_new_array();
1092 }
1093 item = json_object_new_string(node->name);
1094 json_object_array_add(array2, item);
1095 } else {
1096 if (!array) {
1097 array = json_object_new_array();
1098 }
1099 item = json_object_new_string(node->name);
1100 json_object_array_add(array, item);
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001101 }
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001102 }
1103 if (array) {
1104 json_object_object_add(parent, "top-nodes", array);
1105 }
Michal Vaskob1e28f22016-05-02 14:07:38 +02001106 if (array2) {
1107 json_object_object_add(parent, "rpcs", array2);
1108 }
Michal Vaskoa45770b2015-11-23 15:49:41 +01001109}
1110
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001111/**
1112 * \defgroup netconf_operations NETCONF operations
1113 * The list of NETCONF operations that mod_netconf supports.
1114 * @{
1115 */
1116
1117/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001118 * \brief Send RPC and wait for reply with timeout.
1119 *
1120 * \param[in] session libnetconf session
1121 * \param[in] rpc prepared RPC message
1122 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1123 * \param[out] reply reply from the server
Michal Vaskof35ea502016-02-24 10:44:54 +01001124 * \return NC_MSG_WOULDBLOCK or NC_MSG_ERROR.
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001125 * On success, it returns NC_MSG_REPLY.
1126 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001127NC_MSG_TYPE
Michal Vaskof35ea502016-02-24 10:44:54 +01001128netconf_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 +02001129{
Michal Vaskof35ea502016-02-24 10:44:54 +01001130 uint64_t msgid;
1131 NC_MSG_TYPE ret;
1132 ret = nc_send_rpc(session, rpc, timeout, &msgid);
1133 if (ret != NC_MSG_RPC) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001134 return ret;
1135 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001136
1137 while ((ret = nc_recv_reply(session, rpc, msgid, timeout, (strict ? LYD_OPT_STRICT : 0), reply)) == NC_MSG_NOTIF);
1138
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001139 return ret;
1140}
1141
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001142/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001143 * \brief Connect to NETCONF server
1144 *
1145 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1146 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001147static unsigned int
Michal Vaskof35ea502016-02-24 10:44:54 +01001148netconf_connect(const char *host, const char *port, const char *user, const char *pass, const char *privkey)
Radek Krejci469aab82012-07-22 18:42:20 +02001149{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001150 struct nc_session* session = NULL;
1151 struct session_with_mutex *locked_session, *last_session;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001152 char *pubkey;
Radek Krejci469aab82012-07-22 18:42:20 +02001153
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001154 /* connect to the requested NETCONF server */
1155 password = (char*)pass;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001156 if (privkey) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001157 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 3);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001158 asprintf(&pubkey, "%s.pub", privkey);
Michal Vaskof35ea502016-02-24 10:44:54 +01001159 nc_client_ssh_add_keypair(pubkey, privkey);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001160 free(pubkey);
1161 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001162 nc_client_ssh_set_username(user);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001163 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Michal Vaskof35ea502016-02-24 10:44:54 +01001164 session = nc_connect_ssh(host, (unsigned short)atoi(port), NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001165 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001166
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001167 /* if connected successful, add session to the list */
1168 if (session != NULL) {
1169 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 +01001170 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001171 session = NULL;
1172 free(locked_session);
1173 locked_session = NULL;
1174 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1175 return 0;
1176 }
1177 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001178 locked_session->hello_message = NULL;
1179 locked_session->closed = 0;
1180 pthread_mutex_init(&locked_session->lock, NULL);
1181 DEBUG("Before session_lock");
1182 /* get exclusive access to sessions_list (conns) */
1183 DEBUG("LOCK wrlock %s", __func__);
1184 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001185 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001186 free(locked_session);
1187 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1188 return 0;
1189 }
1190 locked_session->ntfc_subscribed = 0;
1191 DEBUG("Add connection to the list");
1192 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001193 netconf_sessions_list = locked_session;
1194 } else {
1195 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1196 last_session->next = locked_session;
1197 locked_session->prev = last_session;
1198 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001199 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001200
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001201 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001202
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001203 /* store information about session from hello message for future usage */
1204 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001205
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001206 DEBUG("NETCONF session established");
1207 locked_session->session_key = session_key_generator;
1208 ++session_key_generator;
1209 if (session_key_generator == UINT_MAX) {
1210 session_key_generator = 1;
1211 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001212
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001213 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001214 /* unlock session list */
1215 DEBUG("UNLOCK wrlock %s", __func__);
1216 if (pthread_rwlock_unlock(&session_lock) != 0) {
1217 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1218 }
Radek Krejci469aab82012-07-22 18:42:20 +02001219
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001220 return locked_session->session_key;
1221 }
1222
1223 ERROR("Connection could not be established");
1224 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001225}
1226
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001227static int
1228close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001229{
Michal Vaskoa53ef882015-11-24 11:02:01 +01001230 int i;
1231
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001232 DEBUG("LOCK mutex %s", __func__);
1233 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1234 ERROR("Error while locking rwlock");
1235 }
1236 locked_session->ntfc_subscribed = 0;
1237 locked_session->closed = 1;
1238 if (locked_session->session != NULL) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001239 nc_session_free(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001240 locked_session->session = NULL;
1241 }
1242 DEBUG("session closed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001243 DEBUG("UNLOCK mutex %s", __func__);
1244 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1245 ERROR("Error while locking rwlock");
1246 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001247
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001248 DEBUG("closed session, disabled notif(?), wait 0.5s");
1249 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001250
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001251 /* session shouldn't be used by now */
Michal Vaskoa53ef882015-11-24 11:02:01 +01001252 for (i = 0; i < locked_session->notif_count; ++i) {
1253 free(locked_session->notifications[i].content);
1254 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001255 free(locked_session->notifications);
1256 pthread_mutex_destroy(&locked_session->lock);
1257 if (locked_session->hello_message != NULL) {
1258 json_object_put(locked_session->hello_message);
1259 locked_session->hello_message = NULL;
1260 }
1261 locked_session->session = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001262 free(locked_session);
1263 locked_session = NULL;
1264 DEBUG("NETCONF session closed, everything cleared.");
1265 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001266}
1267
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001268static int
1269netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001270{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001271 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001272
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001273 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001274
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001275 /* get exclusive (write) access to sessions_list (conns) */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001276 DEBUG("LOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02001277 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001278 ERROR("Error while locking rwlock");
1279 (*reply) = create_error_reply("Internal: Error while locking.");
1280 return EXIT_FAILURE;
1281 }
1282 /* remove session from the active sessions list -> nobody new can now work with session */
1283 for (locked_session = netconf_sessions_list;
1284 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001285 locked_session = locked_session->next);
1286
1287 if (!locked_session) {
Michal Vaskodc40b3a2016-04-11 14:36:16 +02001288 DEBUG("UNLOCK wrlock %s", __func__);
Michal Vasko642cad02016-03-17 12:31:18 +01001289 pthread_rwlock_unlock(&session_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001290 ERROR("Could not find the session %u to close.", session_key);
1291 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001292 return EXIT_FAILURE;
1293 }
1294
1295 if (!locked_session->prev) {
1296 netconf_sessions_list = netconf_sessions_list->next;
Michal Vaskof0b6fbb2016-03-08 12:04:34 +01001297 if (netconf_sessions_list) {
1298 netconf_sessions_list->prev = NULL;
1299 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001300 } else {
1301 locked_session->prev->next = locked_session->next;
1302 if (locked_session->next) {
1303 locked_session->next->prev = locked_session->prev;
1304 }
1305 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001306
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001307 DEBUG("UNLOCK wrlock %s", __func__);
1308 if (pthread_rwlock_unlock (&session_lock) != 0) {
1309 ERROR("Error while unlocking rwlock");
1310 (*reply) = create_error_reply("Internal: Error while unlocking.");
1311 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001312
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001313 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1314 return close_and_free_session(locked_session);
1315 } else {
1316 ERROR("Unknown session to close");
1317 (*reply) = create_error_reply("Internal: Unkown session to close.");
1318 return (EXIT_FAILURE);
1319 }
1320 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001321}
1322
Tomas Cejkac7929632013-10-24 19:25:15 +02001323/**
1324 * Test reply message type and return error message.
1325 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001326 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001327 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001328 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001329 * \param[out] data
1330 * \return NULL on success
1331 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001332json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001333netconf_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 +02001334{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001335 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001336
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001337 /* process the result of the operation */
1338 switch (msgt) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001339 case NC_MSG_ERROR:
1340 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001341 ERROR("mod_netconf: receiving rpc-reply failed");
1342 if (session_key) {
1343 netconf_close(session_key, &err);
1344 }
1345 if (err != NULL) {
1346 return err;
1347 }
1348 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1349 }
1350 case NC_MSG_NONE:
1351 /* there is error handled by callback */
1352 if (data != NULL) {
1353 free(*data);
1354 (*data) = NULL;
1355 }
1356 return NULL;
1357 case NC_MSG_REPLY:
Michal Vaskof35ea502016-02-24 10:44:54 +01001358 switch (reply->type) {
1359 case NC_RPL_OK:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001360 if ((data != NULL) && (*data != NULL)) {
1361 free(*data);
1362 (*data) = NULL;
1363 }
1364 return create_ok_reply();
Michal Vaskof35ea502016-02-24 10:44:54 +01001365 case NC_RPL_DATA:
1366 if (((*data) = ((struct nc_reply_data *)reply)->data) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001367 ERROR("mod_netconf: no data from reply");
1368 return create_error_reply("Internal: No data from reply received.");
1369 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001370 ((struct nc_reply_data *)reply)->data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001371 return NULL;
1372 }
1373 break;
Michal Vaskof35ea502016-02-24 10:44:54 +01001374 case NC_RPL_ERROR:
1375 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001376 if (data != NULL) {
1377 free(*data);
1378 (*data) = NULL;
1379 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001380 return create_error_reply(((struct nc_reply_error *)reply)->err[0].message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001381 default:
Michal Vaskof35ea502016-02-24 10:44:54 +01001382 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001383 if (data != NULL) {
1384 free(*data);
1385 (*data) = NULL;
1386 }
1387 return create_error_reply("Unknown type of NETCONF reply.");
1388 }
1389 break;
1390 default:
1391 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1392 if (data != NULL) {
1393 free(*data);
1394 (*data) = NULL;
1395 }
1396 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1397 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001398}
1399
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001400json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001401netconf_unlocked_op(struct nc_session *session, struct nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001402{
Michal Vaskof35ea502016-02-24 10:44:54 +01001403 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001404 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001405
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001406 /* check requests */
1407 if (rpc == NULL) {
1408 ERROR("mod_netconf: rpc is not created");
1409 return create_error_reply("Internal error: RPC is not created");
1410 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001411
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001412 if (session != NULL) {
1413 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001414 msgt = netconf_send_recv_timed(session, rpc, 50000, 0, &reply);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001415 /* process the result of the operation */
1416 return netconf_test_reply(session, 0, msgt, reply, NULL);
1417 } else {
1418 ERROR("Unknown session to process.");
1419 return create_error_reply("Internal error: Unknown session to process.");
1420 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001421}
1422
Tomas Cejkac7929632013-10-24 19:25:15 +02001423/**
1424 * Perform RPC method that returns data.
1425 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001426 * \param[in] session_id session identifier
1427 * \param[in] rpc RPC message to perform
1428 * \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 +02001429 * \return NULL on success, json object with error otherwise
1430 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001431static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001432netconf_op(unsigned int session_key, struct nc_rpc *rpc, int strict, struct lyd_node **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001433{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001434 struct session_with_mutex * locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001435 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001436 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001437 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001438 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001439
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001440 /* check requests */
1441 if (rpc == NULL) {
1442 ERROR("mod_netconf: rpc is not created");
1443 res = create_error_reply("Internal: RPC could not be created.");
1444 data = NULL;
1445 goto finished;
1446 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001447
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001448 locked_session = session_get_locked(session_key, &res);
1449 if (!locked_session) {
1450 ERROR("Unknown session or locking failed.");
1451 goto finished;
1452 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001453
Michal Vaskof35ea502016-02-24 10:44:54 +01001454 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001455
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001456 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001457 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, strict, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001458
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001459 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001460
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001461 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001462
Tomas Cejkac7929632013-10-24 19:25:15 +02001463finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001464 nc_reply_free(reply);
1465 if (received_data != NULL) {
1466 (*received_data) = data;
1467 } else {
1468 if (data != NULL) {
1469 free(data);
1470 data = NULL;
1471 }
1472 }
1473 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001474}
1475
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001476static char *
1477netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001478{
Michal Vaskof35ea502016-02-24 10:44:54 +01001479 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001480 struct session_with_mutex *locked_session;
Michal Vasko04245ee2016-03-16 09:32:59 +01001481 json_object *res = NULL, *data_cjson;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001482 enum json_tokener_error tok_err;
Michal Vasko04245ee2016-03-16 09:32:59 +01001483 char *data_json = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001484 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001485
Michal Vaskof35ea502016-02-24 10:44:54 +01001486 /* tell server to show all elements even if they have default values */
1487#ifdef HAVE_WITHDEFAULTS_TAGGED
1488 rpc = nc_rpc_getconfig(source, filter, NC_WD_MODE_ALL_TAG, NC_PARAMTYPE_CONST);
1489#else
1490 rpc = nc_rpc_getconfig(source, filter, 0, NC_PARAMTYPE_CONST);
1491#endif
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001492 if (rpc == NULL) {
1493 ERROR("mod_netconf: creating rpc request failed");
1494 return (NULL);
1495 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001496
Michal Vaskof35ea502016-02-24 10:44:54 +01001497 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001498 nc_rpc_free(rpc);
1499 if (res != NULL) {
1500 (*err) = res;
1501 } else {
1502 (*err) = NULL;
1503 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001504
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001505 if (data) {
1506 for (locked_session = netconf_sessions_list;
1507 locked_session && (locked_session->session_key != session_key);
1508 locked_session = locked_session->next);
1509 /* won't fail */
1510
Michal Vaskof35ea502016-02-24 10:44:54 +01001511 /* print data into JSON */
1512 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001513 ERROR("Printing JSON <get-config> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001514 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001515 return NULL;
1516 }
1517
1518 /* parse JSON data into cjson */
1519 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001520 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001521 if (!data_cjson) {
1522 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1523 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001524 lyd_free_withsiblings(data);
1525 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001526 return NULL;
1527 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001528 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001529
1530 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001531 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001532 node_add_metadata_recursive(sibling, NULL, data_cjson);
1533 lyd_free(sibling);
1534 }
1535
Michal Vaskof35ea502016-02-24 10:44:54 +01001536 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001537 json_object_put(data_cjson);
1538 pthread_mutex_unlock(&json_lock);
1539 }
1540
Michal Vaskof35ea502016-02-24 10:44:54 +01001541 return (data_json);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001542}
1543
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001544static char *
1545netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001546{
Michal Vaskof35ea502016-02-24 10:44:54 +01001547 struct nc_rpc *rpc;
1548 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001549 json_object *res = NULL;
Michal Vasko56ec5952016-04-05 14:30:23 +02001550 char *model_data = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001551
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001552 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001553 rpc = nc_rpc_getschema(identifier, version, format, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001554 if (rpc == NULL) {
1555 ERROR("mod_netconf: creating rpc request failed");
1556 return (NULL);
1557 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001558
Michal Vaskof35ea502016-02-24 10:44:54 +01001559 res = netconf_op(session_key, rpc, 0, &data);
1560 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001561 if (res != NULL) {
1562 (*err) = res;
1563 } else {
1564 (*err) = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001565
1566 if (data) {
Michal Vasko56ec5952016-04-05 14:30:23 +02001567 if (((struct lyd_node_anyxml *)data)->xml_struct) {
1568 lyxml_print_mem(&model_data, ((struct lyd_node_anyxml *)data)->value.xml, 0);
1569 } else {
1570 model_data = strdup(((struct lyd_node_anyxml *)data)->value.str);
1571 }
1572 if (!model_data) {
1573 ERROR("memory allocation fail (%s:%d)", __FILE__, __LINE__);
Michal Vaskof35ea502016-02-24 10:44:54 +01001574 }
1575 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001576 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001577
Michal Vaskof35ea502016-02-24 10:44:54 +01001578 return (model_data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001579}
1580
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001581static char *
1582netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001583{
Michal Vaskof35ea502016-02-24 10:44:54 +01001584 struct nc_rpc* rpc;
1585 char* data_json = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001586 json_object *res = NULL, *data_cjson;
1587 enum json_tokener_error tok_err;
1588 struct session_with_mutex *locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001589 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001590
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001591 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001592 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001593 if (rpc == NULL) {
1594 ERROR("mod_netconf: creating rpc request failed");
1595 return (NULL);
1596 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001597
Michal Vaskof35ea502016-02-24 10:44:54 +01001598 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001599 nc_rpc_free(rpc);
1600 if (res != NULL) {
1601 (*err) = res;
1602 } else {
1603 (*err) = NULL;
1604 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001605
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001606 if (data) {
1607 for (locked_session = netconf_sessions_list;
1608 locked_session && (locked_session->session_key != session_key);
1609 locked_session = locked_session->next);
1610 /* won't fail */
1611
Michal Vaskof35ea502016-02-24 10:44:54 +01001612 /* print JSON data */
1613 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001614 ERROR("Printing JSON <get> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001615 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001616 return NULL;
1617 }
1618
1619 /* parse JSON data into cjson */
1620 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001621 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001622 if (!data_cjson) {
1623 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1624 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001625 lyd_free_withsiblings(data);
1626 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001627 return NULL;
1628 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001629 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001630
1631 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001632 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001633 node_add_metadata_recursive(sibling, NULL, data_cjson);
1634 lyd_free(sibling);
1635 }
1636
Michal Vaskof35ea502016-02-24 10:44:54 +01001637 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001638 json_object_put(data_cjson);
1639 pthread_mutex_unlock(&json_lock);
1640 }
1641
Michal Vaskof35ea502016-02-24 10:44:54 +01001642 return data_json;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001643}
1644
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001645static json_object *
1646netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1647 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001648{
Michal Vaskof35ea502016-02-24 10:44:54 +01001649 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001650 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001651
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001652 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001653 rpc = nc_rpc_copy(target, uri_trg, source, (config ? config : uri_src), 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001654 if (rpc == NULL) {
1655 ERROR("mod_netconf: creating rpc request failed");
1656 return create_error_reply("Internal: Creating rpc request failed");
1657 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001658
Michal Vaskof35ea502016-02-24 10:44:54 +01001659 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001660 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001661
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001662 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001663}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001664
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001665static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001666netconf_editconfig(unsigned int session_key, NC_DATASTORE target, NC_RPC_EDIT_DFLTOP defop,
1667 NC_RPC_EDIT_ERROPT erropt, NC_RPC_EDIT_TESTOPT testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001668{
Michal Vaskof35ea502016-02-24 10:44:54 +01001669 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001670 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001671
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001672 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001673 rpc = nc_rpc_edit(target, defop, testopt, erropt, config_or_url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001674 if (rpc == NULL) {
1675 ERROR("mod_netconf: creating rpc request failed");
1676 return create_error_reply("Internal: Creating rpc request failed");
1677 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001678
Michal Vaskof35ea502016-02-24 10:44:54 +01001679 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001680 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001681
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001682 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001683}
1684
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001685static json_object *
1686netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001687{
Michal Vaskof35ea502016-02-24 10:44:54 +01001688 struct nc_rpc *rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001689 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001690
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001691 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001692 rpc = nc_rpc_kill(atoi(sid));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001693 if (rpc == NULL) {
1694 ERROR("mod_netconf: creating rpc request failed");
1695 return create_error_reply("Internal: Creating rpc request failed");
1696 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001697
Michal Vaskof35ea502016-02-24 10:44:54 +01001698 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001699 nc_rpc_free(rpc);
1700 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001701}
1702
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001703static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001704netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, struct nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001705{
Michal Vaskof35ea502016-02-24 10:44:54 +01001706 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001707 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001708
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001709 /* create requests */
1710 rpc = op_func(target);
1711 if (rpc == NULL) {
1712 ERROR("mod_netconf: creating rpc request failed");
1713 return create_error_reply("Internal: Creating rpc request failed");
1714 }
Radek Krejci2f318372012-07-26 14:22:35 +02001715
Michal Vaskof35ea502016-02-24 10:44:54 +01001716 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001717 nc_rpc_free (rpc);
1718 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001719}
1720
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001721static json_object *
1722netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001723{
Michal Vaskof35ea502016-02-24 10:44:54 +01001724 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001725 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001726 rpc = nc_rpc_delete(target, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001727 if (rpc == NULL) {
1728 ERROR("mod_netconf: creating rpc request failed");
1729 return create_error_reply("Internal: Creating rpc request failed");
1730 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001731
Michal Vaskof35ea502016-02-24 10:44:54 +01001732 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001733 nc_rpc_free (rpc);
1734 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001735}
1736
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001737static json_object *
1738netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001739{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001740 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001741}
1742
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001743static json_object *
1744netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001745{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001746 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001747}
1748
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001749static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001750netconf_generic(unsigned int session_key, const char *xml_content, struct lyd_node **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001751{
Michal Vaskof35ea502016-02-24 10:44:54 +01001752 struct nc_rpc* rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001753 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001754
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001755 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001756 rpc = nc_rpc_generic_xml(xml_content, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001757 if (rpc == NULL) {
1758 ERROR("mod_netconf: creating rpc request failed");
1759 return create_error_reply("Internal: Creating rpc request failed");
1760 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001761
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001762 /* get session where send the RPC */
Michal Vaskof35ea502016-02-24 10:44:54 +01001763 res = netconf_op(session_key, rpc, 0, data);
1764 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001765 return res;
1766}
1767
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001768static int
Michal Vaskof35ea502016-02-24 10:44:54 +01001769node_add_metadata(const struct lys_node *node, const struct lys_module *module, json_object *parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001770{
1771 struct lys_module *cur_module;
1772 json_object *meta_obj;
1773 char *obj_name;
1774
Michal Vaskoa45770b2015-11-23 15:49:41 +01001775 if (node->nodetype == LYS_INPUT) {
1776 /* silently skipped */
1777 return 0;
1778 }
1779
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001780 cur_module = node->module;
1781 if (cur_module->type) {
1782 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1783 }
1784 if (cur_module == module) {
1785 asprintf(&obj_name, "$@%s", node->name);
1786 } else {
1787 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1788 }
1789
1790 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01001791 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001792 free(obj_name);
1793 return 1;
1794 }
1795
1796 meta_obj = json_object_new_object();
1797
1798 switch (node->nodetype) {
1799 case LYS_CONTAINER:
1800 node_metadata_container((struct lys_node_container *)node, meta_obj);
1801 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01001802 case LYS_CHOICE:
1803 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
1804 break;
1805 case LYS_LEAF:
1806 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
1807 break;
1808 case LYS_LEAFLIST:
1809 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
1810 break;
1811 case LYS_LIST:
1812 node_metadata_list((struct lys_node_list *)node, meta_obj);
1813 break;
1814 case LYS_ANYXML:
1815 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
1816 break;
1817 case LYS_CASE:
1818 node_metadata_case((struct lys_node_case *)node, meta_obj);
1819 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001820 case LYS_RPC:
1821 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
1822 break;
1823 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01001824 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
1825 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001826 }
1827
1828 /* just a precaution */
1829 if (json_object_get_type(parent) != json_type_object) {
1830 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1831 free(obj_name);
1832 return 1;
1833 }
1834
1835 json_object_object_add(parent, obj_name, meta_obj);
1836 free(obj_name);
1837 return 0;
1838}
1839
1840static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001841node_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 +01001842{
1843 struct lys_module *cur_module;
1844 struct lys_node *list_schema;
1845 struct lyd_node *child, *list_item;
1846 json_object *child_json, *list_child_json;
1847 char *child_name;
1848 int list_idx;
1849
Michal Vaskoa45770b2015-11-23 15:49:41 +01001850 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1851 return;
1852 }
1853
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001854 /* add data_tree metadata */
1855 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
1856 return;
1857 }
1858
1859 /* get data_tree module */
1860 cur_module = data_tree->schema->module;
1861 if (cur_module->type) {
1862 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1863 }
1864
1865 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1866 /* print correct data_tree JSON name */
1867 if (cur_module == module) {
1868 asprintf(&child_name, "%s", data_tree->schema->name);
1869 } else {
1870 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
1871 }
1872
1873 /* go down in JSON object */
1874 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
1875 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
1876 free(child_name);
1877 return;
1878 }
1879 free(child_name);
1880
1881 if (data_tree->schema->nodetype == LYS_LIST) {
1882 if (json_object_get_type(child_json) != json_type_array) {
1883 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1884 return;
1885 }
1886 /* go down in data tree for every item, we process them all now, skip later
1887 * (metadata duplicate will be detected at the beginning of this function) */
1888 list_idx = 0;
1889 list_schema = data_tree->schema;
1890
1891 LY_TREE_FOR(data_tree, list_item) {
1892 /* another list member */
1893 if (list_item->schema == list_schema) {
1894 list_child_json = json_object_array_get_idx(child_json, list_idx);
1895 if (!list_child_json) {
1896 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
1897 return;
1898 }
1899 LY_TREE_FOR(list_item->child, child) {
1900 node_add_metadata_recursive(child, cur_module, list_child_json);
1901 }
1902
1903 ++list_idx;
1904 }
1905 }
1906 } else {
1907 if (json_object_get_type(child_json) != json_type_object) {
1908 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1909 return;
1910 }
1911 /* go down in data tree */
1912 LY_TREE_FOR(data_tree->child, child) {
1913 node_add_metadata_recursive(child, cur_module, child_json);
1914 }
1915 }
1916 }
1917}
1918
1919static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001920node_add_model_metadata(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001921{
1922 json_object *obj;
1923 char *str;
1924
1925 obj = json_object_new_object();
1926 node_metadata_model(module, obj);
1927 asprintf(&str, "$@@%s", module->name);
1928 json_object_object_add(parent, str, obj);
1929 free(str);
1930}
1931
1932static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001933node_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 +01001934{
Michal Vaskof35ea502016-02-24 10:44:54 +01001935 const struct lys_module *cur_module;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001936 struct lys_node *child;
1937 json_object *node_json;
1938 char *json_name;
1939
Michal Vaskoa45770b2015-11-23 15:49:41 +01001940 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1941 return;
1942 }
1943
1944 if (node->nodetype & LYS_USES) {
1945 cur_module = module;
1946 node_json = parent;
1947 goto children;
1948 }
1949
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001950 /* add node metadata */
1951 if (node_add_metadata(node, module, parent)) {
1952 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
1953 return;
1954 }
1955
Michal Vaskoa45770b2015-11-23 15:49:41 +01001956 /* no other metadata */
1957 if (!node->child) {
1958 return;
1959 }
1960
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001961 /* get node module */
1962 cur_module = node->module;
1963 if (cur_module->type) {
1964 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1965 }
1966
1967 /* create JSON object for child metadata */
1968 node_json = json_object_new_object();
1969 if (cur_module == module) {
1970 json_object_object_add(parent, node->name, node_json);
1971 } else {
1972 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
1973 json_object_object_add(parent, json_name, node_json);
1974 free(json_name);
1975 }
1976
Michal Vaskoa45770b2015-11-23 15:49:41 +01001977children:
Michal Vaskoea2ddd92016-03-17 15:43:58 +01001978 if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1979 LY_TREE_FOR(node->child, child) {
1980 node_add_children_with_metadata_recursive(child, cur_module, node_json);
1981 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001982 }
1983}
1984
1985static json_object *
1986libyang_query(unsigned int session_key, const char *filter, int load_children)
1987{
Michal Vaskof35ea502016-02-24 10:44:54 +01001988 const struct lys_node *node;
1989 const struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001990 struct session_with_mutex *locked_session;
1991 json_object *ret = NULL, *data;
1992
1993 locked_session = session_get_locked(session_key, &ret);
1994 if (!locked_session) {
1995 ERROR("Locking failed or session not found.");
1996 goto finish;
1997 }
1998
Michal Vaskof35ea502016-02-24 10:44:54 +01001999 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002000
Michal Vaskoa45770b2015-11-23 15:49:41 +01002001 if (filter[0] == '/') {
Michal Vasko56ec5952016-04-05 14:30:23 +02002002 node = ly_ctx_get_node(nc_session_get_ctx(locked_session->session), NULL, filter);
Michal Vaskoa45770b2015-11-23 15:49:41 +01002003 if (!node) {
2004 ret = create_error_reply("Failed to resolve XPath filter node.");
2005 goto finish;
2006 }
2007 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002008 module = ly_ctx_get_module(nc_session_get_ctx(locked_session->session), filter, NULL);
Michal Vaskoa45770b2015-11-23 15:49:41 +01002009 if (!module) {
2010 ret = create_error_reply("Failed to find model.");
2011 goto finish;
2012 }
2013 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002014
Michal Vaskoa45770b2015-11-23 15:49:41 +01002015 pthread_mutex_lock(&json_lock);
2016 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002017
Michal Vaskoa45770b2015-11-23 15:49:41 +01002018 if (module) {
2019 node_add_model_metadata(module, data);
2020 if (load_children) {
2021 LY_TREE_FOR(module->data, node) {
2022 node_add_children_with_metadata_recursive(node, NULL, data);
2023 }
2024 }
2025 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002026 if (load_children) {
2027 node_add_children_with_metadata_recursive(node, NULL, data);
2028 } else {
2029 node_add_metadata(node, NULL, data);
2030 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002031 }
2032
Michal Vaskoa45770b2015-11-23 15:49:41 +01002033 pthread_mutex_unlock(&json_lock);
2034 ret = create_data_reply(json_object_to_json_string(data));
2035 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002036
2037finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002038 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002039 return ret;
2040}
2041
2042static json_object *
2043libyang_merge(unsigned int session_key, const char *config)
2044{
2045 struct lyd_node *data_tree = NULL, *sibling;
2046 struct session_with_mutex *locked_session;
2047 json_object *ret = NULL, *data_json = NULL;
2048 enum json_tokener_error err = 0;
2049
2050 locked_session = session_get_locked(session_key, &ret);
2051 if (!locked_session) {
2052 ERROR("Locking failed or session not found.");
2053 goto finish;
2054 }
2055
Michal Vaskof35ea502016-02-24 10:44:54 +01002056 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002057
Michal Vaskof35ea502016-02-24 10:44:54 +01002058 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 +01002059 if (!data_tree) {
2060 ERROR("Creating data tree failed.");
2061 ret = create_error_reply("Failed to create data tree from JSON config.");
2062 session_unlock(locked_session);
2063 goto finish;
2064 }
2065
2066 session_unlock(locked_session);
2067
2068 pthread_mutex_lock(&json_lock);
2069 data_json = json_tokener_parse_verbose(config, &err);
2070 if (!data_json) {
2071 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2072 pthread_mutex_unlock(&json_lock);
2073 ret = create_error_reply(json_tokener_error_desc(err));
2074 goto finish;
2075 }
2076
2077 /* go simultaneously through both trees and add metadata */
2078 LY_TREE_FOR(data_tree, sibling) {
2079 node_add_metadata_recursive(sibling, NULL, data_json);
2080 }
2081 pthread_mutex_unlock(&json_lock);
2082 ret = create_data_reply(json_object_to_json_string(data_json));
2083
2084finish:
2085 LY_TREE_FOR(data_tree, sibling) {
2086 lyd_free(sibling);
2087 }
2088 json_object_put(data_json);
2089 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002090}
2091
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002092/**
2093 * @}
2094 *//* netconf_operations */
2095
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002096void
2097clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002098{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002099 switch (level) {
Michal Vasko56c02b22016-04-06 09:59:09 +02002100 case NC_VERB_ERROR:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002101 ERROR("lib ERROR: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002102 break;
2103 case NC_VERB_WARNING:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002104 ERROR("lib WARNING: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002105 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002106 case NC_VERB_VERBOSE:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002107 ERROR("lib VERBOSE: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002108 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002109 case NC_VERB_DEBUG:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002110 DEBUG("lib DEBUG: %s", msg);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002111 break;
2112 }
Michal Vasko56c02b22016-04-06 09:59:09 +02002113
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002114 if (level == NC_VERB_ERROR) {
2115 /* return global error */
Michal Vasko56c02b22016-04-06 09:59:09 +02002116 netconf_callback_error_process(msg);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002117 }
Radek Krejci469aab82012-07-22 18:42:20 +02002118}
2119
Tomas Cejka64b87482013-06-03 16:30:53 +02002120/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002121 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002122 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002123 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002124 * \return pointer to message
2125 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002126char *
2127get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002128{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002129 /* read json in chunked framing */
2130 unsigned int buffer_size = 0;
2131 ssize_t buffer_len = 0;
2132 char *buffer = NULL;
2133 char c;
2134 ssize_t ret;
2135 int i, chunk_len;
2136 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002137
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002138 while (1) {
2139 /* read chunk length */
2140 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2141 if (buffer != NULL) {
2142 free (buffer);
2143 buffer = NULL;
2144 }
2145 break;
2146 }
2147 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2148 if (buffer != NULL) {
2149 free (buffer);
2150 buffer = NULL;
2151 }
2152 break;
2153 }
2154 i=0;
2155 memset (chunk_len_str, 0, 12);
2156 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2157 if (i==0 && c == '#') {
2158 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2159 /* end but invalid */
2160 if (buffer != NULL) {
2161 free (buffer);
2162 buffer = NULL;
2163 }
2164 }
2165 /* end of message, double-loop break */
2166 goto msg_complete;
2167 }
2168 chunk_len_str[i++] = c;
2169 if (i==11) {
2170 ERROR("Message is too long, buffer for length is not big enought!!!!");
2171 break;
2172 }
2173 }
2174 if (c != '\n') {
2175 if (buffer != NULL) {
2176 free (buffer);
2177 buffer = NULL;
2178 }
2179 break;
2180 }
2181 chunk_len_str[i] = 0;
2182 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2183 if (buffer != NULL) {
2184 free (buffer);
2185 buffer = NULL;
2186 }
2187 break;
2188 }
2189 buffer_size += chunk_len+1;
2190 buffer = realloc (buffer, sizeof(char)*buffer_size);
2191 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2192 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2193 if (buffer != NULL) {
2194 free (buffer);
2195 buffer = NULL;
2196 }
2197 break;
2198 }
2199 buffer_len += ret;
2200 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002201msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002202 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002203}
2204
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002205NC_DATASTORE
2206parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002207{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002208 if (strcmp(ds, "running") == 0) {
2209 return NC_DATASTORE_RUNNING;
2210 } else if (strcmp(ds, "startup") == 0) {
2211 return NC_DATASTORE_STARTUP;
2212 } else if (strcmp(ds, "candidate") == 0) {
2213 return NC_DATASTORE_CANDIDATE;
2214 } else if (strcmp(ds, "url") == 0) {
2215 return NC_DATASTORE_URL;
2216 } else if (strcmp(ds, "config") == 0) {
2217 return NC_DATASTORE_CONFIG;
2218 }
2219 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002220}
2221
Michal Vaskof35ea502016-02-24 10:44:54 +01002222NC_RPC_EDIT_TESTOPT
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002223parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002224{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002225 if (strcmp(t, "notset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002226 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002227 } else if (strcmp(t, "testset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002228 return NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002229 } else if (strcmp(t, "set") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002230 return NC_RPC_EDIT_TESTOPT_SET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002231 } else if (strcmp(t, "test") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002232 return NC_RPC_EDIT_TESTOPT_TEST;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002233 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002234 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002235}
2236
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002237json_object *
2238create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002239{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002240 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002241
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002242 pthread_mutex_lock(&json_lock);
2243 reply = json_object_new_object();
2244 array = json_object_new_array();
2245 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2246 json_object_array_add(array, json_object_new_string(errmess));
2247 json_object_object_add(reply, "errors", array);
2248 pthread_mutex_unlock(&json_lock);
2249
2250 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002251}
2252
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002253json_object *
2254create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002255{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002256 pthread_mutex_lock(&json_lock);
2257 json_object *reply = json_object_new_object();
2258 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2259 json_object_object_add(reply, "data", json_object_new_string(data));
2260 pthread_mutex_unlock(&json_lock);
2261 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002262}
2263
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002264json_object *
2265create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002266{
Michal Vasko365dc4c2016-03-17 10:03:58 +01002267 json_object *reply;
2268
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002269 pthread_mutex_lock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002270 reply = json_object_new_object();
2271 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2272 pthread_mutex_unlock(&json_lock);
2273 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002274}
2275
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002276json_object *
2277create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002278{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002279 json_object *replies;
2280
2281 pthread_mutex_lock(&json_lock);
2282 replies = json_object_new_object();
2283 pthread_mutex_unlock(&json_lock);
2284
2285 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002286}
2287
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002288void
2289add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002290{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002291 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002292
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002293 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002294
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002295 pthread_mutex_lock(&json_lock);
2296 json_object_object_add(replies, str, reply);
2297 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002298
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002299 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002300}
2301
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002302char *
2303get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002304{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002305 json_object *js_tmp = NULL;
2306 char *res = NULL;
2307 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2308 res = strdup(json_object_get_string(js_tmp));
2309 }
2310 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002311}
2312
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002313json_object *
2314handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002315{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002316 char *host = NULL;
2317 char *port = NULL;
2318 char *user = NULL;
2319 char *pass = NULL;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002320 char *privkey = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002321 json_object *reply = NULL;
2322 unsigned int session_key = 0;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002323
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002324 DEBUG("Request: connect");
2325 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002326
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002327 host = get_param_string(request, "host");
2328 port = get_param_string(request, "port");
2329 user = get_param_string(request, "user");
2330 pass = get_param_string(request, "pass");
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002331 privkey = get_param_string(request, "privatekey");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002332
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002333 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002334
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002335 if (host == NULL) {
2336 host = "localhost";
2337 }
2338
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002339 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002340 if (user == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002341 ERROR("Cannot connect - insufficient input.");
2342 session_key = 0;
2343 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002344 session_key = netconf_connect(host, port, user, pass, privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002345 DEBUG("Session key: %u", session_key);
2346 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002347
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002348 GETSPEC_ERR_REPLY
2349
2350 pthread_mutex_lock(&json_lock);
2351 if (session_key == 0) {
2352 /* negative reply */
2353 if (err_reply == NULL) {
2354 reply = json_object_new_object();
2355 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2356 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2357 ERROR("Connection failed.");
2358 } else {
2359 /* use filled err_reply from libnetconf's callback */
2360 reply = err_reply;
2361 ERROR("Connect - error from libnetconf's callback.");
2362 }
2363 } else {
2364 /* positive reply */
2365 reply = json_object_new_object();
2366 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2367 json_object_object_add(reply, "session", json_object_new_int(session_key));
2368 }
2369 memset(pass, 0, strlen(pass));
2370 pthread_mutex_unlock(&json_lock);
2371 CHECK_AND_FREE(host);
2372 CHECK_AND_FREE(user);
2373 CHECK_AND_FREE(port);
2374 CHECK_AND_FREE(pass);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002375 CHECK_AND_FREE(privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002376 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002377}
2378
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002379json_object *
2380handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002381{
Michal Vasko53260242016-04-06 11:18:18 +02002382 json_object *reply = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002383
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002384 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002385
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002386 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2387 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2388 } else {
2389 reply = create_ok_reply();
2390 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002391
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002392 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002393}
2394
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002395json_object *
2396handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002397{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002398 char *filter = NULL;
2399 char *data = NULL;
2400 json_object *reply = NULL, *obj;
2401 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002402
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002403 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002404
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002405 pthread_mutex_lock(&json_lock);
2406 filter = get_param_string(request, "filter");
2407 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2408 pthread_mutex_unlock(&json_lock);
2409 reply = create_error_reply("Missing strict parameter.");
Michal Vaskoedab4df2016-04-13 11:33:58 +02002410 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002411 }
2412 strict = json_object_get_boolean(obj);
2413 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002414
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002415 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2416 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2417 } else {
2418 reply = create_data_reply(data);
2419 free(data);
2420 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002421
Michal Vaskoedab4df2016-04-13 11:33:58 +02002422finalize:
2423 CHECK_AND_FREE(filter);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002424 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002425}
2426
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002427json_object *
2428handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002429{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002430 NC_DATASTORE ds_type_s = -1;
2431 char *filter = NULL;
2432 char *data = NULL;
2433 char *source = NULL;
2434 json_object *reply = NULL, *obj;
2435 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002436
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002437 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002438
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002439 pthread_mutex_lock(&json_lock);
2440 filter = get_param_string(request, "filter");
2441 source = get_param_string(request, "source");
2442 if (source != NULL) {
2443 ds_type_s = parse_datastore(source);
2444 }
2445 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2446 pthread_mutex_unlock(&json_lock);
2447 reply = create_error_reply("Missing strict parameter.");
Michal Vaskoedab4df2016-04-13 11:33:58 +02002448 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002449 }
2450 strict = json_object_get_boolean(obj);
2451 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002452
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002453 if ((int)ds_type_s == -1) {
2454 reply = create_error_reply("Invalid source repository type requested.");
2455 goto finalize;
2456 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002457
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002458 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2459 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2460 } else {
2461 reply = create_data_reply(data);
2462 free(data);
2463 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002464
Tomas Cejka09629492014-07-10 15:58:06 +02002465finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002466 CHECK_AND_FREE(filter);
2467 CHECK_AND_FREE(source);
2468 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002469}
2470
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002471json_object *
2472handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002473{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002474 NC_DATASTORE ds_type_t = -1;
Michal Vaskof35ea502016-02-24 10:44:54 +01002475 NC_RPC_EDIT_DFLTOP defop_type = 0;
2476 NC_RPC_EDIT_ERROPT erropt_type = 0;
2477 NC_RPC_EDIT_TESTOPT testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002478 char *defop = NULL;
2479 char *erropt = NULL;
2480 char *config = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002481 char *target = NULL;
2482 char *testopt = NULL;
2483 char *urisource = NULL;
2484 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002485 struct lyd_node *content;
2486 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002487
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002488 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002489
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002490 pthread_mutex_lock(&json_lock);
2491 /* get parameters */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002492 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2493 pthread_mutex_unlock(&json_lock);
2494 reply = create_error_reply("Missing configs parameter.");
2495 goto finalize;
2496 }
2497 obj = json_object_array_get_idx(configs, idx);
2498 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002499
Michal Vaskof35ea502016-02-24 10:44:54 +01002500 target = get_param_string(request, "target");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002501 defop = get_param_string(request, "default-operation");
2502 erropt = get_param_string(request, "error-option");
2503 urisource = get_param_string(request, "uri-source");
2504 testopt = get_param_string(request, "test-option");
2505 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002506
Michal Vaskob8f5d442016-04-05 14:48:37 +02002507 if (!target) {
2508 ERROR("Missing the target parameter.");
2509 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002510 }
Michal Vaskob8f5d442016-04-05 14:48:37 +02002511 ds_type_t = parse_datastore(target);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002512
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002513 if (defop != NULL) {
2514 if (strcmp(defop, "merge") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002515 defop_type = NC_RPC_EDIT_DFLTOP_MERGE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002516 } else if (strcmp(defop, "replace") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002517 defop_type = NC_RPC_EDIT_DFLTOP_REPLACE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002518 } else if (strcmp(defop, "none") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002519 defop_type = NC_RPC_EDIT_DFLTOP_NONE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002520 } else {
2521 reply = create_error_reply("Invalid default-operation parameter.");
2522 goto finalize;
2523 }
2524 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002525 defop_type = NC_RPC_EDIT_DFLTOP_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002526 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002527
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002528 if (erropt != NULL) {
2529 if (strcmp(erropt, "continue-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002530 erropt_type = NC_RPC_EDIT_ERROPT_CONTINUE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002531 } else if (strcmp(erropt, "stop-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002532 erropt_type = NC_RPC_EDIT_ERROPT_STOP;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002533 } else if (strcmp(erropt, "rollback-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002534 erropt_type = NC_RPC_EDIT_ERROPT_ROLLBACK;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002535 } else {
2536 reply = create_error_reply("Invalid error-option parameter.");
2537 goto finalize;
2538 }
2539 } else {
2540 erropt_type = 0;
2541 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002542
Michal Vaskof35ea502016-02-24 10:44:54 +01002543 if ((config && urisource) || (!config && !urisource)) {
2544 reply = create_error_reply("Invalid config and uri-source data parameters.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002545 goto finalize;
2546 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002547
2548 if (config) {
2549 locked_session = session_get_locked(session_key, NULL);
2550 if (!locked_session) {
2551 ERROR("Unknown session or locking failed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002552 goto finalize;
2553 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002554
2555 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_EDIT);
2556 session_unlock(locked_session);
2557
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002558 if (!content) {
2559 ERROR("Failed to parse edit-config content.");
2560 goto finalize;
2561 }
2562
Michal Vaskof35ea502016-02-24 10:44:54 +01002563 free(config);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002564 config = NULL;
2565
Michal Vaskof35ea502016-02-24 10:44:54 +01002566 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2567 lyd_free_withsiblings(content);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002568 if (!config) {
2569 ERROR("Failed to print edit-config content.");
2570 goto finalize;
2571 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002572 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002573 config = urisource;
2574 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002575
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002576 if (testopt != NULL) {
2577 testopt_type = parse_testopt(testopt);
2578 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002579 testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002580 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002581
Michal Vaskof35ea502016-02-24 10:44:54 +01002582 reply = netconf_editconfig(session_key, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002583
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002584 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002585
Tomas Cejka09629492014-07-10 15:58:06 +02002586finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002587 CHECK_AND_FREE(defop);
2588 CHECK_AND_FREE(erropt);
2589 CHECK_AND_FREE(config);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002590 CHECK_AND_FREE(urisource);
2591 CHECK_AND_FREE(target);
2592 CHECK_AND_FREE(testopt);
2593
2594 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002595}
2596
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002597json_object *
2598handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002599{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002600 NC_DATASTORE ds_type_s = -1;
2601 NC_DATASTORE ds_type_t = -1;
2602 char *config = NULL;
2603 char *target = NULL;
2604 char *source = NULL;
2605 char *uri_src = NULL;
2606 char *uri_trg = NULL;
2607 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002608 struct lyd_node *content;
2609 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002610
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002611 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002612
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002613 /* get parameters */
2614 pthread_mutex_lock(&json_lock);
2615 target = get_param_string(request, "target");
2616 source = get_param_string(request, "source");
2617 uri_src = get_param_string(request, "uri-source");
2618 uri_trg = get_param_string(request, "uri-target");
2619 if (!strcmp(source, "config")) {
2620 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2621 pthread_mutex_unlock(&json_lock);
2622 reply = create_error_reply("Missing configs parameter.");
2623 goto finalize;
2624 }
2625 obj = json_object_array_get_idx(configs, idx);
2626 if (!obj) {
2627 pthread_mutex_unlock(&json_lock);
2628 reply = create_error_reply("Configs array parameter shorter than sessions.");
2629 goto finalize;
2630 }
2631 config = strdup(json_object_get_string(obj));
2632 }
2633 pthread_mutex_unlock(&json_lock);
2634
2635 if (target != NULL) {
2636 ds_type_t = parse_datastore(target);
2637 }
2638 if (source != NULL) {
2639 ds_type_s = parse_datastore(source);
2640 }
2641
2642 if ((int)ds_type_s == -1) {
2643 /* invalid source datastore specified */
2644 reply = create_error_reply("Invalid source repository type requested.");
2645 goto finalize;
2646 }
2647
2648 if ((int)ds_type_t == -1) {
2649 /* invalid target datastore specified */
2650 reply = create_error_reply("Invalid target repository type requested.");
2651 goto finalize;
2652 }
2653
2654 if (ds_type_s == NC_DATASTORE_URL) {
2655 if (uri_src == NULL) {
2656 uri_src = "";
2657 }
2658 }
2659 if (ds_type_t == NC_DATASTORE_URL) {
2660 if (uri_trg == NULL) {
2661 uri_trg = "";
2662 }
2663 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002664
2665 if (config) {
2666 locked_session = session_get_locked(session_key, NULL);
2667 if (!locked_session) {
2668 ERROR("Unknown session or locking failed.");
2669 goto finalize;
2670 }
2671
2672 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_CONFIG);
2673 session_unlock(locked_session);
2674
2675 free(config);
2676 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2677 lyd_free_withsiblings(content);
2678 }
2679
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002680 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2681
2682 CHECK_ERR_SET_REPLY
2683
2684finalize:
2685 CHECK_AND_FREE(config);
2686 CHECK_AND_FREE(target);
2687 CHECK_AND_FREE(source);
2688 CHECK_AND_FREE(uri_src);
2689 CHECK_AND_FREE(uri_trg);
2690
2691 return reply;
2692}
2693
2694json_object *
2695handle_op_deleteconfig(json_object *request, unsigned int session_key)
2696{
2697 json_object *reply;
2698 NC_DATASTORE ds_type = -1;
2699 char *target, *url;
2700
2701 DEBUG("Request: delete-config (session %u)", session_key);
2702
2703 pthread_mutex_lock(&json_lock);
2704 target = get_param_string(request, "target");
2705 url = get_param_string(request, "url");
2706 pthread_mutex_unlock(&json_lock);
2707
2708 if (target != NULL) {
2709 ds_type = parse_datastore(target);
2710 }
2711 if ((int)ds_type == -1) {
2712 reply = create_error_reply("Invalid target repository type requested.");
2713 goto finalize;
2714 }
2715 if (ds_type == NC_DATASTORE_URL) {
2716 if (!url) {
2717 url = "";
2718 }
2719 }
2720
2721 reply = netconf_deleteconfig(session_key, ds_type, url);
2722
2723 CHECK_ERR_SET_REPLY
2724 if (reply == NULL) {
2725 reply = create_ok_reply();
2726 }
2727
2728finalize:
2729 CHECK_AND_FREE(target);
2730 CHECK_AND_FREE(url);
2731 return reply;
2732}
2733
2734json_object *
2735handle_op_lock(json_object *request, unsigned int session_key)
2736{
2737 json_object *reply;
2738 NC_DATASTORE ds_type = -1;
2739 char *target;
2740
2741 DEBUG("Request: lock (session %u)", session_key);
2742
2743 pthread_mutex_lock(&json_lock);
2744 target = get_param_string(request, "target");
2745 pthread_mutex_unlock(&json_lock);
2746
2747 if (target != NULL) {
2748 ds_type = parse_datastore(target);
2749 }
2750 if ((int)ds_type == -1) {
2751 reply = create_error_reply("Invalid target repository type requested.");
2752 goto finalize;
2753 }
2754
2755 reply = netconf_lock(session_key, ds_type);
2756
2757 CHECK_ERR_SET_REPLY
2758 if (reply == NULL) {
2759 reply = create_ok_reply();
2760 }
2761
2762finalize:
2763 CHECK_AND_FREE(target);
2764 return reply;
2765}
2766
2767json_object *
2768handle_op_unlock(json_object *request, unsigned int session_key)
2769{
2770 json_object *reply;
2771 NC_DATASTORE ds_type = -1;
2772 char *target;
2773
2774 DEBUG("Request: unlock (session %u)", session_key);
2775
2776 pthread_mutex_lock(&json_lock);
2777 target = get_param_string(request, "target");
2778 pthread_mutex_unlock(&json_lock);
2779
2780 if (target != NULL) {
2781 ds_type = parse_datastore(target);
2782 }
2783 if ((int)ds_type == -1) {
2784 reply = create_error_reply("Invalid target repository type requested.");
2785 goto finalize;
2786 }
2787
2788 reply = netconf_unlock(session_key, ds_type);
2789
2790 CHECK_ERR_SET_REPLY
2791 if (reply == NULL) {
2792 reply = create_ok_reply();
2793 }
2794
2795finalize:
2796 CHECK_AND_FREE(target);
2797 return reply;
2798}
2799
2800json_object *
2801handle_op_kill(json_object *request, unsigned int session_key)
2802{
2803 json_object *reply = NULL;
2804 char *sid = NULL;
2805
2806 DEBUG("Request: kill-session (session %u)", session_key);
2807
2808 pthread_mutex_lock(&json_lock);
2809 sid = get_param_string(request, "session-id");
2810 pthread_mutex_unlock(&json_lock);
2811
2812 if (sid == NULL) {
2813 reply = create_error_reply("Missing session-id parameter.");
2814 goto finalize;
2815 }
2816
2817 reply = netconf_killsession(session_key, sid);
2818
2819 CHECK_ERR_SET_REPLY
2820
2821finalize:
2822 CHECK_AND_FREE(sid);
2823 return reply;
2824}
2825
2826json_object *
2827handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2828{
2829 json_object *reply = NULL;
2830 struct session_with_mutex *locked_session = NULL;
2831 DEBUG("Request: get info about session %u", session_key);
2832
2833 DEBUG("LOCK wrlock %s", __func__);
2834 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2835 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2836 }
2837
2838 for (locked_session = netconf_sessions_list;
2839 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002840 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002841 if (locked_session != NULL) {
2842 DEBUG("LOCK mutex %s", __func__);
2843 pthread_mutex_lock(&locked_session->lock);
2844 DEBUG("UNLOCK wrlock %s", __func__);
2845 if (pthread_rwlock_unlock(&session_lock) != 0) {
2846 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2847 }
2848 if (locked_session->hello_message != NULL) {
Michal Vaskoa266afd2016-04-13 10:33:53 +02002849 reply = json_object_get(locked_session->hello_message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002850 } else {
2851 reply = create_error_reply("Invalid session identifier.");
2852 }
2853 DEBUG("UNLOCK mutex %s", __func__);
2854 pthread_mutex_unlock(&locked_session->lock);
2855 } else {
2856 DEBUG("UNLOCK wrlock %s", __func__);
2857 if (pthread_rwlock_unlock(&session_lock) != 0) {
2858 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2859 }
2860 reply = create_error_reply("Invalid session identifier.");
2861 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002862
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002863 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002864}
2865
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002866json_object *
2867handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002868{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002869 json_object *reply = NULL, *contents, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002870 char *content = NULL, *str;
2871 struct lyd_node *data = NULL, *node_content;
2872 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002873
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002874 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002875
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002876 pthread_mutex_lock(&json_lock);
2877 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2878 pthread_mutex_unlock(&json_lock);
2879 reply = create_error_reply("Missing contents parameter.");
2880 goto finalize;
2881 }
2882 obj = json_object_array_get_idx(contents, idx);
2883 if (!obj) {
2884 pthread_mutex_unlock(&json_lock);
2885 reply = create_error_reply("Contents array parameter shorter than sessions.");
2886 goto finalize;
2887 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002888 content = strdup(json_object_get_string(obj));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002889 pthread_mutex_unlock(&json_lock);
2890
Michal Vaskof35ea502016-02-24 10:44:54 +01002891 locked_session = session_get_locked(session_key, NULL);
2892 if (!locked_session) {
2893 ERROR("Unknown session or locking failed.");
2894 goto finalize;
2895 }
2896
2897 node_content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), content, LYD_JSON, LYD_OPT_RPC);
2898 session_unlock(locked_session);
2899
2900 free(content);
2901 lyd_print_mem(&content, node_content, LYD_XML, LYP_WITHSIBLINGS);
2902 lyd_free_withsiblings(node_content);
2903
2904 reply = netconf_generic(session_key, content, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002905 if (reply == NULL) {
2906 GETSPEC_ERR_REPLY
2907 if (err_reply != NULL) {
2908 /* use filled err_reply from libnetconf's callback */
2909 reply = err_reply;
2910 }
2911 } else {
2912 if (data == NULL) {
2913 pthread_mutex_lock(&json_lock);
2914 reply = json_object_new_object();
2915 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2916 pthread_mutex_unlock(&json_lock);
2917 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002918 lyd_print_mem(&str, data, LYD_JSON, LYP_WITHSIBLINGS);
2919 lyd_free_withsiblings(data);
2920 reply = create_data_reply(str);
2921 free(str);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002922 }
2923 }
2924
2925finalize:
Michal Vaskof35ea502016-02-24 10:44:54 +01002926 CHECK_AND_FREE(content);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002927 return reply;
2928}
2929
2930json_object *
2931handle_op_getschema(json_object *request, unsigned int session_key)
2932{
2933 char *data = NULL;
2934 char *identifier = NULL;
2935 char *version = NULL;
2936 char *format = NULL;
2937 json_object *reply = NULL;
2938
2939 DEBUG("Request: get-schema (session %u)", session_key);
2940
2941 pthread_mutex_lock(&json_lock);
2942 identifier = get_param_string(request, "identifier");
2943 version = get_param_string(request, "version");
2944 format = get_param_string(request, "format");
2945 pthread_mutex_unlock(&json_lock);
2946
2947 if (identifier == NULL) {
2948 reply = create_error_reply("No identifier for get-schema supplied.");
2949 goto finalize;
2950 }
2951
2952 DEBUG("get-schema(version: %s, format: %s)", version, format);
2953 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
2954 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
2955 } else {
2956 reply = create_data_reply(data);
2957 free(data);
2958 }
2959
2960finalize:
2961 CHECK_AND_FREE(identifier);
2962 CHECK_AND_FREE(version);
2963 CHECK_AND_FREE(format);
2964 return reply;
2965}
2966
2967json_object *
2968handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
2969{
2970 struct nc_session *temp_session = NULL;
2971 struct session_with_mutex * locked_session = NULL;
2972 json_object *reply = NULL;
2973
2974 DEBUG("Request: reload hello (session %u)", session_key);
2975
2976 DEBUG("LOCK wrlock %s", __func__);
2977 if (pthread_rwlock_wrlock(&session_lock) != 0) {
2978 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2979 return NULL;
2980 }
2981
2982 for (locked_session = netconf_sessions_list;
2983 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002984 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002985 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
2986 DEBUG("LOCK mutex %s", __func__);
2987 pthread_mutex_lock(&locked_session->lock);
2988 DEBUG("creating temporary NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01002989 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002990 if (temp_session != NULL) {
2991 prepare_status_message(locked_session, temp_session);
2992 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01002993 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002994 temp_session = NULL;
2995 } else {
2996 DEBUG("Reload hello failed due to channel establishment");
2997 reply = create_error_reply("Reload was unsuccessful, connection failed.");
2998 }
2999 DEBUG("UNLOCK mutex %s", __func__);
3000 pthread_mutex_unlock(&locked_session->lock);
3001 DEBUG("UNLOCK wrlock %s", __func__);
3002 if (pthread_rwlock_unlock(&session_lock) != 0) {
3003 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3004 }
3005 } else {
3006 DEBUG("UNLOCK wrlock %s", __func__);
3007 if (pthread_rwlock_unlock(&session_lock) != 0) {
3008 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3009 }
3010 reply = create_error_reply("Invalid session identifier.");
3011 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003012
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003013 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
Michal Vaskoa266afd2016-04-13 10:33:53 +02003014 reply = json_object_get(locked_session->hello_message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003015 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003016
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003017 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003018}
3019
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003020void
Michal Vaskof35ea502016-02-24 10:44:54 +01003021notification_history(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003022{
Michal Vaskof35ea502016-02-24 10:44:54 +01003023 time_t eventtime;
3024 char *content;
3025 (void)session;
3026
3027 eventtime = nc_datetime2time(notif->datetime);
3028
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003029 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3030 if (notif_history_array == NULL) {
3031 ERROR("No list of notification history found.");
3032 return;
3033 }
3034 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3035 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01003036 json_object *notif_obj = json_object_new_object();
3037 if (notif_obj == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003038 ERROR("Could not allocate memory for notification (json).");
3039 goto failed;
3040 }
Michal Vaskof35ea502016-02-24 10:44:54 +01003041 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
3042
3043 json_object_object_add(notif_obj, "eventtime", json_object_new_int64(eventtime));
3044 json_object_object_add(notif_obj, "content", json_object_new_string(content));
3045
3046 free(content);
3047
3048 json_object_array_add(notif_history_array, notif_obj);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003049failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003050 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003051}
3052
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003053json_object *
3054handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003055{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003056 json_object *reply = NULL;
3057 json_object *js_tmp = NULL;
3058 struct session_with_mutex *locked_session = NULL;
3059 struct nc_session *temp_session = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003060 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003061 time_t start = 0;
3062 time_t stop = 0;
3063 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003064
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003065 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003066
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003067 pthread_mutex_lock(&json_lock);
3068 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3069 from = json_object_get_int64(js_tmp);
3070 }
3071 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3072 to = json_object_get_int64(js_tmp);
3073 }
3074 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003075
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003076 start = time(NULL) + from;
3077 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003078
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003079 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003080
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003081 DEBUG("LOCK wrlock %s", __func__);
3082 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3083 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3084 reply = create_error_reply("Internal lock failed.");
3085 goto finalize;
3086 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003087
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003088 for (locked_session = netconf_sessions_list;
3089 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003090 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003091 if (locked_session != NULL) {
3092 DEBUG("LOCK mutex %s", __func__);
3093 pthread_mutex_lock(&locked_session->lock);
3094 DEBUG("UNLOCK wrlock %s", __func__);
3095 if (pthread_rwlock_unlock(&session_lock) != 0) {
3096 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3097 }
3098 DEBUG("creating temporal NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01003099 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003100 if (temp_session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003101 rpc = nc_rpc_subscribe(NULL, NULL, nc_time2datetime(start, NULL), nc_time2datetime(stop, NULL), NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003102 if (rpc == NULL) {
3103 DEBUG("UNLOCK mutex %s", __func__);
3104 pthread_mutex_unlock(&locked_session->lock);
3105 DEBUG("notifications: creating an rpc request failed.");
3106 reply = create_error_reply("notifications: creating an rpc request failed.");
3107 goto finalize;
3108 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003109
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003110 DEBUG("Send NC subscribe.");
3111 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3112 json_object *res = netconf_unlocked_op(temp_session, rpc);
3113 if (res != NULL) {
3114 DEBUG("UNLOCK mutex %s", __func__);
3115 pthread_mutex_unlock(&locked_session->lock);
3116 DEBUG("Subscription RPC failed.");
3117 reply = res;
3118 goto finalize;
3119 }
3120 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003121
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003122 DEBUG("UNLOCK mutex %s", __func__);
3123 pthread_mutex_unlock(&locked_session->lock);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003124 DEBUG("LOCK ntf mutex %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003125 pthread_mutex_lock(&ntf_history_lock);
3126 pthread_mutex_lock(&json_lock);
3127 json_object *notif_history_array = json_object_new_array();
3128 pthread_mutex_unlock(&json_lock);
3129 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3130 ERROR("notif_history: cannot set thread-specific hash value.");
3131 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003132
Michal Vaskof35ea502016-02-24 10:44:54 +01003133 nc_recv_notif_dispatch(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003134
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003135 pthread_mutex_lock(&json_lock);
3136 reply = json_object_new_object();
3137 json_object_object_add(reply, "notifications", notif_history_array);
3138 //json_object_put(notif_history_array);
3139 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003140
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003141 DEBUG("UNLOCK ntf mutex %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003142 pthread_mutex_unlock(&ntf_history_lock);
3143 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01003144 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003145 temp_session = NULL;
3146 } else {
3147 DEBUG("UNLOCK mutex %s", __func__);
3148 pthread_mutex_unlock(&locked_session->lock);
3149 DEBUG("Get history of notification failed due to channel establishment");
3150 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3151 }
3152 } else {
3153 DEBUG("UNLOCK wrlock %s", __func__);
3154 if (pthread_rwlock_unlock(&session_lock) != 0) {
3155 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3156 }
3157 reply = create_error_reply("Invalid session identifier.");
3158 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003159
Tomas Cejka09629492014-07-10 15:58:06 +02003160finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003161 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003162}
3163
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003164json_object *
3165handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003166{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003167 json_object *reply = NULL;
3168 char *target = NULL;
3169 char *url = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003170 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003171 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003172
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003173 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003174
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003175 pthread_mutex_lock(&json_lock);
3176 target = get_param_string(request, "target");
3177 url = get_param_string(request, "url");
3178 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003179
3180
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003181 if (target == NULL) {
3182 reply = create_error_reply("Missing target parameter.");
3183 goto finalize;
3184 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003185
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003186 /* validation */
3187 target_ds = parse_datastore(target);
Michal Vaskof35ea502016-02-24 10:44:54 +01003188 rpc = nc_rpc_validate(target_ds, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003189 if (rpc == NULL) {
3190 DEBUG("mod_netconf: creating rpc request failed");
3191 reply = create_error_reply("Creation of RPC request failed.");
3192 goto finalize;
3193 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003194
Michal Vaskof35ea502016-02-24 10:44:54 +01003195 if ((reply = netconf_op(session_key, rpc, 0, NULL)) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003196 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003197
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003198 if (reply == NULL) {
3199 DEBUG("Request: validation ok.");
3200 reply = create_ok_reply();
3201 }
3202 }
3203 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003204
Tomas Cejka09629492014-07-10 15:58:06 +02003205finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003206 CHECK_AND_FREE(target);
3207 CHECK_AND_FREE(url);
3208 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003209}
3210
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003211json_object *
3212handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003213{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003214 json_object *reply = NULL, *filters, *obj;
3215 char *filter = NULL;
3216 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003217
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003218 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003219
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003220 pthread_mutex_lock(&json_lock);
3221 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3222 pthread_mutex_unlock(&json_lock);
3223 reply = create_error_reply("Missing filters parameter.");
3224 goto finalize;
3225 }
3226 obj = json_object_array_get_idx(filters, idx);
3227 if (!obj) {
3228 pthread_mutex_unlock(&json_lock);
3229 reply = create_error_reply("Filters array parameter shorter than sessions.");
3230 goto finalize;
3231 }
3232 filter = strdup(json_object_get_string(obj));
3233 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3234 load_children = json_object_get_boolean(obj);
3235 }
3236 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003237
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003238 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003239
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003240 CHECK_ERR_SET_REPLY
3241 if (!reply) {
3242 reply = create_error_reply("Query failed.");
3243 }
David Kupka8e60a372012-09-04 09:15:20 +02003244
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003245finalize:
3246 CHECK_AND_FREE(filter);
3247 return reply;
3248}
David Kupka8e60a372012-09-04 09:15:20 +02003249
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003250json_object *
3251handle_op_merge(json_object *request, unsigned int session_key, int idx)
3252{
3253 json_object *reply = NULL, *configs, *obj;
3254 char *config = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003255 struct lyd_node *content;
3256 struct session_with_mutex *locked_session;
David Kupka8e60a372012-09-04 09:15:20 +02003257
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003258 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003259
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003260 pthread_mutex_lock(&json_lock);
3261 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3262 pthread_mutex_unlock(&json_lock);
3263 reply = create_error_reply("Missing configurations parameter.");
3264 goto finalize;
3265 }
3266 obj = json_object_array_get_idx(configs, idx);
3267 if (!obj) {
3268 pthread_mutex_unlock(&json_lock);
3269 reply = create_error_reply("Filters array parameter shorter than sessions.");
3270 goto finalize;
3271 }
3272 config = strdup(json_object_get_string(obj));
3273 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003274
Michal Vaskof35ea502016-02-24 10:44:54 +01003275 locked_session = session_get_locked(session_key, NULL);
3276 if (!locked_session) {
3277 ERROR("Unknown session or locking failed.");
3278 goto finalize;
3279 }
3280
3281 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_DATA);
3282 session_unlock(locked_session);
3283
3284 free(config);
3285 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
3286 lyd_free_withsiblings(content);
3287
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003288 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003289
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003290 CHECK_ERR_SET_REPLY
3291 if (!reply) {
3292 reply = create_error_reply("Merge failed.");
3293 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003294
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003295finalize:
3296 CHECK_AND_FREE(config);
3297 return reply;
3298}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003299
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003300void *
3301thread_routine(void *arg)
3302{
3303 void *retval = NULL;
3304 struct pollfd fds;
3305 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3306 json_object *js_tmp = NULL;
Michal Vaskodf280a22016-03-17 09:19:53 +01003307 int operation = (-1), count, i, sent;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003308 int status = 0;
3309 const char *msgtext;
3310 unsigned int session_key = 0;
3311 char *chunked_out_msg = NULL;
3312 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003313
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003314 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003315
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003316 /* init thread specific err_reply memory */
3317 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003318
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003319 while (!isterminated) {
3320 fds.fd = client;
3321 fds.events = POLLIN;
3322 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003323
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003324 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003325
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003326 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3327 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3328 continue;
3329 } else if (status < 0) {
3330 /* 0: poll time outed
3331 * close socket and ignore this request from the client, it can try it again
3332 * -1: poll failed
3333 * something wrong happend, close this socket and wait for another request
3334 */
3335 close(client);
3336 break;
3337 }
3338 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003339
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003340 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003341
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003342 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3343 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3344 /* close client's socket (it's probably already closed by client */
3345 close(client);
3346 break;
3347 }
Tomas Cejka09629492014-07-10 15:58:06 +02003348
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003349 buffer = get_framed_message(client);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003350 if (buffer != NULL) {
Michal Vaskoda384162016-04-12 15:33:23 +02003351 DEBUG("Received message:\n%.*s\n", 1024, buffer);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003352 enum json_tokener_error jerr;
3353 pthread_mutex_lock(&json_lock);
3354 request = json_tokener_parse_verbose(buffer, &jerr);
3355 if (jerr != json_tokener_success) {
3356 ERROR("JSON parsing error");
3357 pthread_mutex_unlock(&json_lock);
3358 continue;
3359 }
3360
3361 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3362 operation = json_object_get_int(js_tmp);
3363 }
3364 pthread_mutex_unlock(&json_lock);
3365 if (operation == -1) {
3366 replies = create_replies();
3367 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3368 goto send_reply;
3369 }
3370
3371 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3372 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3373 replies = create_replies();
3374 add_reply(replies, create_error_reply("Operation not supported."), 0);
3375 goto send_reply;
3376 }
3377
3378 DEBUG("operation %d", operation);
3379
3380 /* null global JSON error-reply */
3381 clean_err_reply();
3382
3383 /* clean replies envelope */
3384 if (replies != NULL) {
3385 pthread_mutex_lock(&json_lock);
3386 json_object_put(replies);
3387 pthread_mutex_unlock(&json_lock);
3388 }
3389 replies = create_replies();
3390
3391 if (operation == MSG_CONNECT) {
3392 count = 1;
3393 } else {
3394 pthread_mutex_lock(&json_lock);
3395 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
Michal Vasko642cad02016-03-17 12:31:18 +01003396 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003397 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3398 goto send_reply;
3399 }
3400 count = json_object_array_length(sessions);
3401 pthread_mutex_unlock(&json_lock);
3402 }
3403
3404 for (i = 0; i < count; ++i) {
3405 if (operation != MSG_CONNECT) {
3406 js_tmp = json_object_array_get_idx(sessions, i);
3407 session_key = json_object_get_int(js_tmp);
3408 }
3409
3410 /* process required operation */
Michal Vasko977bad92016-03-15 16:20:51 +01003411 reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003412 switch (operation) {
3413 case MSG_CONNECT:
3414 reply = handle_op_connect(request);
3415 break;
3416 case MSG_DISCONNECT:
3417 reply = handle_op_disconnect(request, session_key);
3418 break;
3419 case MSG_GET:
3420 reply = handle_op_get(request, session_key);
3421 break;
3422 case MSG_GETCONFIG:
3423 reply = handle_op_getconfig(request, session_key);
3424 break;
3425 case MSG_EDITCONFIG:
3426 reply = handle_op_editconfig(request, session_key, i);
3427 break;
3428 case MSG_COPYCONFIG:
3429 reply = handle_op_copyconfig(request, session_key, i);
3430 break;
3431 case MSG_DELETECONFIG:
3432 reply = handle_op_deleteconfig(request, session_key);
3433 break;
3434 case MSG_LOCK:
3435 reply = handle_op_lock(request, session_key);
3436 break;
3437 case MSG_UNLOCK:
3438 reply = handle_op_unlock(request, session_key);
3439 break;
3440 case MSG_KILL:
3441 reply = handle_op_kill(request, session_key);
3442 break;
3443 case MSG_INFO:
3444 reply = handle_op_info(request, session_key);
3445 break;
3446 case MSG_GENERIC:
3447 reply = handle_op_generic(request, session_key, i);
3448 break;
3449 case MSG_GETSCHEMA:
3450 reply = handle_op_getschema(request, session_key);
3451 break;
3452 case MSG_RELOADHELLO:
3453 reply = handle_op_reloadhello(request, session_key);
3454 break;
3455 case MSG_NTF_GETHISTORY:
3456 reply = handle_op_ntfgethistory(request, session_key);
3457 break;
3458 case MSG_VALIDATE:
3459 reply = handle_op_validate(request, session_key);
3460 break;
3461 case SCH_QUERY:
3462 reply = handle_op_query(request, session_key, i);
3463 break;
3464 case SCH_MERGE:
3465 reply = handle_op_merge(request, session_key, i);
3466 break;
3467 }
3468
3469 add_reply(replies, reply, session_key);
3470 }
3471
3472 /* free parameters */
3473 operation = (-1);
3474
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003475 if (request != NULL) {
3476 pthread_mutex_lock(&json_lock);
3477 json_object_put(request);
3478 pthread_mutex_unlock(&json_lock);
Michal Vasko365dc4c2016-03-17 10:03:58 +01003479 request = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003480 }
Tomas Cejka09629492014-07-10 15:58:06 +02003481
3482send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003483 /* send reply to caller */
3484 if (replies) {
3485 pthread_mutex_lock(&json_lock);
3486 msgtext = json_object_to_json_string(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003487 pthread_mutex_unlock(&json_lock);
Michal Vaskoda384162016-04-12 15:33:23 +02003488 DEBUG("Sending message:\n%.*s\n", 1024, msgtext);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003489 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3490 if (buffer != NULL) {
3491 free(buffer);
3492 buffer = NULL;
3493 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003494 break;
3495 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003496
Michal Vaskodf280a22016-03-17 09:19:53 +01003497 i = 0;
3498 sent = 0;
3499 count = strlen(chunked_out_msg) + 1;
3500 while (count && ((i = send(client, chunked_out_msg + sent, count, 0)) != -1)) {
3501 sent += i;
3502 count -= i;
3503 }
3504 if (i == -1) {
3505 ERROR("Sending message failed (%s).", strerror(errno));
3506 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003507 pthread_mutex_lock(&json_lock);
3508 json_object_put(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003509 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003510 replies = NULL;
Michal Vasko2314d402016-04-06 13:24:06 +02003511
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003512 CHECK_AND_FREE(chunked_out_msg);
3513 chunked_out_msg = NULL;
3514 if (buffer) {
3515 free(buffer);
3516 buffer = NULL;
3517 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003518 clean_err_reply();
3519 } else {
3520 ERROR("Reply is NULL, shouldn't be...");
3521 continue;
3522 }
3523 }
3524 }
3525 free(arg);
3526 free_err_reply();
Michal Vaskod0205992016-03-17 10:04:49 +01003527 nc_thread_destroy();
David Kupka8e60a372012-09-04 09:15:20 +02003528
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003529 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003530}
3531
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003532/**
3533 * \brief Close all open NETCONF sessions.
3534 *
3535 * During termination of mod_netconf, it is useful to close all remaining
3536 * sessions. This function iterates over the list of sessions and close them
3537 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003538 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003539static void
3540close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003541{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003542 struct session_with_mutex *locked_session, *next_session;
3543 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003544
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003545 /* get exclusive access to sessions_list (conns) */
3546 DEBUG("LOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003547 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003548 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3549 return;
3550 }
3551 for (next_session = netconf_sessions_list; next_session;) {
3552 locked_session = next_session;
3553 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003554
Michal Vaskoc3146782015-11-04 14:46:41 +01003555 /* close_and_free_session handles locking on its own */
Michal Vaskof35ea502016-02-24 10:44:54 +01003556 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 +01003557 close_and_free_session(locked_session);
3558 }
3559 netconf_sessions_list = NULL;
3560
3561 /* get exclusive access to sessions_list (conns) */
3562 DEBUG("UNLOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003563 if (pthread_rwlock_unlock(&session_lock) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003564 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3565 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003566}
3567
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003568static void
3569check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003570{
Michal Vaskoda384162016-04-12 15:33:23 +02003571 struct session_with_mutex *locked_session = NULL, *next_session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003572 time_t current_time = time(NULL);
3573 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003574
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003575 /* get exclusive access to sessions_list (conns) */
Michal Vaskoe41fb6e2016-04-12 08:53:23 +02003576 //DEBUG("LOCK wrlock %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003577 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3578 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3579 return;
3580 }
Michal Vaskoda384162016-04-12 15:33:23 +02003581
3582 locked_session = netconf_sessions_list;
3583 while (locked_session) {
3584 next_session = locked_session->next;
3585
3586 if (!locked_session->session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003587 continue;
3588 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003589 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003590 DEBUG("Closing NETCONF session %u (SID %u).", locked_session->session_key, nc_session_get_id(locked_session->session));
Tomas Cejka47387fd2013-06-10 20:37:46 +02003591
Michal Vaskoda384162016-04-12 15:33:23 +02003592 /* remove it from the list */
3593 if (!locked_session->prev) {
3594 netconf_sessions_list = netconf_sessions_list->next;
3595 if (netconf_sessions_list) {
3596 netconf_sessions_list->prev = NULL;
3597 }
3598 } else {
3599 locked_session->prev->next = locked_session->next;
3600 if (locked_session->next) {
3601 locked_session->next->prev = locked_session->prev;
3602 }
3603 }
3604
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003605 /* close_and_free_session handles locking on its own */
3606 close_and_free_session(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003607 }
Michal Vaskoda384162016-04-12 15:33:23 +02003608
3609 locked_session = next_session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003610 }
Michal Vaskoe41fb6e2016-04-12 08:53:23 +02003611 //DEBUG("UNLOCK wrlock %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003612 if (pthread_rwlock_unlock(&session_lock) != 0) {
3613 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3614 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003615}
3616
3617
3618/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003619 * This is actually implementation of NETCONF client
3620 * - requests are received from UNIX socket in the predefined format
3621 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003622 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003623 *
3624 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003625static void
3626forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003627{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003628 struct timeval tv;
3629 struct sockaddr_un local, remote;
3630 int lsock, client, ret, i, pthread_count = 0;
3631 unsigned int olds = 0, timediff = 0;
3632 socklen_t len;
3633 struct pass_to_thread *arg;
3634 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3635 struct timespec maxtime;
3636 pthread_rwlockattr_t lock_attrs;
3637 #ifdef WITH_NOTIFICATIONS
3638 char use_notifications = 0;
3639 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003640
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003641 /* wait at most 5 seconds for every thread to terminate */
3642 maxtime.tv_sec = 5;
3643 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003644
Tomas Cejka04e08f42014-03-27 19:52:34 +01003645#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003646 /* change uid and gid of process for security reasons */
3647 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003648#else
3649# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003650 if (strlen(SU_GROUP) > 0) {
3651 struct group *g = getgrnam(SU_GROUP);
3652 if (g == NULL) {
3653 ERROR("GID (%s) was not found.", SU_GROUP);
3654 return;
3655 }
3656 if (setgid(g->gr_gid) != 0) {
3657 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3658 return;
3659 }
3660 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003661# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003662 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003663# endif
3664# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003665 if (strlen(SU_USER) > 0) {
3666 struct passwd *p = getpwnam(SU_USER);
3667 if (p == NULL) {
3668 ERROR("UID (%s) was not found.", SU_USER);
3669 return;
3670 }
3671 if (setuid(p->pw_uid) != 0) {
3672 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3673 return;
3674 }
3675 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003676# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003677 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003678# endif
3679#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003680
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003681 /* try to remove if exists */
3682 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003683
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003684 /* create listening UNIX socket to accept incoming connections */
3685 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3686 ERROR("Creating socket failed (%s)", strerror(errno));
3687 goto error_exit;
3688 }
Radek Krejci469aab82012-07-22 18:42:20 +02003689
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003690 local.sun_family = AF_UNIX;
3691 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3692 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003693
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003694 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3695 if (errno == EADDRINUSE) {
3696 ERROR("mod_netconf socket address already in use");
3697 goto error_exit;
3698 }
3699 ERROR("Binding socket failed (%s)", strerror(errno));
3700 goto error_exit;
3701 }
Radek Krejci469aab82012-07-22 18:42:20 +02003702
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003703 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3704 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3705 goto error_exit;
3706 }
3707 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003708
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003709 uid_t user = -1;
3710 if (strlen(CHOWN_USER) > 0) {
3711 struct passwd *p = getpwnam(CHOWN_USER);
3712 if (p != NULL) {
3713 user = p->pw_uid;
3714 }
3715 }
3716 gid_t group = -1;
3717 if (strlen(CHOWN_GROUP) > 0) {
3718 struct group *g = getgrnam(CHOWN_GROUP);
3719 if (g != NULL) {
3720 group = g->gr_gid;
3721 }
3722 }
3723 if (chown(sockname, user, group) == -1) {
3724 ERROR("Chown on socket file failed (%s).", strerror(errno));
3725 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003726
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003727 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003728
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003729 #ifdef WITH_NOTIFICATIONS
3730 if (notification_init() == -1) {
3731 ERROR("libwebsockets initialization failed");
3732 use_notifications = 0;
3733 } else {
3734 use_notifications = 1;
3735 }
3736 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003737
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003738 /* setup libnetconf's callbacks */
Michal Vaskod0205992016-03-17 10:04:49 +01003739 nc_client_init();
Michal Vaskodedf91b2016-04-06 10:00:31 +02003740 nc_verbosity(NC_VERB_VERBOSE);
Michal Vaskof35ea502016-02-24 10:44:54 +01003741 nc_set_print_clb(clb_print);
3742 nc_client_ssh_set_auth_hostkey_check_clb(netconf_callback_ssh_hostkey_check);
3743 nc_client_ssh_set_auth_interactive_clb(netconf_callback_sshauth_interactive);
3744 nc_client_ssh_set_auth_password_clb(netconf_callback_sshauth_password);
3745 nc_client_ssh_set_auth_privkey_passphrase_clb(netconf_callback_sshauth_passphrase);
Radek Krejci469aab82012-07-22 18:42:20 +02003746
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003747 /* disable publickey authentication */
Michal Vaskof35ea502016-02-24 10:44:54 +01003748 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003749
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003750 /* create mutex protecting session list */
3751 pthread_rwlockattr_init(&lock_attrs);
3752 /* rwlock is shared only with threads in this process */
3753 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3754 /* create rw lock */
3755 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3756 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3757 goto error_exit;
3758 }
3759 pthread_mutex_init(&ntf_history_lock, NULL);
3760 pthread_mutex_init(&json_lock, NULL);
3761 DEBUG("Initialization of notification history.");
3762 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3763 ERROR("Initialization of notification history failed.");
3764 }
3765 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3766 ERROR("Initialization of reply key failed.");
3767 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003768
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003769 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3770 while (isterminated == 0) {
3771 gettimeofday(&tv, NULL);
3772 timediff = (unsigned int)tv.tv_sec - olds;
3773 #ifdef WITH_NOTIFICATIONS
3774 if (use_notifications == 1) {
3775 notification_handle();
3776 }
3777 #endif
3778 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3779 check_timeout_and_close();
3780 }
Radek Krejci469aab82012-07-22 18:42:20 +02003781
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003782 /* open incoming connection if any */
3783 len = sizeof(remote);
3784 client = accept(lsock, (struct sockaddr *) &remote, &len);
3785 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3786 usleep(SLEEP_TIME * 1000);
3787 continue;
3788 } else if (client == -1 && (errno == EINTR)) {
3789 continue;
3790 } else if (client == -1) {
3791 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3792 continue;
3793 }
Radek Krejci469aab82012-07-22 18:42:20 +02003794
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003795 /* set client's socket as non-blocking */
3796 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003797
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003798 arg = malloc(sizeof(struct pass_to_thread));
3799 arg->client = client;
3800 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003801
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003802 /* start new thread. It will serve this particular request and then terminate */
3803 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3804 ERROR("Creating POSIX thread failed: %d\n", ret);
3805 } else {
3806 DEBUG("Thread %lu created", ptids[pthread_count]);
3807 pthread_count++;
3808 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3809 ptids[pthread_count] = 0;
3810 }
Radek Krejci469aab82012-07-22 18:42:20 +02003811
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003812 /* check if some thread already terminated, free some resources by joining it */
3813 for (i = 0; i < pthread_count; i++) {
3814 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3815 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3816 pthread_count--;
3817 if (pthread_count > 0) {
3818 /* place last Thread ID on the place of joined one */
3819 ptids[i] = ptids[pthread_count];
3820 }
3821 }
3822 }
3823 DEBUG("Running %d threads", pthread_count);
3824 }
Radek Krejci469aab82012-07-22 18:42:20 +02003825
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003826 DEBUG("mod_netconf terminating...");
3827 /* join all threads */
3828 for (i = 0; i < pthread_count; i++) {
3829 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3830 }
Radek Krejci469aab82012-07-22 18:42:20 +02003831
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003832 #ifdef WITH_NOTIFICATIONS
3833 notification_close();
3834 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003835
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003836 /* close all NETCONF sessions */
3837 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003838
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003839 /* destroy rwlock */
3840 pthread_rwlock_destroy(&session_lock);
3841 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003842
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003843 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003844
Michal Vaskod0205992016-03-17 10:04:49 +01003845 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003846 free(ptids);
3847 close(lsock);
3848 exit(0);
3849 return;
3850
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003851error_exit:
Michal Vaskod0205992016-03-17 10:04:49 +01003852 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003853 close(lsock);
3854 free(ptids);
3855 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003856}
3857
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003858int
3859main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003860{
Michal Vaskoc3146782015-11-04 14:46:41 +01003861 struct sigaction action;
3862 sigset_t block_mask;
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003863 int i;
Michal Vaskoc3146782015-11-04 14:46:41 +01003864
Michal Vaskoa53ef882015-11-24 11:02:01 +01003865 if (argc > 3) {
3866 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3867 return 1;
3868 }
3869
3870 sockname = SOCKET_FILENAME;
3871 for (i = 1; i < argc; ++i) {
3872 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
3873 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3874 return 0;
3875 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon")) {
3876 daemonize = 1;
3877 } else {
3878 sockname = argv[i];
3879 }
3880 }
3881
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003882 if (daemonize) {
3883 if (daemon(0, 0) == -1) {
3884 ERROR("daemon() failed (%s)", strerror(errno));
3885 return 1;
3886 }
3887 openlog("netopeerguid", LOG_PID, LOG_DAEMON);
Michal Vaskoc3146782015-11-04 14:46:41 +01003888 }
3889
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003890 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003891 action.sa_handler = signal_handler;
3892 action.sa_mask = block_mask;
3893 action.sa_flags = 0;
3894 sigaction(SIGINT, &action, NULL);
3895 sigaction(SIGTERM, &action, NULL);
3896
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003897 forked_proc();
3898 DEBUG("Terminated");
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003899 if (daemonize) {
3900 closelog();
3901 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003902 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003903}