blob: 19588a2fb0c53c63f8b76933042adf8cbcc20f9e [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 }
Michal Vaskobf988812016-05-04 15:58:31 +0200352 /* get session where to send the RPC */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100353 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 }
Michal Vaskobf988812016-05-04 15:58:31 +0200360 goto rwlock_fail;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100361 }
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 }
Michal Vaskobf988812016-05-04 15:58:31 +0200369 goto rwlock_fail;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100370 }
371 return locked_session;
372
Michal Vaskobf988812016-05-04 15:58:31 +0200373rwlock_fail:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100374 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 Vasko0cfe7882016-05-02 14:44:33 +0200585 if (ident->der) {
586 for (i = 0; ident->der[i]; ++i) {
587 node_metadata_ident_recursive(ident->der[i], array);
588 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100589 }
590}
591
592static void
593node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
594{
595 json_object *obj, *array, *item;
596 char *str;
597 int i;
598
599 /* built-in YANG type */
600 if (!type->der->module) {
601 switch (type->base) {
602 case LY_TYPE_BINARY:
603 node_metadata_text("binary", "type", parent);
604 node_metadata_restr(type->info.binary.length, "length", parent);
605 break;
606 case LY_TYPE_BITS:
607 node_metadata_text("bits", "type", parent);
608
609 array = json_object_new_array();
610 for (i = 0; i < type->info.bits.count; ++i) {
611 item = json_object_new_object();
612 obj = json_object_new_string(type->info.bits.bit[i].name);
613 json_object_object_add(item, "name", obj);
614 obj = json_object_new_int(type->info.bits.bit[i].pos);
615 json_object_object_add(item, "position", obj);
616 json_object_array_add(array, item);
617 }
618 json_object_object_add(parent, "bits", array);
619 break;
620 case LY_TYPE_BOOL:
621 node_metadata_text("bool", "type", parent);
622 break;
623 case LY_TYPE_DEC64:
624 node_metadata_text("decimal64", "type", parent);
625 node_metadata_restr(type->info.dec64.range, "range", parent);
626 obj = json_object_new_int(type->info.dec64.dig);
627 json_object_object_add(parent, "fraction-digits", obj);
628 break;
629 case LY_TYPE_EMPTY:
630 node_metadata_text("empty", "type", parent);
631 break;
632 case LY_TYPE_ENUM:
633 node_metadata_text("enumeration", "type", parent);
634
635 array = json_object_new_array();
636 for (i = 0; i < type->info.enums.count; ++i) {
637 obj = json_object_new_string(type->info.enums.enm[i].name);
638 json_object_array_add(array, obj);
639 }
640 json_object_object_add(parent, "enumval", array);
641 break;
642 case LY_TYPE_IDENT:
643 node_metadata_text("identityref", "type", parent);
644
645 array = json_object_new_array();
646 node_metadata_ident_recursive(type->info.ident.ref, array);
647 json_object_object_add(parent, "identityval", array);
648 break;
649 case LY_TYPE_INST:
650 node_metadata_text("instance-identifier", "type", parent);
651 if (type->info.inst.req == -1) {
652 obj = json_object_new_boolean(0);
653 } else {
654 obj = json_object_new_boolean(1);
655 }
656 json_object_object_add(parent, "require-instance", obj);
657 break;
658 case LY_TYPE_LEAFREF:
659 node_metadata_text("leafref", "type", parent);
660 node_metadata_text(type->info.lref.path, "path", parent);
661 break;
662 case LY_TYPE_STRING:
663 node_metadata_text("string", "type", parent);
664 node_metadata_restr(type->info.str.length, "length", parent);
665 if (type->info.str.pat_count) {
666 array = json_object_new_array();
667 for (i = 0; i < type->info.str.pat_count; ++i) {
668 obj = json_object_new_string(type->info.str.patterns[i].expr);
669 json_object_array_add(array, obj);
670 }
671 json_object_object_add(parent, "pattern", array);
672 }
673 break;
674 case LY_TYPE_UNION:
675 node_metadata_text("union", "type", parent);
676 array = json_object_new_array();
677 for (i = 0; i < type->info.uni.count; ++i) {
678 obj = json_object_new_object();
679 node_metadata_type(&type->info.uni.types[i], module, obj);
680 json_object_array_add(array, obj);
681 }
682 json_object_object_add(parent, "types", array);
683 break;
684 case LY_TYPE_INT8:
685 node_metadata_text("int8", "type", parent);
686 node_metadata_restr(type->info.num.range, "range", parent);
687 break;
688 case LY_TYPE_UINT8:
689 node_metadata_text("uint8", "type", parent);
690 node_metadata_restr(type->info.num.range, "range", parent);
691 break;
692 case LY_TYPE_INT16:
693 node_metadata_text("int16", "type", parent);
694 node_metadata_restr(type->info.num.range, "range", parent);
695 break;
696 case LY_TYPE_UINT16:
697 node_metadata_text("uint16", "type", parent);
698 node_metadata_restr(type->info.num.range, "range", parent);
699 break;
700 case LY_TYPE_INT32:
701 node_metadata_text("int32", "type", parent);
702 node_metadata_restr(type->info.num.range, "range", parent);
703 break;
704 case LY_TYPE_UINT32:
705 node_metadata_text("uint32", "type", parent);
706 node_metadata_restr(type->info.num.range, "range", parent);
707 break;
708 case LY_TYPE_INT64:
709 node_metadata_text("int64", "type", parent);
710 node_metadata_restr(type->info.num.range, "range", parent);
711 break;
712 case LY_TYPE_UINT64:
713 node_metadata_text("uint64", "type", parent);
714 node_metadata_restr(type->info.num.range, "range", parent);
715 break;
716 default:
717 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
718 break;
719 }
720
721 /* typedef */
722 } else {
723 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
724 node_metadata_text(type->der->name, "type", parent);
725 } else {
726 asprintf(&str, "%s:%s", type->module_name, type->der->name);
727 node_metadata_text(str, "type", parent);
728 free(str);
729 }
730 obj = json_object_new_object();
731 node_metadata_typedef(type->der, obj);
732 json_object_object_add(parent, "typedef", obj);
733 }
734}
735
736static void
737node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
738{
739 json_object *obj;
740
741 /* description */
742 node_metadata_text(tpdf->dsc, "description", parent);
743
744 /* reference */
745 node_metadata_text(tpdf->ref, "reference", parent);
746
747 /* status */
748 if (tpdf->flags & LYS_STATUS_DEPRC) {
749 obj = json_object_new_string("deprecated");
750 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
751 obj = json_object_new_string("obsolete");
752 } else {
753 obj = json_object_new_string("current");
754 }
755 json_object_object_add(parent, "status", obj);
756
757 /* type */
758 node_metadata_type(&tpdf->type, tpdf->module, parent);
759
760 /* units */
761 node_metadata_text(tpdf->units, "units", parent);
762
763 /* default */
764 node_metadata_text(tpdf->dflt, "default", parent);
765}
766
767static void
768node_metadata_container(struct lys_node_container *cont, json_object *parent)
769{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100770 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100771
772 /* element type */
773 obj = json_object_new_string("container");
774 json_object_object_add(parent, "eltype", obj);
775
776 /* shared info */
777 node_metadata_basic((struct lys_node *)cont, parent);
778
779 /* must */
780 node_metadata_must(cont->must_size, cont->must, parent);
781
782 /* presence */
783 node_metadata_text(cont->presence, "presence", parent);
784
785 /* when */
786 node_metadata_when(cont->when, parent);
787
788 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100789 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
790 if (child_array) {
791 json_object_object_add(parent, "children", child_array);
792 }
793 if (choice_array) {
794 json_object_object_add(parent, "choice", choice_array);
795 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100796}
797
798static void
799node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
800{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100801 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100802
803 /* element type */
804 obj = json_object_new_string("choice");
805 json_object_object_add(parent, "eltype", obj);
806
807 /* shared info */
808 node_metadata_basic((struct lys_node *)choice, parent);
809
810 /* default */
Michal Vasko5e1c6052016-03-17 15:43:33 +0100811 if (choice->dflt) {
812 node_metadata_text(choice->dflt->name, "default", parent);
813 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100814
815 /* when */
816 node_metadata_when(choice->when, parent);
817
818 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100819 if (choice->child) {
820 array = json_object_new_array();
821 node_metadata_cases_recursive(choice, array);
822 json_object_object_add(parent, "cases", array);
823 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100824}
825
826static void
827node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
828{
829 json_object *obj;
830 struct lys_node_list *list;
831 int is_key, i;
832
833 /* element type */
834 obj = json_object_new_string("leaf");
835 json_object_object_add(parent, "eltype", obj);
836
837 /* shared info */
838 node_metadata_basic((struct lys_node *)leaf, parent);
839
840 /* type */
841 node_metadata_type(&leaf->type, leaf->module, parent);
842
843 /* units */
844 node_metadata_text(leaf->units, "units", parent);
845
846 /* default */
847 node_metadata_text(leaf->dflt, "default", parent);
848
849 /* must */
850 node_metadata_must(leaf->must_size, leaf->must, parent);
851
852 /* when */
853 node_metadata_when(leaf->when, parent);
854
855 /* iskey */
856 is_key = 0;
857 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
858 if (list && (list->nodetype == LYS_LIST)) {
859 for (i = 0; i < list->keys_size; ++i) {
860 if (list->keys[i] == leaf) {
861 is_key = 1;
862 break;
863 }
864 }
865 }
866 obj = json_object_new_boolean(is_key);
867 json_object_object_add(parent, "iskey", obj);
868}
869
870static void
871node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
872{
873 json_object *obj;
874
875 /* element type */
876 obj = json_object_new_string("leaf-list");
877 json_object_object_add(parent, "eltype", obj);
878
879 /* shared info */
880 node_metadata_basic((struct lys_node *)llist, parent);
881
882 /* type */
883 node_metadata_type(&llist->type, llist->module, parent);
884
885 /* units */
886 node_metadata_text(llist->units, "units", parent);
887
888 /* must */
889 node_metadata_must(llist->must_size, llist->must, parent);
890
891 /* when */
892 node_metadata_when(llist->when, parent);
893
894 /* min/max-elements */
895 node_metadata_min_max(llist->min, llist->max, parent);
896}
897
898static void
899node_metadata_list(struct lys_node_list *list, json_object *parent)
900{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100901 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100902 int i;
903 unsigned int j;
904
905 /* element type */
906 obj = json_object_new_string("list");
907 json_object_object_add(parent, "eltype", obj);
908
909 /* shared info */
910 node_metadata_basic((struct lys_node *)list, parent);
911
912 /* must */
913 node_metadata_must(list->must_size, list->must, parent);
914
915 /* when */
916 node_metadata_when(list->when, parent);
917
918 /* min/max-elements */
919 node_metadata_min_max(list->min, list->max, parent);
920
921 /* keys */
922 if (list->keys_size) {
923 array = json_object_new_array();
924 for (i = 0; i < list->keys_size; ++i) {
925 obj = json_object_new_string(list->keys[i]->name);
926 json_object_array_add(array, obj);
927 }
928 json_object_object_add(parent, "keys", array);
929 }
930
931 /* unique */
932 if (list->unique_size) {
933 array = json_object_new_array();
934 for (i = 0; i < list->unique_size; ++i) {
935 for (j = 0; j < list->unique[i].expr_size; ++j) {
936 obj = json_object_new_string(list->unique[i].expr[j]);
937 json_object_array_add(array, obj);
938 }
939 }
940 json_object_object_add(parent, "unique", array);
941 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100942
943 /* children & choice */
944 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
945 if (child_array) {
946 json_object_object_add(parent, "children", child_array);
947 }
948 if (choice_array) {
949 json_object_object_add(parent, "choice", choice_array);
950 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100951}
952
953static void
954node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
955{
956 json_object *obj;
957
958 /* element type */
959 obj = json_object_new_string("anyxml");
960 json_object_object_add(parent, "eltype", obj);
961
962 /* shared info */
963 node_metadata_basic((struct lys_node *)anyxml, parent);
964
965 /* must */
966 node_metadata_must(anyxml->must_size, anyxml->must, parent);
967
968 /* when */
969 node_metadata_when(anyxml->when, parent);
970
971}
972
973static void
974node_metadata_case(struct lys_node_case *cas, json_object *parent)
975{
976 json_object *obj;
977
978 /* element type */
979 obj = json_object_new_string("case");
980 json_object_object_add(parent, "eltype", obj);
981
982 /* shared info */
983 node_metadata_basic((struct lys_node *)cas, parent);
984
985 /* when */
986 node_metadata_when(cas->when, parent);
987}
988
Michal Vaskoa45770b2015-11-23 15:49:41 +0100989static void
990node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
991{
992 json_object *obj;
993
994 /* element type */
995 obj = json_object_new_string("rpc");
996 json_object_object_add(parent, "eltype", obj);
997
998 /* description */
999 node_metadata_text(rpc->dsc, "description", parent);
1000
1001 /* reference */
1002 node_metadata_text(rpc->ref, "reference", parent);
1003
1004 /* status */
1005 if (rpc->flags & LYS_STATUS_DEPRC) {
1006 obj = json_object_new_string("deprecated");
1007 } else if (rpc->flags & LYS_STATUS_OBSLT) {
1008 obj = json_object_new_string("obsolete");
1009 } else {
1010 obj = json_object_new_string("current");
1011 }
1012 json_object_object_add(parent, "status", obj);
1013}
1014
1015static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001016node_metadata_model(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001017{
Michal Vaskob1e28f22016-05-02 14:07:38 +02001018 json_object *obj, *array, *array2, *item;
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001019 const struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001020 int i;
1021
1022 /* yang-version */
1023 if (module->version == 2) {
1024 obj = json_object_new_string("1.1");
1025 } else {
1026 obj = json_object_new_string("1.0");
1027 }
1028 json_object_object_add(parent, "yang-version", obj);
1029
1030 /* namespace */
1031 node_metadata_text(module->ns, "namespace", parent);
1032
1033 /* prefix */
1034 node_metadata_text(module->prefix, "prefix", parent);
1035
1036 /* contact */
1037 node_metadata_text(module->contact, "contact", parent);
1038
1039 /* organization */
1040 node_metadata_text(module->org, "organization", parent);
1041
1042 /* revision */
1043 if (module->rev_size) {
1044 node_metadata_text(module->rev[0].date, "revision", parent);
1045 }
1046
1047 /* description */
1048 node_metadata_text(module->dsc, "description", parent);
1049
1050 /* import */
1051 if (module->imp_size) {
1052 array = json_object_new_array();
1053 for (i = 0; i < module->imp_size; ++i) {
1054 item = json_object_new_object();
1055
1056 node_metadata_text(module->imp[i].module->name, "name", item);
1057 node_metadata_text(module->imp[i].prefix, "prefix", item);
1058 if (module->imp[i].rev && module->imp[i].rev[0]) {
1059 node_metadata_text(module->imp[i].rev, "revision", item);
1060 }
1061
1062 json_object_array_add(array, item);
1063 }
1064 json_object_object_add(parent, "imports", array);
1065 }
1066
1067 /* include */
1068 if (module->inc_size) {
1069 array = json_object_new_array();
1070 for (i = 0; i < module->inc_size; ++i) {
1071 item = json_object_new_object();
1072
1073 node_metadata_text(module->inc[i].submodule->name, "name", item);
1074 if (module->inc[i].rev && module->inc[i].rev[0]) {
1075 node_metadata_text(module->inc[i].rev, "revision", item);
1076 }
1077
1078 json_object_array_add(array, item);
1079 }
1080 json_object_object_add(parent, "includes", array);
1081 }
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001082
Michal Vaskob1e28f22016-05-02 14:07:38 +02001083 /* top-nodes and RPCs */
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001084 node = NULL;
1085 array = NULL;
Michal Vaskob1e28f22016-05-02 14:07:38 +02001086 array2 = NULL;
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001087 while ((node = lys_getnext(node, NULL, module, LYS_GETNEXT_WITHCHOICE))) {
Michal Vaskob1e28f22016-05-02 14:07:38 +02001088 if (node->nodetype == LYS_NOTIF) {
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001089 continue;
1090 }
Michal Vaskob1e28f22016-05-02 14:07:38 +02001091 if (node->nodetype == LYS_RPC) {
1092 if (!array2) {
1093 array2 = json_object_new_array();
1094 }
1095 item = json_object_new_string(node->name);
1096 json_object_array_add(array2, item);
1097 } else {
1098 if (!array) {
1099 array = json_object_new_array();
1100 }
1101 item = json_object_new_string(node->name);
1102 json_object_array_add(array, item);
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001103 }
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001104 }
1105 if (array) {
1106 json_object_object_add(parent, "top-nodes", array);
1107 }
Michal Vaskob1e28f22016-05-02 14:07:38 +02001108 if (array2) {
1109 json_object_object_add(parent, "rpcs", array2);
1110 }
Michal Vaskoa45770b2015-11-23 15:49:41 +01001111}
1112
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001113/**
1114 * \defgroup netconf_operations NETCONF operations
1115 * The list of NETCONF operations that mod_netconf supports.
1116 * @{
1117 */
1118
1119/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001120 * \brief Send RPC and wait for reply with timeout.
1121 *
1122 * \param[in] session libnetconf session
1123 * \param[in] rpc prepared RPC message
1124 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1125 * \param[out] reply reply from the server
Michal Vaskof35ea502016-02-24 10:44:54 +01001126 * \return NC_MSG_WOULDBLOCK or NC_MSG_ERROR.
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001127 * On success, it returns NC_MSG_REPLY.
1128 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001129NC_MSG_TYPE
Michal Vaskof35ea502016-02-24 10:44:54 +01001130netconf_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 +02001131{
Michal Vaskof35ea502016-02-24 10:44:54 +01001132 uint64_t msgid;
1133 NC_MSG_TYPE ret;
1134 ret = nc_send_rpc(session, rpc, timeout, &msgid);
1135 if (ret != NC_MSG_RPC) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001136 return ret;
1137 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001138
1139 while ((ret = nc_recv_reply(session, rpc, msgid, timeout, (strict ? LYD_OPT_STRICT : 0), reply)) == NC_MSG_NOTIF);
1140
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001141 return ret;
1142}
1143
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001144/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001145 * \brief Connect to NETCONF server
1146 *
1147 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1148 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001149static unsigned int
Michal Vaskof35ea502016-02-24 10:44:54 +01001150netconf_connect(const char *host, const char *port, const char *user, const char *pass, const char *privkey)
Radek Krejci469aab82012-07-22 18:42:20 +02001151{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001152 struct nc_session* session = NULL;
1153 struct session_with_mutex *locked_session, *last_session;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001154 char *pubkey;
Radek Krejci469aab82012-07-22 18:42:20 +02001155
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001156 /* connect to the requested NETCONF server */
1157 password = (char*)pass;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001158 if (privkey) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001159 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 3);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001160 asprintf(&pubkey, "%s.pub", privkey);
Michal Vaskof35ea502016-02-24 10:44:54 +01001161 nc_client_ssh_add_keypair(pubkey, privkey);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001162 free(pubkey);
1163 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001164 nc_client_ssh_set_username(user);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001165 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Michal Vaskof35ea502016-02-24 10:44:54 +01001166 session = nc_connect_ssh(host, (unsigned short)atoi(port), NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001167 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001168
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001169 /* if connected successful, add session to the list */
1170 if (session != NULL) {
1171 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 +01001172 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001173 session = NULL;
1174 free(locked_session);
1175 locked_session = NULL;
1176 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1177 return 0;
1178 }
1179 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001180 locked_session->hello_message = NULL;
1181 locked_session->closed = 0;
1182 pthread_mutex_init(&locked_session->lock, NULL);
1183 DEBUG("Before session_lock");
1184 /* get exclusive access to sessions_list (conns) */
1185 DEBUG("LOCK wrlock %s", __func__);
1186 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001187 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001188 free(locked_session);
1189 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1190 return 0;
1191 }
1192 locked_session->ntfc_subscribed = 0;
1193 DEBUG("Add connection to the list");
1194 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001195 netconf_sessions_list = locked_session;
1196 } else {
1197 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1198 last_session->next = locked_session;
1199 locked_session->prev = last_session;
1200 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001201 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001202
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001203 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001204
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001205 /* store information about session from hello message for future usage */
1206 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001207
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001208 DEBUG("NETCONF session established");
1209 locked_session->session_key = session_key_generator;
1210 ++session_key_generator;
1211 if (session_key_generator == UINT_MAX) {
1212 session_key_generator = 1;
1213 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001214
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001215 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001216 /* unlock session list */
1217 DEBUG("UNLOCK wrlock %s", __func__);
1218 if (pthread_rwlock_unlock(&session_lock) != 0) {
1219 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1220 }
Radek Krejci469aab82012-07-22 18:42:20 +02001221
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001222 return locked_session->session_key;
1223 }
1224
1225 ERROR("Connection could not be established");
1226 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001227}
1228
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001229static int
1230close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001231{
Michal Vaskoa53ef882015-11-24 11:02:01 +01001232 int i;
1233
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001234 DEBUG("LOCK mutex %s", __func__);
1235 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1236 ERROR("Error while locking rwlock");
1237 }
1238 locked_session->ntfc_subscribed = 0;
1239 locked_session->closed = 1;
1240 if (locked_session->session != NULL) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001241 nc_session_free(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001242 locked_session->session = NULL;
1243 }
1244 DEBUG("session closed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001245 DEBUG("UNLOCK mutex %s", __func__);
1246 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1247 ERROR("Error while locking rwlock");
1248 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001249
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001250 DEBUG("closed session, disabled notif(?), wait 0.5s");
1251 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001252
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001253 /* session shouldn't be used by now */
Michal Vaskoa53ef882015-11-24 11:02:01 +01001254 for (i = 0; i < locked_session->notif_count; ++i) {
1255 free(locked_session->notifications[i].content);
1256 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001257 free(locked_session->notifications);
1258 pthread_mutex_destroy(&locked_session->lock);
1259 if (locked_session->hello_message != NULL) {
1260 json_object_put(locked_session->hello_message);
1261 locked_session->hello_message = NULL;
1262 }
1263 locked_session->session = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001264 free(locked_session);
1265 locked_session = NULL;
1266 DEBUG("NETCONF session closed, everything cleared.");
1267 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001268}
1269
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001270static int
1271netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001272{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001273 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001274
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001275 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001276
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001277 /* get exclusive (write) access to sessions_list (conns) */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001278 DEBUG("LOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02001279 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001280 ERROR("Error while locking rwlock");
1281 (*reply) = create_error_reply("Internal: Error while locking.");
1282 return EXIT_FAILURE;
1283 }
1284 /* remove session from the active sessions list -> nobody new can now work with session */
1285 for (locked_session = netconf_sessions_list;
1286 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001287 locked_session = locked_session->next);
1288
1289 if (!locked_session) {
Michal Vaskodc40b3a2016-04-11 14:36:16 +02001290 DEBUG("UNLOCK wrlock %s", __func__);
Michal Vasko642cad02016-03-17 12:31:18 +01001291 pthread_rwlock_unlock(&session_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001292 ERROR("Could not find the session %u to close.", session_key);
1293 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001294 return EXIT_FAILURE;
1295 }
1296
1297 if (!locked_session->prev) {
1298 netconf_sessions_list = netconf_sessions_list->next;
Michal Vaskof0b6fbb2016-03-08 12:04:34 +01001299 if (netconf_sessions_list) {
1300 netconf_sessions_list->prev = NULL;
1301 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001302 } else {
1303 locked_session->prev->next = locked_session->next;
1304 if (locked_session->next) {
1305 locked_session->next->prev = locked_session->prev;
1306 }
1307 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001308
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001309 DEBUG("UNLOCK wrlock %s", __func__);
1310 if (pthread_rwlock_unlock (&session_lock) != 0) {
1311 ERROR("Error while unlocking rwlock");
1312 (*reply) = create_error_reply("Internal: Error while unlocking.");
1313 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001314
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001315 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1316 return close_and_free_session(locked_session);
1317 } else {
1318 ERROR("Unknown session to close");
1319 (*reply) = create_error_reply("Internal: Unkown session to close.");
1320 return (EXIT_FAILURE);
1321 }
1322 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001323}
1324
Tomas Cejkac7929632013-10-24 19:25:15 +02001325/**
1326 * Test reply message type and return error message.
1327 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001328 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001329 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001330 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001331 * \param[out] data
1332 * \return NULL on success
1333 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001334json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001335netconf_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 +02001336{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001337 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001338
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001339 /* process the result of the operation */
1340 switch (msgt) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001341 case NC_MSG_ERROR:
1342 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001343 ERROR("mod_netconf: receiving rpc-reply failed");
1344 if (session_key) {
1345 netconf_close(session_key, &err);
1346 }
1347 if (err != NULL) {
1348 return err;
1349 }
1350 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1351 }
1352 case NC_MSG_NONE:
1353 /* there is error handled by callback */
1354 if (data != NULL) {
1355 free(*data);
1356 (*data) = NULL;
1357 }
1358 return NULL;
1359 case NC_MSG_REPLY:
Michal Vaskof35ea502016-02-24 10:44:54 +01001360 switch (reply->type) {
1361 case NC_RPL_OK:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001362 if ((data != NULL) && (*data != NULL)) {
1363 free(*data);
1364 (*data) = NULL;
1365 }
1366 return create_ok_reply();
Michal Vaskof35ea502016-02-24 10:44:54 +01001367 case NC_RPL_DATA:
1368 if (((*data) = ((struct nc_reply_data *)reply)->data) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001369 ERROR("mod_netconf: no data from reply");
1370 return create_error_reply("Internal: No data from reply received.");
1371 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001372 ((struct nc_reply_data *)reply)->data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001373 return NULL;
1374 }
1375 break;
Michal Vaskof35ea502016-02-24 10:44:54 +01001376 case NC_RPL_ERROR:
1377 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001378 if (data != NULL) {
1379 free(*data);
1380 (*data) = NULL;
1381 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001382 return create_error_reply(((struct nc_reply_error *)reply)->err[0].message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001383 default:
Michal Vaskof35ea502016-02-24 10:44:54 +01001384 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001385 if (data != NULL) {
1386 free(*data);
1387 (*data) = NULL;
1388 }
1389 return create_error_reply("Unknown type of NETCONF reply.");
1390 }
1391 break;
1392 default:
1393 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1394 if (data != NULL) {
1395 free(*data);
1396 (*data) = NULL;
1397 }
1398 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1399 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001400}
1401
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001402json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001403netconf_unlocked_op(struct nc_session *session, struct nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001404{
Michal Vaskof35ea502016-02-24 10:44:54 +01001405 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001406 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001407
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001408 /* check requests */
1409 if (rpc == NULL) {
1410 ERROR("mod_netconf: rpc is not created");
1411 return create_error_reply("Internal error: RPC is not created");
1412 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001413
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001414 if (session != NULL) {
1415 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001416 msgt = netconf_send_recv_timed(session, rpc, 50000, 0, &reply);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001417 /* process the result of the operation */
1418 return netconf_test_reply(session, 0, msgt, reply, NULL);
1419 } else {
1420 ERROR("Unknown session to process.");
1421 return create_error_reply("Internal error: Unknown session to process.");
1422 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001423}
1424
Tomas Cejkac7929632013-10-24 19:25:15 +02001425/**
1426 * Perform RPC method that returns data.
1427 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001428 * \param[in] session_id session identifier
1429 * \param[in] rpc RPC message to perform
1430 * \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 +02001431 * \return NULL on success, json object with error otherwise
1432 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001433static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001434netconf_op(unsigned int session_key, struct nc_rpc *rpc, int strict, struct lyd_node **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001435{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001436 struct session_with_mutex * locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001437 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001438 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001439 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001440 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001441
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001442 /* check requests */
1443 if (rpc == NULL) {
1444 ERROR("mod_netconf: rpc is not created");
1445 res = create_error_reply("Internal: RPC could not be created.");
1446 data = NULL;
1447 goto finished;
1448 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001449
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001450 locked_session = session_get_locked(session_key, &res);
1451 if (!locked_session) {
1452 ERROR("Unknown session or locking failed.");
1453 goto finished;
1454 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001455
Michal Vaskof35ea502016-02-24 10:44:54 +01001456 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001457
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001458 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001459 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, strict, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001460
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001461 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001462
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001463 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001464
Tomas Cejkac7929632013-10-24 19:25:15 +02001465finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001466 nc_reply_free(reply);
1467 if (received_data != NULL) {
1468 (*received_data) = data;
1469 } else {
1470 if (data != NULL) {
1471 free(data);
1472 data = NULL;
1473 }
1474 }
1475 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001476}
1477
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001478static char *
1479netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001480{
Michal Vaskof35ea502016-02-24 10:44:54 +01001481 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001482 struct session_with_mutex *locked_session;
Michal Vasko04245ee2016-03-16 09:32:59 +01001483 json_object *res = NULL, *data_cjson;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001484 enum json_tokener_error tok_err;
Michal Vasko04245ee2016-03-16 09:32:59 +01001485 char *data_json = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001486 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001487
Michal Vaskof35ea502016-02-24 10:44:54 +01001488 /* tell server to show all elements even if they have default values */
1489#ifdef HAVE_WITHDEFAULTS_TAGGED
1490 rpc = nc_rpc_getconfig(source, filter, NC_WD_MODE_ALL_TAG, NC_PARAMTYPE_CONST);
1491#else
1492 rpc = nc_rpc_getconfig(source, filter, 0, NC_PARAMTYPE_CONST);
1493#endif
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001494 if (rpc == NULL) {
1495 ERROR("mod_netconf: creating rpc request failed");
1496 return (NULL);
1497 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001498
Michal Vaskof35ea502016-02-24 10:44:54 +01001499 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001500 nc_rpc_free(rpc);
1501 if (res != NULL) {
1502 (*err) = res;
1503 } else {
1504 (*err) = NULL;
1505 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001506
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001507 if (data) {
1508 for (locked_session = netconf_sessions_list;
1509 locked_session && (locked_session->session_key != session_key);
1510 locked_session = locked_session->next);
1511 /* won't fail */
1512
Michal Vaskof35ea502016-02-24 10:44:54 +01001513 /* print data into JSON */
1514 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001515 ERROR("Printing JSON <get-config> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001516 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001517 return NULL;
1518 }
1519
1520 /* parse JSON data into cjson */
1521 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001522 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001523 if (!data_cjson) {
1524 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1525 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001526 lyd_free_withsiblings(data);
1527 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001528 return NULL;
1529 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001530 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001531
1532 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001533 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001534 node_add_metadata_recursive(sibling, NULL, data_cjson);
1535 lyd_free(sibling);
1536 }
1537
Michal Vaskof35ea502016-02-24 10:44:54 +01001538 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001539 json_object_put(data_cjson);
1540 pthread_mutex_unlock(&json_lock);
1541 }
1542
Michal Vaskof35ea502016-02-24 10:44:54 +01001543 return (data_json);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001544}
1545
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001546static char *
1547netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001548{
Michal Vaskof35ea502016-02-24 10:44:54 +01001549 struct nc_rpc *rpc;
1550 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001551 json_object *res = NULL;
Michal Vasko56ec5952016-04-05 14:30:23 +02001552 char *model_data = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001553
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001554 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001555 rpc = nc_rpc_getschema(identifier, version, format, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001556 if (rpc == NULL) {
1557 ERROR("mod_netconf: creating rpc request failed");
1558 return (NULL);
1559 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001560
Michal Vaskof35ea502016-02-24 10:44:54 +01001561 res = netconf_op(session_key, rpc, 0, &data);
1562 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001563 if (res != NULL) {
1564 (*err) = res;
1565 } else {
1566 (*err) = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001567
1568 if (data) {
Michal Vasko56ec5952016-04-05 14:30:23 +02001569 if (((struct lyd_node_anyxml *)data)->xml_struct) {
1570 lyxml_print_mem(&model_data, ((struct lyd_node_anyxml *)data)->value.xml, 0);
1571 } else {
1572 model_data = strdup(((struct lyd_node_anyxml *)data)->value.str);
1573 }
1574 if (!model_data) {
1575 ERROR("memory allocation fail (%s:%d)", __FILE__, __LINE__);
Michal Vaskof35ea502016-02-24 10:44:54 +01001576 }
1577 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001578 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001579
Michal Vaskof35ea502016-02-24 10:44:54 +01001580 return (model_data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001581}
1582
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001583static char *
1584netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001585{
Michal Vaskof35ea502016-02-24 10:44:54 +01001586 struct nc_rpc* rpc;
1587 char* data_json = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001588 json_object *res = NULL, *data_cjson;
1589 enum json_tokener_error tok_err;
1590 struct session_with_mutex *locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001591 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001592
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001593 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001594 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001595 if (rpc == NULL) {
1596 ERROR("mod_netconf: creating rpc request failed");
1597 return (NULL);
1598 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001599
Michal Vaskof35ea502016-02-24 10:44:54 +01001600 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001601 nc_rpc_free(rpc);
1602 if (res != NULL) {
1603 (*err) = res;
1604 } else {
1605 (*err) = NULL;
1606 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001607
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001608 if (data) {
1609 for (locked_session = netconf_sessions_list;
1610 locked_session && (locked_session->session_key != session_key);
1611 locked_session = locked_session->next);
1612 /* won't fail */
1613
Michal Vaskof35ea502016-02-24 10:44:54 +01001614 /* print JSON data */
1615 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001616 ERROR("Printing JSON <get> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001617 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001618 return NULL;
1619 }
1620
1621 /* parse JSON data into cjson */
1622 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001623 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001624 if (!data_cjson) {
1625 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1626 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001627 lyd_free_withsiblings(data);
1628 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001629 return NULL;
1630 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001631 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001632
1633 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001634 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001635 node_add_metadata_recursive(sibling, NULL, data_cjson);
1636 lyd_free(sibling);
1637 }
1638
Michal Vaskof35ea502016-02-24 10:44:54 +01001639 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001640 json_object_put(data_cjson);
1641 pthread_mutex_unlock(&json_lock);
1642 }
1643
Michal Vaskof35ea502016-02-24 10:44:54 +01001644 return data_json;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001645}
1646
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001647static json_object *
1648netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1649 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001650{
Michal Vaskof35ea502016-02-24 10:44:54 +01001651 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001652 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001653
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001654 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001655 rpc = nc_rpc_copy(target, uri_trg, source, (config ? config : uri_src), 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001656 if (rpc == NULL) {
1657 ERROR("mod_netconf: creating rpc request failed");
1658 return create_error_reply("Internal: Creating rpc request failed");
1659 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001660
Michal Vaskof35ea502016-02-24 10:44:54 +01001661 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001662 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001663
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001664 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001665}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001666
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001667static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001668netconf_editconfig(unsigned int session_key, NC_DATASTORE target, NC_RPC_EDIT_DFLTOP defop,
1669 NC_RPC_EDIT_ERROPT erropt, NC_RPC_EDIT_TESTOPT testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001670{
Michal Vaskof35ea502016-02-24 10:44:54 +01001671 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001672 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001673
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001674 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001675 rpc = nc_rpc_edit(target, defop, testopt, erropt, config_or_url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001676 if (rpc == NULL) {
1677 ERROR("mod_netconf: creating rpc request failed");
1678 return create_error_reply("Internal: Creating rpc request failed");
1679 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001680
Michal Vaskof35ea502016-02-24 10:44:54 +01001681 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001682 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001683
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001684 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001685}
1686
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001687static json_object *
1688netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001689{
Michal Vaskof35ea502016-02-24 10:44:54 +01001690 struct nc_rpc *rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001691 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001692
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001693 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001694 rpc = nc_rpc_kill(atoi(sid));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001695 if (rpc == NULL) {
1696 ERROR("mod_netconf: creating rpc request failed");
1697 return create_error_reply("Internal: Creating rpc request failed");
1698 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001699
Michal Vaskof35ea502016-02-24 10:44:54 +01001700 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001701 nc_rpc_free(rpc);
1702 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001703}
1704
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001705static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001706netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, struct nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001707{
Michal Vaskof35ea502016-02-24 10:44:54 +01001708 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001709 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001710
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001711 /* create requests */
1712 rpc = op_func(target);
1713 if (rpc == NULL) {
1714 ERROR("mod_netconf: creating rpc request failed");
1715 return create_error_reply("Internal: Creating rpc request failed");
1716 }
Radek Krejci2f318372012-07-26 14:22:35 +02001717
Michal Vaskof35ea502016-02-24 10:44:54 +01001718 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001719 nc_rpc_free (rpc);
1720 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001721}
1722
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001723static json_object *
1724netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001725{
Michal Vaskof35ea502016-02-24 10:44:54 +01001726 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001727 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001728 rpc = nc_rpc_delete(target, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001729 if (rpc == NULL) {
1730 ERROR("mod_netconf: creating rpc request failed");
1731 return create_error_reply("Internal: Creating rpc request failed");
1732 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001733
Michal Vaskof35ea502016-02-24 10:44:54 +01001734 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001735 nc_rpc_free (rpc);
1736 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001737}
1738
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001739static json_object *
1740netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001741{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001742 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001743}
1744
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001745static json_object *
1746netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001747{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001748 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001749}
1750
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001751static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001752netconf_generic(unsigned int session_key, const char *xml_content, struct lyd_node **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001753{
Michal Vaskof35ea502016-02-24 10:44:54 +01001754 struct nc_rpc* rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001755 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001756
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001757 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001758 rpc = nc_rpc_generic_xml(xml_content, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001759 if (rpc == NULL) {
1760 ERROR("mod_netconf: creating rpc request failed");
1761 return create_error_reply("Internal: Creating rpc request failed");
1762 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001763
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001764 /* get session where send the RPC */
Michal Vaskof35ea502016-02-24 10:44:54 +01001765 res = netconf_op(session_key, rpc, 0, data);
1766 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001767 return res;
1768}
1769
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001770static int
Michal Vaskof35ea502016-02-24 10:44:54 +01001771node_add_metadata(const struct lys_node *node, const struct lys_module *module, json_object *parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001772{
1773 struct lys_module *cur_module;
1774 json_object *meta_obj;
1775 char *obj_name;
1776
Michal Vaskoa45770b2015-11-23 15:49:41 +01001777 if (node->nodetype == LYS_INPUT) {
1778 /* silently skipped */
1779 return 0;
1780 }
1781
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001782 cur_module = node->module;
1783 if (cur_module->type) {
1784 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1785 }
1786 if (cur_module == module) {
1787 asprintf(&obj_name, "$@%s", node->name);
1788 } else {
1789 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1790 }
1791
1792 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01001793 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001794 free(obj_name);
1795 return 1;
1796 }
1797
1798 meta_obj = json_object_new_object();
1799
1800 switch (node->nodetype) {
1801 case LYS_CONTAINER:
1802 node_metadata_container((struct lys_node_container *)node, meta_obj);
1803 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01001804 case LYS_CHOICE:
1805 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
1806 break;
1807 case LYS_LEAF:
1808 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
1809 break;
1810 case LYS_LEAFLIST:
1811 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
1812 break;
1813 case LYS_LIST:
1814 node_metadata_list((struct lys_node_list *)node, meta_obj);
1815 break;
1816 case LYS_ANYXML:
1817 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
1818 break;
1819 case LYS_CASE:
1820 node_metadata_case((struct lys_node_case *)node, meta_obj);
1821 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001822 case LYS_RPC:
1823 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
1824 break;
1825 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01001826 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
1827 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001828 }
1829
1830 /* just a precaution */
1831 if (json_object_get_type(parent) != json_type_object) {
1832 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1833 free(obj_name);
1834 return 1;
1835 }
1836
1837 json_object_object_add(parent, obj_name, meta_obj);
1838 free(obj_name);
1839 return 0;
1840}
1841
1842static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001843node_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 +01001844{
1845 struct lys_module *cur_module;
1846 struct lys_node *list_schema;
1847 struct lyd_node *child, *list_item;
1848 json_object *child_json, *list_child_json;
1849 char *child_name;
1850 int list_idx;
1851
Michal Vaskoa45770b2015-11-23 15:49:41 +01001852 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1853 return;
1854 }
1855
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001856 /* add data_tree metadata */
1857 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
1858 return;
1859 }
1860
1861 /* get data_tree module */
1862 cur_module = data_tree->schema->module;
1863 if (cur_module->type) {
1864 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1865 }
1866
1867 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1868 /* print correct data_tree JSON name */
1869 if (cur_module == module) {
1870 asprintf(&child_name, "%s", data_tree->schema->name);
1871 } else {
1872 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
1873 }
1874
1875 /* go down in JSON object */
1876 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
1877 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
1878 free(child_name);
1879 return;
1880 }
1881 free(child_name);
1882
1883 if (data_tree->schema->nodetype == LYS_LIST) {
1884 if (json_object_get_type(child_json) != json_type_array) {
1885 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1886 return;
1887 }
1888 /* go down in data tree for every item, we process them all now, skip later
1889 * (metadata duplicate will be detected at the beginning of this function) */
1890 list_idx = 0;
1891 list_schema = data_tree->schema;
1892
1893 LY_TREE_FOR(data_tree, list_item) {
1894 /* another list member */
1895 if (list_item->schema == list_schema) {
1896 list_child_json = json_object_array_get_idx(child_json, list_idx);
1897 if (!list_child_json) {
1898 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
1899 return;
1900 }
1901 LY_TREE_FOR(list_item->child, child) {
1902 node_add_metadata_recursive(child, cur_module, list_child_json);
1903 }
1904
1905 ++list_idx;
1906 }
1907 }
1908 } else {
1909 if (json_object_get_type(child_json) != json_type_object) {
1910 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1911 return;
1912 }
1913 /* go down in data tree */
1914 LY_TREE_FOR(data_tree->child, child) {
1915 node_add_metadata_recursive(child, cur_module, child_json);
1916 }
1917 }
1918 }
1919}
1920
1921static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001922node_add_model_metadata(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001923{
1924 json_object *obj;
1925 char *str;
1926
1927 obj = json_object_new_object();
1928 node_metadata_model(module, obj);
1929 asprintf(&str, "$@@%s", module->name);
1930 json_object_object_add(parent, str, obj);
1931 free(str);
1932}
1933
1934static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001935node_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 +01001936{
Michal Vaskof35ea502016-02-24 10:44:54 +01001937 const struct lys_module *cur_module;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001938 struct lys_node *child;
1939 json_object *node_json;
1940 char *json_name;
1941
Michal Vaskoa45770b2015-11-23 15:49:41 +01001942 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1943 return;
1944 }
1945
1946 if (node->nodetype & LYS_USES) {
1947 cur_module = module;
1948 node_json = parent;
1949 goto children;
1950 }
1951
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001952 /* add node metadata */
1953 if (node_add_metadata(node, module, parent)) {
1954 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
1955 return;
1956 }
1957
Michal Vaskoa45770b2015-11-23 15:49:41 +01001958 /* no other metadata */
1959 if (!node->child) {
1960 return;
1961 }
1962
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001963 /* get node module */
1964 cur_module = node->module;
1965 if (cur_module->type) {
1966 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1967 }
1968
1969 /* create JSON object for child metadata */
1970 node_json = json_object_new_object();
1971 if (cur_module == module) {
1972 json_object_object_add(parent, node->name, node_json);
1973 } else {
1974 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
1975 json_object_object_add(parent, json_name, node_json);
1976 free(json_name);
1977 }
1978
Michal Vaskoa45770b2015-11-23 15:49:41 +01001979children:
Michal Vaskoea2ddd92016-03-17 15:43:58 +01001980 if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1981 LY_TREE_FOR(node->child, child) {
1982 node_add_children_with_metadata_recursive(child, cur_module, node_json);
1983 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001984 }
1985}
1986
1987static json_object *
Michal Vasko23b30752016-05-06 14:29:10 +02001988libyang_query(unsigned int session_key, json_object *filter_array, int load_children)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001989{
Michal Vasko23b30752016-05-06 14:29:10 +02001990 int i;
1991 const char *filter;
Michal Vaskof35ea502016-02-24 10:44:54 +01001992 const struct lys_node *node;
1993 const struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001994 struct session_with_mutex *locked_session;
Michal Vasko23b30752016-05-06 14:29:10 +02001995 json_object *ret = NULL, *data = NULL, *obj;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001996
1997 locked_session = session_get_locked(session_key, &ret);
1998 if (!locked_session) {
1999 ERROR("Locking failed or session not found.");
2000 goto finish;
2001 }
2002
Michal Vaskof35ea502016-02-24 10:44:54 +01002003 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002004
Michal Vasko23b30752016-05-06 14:29:10 +02002005 for (i = 0; i < json_object_array_length(filter_array); ++i) {
2006 obj = json_object_array_get_idx(filter_array, i);
2007 filter = json_object_get_string(obj);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002008
Michal Vasko23b30752016-05-06 14:29:10 +02002009 if (filter[0] == '/') {
2010 node = ly_ctx_get_node(nc_session_get_ctx(locked_session->session), NULL, filter);
2011 if (!node) {
2012 ret = create_error_reply("Failed to resolve XPath filter node.");
2013 goto finish;
2014 }
2015 } else {
2016 module = ly_ctx_get_module(nc_session_get_ctx(locked_session->session), filter, NULL);
2017 if (!module) {
2018 ret = create_error_reply("Failed to find model.");
2019 goto finish;
Michal Vaskoa45770b2015-11-23 15:49:41 +01002020 }
2021 }
Michal Vasko23b30752016-05-06 14:29:10 +02002022
2023 pthread_mutex_lock(&json_lock);
2024 if (!data) {
2025 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002026 }
Michal Vasko23b30752016-05-06 14:29:10 +02002027
2028 if (module) {
2029 node_add_model_metadata(module, data);
2030 if (load_children) {
2031 LY_TREE_FOR(module->data, node) {
2032 node_add_children_with_metadata_recursive(node, NULL, data);
2033 }
2034 }
2035 } else {
2036 if (load_children) {
2037 node_add_children_with_metadata_recursive(node, NULL, data);
2038 } else {
2039 node_add_metadata(node, NULL, data);
2040 }
2041 }
2042
2043 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002044 }
2045
Michal Vaskoa45770b2015-11-23 15:49:41 +01002046 ret = create_data_reply(json_object_to_json_string(data));
2047 json_object_put(data);
Michal Vasko23b30752016-05-06 14:29:10 +02002048 data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002049
2050finish:
Michal Vasko23b30752016-05-06 14:29:10 +02002051 json_object_put(data);
Michal Vaskoa45770b2015-11-23 15:49:41 +01002052 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002053 return ret;
2054}
2055
2056static json_object *
2057libyang_merge(unsigned int session_key, const char *config)
2058{
2059 struct lyd_node *data_tree = NULL, *sibling;
2060 struct session_with_mutex *locked_session;
2061 json_object *ret = NULL, *data_json = NULL;
2062 enum json_tokener_error err = 0;
2063
2064 locked_session = session_get_locked(session_key, &ret);
2065 if (!locked_session) {
2066 ERROR("Locking failed or session not found.");
2067 goto finish;
2068 }
2069
Michal Vaskof35ea502016-02-24 10:44:54 +01002070 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002071
Michal Vaskof35ea502016-02-24 10:44:54 +01002072 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 +01002073 if (!data_tree) {
2074 ERROR("Creating data tree failed.");
2075 ret = create_error_reply("Failed to create data tree from JSON config.");
2076 session_unlock(locked_session);
2077 goto finish;
2078 }
2079
2080 session_unlock(locked_session);
2081
2082 pthread_mutex_lock(&json_lock);
2083 data_json = json_tokener_parse_verbose(config, &err);
2084 if (!data_json) {
2085 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2086 pthread_mutex_unlock(&json_lock);
2087 ret = create_error_reply(json_tokener_error_desc(err));
2088 goto finish;
2089 }
2090
2091 /* go simultaneously through both trees and add metadata */
2092 LY_TREE_FOR(data_tree, sibling) {
2093 node_add_metadata_recursive(sibling, NULL, data_json);
2094 }
2095 pthread_mutex_unlock(&json_lock);
2096 ret = create_data_reply(json_object_to_json_string(data_json));
2097
2098finish:
2099 LY_TREE_FOR(data_tree, sibling) {
2100 lyd_free(sibling);
2101 }
2102 json_object_put(data_json);
2103 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002104}
2105
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002106/**
2107 * @}
2108 *//* netconf_operations */
2109
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002110void
2111clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002112{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002113 switch (level) {
Michal Vasko56c02b22016-04-06 09:59:09 +02002114 case NC_VERB_ERROR:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002115 ERROR("lib ERROR: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002116 break;
2117 case NC_VERB_WARNING:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002118 ERROR("lib WARNING: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002119 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002120 case NC_VERB_VERBOSE:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002121 ERROR("lib VERBOSE: %s", msg);
Michal Vasko56c02b22016-04-06 09:59:09 +02002122 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002123 case NC_VERB_DEBUG:
Michal Vaskoefdd49f2016-04-06 11:13:27 +02002124 DEBUG("lib DEBUG: %s", msg);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002125 break;
2126 }
Michal Vasko56c02b22016-04-06 09:59:09 +02002127
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002128 if (level == NC_VERB_ERROR) {
2129 /* return global error */
Michal Vasko56c02b22016-04-06 09:59:09 +02002130 netconf_callback_error_process(msg);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002131 }
Radek Krejci469aab82012-07-22 18:42:20 +02002132}
2133
Tomas Cejka64b87482013-06-03 16:30:53 +02002134/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002135 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002136 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002137 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002138 * \return pointer to message
2139 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002140char *
2141get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002142{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002143 /* read json in chunked framing */
2144 unsigned int buffer_size = 0;
2145 ssize_t buffer_len = 0;
2146 char *buffer = NULL;
2147 char c;
2148 ssize_t ret;
2149 int i, chunk_len;
2150 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002151
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002152 while (1) {
2153 /* read chunk length */
2154 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2155 if (buffer != NULL) {
2156 free (buffer);
2157 buffer = NULL;
2158 }
2159 break;
2160 }
2161 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2162 if (buffer != NULL) {
2163 free (buffer);
2164 buffer = NULL;
2165 }
2166 break;
2167 }
2168 i=0;
2169 memset (chunk_len_str, 0, 12);
2170 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2171 if (i==0 && c == '#') {
2172 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2173 /* end but invalid */
2174 if (buffer != NULL) {
2175 free (buffer);
2176 buffer = NULL;
2177 }
2178 }
2179 /* end of message, double-loop break */
2180 goto msg_complete;
2181 }
2182 chunk_len_str[i++] = c;
2183 if (i==11) {
2184 ERROR("Message is too long, buffer for length is not big enought!!!!");
2185 break;
2186 }
2187 }
2188 if (c != '\n') {
2189 if (buffer != NULL) {
2190 free (buffer);
2191 buffer = NULL;
2192 }
2193 break;
2194 }
2195 chunk_len_str[i] = 0;
2196 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2197 if (buffer != NULL) {
2198 free (buffer);
2199 buffer = NULL;
2200 }
2201 break;
2202 }
2203 buffer_size += chunk_len+1;
2204 buffer = realloc (buffer, sizeof(char)*buffer_size);
2205 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2206 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2207 if (buffer != NULL) {
2208 free (buffer);
2209 buffer = NULL;
2210 }
2211 break;
2212 }
2213 buffer_len += ret;
2214 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002215msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002216 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002217}
2218
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002219NC_DATASTORE
2220parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002221{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002222 if (strcmp(ds, "running") == 0) {
2223 return NC_DATASTORE_RUNNING;
2224 } else if (strcmp(ds, "startup") == 0) {
2225 return NC_DATASTORE_STARTUP;
2226 } else if (strcmp(ds, "candidate") == 0) {
2227 return NC_DATASTORE_CANDIDATE;
2228 } else if (strcmp(ds, "url") == 0) {
2229 return NC_DATASTORE_URL;
2230 } else if (strcmp(ds, "config") == 0) {
2231 return NC_DATASTORE_CONFIG;
2232 }
2233 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002234}
2235
Michal Vaskof35ea502016-02-24 10:44:54 +01002236NC_RPC_EDIT_TESTOPT
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002237parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002238{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002239 if (strcmp(t, "notset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002240 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002241 } else if (strcmp(t, "testset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002242 return NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002243 } else if (strcmp(t, "set") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002244 return NC_RPC_EDIT_TESTOPT_SET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002245 } else if (strcmp(t, "test") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002246 return NC_RPC_EDIT_TESTOPT_TEST;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002247 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002248 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002249}
2250
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002251json_object *
2252create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002253{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002254 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002255
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002256 pthread_mutex_lock(&json_lock);
2257 reply = json_object_new_object();
2258 array = json_object_new_array();
2259 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2260 json_object_array_add(array, json_object_new_string(errmess));
2261 json_object_object_add(reply, "errors", array);
2262 pthread_mutex_unlock(&json_lock);
2263
2264 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002265}
2266
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002267json_object *
2268create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002269{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002270 pthread_mutex_lock(&json_lock);
2271 json_object *reply = json_object_new_object();
2272 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2273 json_object_object_add(reply, "data", json_object_new_string(data));
2274 pthread_mutex_unlock(&json_lock);
2275 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002276}
2277
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002278json_object *
2279create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002280{
Michal Vasko365dc4c2016-03-17 10:03:58 +01002281 json_object *reply;
2282
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002283 pthread_mutex_lock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002284 reply = json_object_new_object();
2285 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2286 pthread_mutex_unlock(&json_lock);
2287 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002288}
2289
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002290json_object *
2291create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002292{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002293 json_object *replies;
2294
2295 pthread_mutex_lock(&json_lock);
2296 replies = json_object_new_object();
2297 pthread_mutex_unlock(&json_lock);
2298
2299 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002300}
2301
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002302void
2303add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002304{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002305 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002306
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002307 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002308
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002309 pthread_mutex_lock(&json_lock);
2310 json_object_object_add(replies, str, reply);
2311 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002312
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002313 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002314}
2315
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002316char *
2317get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002318{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002319 json_object *js_tmp = NULL;
2320 char *res = NULL;
2321 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2322 res = strdup(json_object_get_string(js_tmp));
2323 }
2324 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002325}
2326
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002327json_object *
2328handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002329{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002330 char *host = NULL;
2331 char *port = NULL;
2332 char *user = NULL;
2333 char *pass = NULL;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002334 char *privkey = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002335 json_object *reply = NULL;
2336 unsigned int session_key = 0;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002337
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002338 DEBUG("Request: connect");
2339 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002340
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002341 host = get_param_string(request, "host");
2342 port = get_param_string(request, "port");
2343 user = get_param_string(request, "user");
2344 pass = get_param_string(request, "pass");
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002345 privkey = get_param_string(request, "privatekey");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002346
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002347 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002348
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002349 if (host == NULL) {
2350 host = "localhost";
2351 }
2352
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002353 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002354 if (user == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002355 ERROR("Cannot connect - insufficient input.");
2356 session_key = 0;
2357 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002358 session_key = netconf_connect(host, port, user, pass, privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002359 DEBUG("Session key: %u", session_key);
2360 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002361
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002362 GETSPEC_ERR_REPLY
2363
2364 pthread_mutex_lock(&json_lock);
2365 if (session_key == 0) {
2366 /* negative reply */
2367 if (err_reply == NULL) {
2368 reply = json_object_new_object();
2369 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2370 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2371 ERROR("Connection failed.");
2372 } else {
2373 /* use filled err_reply from libnetconf's callback */
2374 reply = err_reply;
2375 ERROR("Connect - error from libnetconf's callback.");
2376 }
2377 } else {
2378 /* positive reply */
2379 reply = json_object_new_object();
2380 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2381 json_object_object_add(reply, "session", json_object_new_int(session_key));
2382 }
2383 memset(pass, 0, strlen(pass));
2384 pthread_mutex_unlock(&json_lock);
2385 CHECK_AND_FREE(host);
2386 CHECK_AND_FREE(user);
2387 CHECK_AND_FREE(port);
2388 CHECK_AND_FREE(pass);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002389 CHECK_AND_FREE(privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002390 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002391}
2392
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002393json_object *
2394handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002395{
Michal Vasko53260242016-04-06 11:18:18 +02002396 json_object *reply = NULL;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002397
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002398 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002399
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002400 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2401 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2402 } else {
2403 reply = create_ok_reply();
2404 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002405
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002406 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002407}
2408
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002409json_object *
2410handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002411{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002412 char *filter = NULL;
2413 char *data = NULL;
2414 json_object *reply = NULL, *obj;
2415 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002416
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002417 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002418
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002419 pthread_mutex_lock(&json_lock);
2420 filter = get_param_string(request, "filter");
2421 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2422 pthread_mutex_unlock(&json_lock);
2423 reply = create_error_reply("Missing strict parameter.");
Michal Vaskoedab4df2016-04-13 11:33:58 +02002424 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002425 }
2426 strict = json_object_get_boolean(obj);
2427 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002428
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002429 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2430 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2431 } else {
2432 reply = create_data_reply(data);
2433 free(data);
2434 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002435
Michal Vaskoedab4df2016-04-13 11:33:58 +02002436finalize:
2437 CHECK_AND_FREE(filter);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002438 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002439}
2440
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002441json_object *
2442handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002443{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002444 NC_DATASTORE ds_type_s = -1;
2445 char *filter = NULL;
2446 char *data = NULL;
2447 char *source = NULL;
2448 json_object *reply = NULL, *obj;
2449 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002450
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002451 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002452
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002453 pthread_mutex_lock(&json_lock);
2454 filter = get_param_string(request, "filter");
2455 source = get_param_string(request, "source");
2456 if (source != NULL) {
2457 ds_type_s = parse_datastore(source);
2458 }
2459 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2460 pthread_mutex_unlock(&json_lock);
2461 reply = create_error_reply("Missing strict parameter.");
Michal Vaskoedab4df2016-04-13 11:33:58 +02002462 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002463 }
2464 strict = json_object_get_boolean(obj);
2465 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002466
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002467 if ((int)ds_type_s == -1) {
2468 reply = create_error_reply("Invalid source repository type requested.");
2469 goto finalize;
2470 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002471
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002472 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2473 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2474 } else {
2475 reply = create_data_reply(data);
2476 free(data);
2477 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002478
Tomas Cejka09629492014-07-10 15:58:06 +02002479finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002480 CHECK_AND_FREE(filter);
2481 CHECK_AND_FREE(source);
2482 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002483}
2484
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002485json_object *
2486handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002487{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002488 NC_DATASTORE ds_type_t = -1;
Michal Vaskof35ea502016-02-24 10:44:54 +01002489 NC_RPC_EDIT_DFLTOP defop_type = 0;
2490 NC_RPC_EDIT_ERROPT erropt_type = 0;
2491 NC_RPC_EDIT_TESTOPT testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002492 char *defop = NULL;
2493 char *erropt = NULL;
2494 char *config = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002495 char *target = NULL;
2496 char *testopt = NULL;
2497 char *urisource = NULL;
2498 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002499 struct lyd_node *content;
2500 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002501
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002502 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002503
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002504 pthread_mutex_lock(&json_lock);
2505 /* get parameters */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002506 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2507 pthread_mutex_unlock(&json_lock);
2508 reply = create_error_reply("Missing configs parameter.");
2509 goto finalize;
2510 }
2511 obj = json_object_array_get_idx(configs, idx);
2512 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002513
Michal Vaskof35ea502016-02-24 10:44:54 +01002514 target = get_param_string(request, "target");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002515 defop = get_param_string(request, "default-operation");
2516 erropt = get_param_string(request, "error-option");
2517 urisource = get_param_string(request, "uri-source");
2518 testopt = get_param_string(request, "test-option");
2519 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002520
Michal Vaskob8f5d442016-04-05 14:48:37 +02002521 if (!target) {
2522 ERROR("Missing the target parameter.");
2523 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002524 }
Michal Vaskob8f5d442016-04-05 14:48:37 +02002525 ds_type_t = parse_datastore(target);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002526
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002527 if (defop != NULL) {
2528 if (strcmp(defop, "merge") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002529 defop_type = NC_RPC_EDIT_DFLTOP_MERGE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002530 } else if (strcmp(defop, "replace") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002531 defop_type = NC_RPC_EDIT_DFLTOP_REPLACE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002532 } else if (strcmp(defop, "none") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002533 defop_type = NC_RPC_EDIT_DFLTOP_NONE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002534 } else {
2535 reply = create_error_reply("Invalid default-operation parameter.");
2536 goto finalize;
2537 }
2538 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002539 defop_type = NC_RPC_EDIT_DFLTOP_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002540 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002541
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002542 if (erropt != NULL) {
2543 if (strcmp(erropt, "continue-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002544 erropt_type = NC_RPC_EDIT_ERROPT_CONTINUE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002545 } else if (strcmp(erropt, "stop-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002546 erropt_type = NC_RPC_EDIT_ERROPT_STOP;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002547 } else if (strcmp(erropt, "rollback-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002548 erropt_type = NC_RPC_EDIT_ERROPT_ROLLBACK;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002549 } else {
2550 reply = create_error_reply("Invalid error-option parameter.");
2551 goto finalize;
2552 }
2553 } else {
2554 erropt_type = 0;
2555 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002556
Michal Vaskof35ea502016-02-24 10:44:54 +01002557 if ((config && urisource) || (!config && !urisource)) {
2558 reply = create_error_reply("Invalid config and uri-source data parameters.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002559 goto finalize;
2560 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002561
2562 if (config) {
2563 locked_session = session_get_locked(session_key, NULL);
2564 if (!locked_session) {
2565 ERROR("Unknown session or locking failed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002566 goto finalize;
2567 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002568
2569 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_EDIT);
2570 session_unlock(locked_session);
2571
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002572 if (!content) {
2573 ERROR("Failed to parse edit-config content.");
2574 goto finalize;
2575 }
2576
Michal Vaskof35ea502016-02-24 10:44:54 +01002577 free(config);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002578 config = NULL;
2579
Michal Vaskof35ea502016-02-24 10:44:54 +01002580 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2581 lyd_free_withsiblings(content);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002582 if (!config) {
2583 ERROR("Failed to print edit-config content.");
2584 goto finalize;
2585 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002586 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002587 config = urisource;
2588 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002589
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002590 if (testopt != NULL) {
2591 testopt_type = parse_testopt(testopt);
2592 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002593 testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002594 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002595
Michal Vaskof35ea502016-02-24 10:44:54 +01002596 reply = netconf_editconfig(session_key, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002597
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002598 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002599
Tomas Cejka09629492014-07-10 15:58:06 +02002600finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002601 CHECK_AND_FREE(defop);
2602 CHECK_AND_FREE(erropt);
2603 CHECK_AND_FREE(config);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002604 CHECK_AND_FREE(urisource);
2605 CHECK_AND_FREE(target);
2606 CHECK_AND_FREE(testopt);
2607
2608 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002609}
2610
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002611json_object *
2612handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002613{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002614 NC_DATASTORE ds_type_s = -1;
2615 NC_DATASTORE ds_type_t = -1;
2616 char *config = NULL;
2617 char *target = NULL;
2618 char *source = NULL;
2619 char *uri_src = NULL;
2620 char *uri_trg = NULL;
2621 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002622 struct lyd_node *content;
2623 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002624
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002625 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002626
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002627 /* get parameters */
2628 pthread_mutex_lock(&json_lock);
2629 target = get_param_string(request, "target");
2630 source = get_param_string(request, "source");
2631 uri_src = get_param_string(request, "uri-source");
2632 uri_trg = get_param_string(request, "uri-target");
2633 if (!strcmp(source, "config")) {
2634 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2635 pthread_mutex_unlock(&json_lock);
2636 reply = create_error_reply("Missing configs parameter.");
2637 goto finalize;
2638 }
2639 obj = json_object_array_get_idx(configs, idx);
2640 if (!obj) {
2641 pthread_mutex_unlock(&json_lock);
2642 reply = create_error_reply("Configs array parameter shorter than sessions.");
2643 goto finalize;
2644 }
2645 config = strdup(json_object_get_string(obj));
2646 }
2647 pthread_mutex_unlock(&json_lock);
2648
2649 if (target != NULL) {
2650 ds_type_t = parse_datastore(target);
2651 }
2652 if (source != NULL) {
2653 ds_type_s = parse_datastore(source);
2654 }
2655
2656 if ((int)ds_type_s == -1) {
2657 /* invalid source datastore specified */
2658 reply = create_error_reply("Invalid source repository type requested.");
2659 goto finalize;
2660 }
2661
2662 if ((int)ds_type_t == -1) {
2663 /* invalid target datastore specified */
2664 reply = create_error_reply("Invalid target repository type requested.");
2665 goto finalize;
2666 }
2667
2668 if (ds_type_s == NC_DATASTORE_URL) {
2669 if (uri_src == NULL) {
2670 uri_src = "";
2671 }
2672 }
2673 if (ds_type_t == NC_DATASTORE_URL) {
2674 if (uri_trg == NULL) {
2675 uri_trg = "";
2676 }
2677 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002678
2679 if (config) {
2680 locked_session = session_get_locked(session_key, NULL);
2681 if (!locked_session) {
2682 ERROR("Unknown session or locking failed.");
2683 goto finalize;
2684 }
2685
2686 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_CONFIG);
2687 session_unlock(locked_session);
2688
2689 free(config);
2690 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2691 lyd_free_withsiblings(content);
2692 }
2693
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002694 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2695
2696 CHECK_ERR_SET_REPLY
2697
2698finalize:
2699 CHECK_AND_FREE(config);
2700 CHECK_AND_FREE(target);
2701 CHECK_AND_FREE(source);
2702 CHECK_AND_FREE(uri_src);
2703 CHECK_AND_FREE(uri_trg);
2704
2705 return reply;
2706}
2707
2708json_object *
2709handle_op_deleteconfig(json_object *request, unsigned int session_key)
2710{
2711 json_object *reply;
2712 NC_DATASTORE ds_type = -1;
2713 char *target, *url;
2714
2715 DEBUG("Request: delete-config (session %u)", session_key);
2716
2717 pthread_mutex_lock(&json_lock);
2718 target = get_param_string(request, "target");
2719 url = get_param_string(request, "url");
2720 pthread_mutex_unlock(&json_lock);
2721
2722 if (target != NULL) {
2723 ds_type = parse_datastore(target);
2724 }
2725 if ((int)ds_type == -1) {
2726 reply = create_error_reply("Invalid target repository type requested.");
2727 goto finalize;
2728 }
2729 if (ds_type == NC_DATASTORE_URL) {
2730 if (!url) {
2731 url = "";
2732 }
2733 }
2734
2735 reply = netconf_deleteconfig(session_key, ds_type, url);
2736
2737 CHECK_ERR_SET_REPLY
2738 if (reply == NULL) {
2739 reply = create_ok_reply();
2740 }
2741
2742finalize:
2743 CHECK_AND_FREE(target);
2744 CHECK_AND_FREE(url);
2745 return reply;
2746}
2747
2748json_object *
2749handle_op_lock(json_object *request, unsigned int session_key)
2750{
2751 json_object *reply;
2752 NC_DATASTORE ds_type = -1;
2753 char *target;
2754
2755 DEBUG("Request: lock (session %u)", session_key);
2756
2757 pthread_mutex_lock(&json_lock);
2758 target = get_param_string(request, "target");
2759 pthread_mutex_unlock(&json_lock);
2760
2761 if (target != NULL) {
2762 ds_type = parse_datastore(target);
2763 }
2764 if ((int)ds_type == -1) {
2765 reply = create_error_reply("Invalid target repository type requested.");
2766 goto finalize;
2767 }
2768
2769 reply = netconf_lock(session_key, ds_type);
2770
2771 CHECK_ERR_SET_REPLY
2772 if (reply == NULL) {
2773 reply = create_ok_reply();
2774 }
2775
2776finalize:
2777 CHECK_AND_FREE(target);
2778 return reply;
2779}
2780
2781json_object *
2782handle_op_unlock(json_object *request, unsigned int session_key)
2783{
2784 json_object *reply;
2785 NC_DATASTORE ds_type = -1;
2786 char *target;
2787
2788 DEBUG("Request: unlock (session %u)", session_key);
2789
2790 pthread_mutex_lock(&json_lock);
2791 target = get_param_string(request, "target");
2792 pthread_mutex_unlock(&json_lock);
2793
2794 if (target != NULL) {
2795 ds_type = parse_datastore(target);
2796 }
2797 if ((int)ds_type == -1) {
2798 reply = create_error_reply("Invalid target repository type requested.");
2799 goto finalize;
2800 }
2801
2802 reply = netconf_unlock(session_key, ds_type);
2803
2804 CHECK_ERR_SET_REPLY
2805 if (reply == NULL) {
2806 reply = create_ok_reply();
2807 }
2808
2809finalize:
2810 CHECK_AND_FREE(target);
2811 return reply;
2812}
2813
2814json_object *
2815handle_op_kill(json_object *request, unsigned int session_key)
2816{
2817 json_object *reply = NULL;
2818 char *sid = NULL;
2819
2820 DEBUG("Request: kill-session (session %u)", session_key);
2821
2822 pthread_mutex_lock(&json_lock);
2823 sid = get_param_string(request, "session-id");
2824 pthread_mutex_unlock(&json_lock);
2825
2826 if (sid == NULL) {
2827 reply = create_error_reply("Missing session-id parameter.");
2828 goto finalize;
2829 }
2830
2831 reply = netconf_killsession(session_key, sid);
2832
2833 CHECK_ERR_SET_REPLY
2834
2835finalize:
2836 CHECK_AND_FREE(sid);
2837 return reply;
2838}
2839
2840json_object *
2841handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2842{
2843 json_object *reply = NULL;
2844 struct session_with_mutex *locked_session = NULL;
2845 DEBUG("Request: get info about session %u", session_key);
2846
2847 DEBUG("LOCK wrlock %s", __func__);
2848 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2849 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2850 }
2851
2852 for (locked_session = netconf_sessions_list;
2853 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002854 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002855 if (locked_session != NULL) {
2856 DEBUG("LOCK mutex %s", __func__);
2857 pthread_mutex_lock(&locked_session->lock);
2858 DEBUG("UNLOCK wrlock %s", __func__);
2859 if (pthread_rwlock_unlock(&session_lock) != 0) {
2860 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2861 }
2862 if (locked_session->hello_message != NULL) {
Michal Vaskoa266afd2016-04-13 10:33:53 +02002863 reply = json_object_get(locked_session->hello_message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002864 } else {
2865 reply = create_error_reply("Invalid session identifier.");
2866 }
2867 DEBUG("UNLOCK mutex %s", __func__);
2868 pthread_mutex_unlock(&locked_session->lock);
2869 } else {
2870 DEBUG("UNLOCK wrlock %s", __func__);
2871 if (pthread_rwlock_unlock(&session_lock) != 0) {
2872 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2873 }
2874 reply = create_error_reply("Invalid session identifier.");
2875 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002876
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002877 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002878}
2879
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002880json_object *
2881handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002882{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002883 json_object *reply = NULL, *contents, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002884 char *content = NULL, *str;
2885 struct lyd_node *data = NULL, *node_content;
2886 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002887
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002888 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002889
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002890 pthread_mutex_lock(&json_lock);
2891 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2892 pthread_mutex_unlock(&json_lock);
2893 reply = create_error_reply("Missing contents parameter.");
2894 goto finalize;
2895 }
2896 obj = json_object_array_get_idx(contents, idx);
2897 if (!obj) {
2898 pthread_mutex_unlock(&json_lock);
2899 reply = create_error_reply("Contents array parameter shorter than sessions.");
2900 goto finalize;
2901 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002902 content = strdup(json_object_get_string(obj));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002903 pthread_mutex_unlock(&json_lock);
2904
Michal Vaskof35ea502016-02-24 10:44:54 +01002905 locked_session = session_get_locked(session_key, NULL);
2906 if (!locked_session) {
2907 ERROR("Unknown session or locking failed.");
2908 goto finalize;
2909 }
2910
2911 node_content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), content, LYD_JSON, LYD_OPT_RPC);
2912 session_unlock(locked_session);
2913
2914 free(content);
2915 lyd_print_mem(&content, node_content, LYD_XML, LYP_WITHSIBLINGS);
2916 lyd_free_withsiblings(node_content);
2917
2918 reply = netconf_generic(session_key, content, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002919 if (reply == NULL) {
2920 GETSPEC_ERR_REPLY
2921 if (err_reply != NULL) {
2922 /* use filled err_reply from libnetconf's callback */
2923 reply = err_reply;
2924 }
2925 } else {
2926 if (data == NULL) {
2927 pthread_mutex_lock(&json_lock);
2928 reply = json_object_new_object();
2929 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2930 pthread_mutex_unlock(&json_lock);
2931 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002932 lyd_print_mem(&str, data, LYD_JSON, LYP_WITHSIBLINGS);
2933 lyd_free_withsiblings(data);
2934 reply = create_data_reply(str);
2935 free(str);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002936 }
2937 }
2938
2939finalize:
Michal Vaskof35ea502016-02-24 10:44:54 +01002940 CHECK_AND_FREE(content);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002941 return reply;
2942}
2943
2944json_object *
2945handle_op_getschema(json_object *request, unsigned int session_key)
2946{
2947 char *data = NULL;
2948 char *identifier = NULL;
2949 char *version = NULL;
2950 char *format = NULL;
2951 json_object *reply = NULL;
2952
2953 DEBUG("Request: get-schema (session %u)", session_key);
2954
2955 pthread_mutex_lock(&json_lock);
2956 identifier = get_param_string(request, "identifier");
2957 version = get_param_string(request, "version");
2958 format = get_param_string(request, "format");
2959 pthread_mutex_unlock(&json_lock);
2960
2961 if (identifier == NULL) {
2962 reply = create_error_reply("No identifier for get-schema supplied.");
2963 goto finalize;
2964 }
2965
2966 DEBUG("get-schema(version: %s, format: %s)", version, format);
2967 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
2968 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
2969 } else {
2970 reply = create_data_reply(data);
2971 free(data);
2972 }
2973
2974finalize:
2975 CHECK_AND_FREE(identifier);
2976 CHECK_AND_FREE(version);
2977 CHECK_AND_FREE(format);
2978 return reply;
2979}
2980
2981json_object *
2982handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
2983{
2984 struct nc_session *temp_session = NULL;
2985 struct session_with_mutex * locked_session = NULL;
2986 json_object *reply = NULL;
2987
2988 DEBUG("Request: reload hello (session %u)", session_key);
2989
2990 DEBUG("LOCK wrlock %s", __func__);
2991 if (pthread_rwlock_wrlock(&session_lock) != 0) {
2992 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2993 return NULL;
2994 }
2995
2996 for (locked_session = netconf_sessions_list;
2997 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002998 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002999 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
3000 DEBUG("LOCK mutex %s", __func__);
3001 pthread_mutex_lock(&locked_session->lock);
3002 DEBUG("creating temporary NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01003003 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003004 if (temp_session != NULL) {
3005 prepare_status_message(locked_session, temp_session);
3006 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01003007 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003008 temp_session = NULL;
3009 } else {
3010 DEBUG("Reload hello failed due to channel establishment");
3011 reply = create_error_reply("Reload was unsuccessful, connection failed.");
3012 }
3013 DEBUG("UNLOCK mutex %s", __func__);
3014 pthread_mutex_unlock(&locked_session->lock);
3015 DEBUG("UNLOCK wrlock %s", __func__);
3016 if (pthread_rwlock_unlock(&session_lock) != 0) {
3017 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3018 }
3019 } else {
3020 DEBUG("UNLOCK wrlock %s", __func__);
3021 if (pthread_rwlock_unlock(&session_lock) != 0) {
3022 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3023 }
3024 reply = create_error_reply("Invalid session identifier.");
3025 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02003026
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003027 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
Michal Vaskoa266afd2016-04-13 10:33:53 +02003028 reply = json_object_get(locked_session->hello_message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003029 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003030
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003031 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003032}
3033
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003034void
Michal Vaskof35ea502016-02-24 10:44:54 +01003035notification_history(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003036{
Michal Vaskof35ea502016-02-24 10:44:54 +01003037 time_t eventtime;
3038 char *content;
3039 (void)session;
3040
3041 eventtime = nc_datetime2time(notif->datetime);
3042
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003043 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3044 if (notif_history_array == NULL) {
3045 ERROR("No list of notification history found.");
3046 return;
3047 }
3048 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3049 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01003050 json_object *notif_obj = json_object_new_object();
3051 if (notif_obj == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003052 ERROR("Could not allocate memory for notification (json).");
3053 goto failed;
3054 }
Michal Vaskof35ea502016-02-24 10:44:54 +01003055 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
3056
3057 json_object_object_add(notif_obj, "eventtime", json_object_new_int64(eventtime));
3058 json_object_object_add(notif_obj, "content", json_object_new_string(content));
3059
3060 free(content);
3061
3062 json_object_array_add(notif_history_array, notif_obj);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003063failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003064 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003065}
3066
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003067json_object *
3068handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003069{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003070 json_object *reply = NULL;
3071 json_object *js_tmp = NULL;
3072 struct session_with_mutex *locked_session = NULL;
3073 struct nc_session *temp_session = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003074 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003075 time_t start = 0;
3076 time_t stop = 0;
3077 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003078
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003079 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003080
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003081 pthread_mutex_lock(&json_lock);
3082 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3083 from = json_object_get_int64(js_tmp);
3084 }
3085 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3086 to = json_object_get_int64(js_tmp);
3087 }
3088 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003089
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003090 start = time(NULL) + from;
3091 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003092
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003093 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003094
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003095 DEBUG("LOCK wrlock %s", __func__);
3096 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3097 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3098 reply = create_error_reply("Internal lock failed.");
3099 goto finalize;
3100 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003101
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003102 for (locked_session = netconf_sessions_list;
3103 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003104 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003105 if (locked_session != NULL) {
3106 DEBUG("LOCK mutex %s", __func__);
3107 pthread_mutex_lock(&locked_session->lock);
3108 DEBUG("UNLOCK wrlock %s", __func__);
3109 if (pthread_rwlock_unlock(&session_lock) != 0) {
3110 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3111 }
3112 DEBUG("creating temporal NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01003113 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003114 if (temp_session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003115 rpc = nc_rpc_subscribe(NULL, NULL, nc_time2datetime(start, NULL), nc_time2datetime(stop, NULL), NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003116 if (rpc == NULL) {
3117 DEBUG("UNLOCK mutex %s", __func__);
3118 pthread_mutex_unlock(&locked_session->lock);
3119 DEBUG("notifications: creating an rpc request failed.");
3120 reply = create_error_reply("notifications: creating an rpc request failed.");
3121 goto finalize;
3122 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003123
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003124 DEBUG("Send NC subscribe.");
3125 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3126 json_object *res = netconf_unlocked_op(temp_session, rpc);
3127 if (res != NULL) {
3128 DEBUG("UNLOCK mutex %s", __func__);
3129 pthread_mutex_unlock(&locked_session->lock);
3130 DEBUG("Subscription RPC failed.");
3131 reply = res;
3132 goto finalize;
3133 }
3134 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003135
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003136 DEBUG("UNLOCK mutex %s", __func__);
3137 pthread_mutex_unlock(&locked_session->lock);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003138 DEBUG("LOCK ntf mutex %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003139 pthread_mutex_lock(&ntf_history_lock);
3140 pthread_mutex_lock(&json_lock);
3141 json_object *notif_history_array = json_object_new_array();
3142 pthread_mutex_unlock(&json_lock);
3143 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3144 ERROR("notif_history: cannot set thread-specific hash value.");
3145 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003146
Michal Vaskof35ea502016-02-24 10:44:54 +01003147 nc_recv_notif_dispatch(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003148
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003149 pthread_mutex_lock(&json_lock);
3150 reply = json_object_new_object();
3151 json_object_object_add(reply, "notifications", notif_history_array);
3152 //json_object_put(notif_history_array);
3153 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003154
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003155 DEBUG("UNLOCK ntf mutex %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003156 pthread_mutex_unlock(&ntf_history_lock);
3157 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01003158 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003159 temp_session = NULL;
3160 } else {
3161 DEBUG("UNLOCK mutex %s", __func__);
3162 pthread_mutex_unlock(&locked_session->lock);
3163 DEBUG("Get history of notification failed due to channel establishment");
3164 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3165 }
3166 } else {
3167 DEBUG("UNLOCK wrlock %s", __func__);
3168 if (pthread_rwlock_unlock(&session_lock) != 0) {
3169 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3170 }
3171 reply = create_error_reply("Invalid session identifier.");
3172 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003173
Tomas Cejka09629492014-07-10 15:58:06 +02003174finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003175 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003176}
3177
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003178json_object *
3179handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003180{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003181 json_object *reply = NULL;
3182 char *target = NULL;
3183 char *url = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003184 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003185 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003186
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003187 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003188
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003189 pthread_mutex_lock(&json_lock);
3190 target = get_param_string(request, "target");
3191 url = get_param_string(request, "url");
3192 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003193
3194
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003195 if (target == NULL) {
3196 reply = create_error_reply("Missing target parameter.");
3197 goto finalize;
3198 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003199
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003200 /* validation */
3201 target_ds = parse_datastore(target);
Michal Vaskof35ea502016-02-24 10:44:54 +01003202 rpc = nc_rpc_validate(target_ds, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003203 if (rpc == NULL) {
3204 DEBUG("mod_netconf: creating rpc request failed");
3205 reply = create_error_reply("Creation of RPC request failed.");
3206 goto finalize;
3207 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003208
Michal Vaskof35ea502016-02-24 10:44:54 +01003209 if ((reply = netconf_op(session_key, rpc, 0, NULL)) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003210 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003211
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003212 if (reply == NULL) {
3213 DEBUG("Request: validation ok.");
3214 reply = create_ok_reply();
3215 }
3216 }
3217 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003218
Tomas Cejka09629492014-07-10 15:58:06 +02003219finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003220 CHECK_AND_FREE(target);
3221 CHECK_AND_FREE(url);
3222 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003223}
3224
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003225json_object *
3226handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003227{
Michal Vasko23b30752016-05-06 14:29:10 +02003228 json_object *reply = NULL, *filters, *obj, *filter_array;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003229 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003230
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003231 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003232
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003233 pthread_mutex_lock(&json_lock);
3234 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3235 pthread_mutex_unlock(&json_lock);
3236 reply = create_error_reply("Missing filters parameter.");
3237 goto finalize;
3238 }
Michal Vasko23b30752016-05-06 14:29:10 +02003239 filter_array = json_object_array_get_idx(filters, idx);
3240 if (!filter_array || (json_object_get_type(filter_array) != json_type_array)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003241 pthread_mutex_unlock(&json_lock);
Michal Vasko23b30752016-05-06 14:29:10 +02003242 reply = create_error_reply("Filters array parameter wrong.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003243 goto finalize;
3244 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003245 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3246 load_children = json_object_get_boolean(obj);
3247 }
3248 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003249
Michal Vasko23b30752016-05-06 14:29:10 +02003250 reply = libyang_query(session_key, filter_array, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003251
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003252 CHECK_ERR_SET_REPLY
3253 if (!reply) {
3254 reply = create_error_reply("Query failed.");
3255 }
David Kupka8e60a372012-09-04 09:15:20 +02003256
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003257finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003258 return reply;
3259}
David Kupka8e60a372012-09-04 09:15:20 +02003260
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003261json_object *
3262handle_op_merge(json_object *request, unsigned int session_key, int idx)
3263{
3264 json_object *reply = NULL, *configs, *obj;
3265 char *config = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003266 struct lyd_node *content;
3267 struct session_with_mutex *locked_session;
David Kupka8e60a372012-09-04 09:15:20 +02003268
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003269 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003270
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003271 pthread_mutex_lock(&json_lock);
3272 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3273 pthread_mutex_unlock(&json_lock);
3274 reply = create_error_reply("Missing configurations parameter.");
3275 goto finalize;
3276 }
3277 obj = json_object_array_get_idx(configs, idx);
3278 if (!obj) {
3279 pthread_mutex_unlock(&json_lock);
3280 reply = create_error_reply("Filters array parameter shorter than sessions.");
3281 goto finalize;
3282 }
3283 config = strdup(json_object_get_string(obj));
3284 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003285
Michal Vaskof35ea502016-02-24 10:44:54 +01003286 locked_session = session_get_locked(session_key, NULL);
3287 if (!locked_session) {
3288 ERROR("Unknown session or locking failed.");
3289 goto finalize;
3290 }
3291
3292 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_DATA);
3293 session_unlock(locked_session);
3294
3295 free(config);
3296 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
3297 lyd_free_withsiblings(content);
3298
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003299 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003300
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003301 CHECK_ERR_SET_REPLY
3302 if (!reply) {
3303 reply = create_error_reply("Merge failed.");
3304 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003305
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003306finalize:
3307 CHECK_AND_FREE(config);
3308 return reply;
3309}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003311void *
3312thread_routine(void *arg)
3313{
3314 void *retval = NULL;
3315 struct pollfd fds;
3316 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3317 json_object *js_tmp = NULL;
Michal Vaskodf280a22016-03-17 09:19:53 +01003318 int operation = (-1), count, i, sent;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003319 int status = 0;
3320 const char *msgtext;
3321 unsigned int session_key = 0;
3322 char *chunked_out_msg = NULL;
3323 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003324
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003325 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003326
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003327 /* init thread specific err_reply memory */
3328 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003329
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003330 while (!isterminated) {
3331 fds.fd = client;
3332 fds.events = POLLIN;
3333 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003334
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003335 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003336
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003337 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3338 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3339 continue;
3340 } else if (status < 0) {
3341 /* 0: poll time outed
3342 * close socket and ignore this request from the client, it can try it again
3343 * -1: poll failed
3344 * something wrong happend, close this socket and wait for another request
3345 */
3346 close(client);
3347 break;
3348 }
3349 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003350
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003351 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003352
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003353 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3354 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3355 /* close client's socket (it's probably already closed by client */
3356 close(client);
3357 break;
3358 }
Tomas Cejka09629492014-07-10 15:58:06 +02003359
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003360 buffer = get_framed_message(client);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003361 if (buffer != NULL) {
Michal Vaskoda384162016-04-12 15:33:23 +02003362 DEBUG("Received message:\n%.*s\n", 1024, buffer);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003363 enum json_tokener_error jerr;
3364 pthread_mutex_lock(&json_lock);
3365 request = json_tokener_parse_verbose(buffer, &jerr);
3366 if (jerr != json_tokener_success) {
3367 ERROR("JSON parsing error");
3368 pthread_mutex_unlock(&json_lock);
3369 continue;
3370 }
3371
3372 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3373 operation = json_object_get_int(js_tmp);
3374 }
3375 pthread_mutex_unlock(&json_lock);
3376 if (operation == -1) {
3377 replies = create_replies();
3378 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3379 goto send_reply;
3380 }
3381
3382 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3383 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3384 replies = create_replies();
3385 add_reply(replies, create_error_reply("Operation not supported."), 0);
3386 goto send_reply;
3387 }
3388
3389 DEBUG("operation %d", operation);
3390
3391 /* null global JSON error-reply */
3392 clean_err_reply();
3393
3394 /* clean replies envelope */
3395 if (replies != NULL) {
3396 pthread_mutex_lock(&json_lock);
3397 json_object_put(replies);
3398 pthread_mutex_unlock(&json_lock);
3399 }
3400 replies = create_replies();
3401
3402 if (operation == MSG_CONNECT) {
3403 count = 1;
3404 } else {
3405 pthread_mutex_lock(&json_lock);
3406 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
Michal Vasko642cad02016-03-17 12:31:18 +01003407 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003408 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3409 goto send_reply;
3410 }
3411 count = json_object_array_length(sessions);
3412 pthread_mutex_unlock(&json_lock);
3413 }
3414
3415 for (i = 0; i < count; ++i) {
3416 if (operation != MSG_CONNECT) {
3417 js_tmp = json_object_array_get_idx(sessions, i);
3418 session_key = json_object_get_int(js_tmp);
3419 }
3420
3421 /* process required operation */
Michal Vasko977bad92016-03-15 16:20:51 +01003422 reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003423 switch (operation) {
3424 case MSG_CONNECT:
3425 reply = handle_op_connect(request);
3426 break;
3427 case MSG_DISCONNECT:
3428 reply = handle_op_disconnect(request, session_key);
3429 break;
3430 case MSG_GET:
3431 reply = handle_op_get(request, session_key);
3432 break;
3433 case MSG_GETCONFIG:
3434 reply = handle_op_getconfig(request, session_key);
3435 break;
3436 case MSG_EDITCONFIG:
3437 reply = handle_op_editconfig(request, session_key, i);
3438 break;
3439 case MSG_COPYCONFIG:
3440 reply = handle_op_copyconfig(request, session_key, i);
3441 break;
3442 case MSG_DELETECONFIG:
3443 reply = handle_op_deleteconfig(request, session_key);
3444 break;
3445 case MSG_LOCK:
3446 reply = handle_op_lock(request, session_key);
3447 break;
3448 case MSG_UNLOCK:
3449 reply = handle_op_unlock(request, session_key);
3450 break;
3451 case MSG_KILL:
3452 reply = handle_op_kill(request, session_key);
3453 break;
3454 case MSG_INFO:
3455 reply = handle_op_info(request, session_key);
3456 break;
3457 case MSG_GENERIC:
3458 reply = handle_op_generic(request, session_key, i);
3459 break;
3460 case MSG_GETSCHEMA:
3461 reply = handle_op_getschema(request, session_key);
3462 break;
3463 case MSG_RELOADHELLO:
3464 reply = handle_op_reloadhello(request, session_key);
3465 break;
3466 case MSG_NTF_GETHISTORY:
3467 reply = handle_op_ntfgethistory(request, session_key);
3468 break;
3469 case MSG_VALIDATE:
3470 reply = handle_op_validate(request, session_key);
3471 break;
3472 case SCH_QUERY:
3473 reply = handle_op_query(request, session_key, i);
3474 break;
3475 case SCH_MERGE:
3476 reply = handle_op_merge(request, session_key, i);
3477 break;
3478 }
3479
3480 add_reply(replies, reply, session_key);
3481 }
3482
3483 /* free parameters */
3484 operation = (-1);
3485
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003486 if (request != NULL) {
3487 pthread_mutex_lock(&json_lock);
3488 json_object_put(request);
3489 pthread_mutex_unlock(&json_lock);
Michal Vasko365dc4c2016-03-17 10:03:58 +01003490 request = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003491 }
Tomas Cejka09629492014-07-10 15:58:06 +02003492
3493send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003494 /* send reply to caller */
3495 if (replies) {
3496 pthread_mutex_lock(&json_lock);
3497 msgtext = json_object_to_json_string(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003498 pthread_mutex_unlock(&json_lock);
Michal Vaskoda384162016-04-12 15:33:23 +02003499 DEBUG("Sending message:\n%.*s\n", 1024, msgtext);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003500 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3501 if (buffer != NULL) {
3502 free(buffer);
3503 buffer = NULL;
3504 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003505 break;
3506 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003507
Michal Vaskodf280a22016-03-17 09:19:53 +01003508 i = 0;
3509 sent = 0;
3510 count = strlen(chunked_out_msg) + 1;
3511 while (count && ((i = send(client, chunked_out_msg + sent, count, 0)) != -1)) {
3512 sent += i;
3513 count -= i;
3514 }
3515 if (i == -1) {
3516 ERROR("Sending message failed (%s).", strerror(errno));
3517 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003518 pthread_mutex_lock(&json_lock);
3519 json_object_put(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003520 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003521 replies = NULL;
Michal Vasko2314d402016-04-06 13:24:06 +02003522
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003523 CHECK_AND_FREE(chunked_out_msg);
3524 chunked_out_msg = NULL;
3525 if (buffer) {
3526 free(buffer);
3527 buffer = NULL;
3528 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003529 clean_err_reply();
3530 } else {
3531 ERROR("Reply is NULL, shouldn't be...");
3532 continue;
3533 }
3534 }
3535 }
3536 free(arg);
3537 free_err_reply();
Michal Vaskod0205992016-03-17 10:04:49 +01003538 nc_thread_destroy();
David Kupka8e60a372012-09-04 09:15:20 +02003539
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003540 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003541}
3542
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003543/**
3544 * \brief Close all open NETCONF sessions.
3545 *
3546 * During termination of mod_netconf, it is useful to close all remaining
3547 * sessions. This function iterates over the list of sessions and close them
3548 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003549 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003550static void
3551close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003552{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003553 struct session_with_mutex *locked_session, *next_session;
3554 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003555
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003556 /* get exclusive access to sessions_list (conns) */
3557 DEBUG("LOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003558 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003559 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3560 return;
3561 }
3562 for (next_session = netconf_sessions_list; next_session;) {
3563 locked_session = next_session;
3564 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003565
Michal Vaskoc3146782015-11-04 14:46:41 +01003566 /* close_and_free_session handles locking on its own */
Michal Vaskof35ea502016-02-24 10:44:54 +01003567 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 +01003568 close_and_free_session(locked_session);
3569 }
3570 netconf_sessions_list = NULL;
3571
3572 /* get exclusive access to sessions_list (conns) */
3573 DEBUG("UNLOCK wrlock %s", __func__);
Michal Vaskodc40b3a2016-04-11 14:36:16 +02003574 if (pthread_rwlock_unlock(&session_lock) != 0) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003575 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3576 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003577}
3578
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003579static void
3580check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003581{
Michal Vaskoda384162016-04-12 15:33:23 +02003582 struct session_with_mutex *locked_session = NULL, *next_session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003583 time_t current_time = time(NULL);
3584 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003585
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003586 /* get exclusive access to sessions_list (conns) */
Michal Vaskoe41fb6e2016-04-12 08:53:23 +02003587 //DEBUG("LOCK wrlock %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003588 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3589 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3590 return;
3591 }
Michal Vaskoda384162016-04-12 15:33:23 +02003592
3593 locked_session = netconf_sessions_list;
3594 while (locked_session) {
3595 next_session = locked_session->next;
3596
3597 if (!locked_session->session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003598 continue;
3599 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003600 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003601 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 +02003602
Michal Vaskoda384162016-04-12 15:33:23 +02003603 /* remove it from the list */
3604 if (!locked_session->prev) {
3605 netconf_sessions_list = netconf_sessions_list->next;
3606 if (netconf_sessions_list) {
3607 netconf_sessions_list->prev = NULL;
3608 }
3609 } else {
3610 locked_session->prev->next = locked_session->next;
3611 if (locked_session->next) {
3612 locked_session->next->prev = locked_session->prev;
3613 }
3614 }
3615
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003616 /* close_and_free_session handles locking on its own */
3617 close_and_free_session(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003618 }
Michal Vaskoda384162016-04-12 15:33:23 +02003619
3620 locked_session = next_session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003621 }
Michal Vaskoe41fb6e2016-04-12 08:53:23 +02003622 //DEBUG("UNLOCK wrlock %s", __func__);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003623 if (pthread_rwlock_unlock(&session_lock) != 0) {
3624 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3625 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003626}
3627
3628
3629/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003630 * This is actually implementation of NETCONF client
3631 * - requests are received from UNIX socket in the predefined format
3632 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003633 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003634 *
3635 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003636static void
3637forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003638{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003639 struct timeval tv;
3640 struct sockaddr_un local, remote;
3641 int lsock, client, ret, i, pthread_count = 0;
3642 unsigned int olds = 0, timediff = 0;
3643 socklen_t len;
3644 struct pass_to_thread *arg;
3645 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3646 struct timespec maxtime;
3647 pthread_rwlockattr_t lock_attrs;
3648 #ifdef WITH_NOTIFICATIONS
3649 char use_notifications = 0;
3650 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003651
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003652 /* wait at most 5 seconds for every thread to terminate */
3653 maxtime.tv_sec = 5;
3654 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003655
Tomas Cejka04e08f42014-03-27 19:52:34 +01003656#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003657 /* change uid and gid of process for security reasons */
3658 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003659#else
3660# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003661 if (strlen(SU_GROUP) > 0) {
3662 struct group *g = getgrnam(SU_GROUP);
3663 if (g == NULL) {
3664 ERROR("GID (%s) was not found.", SU_GROUP);
3665 return;
3666 }
3667 if (setgid(g->gr_gid) != 0) {
3668 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3669 return;
3670 }
3671 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003672# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003673 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003674# endif
3675# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003676 if (strlen(SU_USER) > 0) {
3677 struct passwd *p = getpwnam(SU_USER);
3678 if (p == NULL) {
3679 ERROR("UID (%s) was not found.", SU_USER);
3680 return;
3681 }
3682 if (setuid(p->pw_uid) != 0) {
3683 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3684 return;
3685 }
3686 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003687# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003688 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003689# endif
3690#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003691
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003692 /* try to remove if exists */
3693 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003694
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003695 /* create listening UNIX socket to accept incoming connections */
3696 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3697 ERROR("Creating socket failed (%s)", strerror(errno));
3698 goto error_exit;
3699 }
Radek Krejci469aab82012-07-22 18:42:20 +02003700
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003701 local.sun_family = AF_UNIX;
3702 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3703 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003704
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003705 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3706 if (errno == EADDRINUSE) {
3707 ERROR("mod_netconf socket address already in use");
3708 goto error_exit;
3709 }
3710 ERROR("Binding socket failed (%s)", strerror(errno));
3711 goto error_exit;
3712 }
Radek Krejci469aab82012-07-22 18:42:20 +02003713
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003714 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3715 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3716 goto error_exit;
3717 }
3718 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003719
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003720 uid_t user = -1;
3721 if (strlen(CHOWN_USER) > 0) {
3722 struct passwd *p = getpwnam(CHOWN_USER);
3723 if (p != NULL) {
3724 user = p->pw_uid;
3725 }
3726 }
3727 gid_t group = -1;
3728 if (strlen(CHOWN_GROUP) > 0) {
3729 struct group *g = getgrnam(CHOWN_GROUP);
3730 if (g != NULL) {
3731 group = g->gr_gid;
3732 }
3733 }
3734 if (chown(sockname, user, group) == -1) {
3735 ERROR("Chown on socket file failed (%s).", strerror(errno));
3736 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003737
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003738 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003739
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003740 #ifdef WITH_NOTIFICATIONS
3741 if (notification_init() == -1) {
3742 ERROR("libwebsockets initialization failed");
3743 use_notifications = 0;
3744 } else {
3745 use_notifications = 1;
3746 }
3747 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003748
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003749 /* setup libnetconf's callbacks */
Michal Vaskod0205992016-03-17 10:04:49 +01003750 nc_client_init();
Michal Vaskodedf91b2016-04-06 10:00:31 +02003751 nc_verbosity(NC_VERB_VERBOSE);
Michal Vaskof35ea502016-02-24 10:44:54 +01003752 nc_set_print_clb(clb_print);
3753 nc_client_ssh_set_auth_hostkey_check_clb(netconf_callback_ssh_hostkey_check);
3754 nc_client_ssh_set_auth_interactive_clb(netconf_callback_sshauth_interactive);
3755 nc_client_ssh_set_auth_password_clb(netconf_callback_sshauth_password);
3756 nc_client_ssh_set_auth_privkey_passphrase_clb(netconf_callback_sshauth_passphrase);
Radek Krejci469aab82012-07-22 18:42:20 +02003757
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003758 /* disable publickey authentication */
Michal Vaskof35ea502016-02-24 10:44:54 +01003759 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003760
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003761 /* create mutex protecting session list */
3762 pthread_rwlockattr_init(&lock_attrs);
3763 /* rwlock is shared only with threads in this process */
3764 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3765 /* create rw lock */
3766 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3767 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3768 goto error_exit;
3769 }
3770 pthread_mutex_init(&ntf_history_lock, NULL);
3771 pthread_mutex_init(&json_lock, NULL);
3772 DEBUG("Initialization of notification history.");
3773 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3774 ERROR("Initialization of notification history failed.");
3775 }
3776 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3777 ERROR("Initialization of reply key failed.");
3778 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003779
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003780 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3781 while (isterminated == 0) {
3782 gettimeofday(&tv, NULL);
3783 timediff = (unsigned int)tv.tv_sec - olds;
3784 #ifdef WITH_NOTIFICATIONS
3785 if (use_notifications == 1) {
3786 notification_handle();
3787 }
3788 #endif
3789 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3790 check_timeout_and_close();
3791 }
Radek Krejci469aab82012-07-22 18:42:20 +02003792
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003793 /* open incoming connection if any */
3794 len = sizeof(remote);
3795 client = accept(lsock, (struct sockaddr *) &remote, &len);
3796 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3797 usleep(SLEEP_TIME * 1000);
3798 continue;
3799 } else if (client == -1 && (errno == EINTR)) {
3800 continue;
3801 } else if (client == -1) {
3802 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3803 continue;
3804 }
Radek Krejci469aab82012-07-22 18:42:20 +02003805
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003806 /* set client's socket as non-blocking */
3807 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003808
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003809 arg = malloc(sizeof(struct pass_to_thread));
3810 arg->client = client;
3811 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003812
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003813 /* start new thread. It will serve this particular request and then terminate */
3814 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3815 ERROR("Creating POSIX thread failed: %d\n", ret);
3816 } else {
3817 DEBUG("Thread %lu created", ptids[pthread_count]);
3818 pthread_count++;
3819 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3820 ptids[pthread_count] = 0;
3821 }
Radek Krejci469aab82012-07-22 18:42:20 +02003822
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003823 /* check if some thread already terminated, free some resources by joining it */
3824 for (i = 0; i < pthread_count; i++) {
3825 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3826 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3827 pthread_count--;
3828 if (pthread_count > 0) {
3829 /* place last Thread ID on the place of joined one */
3830 ptids[i] = ptids[pthread_count];
3831 }
3832 }
3833 }
3834 DEBUG("Running %d threads", pthread_count);
3835 }
Radek Krejci469aab82012-07-22 18:42:20 +02003836
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003837 DEBUG("mod_netconf terminating...");
3838 /* join all threads */
3839 for (i = 0; i < pthread_count; i++) {
3840 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3841 }
Radek Krejci469aab82012-07-22 18:42:20 +02003842
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003843 #ifdef WITH_NOTIFICATIONS
3844 notification_close();
3845 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003846
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003847 /* close all NETCONF sessions */
3848 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003849
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003850 /* destroy rwlock */
3851 pthread_rwlock_destroy(&session_lock);
3852 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003853
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003854 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003855
Michal Vaskod0205992016-03-17 10:04:49 +01003856 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003857 free(ptids);
3858 close(lsock);
3859 exit(0);
3860 return;
3861
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003862error_exit:
Michal Vaskod0205992016-03-17 10:04:49 +01003863 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003864 close(lsock);
3865 free(ptids);
3866 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003867}
3868
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003869int
3870main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003871{
Michal Vaskoc3146782015-11-04 14:46:41 +01003872 struct sigaction action;
3873 sigset_t block_mask;
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003874 int i;
Michal Vaskoc3146782015-11-04 14:46:41 +01003875
Michal Vaskoa53ef882015-11-24 11:02:01 +01003876 if (argc > 3) {
3877 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3878 return 1;
3879 }
3880
3881 sockname = SOCKET_FILENAME;
3882 for (i = 1; i < argc; ++i) {
3883 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
3884 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3885 return 0;
3886 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon")) {
3887 daemonize = 1;
3888 } else {
3889 sockname = argv[i];
3890 }
3891 }
3892
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003893 if (daemonize) {
3894 if (daemon(0, 0) == -1) {
3895 ERROR("daemon() failed (%s)", strerror(errno));
3896 return 1;
3897 }
3898 openlog("netopeerguid", LOG_PID, LOG_DAEMON);
Michal Vaskoc3146782015-11-04 14:46:41 +01003899 }
3900
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003901 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003902 action.sa_handler = signal_handler;
3903 action.sa_mask = block_mask;
3904 action.sa_flags = 0;
3905 sigaction(SIGINT, &action, NULL);
3906 sigaction(SIGTERM, &action, NULL);
3907
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003908 forked_proc();
3909 DEBUG("Terminated");
Michal Vaskoefdd49f2016-04-06 11:13:27 +02003910 if (daemonize) {
3911 closelog();
3912 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003913 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003914}