blob: 90f19b7969cbfdc8a1c95fda192a01aba426c5c1 [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
Michal Vasko30aa3822015-11-25 10:41:54 +01002 * \file netopeerguid.c
3 * \brief NetopeerGUI daemon
Radek Krejci469aab82012-07-22 18:42:20 +02004 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskoa53ef882015-11-24 11:02:01 +01006 * \author Michal Vasko <mvasko@cesnet.cz>
Radek Krejci469aab82012-07-22 18:42:20 +02007 * \date 2011
8 * \date 2012
Tomas Cejka94da2c52013-01-08 18:20:30 +01009 * \date 2013
Michal Vaskoa53ef882015-11-24 11:02:01 +010010 * \date 2015
Radek Krejci469aab82012-07-22 18:42:20 +020011 */
12/*
Michal Vaskoa53ef882015-11-24 11:02:01 +010013 * Copyright (C) 2011-2015 CESNET
Radek Krejci469aab82012-07-22 18:42:20 +020014 *
15 * LICENSE TERMS
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in
24 * the documentation and/or other materials provided with the
25 * distribution.
26 * 3. Neither the name of the Company nor the names of its contributors
27 * may be used to endorse or promote products derived from this
28 * software without specific prior written permission.
29 *
30 * ALTERNATIVELY, provided that this notice is retained in full, this
31 * product may be distributed under the terms of the GNU General Public
32 * License (GPL) version 2 or later, in which case the provisions
33 * of the GPL apply INSTEAD OF those given above.
34 *
35 * This software is provided ``as is'', and any express or implied
36 * warranties, including, but not limited to, the implied warranties of
37 * merchantability and fitness for a particular purpose are disclaimed.
38 * In no event shall the company or contributors be liable for any
39 * direct, indirect, incidental, special, exemplary, or consequential
40 * damages (including, but not limited to, procurement of substitute
41 * goods or services; loss of use, data, or profits; or business
42 * interruption) however caused and on any theory of liability, whether
43 * in contract, strict liability, or tort (including negligence or
44 * otherwise) arising in any way out of the use of this software, even
45 * if advised of the possibility of such damage.
46 *
47 */
Michal Vaskoc3146782015-11-04 14:46:41 +010048#define _GNU_SOURCE
Radek Krejci469aab82012-07-22 18:42:20 +020049
Radek Krejci7b4ddd02012-07-30 08:09:58 +020050#include <unistd.h>
51#include <poll.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010052#include <time.h>
Michal Vaskob69dde22016-03-09 11:42:20 +010053#include <fcntl.h>
Radek Krejci469aab82012-07-22 18:42:20 +020054#include <sys/types.h>
55#include <sys/socket.h>
56#include <sys/un.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010057#include <sys/stat.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010058#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010059#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010060#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010061#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010062#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020063#include <pthread.h>
64#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020065
Michal Vaskof35ea502016-02-24 10:44:54 +010066#include <nc_client.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020067
Tomas Cejka04e08f42014-03-27 19:52:34 +010068#include "../config.h"
69
Tomas Cejkad340dbf2013-03-24 20:36:57 +010070#ifdef WITH_NOTIFICATIONS
Michal Vaskoa53ef882015-11-24 11:02:01 +010071#include "notification_server.h"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010072#endif
73
Tomas Cejka94da2c52013-01-08 18:20:30 +010074#include "message_type.h"
Michal Vaskoa53ef882015-11-24 11:02:01 +010075#include "netopeerguid.h"
Radek Krejci469aab82012-07-22 18:42:20 +020076
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010077#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020078#define MAX_PROCS 5
Michal Vaskoa53ef882015-11-24 11:02:01 +010079#define SOCKET_FILENAME "/var/run/netopeerguid.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020080#define MAX_SOCKET_CL 10
81#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010082#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
83#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020084
Michal Vasko7732dee2015-11-05 10:22:15 +010085/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020086#define SLEEP_TIME 200
87
88#ifndef offsetof
89#define offsetof(type, member) ((size_t) ((type *) 0)->member)
90#endif
91
Tomas Cejka027f3bc2012-11-10 20:28:36 +010092/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020093struct timeval timeout = { 1, 0 };
94
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010095#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010096
Radek Krejci469aab82012-07-22 18:42:20 +020097#define MSG_OK 0
98#define MSG_OPEN 1
99#define MSG_DATA 2
100#define MSG_CLOSE 3
101#define MSG_ERROR 4
102#define MSG_UNKNOWN 5
103
Tomas Cejka47387fd2013-06-10 20:37:46 +0200104pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200105pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100106pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100107pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
108
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100109unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100110struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100111static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200112static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200113pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200114volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200115static char* password;
116
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100117json_object *create_ok_reply(void);
118json_object *create_data_reply(const char *data);
119static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
120 const char *format, json_object **err);
Michal Vaskof35ea502016-02-24 10:44:54 +0100121static void node_add_metadata_recursive(struct lyd_node *data_tree, const struct lys_module *module,
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100122 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100123static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100124
125static void
126signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200127{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100128 switch (sign) {
129 case SIGINT:
130 case SIGTERM:
131 isterminated = 1;
132 break;
133 }
Radek Krejci469aab82012-07-22 18:42:20 +0200134}
135
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100136int
137netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200138{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100139 /* always approve */
140 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200141}
142
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100143char *
Michal Vaskof35ea502016-02-24 10:44:54 +0100144netconf_callback_sshauth_passphrase(const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200145{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100146 char *buf;
147 buf = strdup(password);
148 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200149}
150
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100151char *
152netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200153{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100154 char *buf;
155 buf = strdup(password);
156 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200157}
158
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100159char *
160netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
161 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200162{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100163 char *buf;
164 buf = strdup(password);
165 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200166}
167
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100168void
Michal Vasko56c02b22016-04-06 09:59:09 +0200169netconf_callback_error_process(const char *message)
Radek Krejcic11fd862012-07-26 12:41:21 +0200170{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100171 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
172 if (err_reply_p == NULL) {
173 ERROR("Error message was not allocated. %s", __func__);
174 return;
175 }
176 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100177
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100178 json_object *array = NULL;
179 if (err_reply == NULL) {
180 ERROR("error calback: empty error list");
181 pthread_mutex_lock(&json_lock);
182 err_reply = json_object_new_object();
183 array = json_object_new_array();
184 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
185 json_object_object_add(err_reply, "errors", array);
186 if (message != NULL) {
187 json_object_array_add(array, json_object_new_string(message));
188 }
189 pthread_mutex_unlock(&json_lock);
190 (*err_reply_p) = err_reply;
191 } else {
192 ERROR("error calback: nonempty error list");
193 pthread_mutex_lock(&json_lock);
194 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
195 if (message != NULL) {
196 json_object_array_add(array, json_object_new_string(message));
197 }
198 }
199 pthread_mutex_unlock(&json_lock);
200 }
201 pthread_setspecific(err_reply_key, err_reply_p);
202 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200203}
204
Tomas Cejka47387fd2013-06-10 20:37:46 +0200205/**
206 * should be used in locked area
207 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100208void
209prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200210{
Michal Vaskof35ea502016-02-24 10:44:54 +0100211 int i;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100212 json_object *json_obj = NULL;
213 json_object *js_tmp = NULL;
214 char *old_sid = NULL;
215 const char *j_old_sid = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +0100216 char str_port[6];
217 const char **cpblts;
218 struct lyd_node *yanglib, *module, *node;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200219
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100220 if (s == NULL) {
221 ERROR("No session given.");
222 return;
223 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200224
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100225 pthread_mutex_lock(&json_lock);
226 if (s->hello_message != NULL) {
227 ERROR("clean previous hello message");
228 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
229 j_old_sid = json_object_get_string(js_tmp);
230 if (j_old_sid != NULL) {
231 old_sid = strdup(j_old_sid);
232 }
233 }
234 json_object_put(s->hello_message);
235 s->hello_message = NULL;
236 }
237 s->hello_message = json_object_new_object();
238 if (session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100239 if (!old_sid) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100240 /* we don't have old sid */
Michal Vaskof35ea502016-02-24 10:44:54 +0100241 asprintf(&old_sid, "%u", nc_session_get_id(session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100242 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100243 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
244 free(old_sid);
245 old_sid = NULL;
246
247 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 +0100248 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
Michal Vaskof35ea502016-02-24 10:44:54 +0100249 sprintf(str_port, "%u", nc_session_get_port(session));
250 json_object_object_add(s->hello_message, "port", json_object_new_string(str_port));
251 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_username(session)));
252 cpblts = nc_session_get_cpblts(session);
253 if (cpblts) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100254 json_obj = json_object_new_array();
Michal Vaskof35ea502016-02-24 10:44:54 +0100255 for (i = 0; cpblts[i]; ++i) {
256 json_object_array_add(json_obj, json_object_new_string(cpblts[i]));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100257 }
258 json_object_object_add(s->hello_message, "capabilities", json_obj);
259 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100260
261 yanglib = ly_ctx_info(nc_session_get_ctx(session));
262 if (yanglib) {
263 json_obj = json_object_new_array();
264 LY_TREE_FOR(yanglib->child, module) {
265 if (!strcmp(module->schema->name, "module")) {
266 LY_TREE_FOR(module->child, node) {
267 if (!strcmp(node->schema->name, "name")) {
268 json_object_array_add(json_obj, json_object_new_string(((struct lyd_node_leaf_list *)node)->value_str));
269 break;
270 }
271 }
272 }
273 }
274 json_object_object_add(s->hello_message, "models", json_obj);
275
Michal Vaskoc04900a2016-03-15 16:20:24 +0100276 lyd_free(yanglib);
Michal Vaskof35ea502016-02-24 10:44:54 +0100277 }
278
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100279 DEBUG("%s", json_object_to_json_string(s->hello_message));
280 } else {
281 ERROR("Session was not given.");
282 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
283 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
284 }
285 DEBUG("Status info from hello message prepared");
286 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200287}
288
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100289void
290create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200291{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100292 json_object **err_reply = calloc(1, sizeof(json_object **));
293 if (err_reply == NULL) {
294 ERROR("Allocation of err_reply storage failed!");
295 return;
296 }
297 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
298 ERROR("cannot set thread-specific value.");
299 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200300}
301
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100302void
303clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200304{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100305 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
306 if (err_reply != NULL) {
307 if (*err_reply != NULL) {
308 pthread_mutex_lock(&json_lock);
309 json_object_put(*err_reply);
310 pthread_mutex_unlock(&json_lock);
311 }
312 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
313 ERROR("Cannot set thread-specific hash value.");
314 }
315 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200316}
317
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100318void
319free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200320{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100321 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
322 if (err_reply != NULL) {
323 if (*err_reply != NULL) {
324 pthread_mutex_lock(&json_lock);
325 json_object_put(*err_reply);
326 pthread_mutex_unlock(&json_lock);
327 }
328 free(err_reply);
329 err_reply = NULL;
330 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
331 ERROR("Cannot set thread-specific hash value.");
332 }
333 }
334}
335
336static struct session_with_mutex *
337session_get_locked(unsigned int session_key, json_object **err)
338{
339 struct session_with_mutex *locked_session;
340
341 /* get non-exclusive (read) access to sessions_list (conns) */
342 DEBUG("LOCK wrlock %s", __func__);
343 if (pthread_rwlock_rdlock(&session_lock) != 0) {
344 if (*err) {
345 *err = create_error_reply("Locking failed.");
346 }
347 return NULL;
348 }
349 /* get session where send the RPC */
350 for (locked_session = netconf_sessions_list;
351 locked_session && (locked_session->session_key != session_key);
352 locked_session = locked_session->next);
353 if (!locked_session) {
354 if (*err) {
355 *err = create_error_reply("Session not found.");
356 }
357 return NULL;
358 }
359
360 /* get exclusive access to session */
361 DEBUG("LOCK mutex %s", __func__);
362 if (pthread_mutex_lock(&locked_session->lock) != 0) {
363 if (*err) {
364 *err = create_error_reply("Locking failed.");
365 }
366 goto wrlock_fail;
367 }
368 return locked_session;
369
370wrlock_fail:
371 DEBUG("UNLOCK wrlock %s", __func__);
372 pthread_rwlock_unlock(&session_lock);
373 return NULL;
374}
375
376static void
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100377session_user_activity(const char *username)
378{
379 struct session_with_mutex *sess;
380
381 for (sess = netconf_sessions_list; sess; sess = sess->next) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100382 if (!strcmp(nc_session_get_username(sess->session), username)) {
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100383 sess->last_activity = time(NULL);
384 }
385 }
386}
387
388static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100389session_unlock(struct session_with_mutex *locked_session)
390{
391 DEBUG("UNLOCK mutex %s", __func__);
392 pthread_mutex_unlock(&locked_session->lock);
393 DEBUG("UNLOCK wrlock %s", __func__);
394 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200395}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200396
Michal Vasko3fda9a92015-11-23 10:10:57 +0100397static void
398node_metadata_text(const char *text, const char *name, json_object *parent)
399{
400 json_object *obj;
401
402 if (!text) {
403 return;
404 }
405
406 obj = json_object_new_string(text);
407 json_object_object_add(parent, name, obj);
408}
409
410static void
411node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
412{
413 json_object *obj;
414
415 if (!restr) {
416 return;
417 }
418
419 obj = json_object_new_string(restr->expr);
420 json_object_object_add(parent, name, obj);
421}
422
423static void
424node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
425{
426 uint8_t i;
427 json_object *array, *obj;
428
429 if (!must_size || !must) {
430 return;
431 }
432
433 array = json_object_new_array();
434
435 for (i = 0; i < must_size; ++i) {
436 obj = json_object_new_string(must[i].expr);
437 json_object_array_add(array, obj);
438 }
439
440 json_object_object_add(parent, "must", array);
441}
442
443static void
444node_metadata_basic(struct lys_node *node, json_object *parent)
445{
446 json_object *obj;
447
448 /* description */
449 node_metadata_text(node->dsc, "description", parent);
450
451 /* reference */
452 node_metadata_text(node->ref, "reference", parent);
453
454 /* config */
455 if (node->flags & LYS_CONFIG_R) {
456 obj = json_object_new_boolean(0);
457 } else {
458 obj = json_object_new_boolean(1);
459 }
460 json_object_object_add(parent, "config", obj);
461
462 /* status */
463 if (node->flags & LYS_STATUS_DEPRC) {
464 obj = json_object_new_string("deprecated");
465 } else if (node->flags & LYS_STATUS_OBSLT) {
466 obj = json_object_new_string("obsolete");
467 } else {
468 obj = json_object_new_string("current");
469 }
470 json_object_object_add(parent, "status", obj);
471
472 /* mandatory */
473 if (node->flags & LYS_MAND_TRUE) {
474 obj = json_object_new_boolean(1);
475 } else {
476 obj = json_object_new_boolean(0);
477 }
478 json_object_object_add(parent, "mandatory", obj);
479
480 /* NACM extensions */
481 if (node->nacm) {
482 if (node->nacm & LYS_NACM_DENYW) {
483 obj = json_object_new_string("default-deny-write");
484 } else {
485 obj = json_object_new_string("default-deny-all");
486 }
487 json_object_object_add(parent, "ext", obj);
488 }
489}
490
491static void
492node_metadata_when(struct lys_when *when, json_object *parent)
493{
494 json_object *obj;
495
496 if (!when) {
497 return;
498 }
499
500 obj = json_object_new_string(when->cond);
501 json_object_object_add(parent, "when", obj);
502}
503
504static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100505node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100506{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100507 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100508 struct lys_node *child;
509
510 if (!node->child) {
511 return;
512 }
513
514 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100515 if (child->nodetype == LYS_USES) {
516 node_metadata_children_recursive(child, child_array, choice_array);
517 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100518 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100519 if (!*child_array) {
520 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100521 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100522 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100523 } else if (child->nodetype == LYS_CHOICE) {
524 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100525 if (!*choice_array) {
526 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100527 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100528 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100529 }
530 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100531}
532
533static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100534node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100535{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100536 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100537 struct lys_node *child;
538
539 if (!choice->child) {
540 return;
541 }
542
Michal Vasko3fda9a92015-11-23 10:10:57 +0100543 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100544 if (child->nodetype == LYS_USES) {
545 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
546 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100547 obj = json_object_new_string(child->name);
548 json_object_array_add(array, obj);
549 }
550 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100551}
552
553static void
554node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
555{
556 json_object *obj;
557
558 if (min) {
559 obj = json_object_new_int(min);
560 json_object_object_add(parent, "min-elements", obj);
561 }
562
563 if (max) {
564 obj = json_object_new_int(max);
565 json_object_object_add(parent, "max-elements", obj);
566 }
567}
568
569static void
570node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
571{
572 struct lys_ident_der *cur;
573 json_object *obj;
574
575 if (!ident) {
576 return;
577 }
578
579 obj = json_object_new_string(ident->name);
580 json_object_array_add(array, obj);
581
582 for (cur = ident->der; cur; cur = cur->next) {
583 node_metadata_ident_recursive(cur->ident, array);
584 }
585}
586
587static void
588node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
589{
590 json_object *obj, *array, *item;
591 char *str;
592 int i;
593
594 /* built-in YANG type */
595 if (!type->der->module) {
596 switch (type->base) {
597 case LY_TYPE_BINARY:
598 node_metadata_text("binary", "type", parent);
599 node_metadata_restr(type->info.binary.length, "length", parent);
600 break;
601 case LY_TYPE_BITS:
602 node_metadata_text("bits", "type", parent);
603
604 array = json_object_new_array();
605 for (i = 0; i < type->info.bits.count; ++i) {
606 item = json_object_new_object();
607 obj = json_object_new_string(type->info.bits.bit[i].name);
608 json_object_object_add(item, "name", obj);
609 obj = json_object_new_int(type->info.bits.bit[i].pos);
610 json_object_object_add(item, "position", obj);
611 json_object_array_add(array, item);
612 }
613 json_object_object_add(parent, "bits", array);
614 break;
615 case LY_TYPE_BOOL:
616 node_metadata_text("bool", "type", parent);
617 break;
618 case LY_TYPE_DEC64:
619 node_metadata_text("decimal64", "type", parent);
620 node_metadata_restr(type->info.dec64.range, "range", parent);
621 obj = json_object_new_int(type->info.dec64.dig);
622 json_object_object_add(parent, "fraction-digits", obj);
623 break;
624 case LY_TYPE_EMPTY:
625 node_metadata_text("empty", "type", parent);
626 break;
627 case LY_TYPE_ENUM:
628 node_metadata_text("enumeration", "type", parent);
629
630 array = json_object_new_array();
631 for (i = 0; i < type->info.enums.count; ++i) {
632 obj = json_object_new_string(type->info.enums.enm[i].name);
633 json_object_array_add(array, obj);
634 }
635 json_object_object_add(parent, "enumval", array);
636 break;
637 case LY_TYPE_IDENT:
638 node_metadata_text("identityref", "type", parent);
639
640 array = json_object_new_array();
641 node_metadata_ident_recursive(type->info.ident.ref, array);
642 json_object_object_add(parent, "identityval", array);
643 break;
644 case LY_TYPE_INST:
645 node_metadata_text("instance-identifier", "type", parent);
646 if (type->info.inst.req == -1) {
647 obj = json_object_new_boolean(0);
648 } else {
649 obj = json_object_new_boolean(1);
650 }
651 json_object_object_add(parent, "require-instance", obj);
652 break;
653 case LY_TYPE_LEAFREF:
654 node_metadata_text("leafref", "type", parent);
655 node_metadata_text(type->info.lref.path, "path", parent);
656 break;
657 case LY_TYPE_STRING:
658 node_metadata_text("string", "type", parent);
659 node_metadata_restr(type->info.str.length, "length", parent);
660 if (type->info.str.pat_count) {
661 array = json_object_new_array();
662 for (i = 0; i < type->info.str.pat_count; ++i) {
663 obj = json_object_new_string(type->info.str.patterns[i].expr);
664 json_object_array_add(array, obj);
665 }
666 json_object_object_add(parent, "pattern", array);
667 }
668 break;
669 case LY_TYPE_UNION:
670 node_metadata_text("union", "type", parent);
671 array = json_object_new_array();
672 for (i = 0; i < type->info.uni.count; ++i) {
673 obj = json_object_new_object();
674 node_metadata_type(&type->info.uni.types[i], module, obj);
675 json_object_array_add(array, obj);
676 }
677 json_object_object_add(parent, "types", array);
678 break;
679 case LY_TYPE_INT8:
680 node_metadata_text("int8", "type", parent);
681 node_metadata_restr(type->info.num.range, "range", parent);
682 break;
683 case LY_TYPE_UINT8:
684 node_metadata_text("uint8", "type", parent);
685 node_metadata_restr(type->info.num.range, "range", parent);
686 break;
687 case LY_TYPE_INT16:
688 node_metadata_text("int16", "type", parent);
689 node_metadata_restr(type->info.num.range, "range", parent);
690 break;
691 case LY_TYPE_UINT16:
692 node_metadata_text("uint16", "type", parent);
693 node_metadata_restr(type->info.num.range, "range", parent);
694 break;
695 case LY_TYPE_INT32:
696 node_metadata_text("int32", "type", parent);
697 node_metadata_restr(type->info.num.range, "range", parent);
698 break;
699 case LY_TYPE_UINT32:
700 node_metadata_text("uint32", "type", parent);
701 node_metadata_restr(type->info.num.range, "range", parent);
702 break;
703 case LY_TYPE_INT64:
704 node_metadata_text("int64", "type", parent);
705 node_metadata_restr(type->info.num.range, "range", parent);
706 break;
707 case LY_TYPE_UINT64:
708 node_metadata_text("uint64", "type", parent);
709 node_metadata_restr(type->info.num.range, "range", parent);
710 break;
711 default:
712 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
713 break;
714 }
715
716 /* typedef */
717 } else {
718 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
719 node_metadata_text(type->der->name, "type", parent);
720 } else {
721 asprintf(&str, "%s:%s", type->module_name, type->der->name);
722 node_metadata_text(str, "type", parent);
723 free(str);
724 }
725 obj = json_object_new_object();
726 node_metadata_typedef(type->der, obj);
727 json_object_object_add(parent, "typedef", obj);
728 }
729}
730
731static void
732node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
733{
734 json_object *obj;
735
736 /* description */
737 node_metadata_text(tpdf->dsc, "description", parent);
738
739 /* reference */
740 node_metadata_text(tpdf->ref, "reference", parent);
741
742 /* status */
743 if (tpdf->flags & LYS_STATUS_DEPRC) {
744 obj = json_object_new_string("deprecated");
745 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
746 obj = json_object_new_string("obsolete");
747 } else {
748 obj = json_object_new_string("current");
749 }
750 json_object_object_add(parent, "status", obj);
751
752 /* type */
753 node_metadata_type(&tpdf->type, tpdf->module, parent);
754
755 /* units */
756 node_metadata_text(tpdf->units, "units", parent);
757
758 /* default */
759 node_metadata_text(tpdf->dflt, "default", parent);
760}
761
762static void
763node_metadata_container(struct lys_node_container *cont, json_object *parent)
764{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100765 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100766
767 /* element type */
768 obj = json_object_new_string("container");
769 json_object_object_add(parent, "eltype", obj);
770
771 /* shared info */
772 node_metadata_basic((struct lys_node *)cont, parent);
773
774 /* must */
775 node_metadata_must(cont->must_size, cont->must, parent);
776
777 /* presence */
778 node_metadata_text(cont->presence, "presence", parent);
779
780 /* when */
781 node_metadata_when(cont->when, parent);
782
783 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100784 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
785 if (child_array) {
786 json_object_object_add(parent, "children", child_array);
787 }
788 if (choice_array) {
789 json_object_object_add(parent, "choice", choice_array);
790 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100791}
792
793static void
794node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
795{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100796 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100797
798 /* element type */
799 obj = json_object_new_string("choice");
800 json_object_object_add(parent, "eltype", obj);
801
802 /* shared info */
803 node_metadata_basic((struct lys_node *)choice, parent);
804
805 /* default */
Michal Vasko5e1c6052016-03-17 15:43:33 +0100806 if (choice->dflt) {
807 node_metadata_text(choice->dflt->name, "default", parent);
808 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100809
810 /* when */
811 node_metadata_when(choice->when, parent);
812
813 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100814 if (choice->child) {
815 array = json_object_new_array();
816 node_metadata_cases_recursive(choice, array);
817 json_object_object_add(parent, "cases", array);
818 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100819}
820
821static void
822node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
823{
824 json_object *obj;
825 struct lys_node_list *list;
826 int is_key, i;
827
828 /* element type */
829 obj = json_object_new_string("leaf");
830 json_object_object_add(parent, "eltype", obj);
831
832 /* shared info */
833 node_metadata_basic((struct lys_node *)leaf, parent);
834
835 /* type */
836 node_metadata_type(&leaf->type, leaf->module, parent);
837
838 /* units */
839 node_metadata_text(leaf->units, "units", parent);
840
841 /* default */
842 node_metadata_text(leaf->dflt, "default", parent);
843
844 /* must */
845 node_metadata_must(leaf->must_size, leaf->must, parent);
846
847 /* when */
848 node_metadata_when(leaf->when, parent);
849
850 /* iskey */
851 is_key = 0;
852 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
853 if (list && (list->nodetype == LYS_LIST)) {
854 for (i = 0; i < list->keys_size; ++i) {
855 if (list->keys[i] == leaf) {
856 is_key = 1;
857 break;
858 }
859 }
860 }
861 obj = json_object_new_boolean(is_key);
862 json_object_object_add(parent, "iskey", obj);
863}
864
865static void
866node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
867{
868 json_object *obj;
869
870 /* element type */
871 obj = json_object_new_string("leaf-list");
872 json_object_object_add(parent, "eltype", obj);
873
874 /* shared info */
875 node_metadata_basic((struct lys_node *)llist, parent);
876
877 /* type */
878 node_metadata_type(&llist->type, llist->module, parent);
879
880 /* units */
881 node_metadata_text(llist->units, "units", parent);
882
883 /* must */
884 node_metadata_must(llist->must_size, llist->must, parent);
885
886 /* when */
887 node_metadata_when(llist->when, parent);
888
889 /* min/max-elements */
890 node_metadata_min_max(llist->min, llist->max, parent);
891}
892
893static void
894node_metadata_list(struct lys_node_list *list, json_object *parent)
895{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100896 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100897 int i;
898 unsigned int j;
899
900 /* element type */
901 obj = json_object_new_string("list");
902 json_object_object_add(parent, "eltype", obj);
903
904 /* shared info */
905 node_metadata_basic((struct lys_node *)list, parent);
906
907 /* must */
908 node_metadata_must(list->must_size, list->must, parent);
909
910 /* when */
911 node_metadata_when(list->when, parent);
912
913 /* min/max-elements */
914 node_metadata_min_max(list->min, list->max, parent);
915
916 /* keys */
917 if (list->keys_size) {
918 array = json_object_new_array();
919 for (i = 0; i < list->keys_size; ++i) {
920 obj = json_object_new_string(list->keys[i]->name);
921 json_object_array_add(array, obj);
922 }
923 json_object_object_add(parent, "keys", array);
924 }
925
926 /* unique */
927 if (list->unique_size) {
928 array = json_object_new_array();
929 for (i = 0; i < list->unique_size; ++i) {
930 for (j = 0; j < list->unique[i].expr_size; ++j) {
931 obj = json_object_new_string(list->unique[i].expr[j]);
932 json_object_array_add(array, obj);
933 }
934 }
935 json_object_object_add(parent, "unique", array);
936 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100937
938 /* children & choice */
939 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
940 if (child_array) {
941 json_object_object_add(parent, "children", child_array);
942 }
943 if (choice_array) {
944 json_object_object_add(parent, "choice", choice_array);
945 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100946}
947
948static void
949node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
950{
951 json_object *obj;
952
953 /* element type */
954 obj = json_object_new_string("anyxml");
955 json_object_object_add(parent, "eltype", obj);
956
957 /* shared info */
958 node_metadata_basic((struct lys_node *)anyxml, parent);
959
960 /* must */
961 node_metadata_must(anyxml->must_size, anyxml->must, parent);
962
963 /* when */
964 node_metadata_when(anyxml->when, parent);
965
966}
967
968static void
969node_metadata_case(struct lys_node_case *cas, json_object *parent)
970{
971 json_object *obj;
972
973 /* element type */
974 obj = json_object_new_string("case");
975 json_object_object_add(parent, "eltype", obj);
976
977 /* shared info */
978 node_metadata_basic((struct lys_node *)cas, parent);
979
980 /* when */
981 node_metadata_when(cas->when, parent);
982}
983
Michal Vaskoa45770b2015-11-23 15:49:41 +0100984static void
985node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
986{
987 json_object *obj;
988
989 /* element type */
990 obj = json_object_new_string("rpc");
991 json_object_object_add(parent, "eltype", obj);
992
993 /* description */
994 node_metadata_text(rpc->dsc, "description", parent);
995
996 /* reference */
997 node_metadata_text(rpc->ref, "reference", parent);
998
999 /* status */
1000 if (rpc->flags & LYS_STATUS_DEPRC) {
1001 obj = json_object_new_string("deprecated");
1002 } else if (rpc->flags & LYS_STATUS_OBSLT) {
1003 obj = json_object_new_string("obsolete");
1004 } else {
1005 obj = json_object_new_string("current");
1006 }
1007 json_object_object_add(parent, "status", obj);
1008}
1009
1010static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001011node_metadata_model(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001012{
1013 json_object *obj, *array, *item;
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001014 const struct lys_node *node;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001015 int i;
1016
1017 /* yang-version */
1018 if (module->version == 2) {
1019 obj = json_object_new_string("1.1");
1020 } else {
1021 obj = json_object_new_string("1.0");
1022 }
1023 json_object_object_add(parent, "yang-version", obj);
1024
1025 /* namespace */
1026 node_metadata_text(module->ns, "namespace", parent);
1027
1028 /* prefix */
1029 node_metadata_text(module->prefix, "prefix", parent);
1030
1031 /* contact */
1032 node_metadata_text(module->contact, "contact", parent);
1033
1034 /* organization */
1035 node_metadata_text(module->org, "organization", parent);
1036
1037 /* revision */
1038 if (module->rev_size) {
1039 node_metadata_text(module->rev[0].date, "revision", parent);
1040 }
1041
1042 /* description */
1043 node_metadata_text(module->dsc, "description", parent);
1044
1045 /* import */
1046 if (module->imp_size) {
1047 array = json_object_new_array();
1048 for (i = 0; i < module->imp_size; ++i) {
1049 item = json_object_new_object();
1050
1051 node_metadata_text(module->imp[i].module->name, "name", item);
1052 node_metadata_text(module->imp[i].prefix, "prefix", item);
1053 if (module->imp[i].rev && module->imp[i].rev[0]) {
1054 node_metadata_text(module->imp[i].rev, "revision", item);
1055 }
1056
1057 json_object_array_add(array, item);
1058 }
1059 json_object_object_add(parent, "imports", array);
1060 }
1061
1062 /* include */
1063 if (module->inc_size) {
1064 array = json_object_new_array();
1065 for (i = 0; i < module->inc_size; ++i) {
1066 item = json_object_new_object();
1067
1068 node_metadata_text(module->inc[i].submodule->name, "name", item);
1069 if (module->inc[i].rev && module->inc[i].rev[0]) {
1070 node_metadata_text(module->inc[i].rev, "revision", item);
1071 }
1072
1073 json_object_array_add(array, item);
1074 }
1075 json_object_object_add(parent, "includes", array);
1076 }
Michal Vaskoa4b8cdb2016-03-17 15:44:45 +01001077
1078 /* top-nodes */
1079 node = NULL;
1080 array = NULL;
1081 while ((node = lys_getnext(node, NULL, module, LYS_GETNEXT_WITHCHOICE))) {
1082 if (node->nodetype & (LYS_RPC | LYS_NOTIF)) {
1083 continue;
1084 }
1085 if (!array) {
1086 array = json_object_new_array();
1087 }
1088 item = json_object_new_string(node->name);
1089 json_object_array_add(array, item);
1090 }
1091 if (array) {
1092 json_object_object_add(parent, "top-nodes", array);
1093 }
Michal Vaskoa45770b2015-11-23 15:49:41 +01001094}
1095
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001096/**
1097 * \defgroup netconf_operations NETCONF operations
1098 * The list of NETCONF operations that mod_netconf supports.
1099 * @{
1100 */
1101
1102/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001103 * \brief Send RPC and wait for reply with timeout.
1104 *
1105 * \param[in] session libnetconf session
1106 * \param[in] rpc prepared RPC message
1107 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1108 * \param[out] reply reply from the server
Michal Vaskof35ea502016-02-24 10:44:54 +01001109 * \return NC_MSG_WOULDBLOCK or NC_MSG_ERROR.
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001110 * On success, it returns NC_MSG_REPLY.
1111 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001112NC_MSG_TYPE
Michal Vaskof35ea502016-02-24 10:44:54 +01001113netconf_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 +02001114{
Michal Vaskof35ea502016-02-24 10:44:54 +01001115 uint64_t msgid;
1116 NC_MSG_TYPE ret;
1117 ret = nc_send_rpc(session, rpc, timeout, &msgid);
1118 if (ret != NC_MSG_RPC) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001119 return ret;
1120 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001121
1122 while ((ret = nc_recv_reply(session, rpc, msgid, timeout, (strict ? LYD_OPT_STRICT : 0), reply)) == NC_MSG_NOTIF);
1123
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001124 return ret;
1125}
1126
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001127/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001128 * \brief Connect to NETCONF server
1129 *
1130 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1131 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001132static unsigned int
Michal Vaskof35ea502016-02-24 10:44:54 +01001133netconf_connect(const char *host, const char *port, const char *user, const char *pass, const char *privkey)
Radek Krejci469aab82012-07-22 18:42:20 +02001134{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001135 struct nc_session* session = NULL;
1136 struct session_with_mutex *locked_session, *last_session;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001137 char *pubkey;
Radek Krejci469aab82012-07-22 18:42:20 +02001138
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001139 /* connect to the requested NETCONF server */
1140 password = (char*)pass;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001141 if (privkey) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001142 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 3);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001143 asprintf(&pubkey, "%s.pub", privkey);
Michal Vaskof35ea502016-02-24 10:44:54 +01001144 nc_client_ssh_add_keypair(pubkey, privkey);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001145 free(pubkey);
1146 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001147 nc_client_ssh_set_username(user);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001148 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Michal Vaskof35ea502016-02-24 10:44:54 +01001149 session = nc_connect_ssh(host, (unsigned short)atoi(port), NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001150 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001151
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001152 /* if connected successful, add session to the list */
1153 if (session != NULL) {
1154 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 +01001155 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001156 session = NULL;
1157 free(locked_session);
1158 locked_session = NULL;
1159 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1160 return 0;
1161 }
1162 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001163 locked_session->hello_message = NULL;
1164 locked_session->closed = 0;
1165 pthread_mutex_init(&locked_session->lock, NULL);
1166 DEBUG("Before session_lock");
1167 /* get exclusive access to sessions_list (conns) */
1168 DEBUG("LOCK wrlock %s", __func__);
1169 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001170 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001171 free(locked_session);
1172 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1173 return 0;
1174 }
1175 locked_session->ntfc_subscribed = 0;
1176 DEBUG("Add connection to the list");
1177 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001178 netconf_sessions_list = locked_session;
1179 } else {
1180 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1181 last_session->next = locked_session;
1182 locked_session->prev = last_session;
1183 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001184 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001185
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001186 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001187
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001188 /* store information about session from hello message for future usage */
1189 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001190
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001191 DEBUG("NETCONF session established");
1192 locked_session->session_key = session_key_generator;
1193 ++session_key_generator;
1194 if (session_key_generator == UINT_MAX) {
1195 session_key_generator = 1;
1196 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001197
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001198 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001199 /* unlock session list */
1200 DEBUG("UNLOCK wrlock %s", __func__);
1201 if (pthread_rwlock_unlock(&session_lock) != 0) {
1202 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1203 }
Radek Krejci469aab82012-07-22 18:42:20 +02001204
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001205 return locked_session->session_key;
1206 }
1207
1208 ERROR("Connection could not be established");
1209 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001210}
1211
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001212static int
1213close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001214{
Michal Vaskoa53ef882015-11-24 11:02:01 +01001215 int i;
1216
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001217 DEBUG("lock private lock.");
1218 DEBUG("LOCK mutex %s", __func__);
1219 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1220 ERROR("Error while locking rwlock");
1221 }
1222 locked_session->ntfc_subscribed = 0;
1223 locked_session->closed = 1;
1224 if (locked_session->session != NULL) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001225 nc_session_free(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001226 locked_session->session = NULL;
1227 }
1228 DEBUG("session closed.");
1229 DEBUG("unlock private lock.");
1230 DEBUG("UNLOCK mutex %s", __func__);
1231 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1232 ERROR("Error while locking rwlock");
1233 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001234
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001235 DEBUG("unlock session lock.");
1236 DEBUG("closed session, disabled notif(?), wait 0.5s");
1237 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001238
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001239 /* session shouldn't be used by now */
Michal Vaskoa53ef882015-11-24 11:02:01 +01001240 for (i = 0; i < locked_session->notif_count; ++i) {
1241 free(locked_session->notifications[i].content);
1242 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001243 free(locked_session->notifications);
1244 pthread_mutex_destroy(&locked_session->lock);
1245 if (locked_session->hello_message != NULL) {
1246 json_object_put(locked_session->hello_message);
1247 locked_session->hello_message = NULL;
1248 }
1249 locked_session->session = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001250 free(locked_session);
1251 locked_session = NULL;
1252 DEBUG("NETCONF session closed, everything cleared.");
1253 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001254}
1255
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001256static int
1257netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001258{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001259 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001260
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001261 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001262
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001263 /* get exclusive (write) access to sessions_list (conns) */
1264 DEBUG("lock session lock.");
1265 DEBUG("LOCK wrlock %s", __func__);
1266 if (pthread_rwlock_wrlock (&session_lock) != 0) {
1267 ERROR("Error while locking rwlock");
1268 (*reply) = create_error_reply("Internal: Error while locking.");
1269 return EXIT_FAILURE;
1270 }
1271 /* remove session from the active sessions list -> nobody new can now work with session */
1272 for (locked_session = netconf_sessions_list;
1273 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001274 locked_session = locked_session->next);
1275
1276 if (!locked_session) {
Michal Vasko642cad02016-03-17 12:31:18 +01001277 pthread_rwlock_unlock(&session_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001278 ERROR("Could not find the session %u to close.", session_key);
1279 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001280 return EXIT_FAILURE;
1281 }
1282
1283 if (!locked_session->prev) {
1284 netconf_sessions_list = netconf_sessions_list->next;
Michal Vaskof0b6fbb2016-03-08 12:04:34 +01001285 if (netconf_sessions_list) {
1286 netconf_sessions_list->prev = NULL;
1287 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001288 } else {
1289 locked_session->prev->next = locked_session->next;
1290 if (locked_session->next) {
1291 locked_session->next->prev = locked_session->prev;
1292 }
1293 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001294
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001295 DEBUG("UNLOCK wrlock %s", __func__);
1296 if (pthread_rwlock_unlock (&session_lock) != 0) {
1297 ERROR("Error while unlocking rwlock");
1298 (*reply) = create_error_reply("Internal: Error while unlocking.");
1299 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001300
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001301 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1302 return close_and_free_session(locked_session);
1303 } else {
1304 ERROR("Unknown session to close");
1305 (*reply) = create_error_reply("Internal: Unkown session to close.");
1306 return (EXIT_FAILURE);
1307 }
1308 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001309}
1310
Tomas Cejkac7929632013-10-24 19:25:15 +02001311/**
1312 * Test reply message type and return error message.
1313 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001314 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001315 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001316 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001317 * \param[out] data
1318 * \return NULL on success
1319 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001320json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001321netconf_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 +02001322{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001323 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001324
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001325 /* process the result of the operation */
1326 switch (msgt) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001327 case NC_MSG_ERROR:
1328 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001329 ERROR("mod_netconf: receiving rpc-reply failed");
1330 if (session_key) {
1331 netconf_close(session_key, &err);
1332 }
1333 if (err != NULL) {
1334 return err;
1335 }
1336 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1337 }
1338 case NC_MSG_NONE:
1339 /* there is error handled by callback */
1340 if (data != NULL) {
1341 free(*data);
1342 (*data) = NULL;
1343 }
1344 return NULL;
1345 case NC_MSG_REPLY:
Michal Vaskof35ea502016-02-24 10:44:54 +01001346 switch (reply->type) {
1347 case NC_RPL_OK:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001348 if ((data != NULL) && (*data != NULL)) {
1349 free(*data);
1350 (*data) = NULL;
1351 }
1352 return create_ok_reply();
Michal Vaskof35ea502016-02-24 10:44:54 +01001353 case NC_RPL_DATA:
1354 if (((*data) = ((struct nc_reply_data *)reply)->data) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001355 ERROR("mod_netconf: no data from reply");
1356 return create_error_reply("Internal: No data from reply received.");
1357 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001358 ((struct nc_reply_data *)reply)->data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001359 return NULL;
1360 }
1361 break;
Michal Vaskof35ea502016-02-24 10:44:54 +01001362 case NC_RPL_ERROR:
1363 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001364 if (data != NULL) {
1365 free(*data);
1366 (*data) = NULL;
1367 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001368 return create_error_reply(((struct nc_reply_error *)reply)->err[0].message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001369 default:
Michal Vaskof35ea502016-02-24 10:44:54 +01001370 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001371 if (data != NULL) {
1372 free(*data);
1373 (*data) = NULL;
1374 }
1375 return create_error_reply("Unknown type of NETCONF reply.");
1376 }
1377 break;
1378 default:
1379 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1380 if (data != NULL) {
1381 free(*data);
1382 (*data) = NULL;
1383 }
1384 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1385 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001386}
1387
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001388json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001389netconf_unlocked_op(struct nc_session *session, struct nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001390{
Michal Vaskof35ea502016-02-24 10:44:54 +01001391 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001392 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001393
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001394 /* check requests */
1395 if (rpc == NULL) {
1396 ERROR("mod_netconf: rpc is not created");
1397 return create_error_reply("Internal error: RPC is not created");
1398 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001399
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001400 if (session != NULL) {
1401 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001402 msgt = netconf_send_recv_timed(session, rpc, 50000, 0, &reply);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001403 /* process the result of the operation */
1404 return netconf_test_reply(session, 0, msgt, reply, NULL);
1405 } else {
1406 ERROR("Unknown session to process.");
1407 return create_error_reply("Internal error: Unknown session to process.");
1408 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001409}
1410
Tomas Cejkac7929632013-10-24 19:25:15 +02001411/**
1412 * Perform RPC method that returns data.
1413 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001414 * \param[in] session_id session identifier
1415 * \param[in] rpc RPC message to perform
1416 * \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 +02001417 * \return NULL on success, json object with error otherwise
1418 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001419static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001420netconf_op(unsigned int session_key, struct nc_rpc *rpc, int strict, struct lyd_node **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001421{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001422 struct session_with_mutex * locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001423 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001424 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001425 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001426 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001427
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001428 /* check requests */
1429 if (rpc == NULL) {
1430 ERROR("mod_netconf: rpc is not created");
1431 res = create_error_reply("Internal: RPC could not be created.");
1432 data = NULL;
1433 goto finished;
1434 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001435
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001436 locked_session = session_get_locked(session_key, &res);
1437 if (!locked_session) {
1438 ERROR("Unknown session or locking failed.");
1439 goto finished;
1440 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001441
Michal Vaskof35ea502016-02-24 10:44:54 +01001442 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001443
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001444 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001445 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, strict, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001446
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001447 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001448
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001449 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001450
Tomas Cejkac7929632013-10-24 19:25:15 +02001451finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001452 nc_reply_free(reply);
1453 if (received_data != NULL) {
1454 (*received_data) = data;
1455 } else {
1456 if (data != NULL) {
1457 free(data);
1458 data = NULL;
1459 }
1460 }
1461 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001462}
1463
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001464static char *
1465netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001466{
Michal Vaskof35ea502016-02-24 10:44:54 +01001467 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001468 struct session_with_mutex *locked_session;
Michal Vasko04245ee2016-03-16 09:32:59 +01001469 json_object *res = NULL, *data_cjson;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001470 enum json_tokener_error tok_err;
Michal Vasko04245ee2016-03-16 09:32:59 +01001471 char *data_json = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001472 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001473
Michal Vaskof35ea502016-02-24 10:44:54 +01001474 /* tell server to show all elements even if they have default values */
1475#ifdef HAVE_WITHDEFAULTS_TAGGED
1476 rpc = nc_rpc_getconfig(source, filter, NC_WD_MODE_ALL_TAG, NC_PARAMTYPE_CONST);
1477#else
1478 rpc = nc_rpc_getconfig(source, filter, 0, NC_PARAMTYPE_CONST);
1479#endif
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001480 if (rpc == NULL) {
1481 ERROR("mod_netconf: creating rpc request failed");
1482 return (NULL);
1483 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001484
Michal Vaskof35ea502016-02-24 10:44:54 +01001485 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001486 nc_rpc_free(rpc);
1487 if (res != NULL) {
1488 (*err) = res;
1489 } else {
1490 (*err) = NULL;
1491 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001492
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001493 if (data) {
1494 for (locked_session = netconf_sessions_list;
1495 locked_session && (locked_session->session_key != session_key);
1496 locked_session = locked_session->next);
1497 /* won't fail */
1498
Michal Vaskof35ea502016-02-24 10:44:54 +01001499 /* print data into JSON */
1500 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001501 ERROR("Printing JSON <get-config> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001502 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001503 return NULL;
1504 }
1505
1506 /* parse JSON data into cjson */
1507 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001508 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001509 if (!data_cjson) {
1510 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1511 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001512 lyd_free_withsiblings(data);
1513 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001514 return NULL;
1515 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001516 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001517
1518 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001519 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001520 node_add_metadata_recursive(sibling, NULL, data_cjson);
1521 lyd_free(sibling);
1522 }
1523
Michal Vaskof35ea502016-02-24 10:44:54 +01001524 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001525 json_object_put(data_cjson);
1526 pthread_mutex_unlock(&json_lock);
1527 }
1528
Michal Vaskof35ea502016-02-24 10:44:54 +01001529 return (data_json);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001530}
1531
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001532static char *
1533netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001534{
Michal Vaskof35ea502016-02-24 10:44:54 +01001535 struct nc_rpc *rpc;
1536 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001537 json_object *res = NULL;
Michal Vasko56ec5952016-04-05 14:30:23 +02001538 char *model_data = NULL;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001539
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001540 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001541 rpc = nc_rpc_getschema(identifier, version, format, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001542 if (rpc == NULL) {
1543 ERROR("mod_netconf: creating rpc request failed");
1544 return (NULL);
1545 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001546
Michal Vaskof35ea502016-02-24 10:44:54 +01001547 res = netconf_op(session_key, rpc, 0, &data);
1548 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001549 if (res != NULL) {
1550 (*err) = res;
1551 } else {
1552 (*err) = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001553
1554 if (data) {
Michal Vasko56ec5952016-04-05 14:30:23 +02001555 if (((struct lyd_node_anyxml *)data)->xml_struct) {
1556 lyxml_print_mem(&model_data, ((struct lyd_node_anyxml *)data)->value.xml, 0);
1557 } else {
1558 model_data = strdup(((struct lyd_node_anyxml *)data)->value.str);
1559 }
1560 if (!model_data) {
1561 ERROR("memory allocation fail (%s:%d)", __FILE__, __LINE__);
Michal Vaskof35ea502016-02-24 10:44:54 +01001562 }
1563 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001564 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001565
Michal Vaskof35ea502016-02-24 10:44:54 +01001566 return (model_data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001567}
1568
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001569static char *
1570netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001571{
Michal Vaskof35ea502016-02-24 10:44:54 +01001572 struct nc_rpc* rpc;
1573 char* data_json = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001574 json_object *res = NULL, *data_cjson;
1575 enum json_tokener_error tok_err;
1576 struct session_with_mutex *locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001577 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001578
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001579 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001580 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001581 if (rpc == NULL) {
1582 ERROR("mod_netconf: creating rpc request failed");
1583 return (NULL);
1584 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001585
Michal Vaskof35ea502016-02-24 10:44:54 +01001586 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001587 nc_rpc_free(rpc);
1588 if (res != NULL) {
1589 (*err) = res;
1590 } else {
1591 (*err) = NULL;
1592 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001593
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001594 if (data) {
1595 for (locked_session = netconf_sessions_list;
1596 locked_session && (locked_session->session_key != session_key);
1597 locked_session = locked_session->next);
1598 /* won't fail */
1599
Michal Vaskof35ea502016-02-24 10:44:54 +01001600 /* print JSON data */
1601 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001602 ERROR("Printing JSON <get> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001603 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001604 return NULL;
1605 }
1606
1607 /* parse JSON data into cjson */
1608 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001609 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001610 if (!data_cjson) {
1611 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1612 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001613 lyd_free_withsiblings(data);
1614 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001615 return NULL;
1616 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001617 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001618
1619 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001620 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001621 node_add_metadata_recursive(sibling, NULL, data_cjson);
1622 lyd_free(sibling);
1623 }
1624
Michal Vaskof35ea502016-02-24 10:44:54 +01001625 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001626 json_object_put(data_cjson);
1627 pthread_mutex_unlock(&json_lock);
1628 }
1629
Michal Vaskof35ea502016-02-24 10:44:54 +01001630 return data_json;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001631}
1632
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001633static json_object *
1634netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1635 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001636{
Michal Vaskof35ea502016-02-24 10:44:54 +01001637 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001638 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001639
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001640 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001641 rpc = nc_rpc_copy(target, uri_trg, source, (config ? config : uri_src), 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001642 if (rpc == NULL) {
1643 ERROR("mod_netconf: creating rpc request failed");
1644 return create_error_reply("Internal: Creating rpc request failed");
1645 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001646
Michal Vaskof35ea502016-02-24 10:44:54 +01001647 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001648 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001649
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001650 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001651}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001652
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001653static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001654netconf_editconfig(unsigned int session_key, NC_DATASTORE target, NC_RPC_EDIT_DFLTOP defop,
1655 NC_RPC_EDIT_ERROPT erropt, NC_RPC_EDIT_TESTOPT testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001656{
Michal Vaskof35ea502016-02-24 10:44:54 +01001657 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001658 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001659
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001660 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001661 rpc = nc_rpc_edit(target, defop, testopt, erropt, config_or_url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001662 if (rpc == NULL) {
1663 ERROR("mod_netconf: creating rpc request failed");
1664 return create_error_reply("Internal: Creating rpc request failed");
1665 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001666
Michal Vaskof35ea502016-02-24 10:44:54 +01001667 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001668 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001669
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001670 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001671}
1672
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001673static json_object *
1674netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001675{
Michal Vaskof35ea502016-02-24 10:44:54 +01001676 struct nc_rpc *rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001677 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001678
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001679 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001680 rpc = nc_rpc_kill(atoi(sid));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001681 if (rpc == NULL) {
1682 ERROR("mod_netconf: creating rpc request failed");
1683 return create_error_reply("Internal: Creating rpc request failed");
1684 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001685
Michal Vaskof35ea502016-02-24 10:44:54 +01001686 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001687 nc_rpc_free(rpc);
1688 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001689}
1690
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001691static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001692netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, struct nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001693{
Michal Vaskof35ea502016-02-24 10:44:54 +01001694 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001695 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001696
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001697 /* create requests */
1698 rpc = op_func(target);
1699 if (rpc == NULL) {
1700 ERROR("mod_netconf: creating rpc request failed");
1701 return create_error_reply("Internal: Creating rpc request failed");
1702 }
Radek Krejci2f318372012-07-26 14:22:35 +02001703
Michal Vaskof35ea502016-02-24 10:44:54 +01001704 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001705 nc_rpc_free (rpc);
1706 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001707}
1708
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001709static json_object *
1710netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001711{
Michal Vaskof35ea502016-02-24 10:44:54 +01001712 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001713 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001714 rpc = nc_rpc_delete(target, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001715 if (rpc == NULL) {
1716 ERROR("mod_netconf: creating rpc request failed");
1717 return create_error_reply("Internal: Creating rpc request failed");
1718 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001719
Michal Vaskof35ea502016-02-24 10:44:54 +01001720 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001721 nc_rpc_free (rpc);
1722 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001723}
1724
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001725static json_object *
1726netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001727{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001728 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001729}
1730
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001731static json_object *
1732netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001733{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001734 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001735}
1736
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001737static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001738netconf_generic(unsigned int session_key, const char *xml_content, struct lyd_node **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001739{
Michal Vaskof35ea502016-02-24 10:44:54 +01001740 struct nc_rpc* rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001741 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001742
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001743 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001744 rpc = nc_rpc_generic_xml(xml_content, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001745 if (rpc == NULL) {
1746 ERROR("mod_netconf: creating rpc request failed");
1747 return create_error_reply("Internal: Creating rpc request failed");
1748 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001749
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001750 /* get session where send the RPC */
Michal Vaskof35ea502016-02-24 10:44:54 +01001751 res = netconf_op(session_key, rpc, 0, data);
1752 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001753 return res;
1754}
1755
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001756static int
Michal Vaskof35ea502016-02-24 10:44:54 +01001757node_add_metadata(const struct lys_node *node, const struct lys_module *module, json_object *parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001758{
1759 struct lys_module *cur_module;
1760 json_object *meta_obj;
1761 char *obj_name;
1762
Michal Vaskoa45770b2015-11-23 15:49:41 +01001763 if (node->nodetype == LYS_INPUT) {
1764 /* silently skipped */
1765 return 0;
1766 }
1767
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001768 cur_module = node->module;
1769 if (cur_module->type) {
1770 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1771 }
1772 if (cur_module == module) {
1773 asprintf(&obj_name, "$@%s", node->name);
1774 } else {
1775 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1776 }
1777
1778 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01001779 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001780 free(obj_name);
1781 return 1;
1782 }
1783
1784 meta_obj = json_object_new_object();
1785
1786 switch (node->nodetype) {
1787 case LYS_CONTAINER:
1788 node_metadata_container((struct lys_node_container *)node, meta_obj);
1789 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01001790 case LYS_CHOICE:
1791 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
1792 break;
1793 case LYS_LEAF:
1794 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
1795 break;
1796 case LYS_LEAFLIST:
1797 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
1798 break;
1799 case LYS_LIST:
1800 node_metadata_list((struct lys_node_list *)node, meta_obj);
1801 break;
1802 case LYS_ANYXML:
1803 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
1804 break;
1805 case LYS_CASE:
1806 node_metadata_case((struct lys_node_case *)node, meta_obj);
1807 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001808 case LYS_RPC:
1809 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
1810 break;
1811 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01001812 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
1813 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001814 }
1815
1816 /* just a precaution */
1817 if (json_object_get_type(parent) != json_type_object) {
1818 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1819 free(obj_name);
1820 return 1;
1821 }
1822
1823 json_object_object_add(parent, obj_name, meta_obj);
1824 free(obj_name);
1825 return 0;
1826}
1827
1828static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001829node_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 +01001830{
1831 struct lys_module *cur_module;
1832 struct lys_node *list_schema;
1833 struct lyd_node *child, *list_item;
1834 json_object *child_json, *list_child_json;
1835 char *child_name;
1836 int list_idx;
1837
Michal Vaskoa45770b2015-11-23 15:49:41 +01001838 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1839 return;
1840 }
1841
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001842 /* add data_tree metadata */
1843 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
1844 return;
1845 }
1846
1847 /* get data_tree module */
1848 cur_module = data_tree->schema->module;
1849 if (cur_module->type) {
1850 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1851 }
1852
1853 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1854 /* print correct data_tree JSON name */
1855 if (cur_module == module) {
1856 asprintf(&child_name, "%s", data_tree->schema->name);
1857 } else {
1858 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
1859 }
1860
1861 /* go down in JSON object */
1862 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
1863 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
1864 free(child_name);
1865 return;
1866 }
1867 free(child_name);
1868
1869 if (data_tree->schema->nodetype == LYS_LIST) {
1870 if (json_object_get_type(child_json) != json_type_array) {
1871 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1872 return;
1873 }
1874 /* go down in data tree for every item, we process them all now, skip later
1875 * (metadata duplicate will be detected at the beginning of this function) */
1876 list_idx = 0;
1877 list_schema = data_tree->schema;
1878
1879 LY_TREE_FOR(data_tree, list_item) {
1880 /* another list member */
1881 if (list_item->schema == list_schema) {
1882 list_child_json = json_object_array_get_idx(child_json, list_idx);
1883 if (!list_child_json) {
1884 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
1885 return;
1886 }
1887 LY_TREE_FOR(list_item->child, child) {
1888 node_add_metadata_recursive(child, cur_module, list_child_json);
1889 }
1890
1891 ++list_idx;
1892 }
1893 }
1894 } else {
1895 if (json_object_get_type(child_json) != json_type_object) {
1896 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1897 return;
1898 }
1899 /* go down in data tree */
1900 LY_TREE_FOR(data_tree->child, child) {
1901 node_add_metadata_recursive(child, cur_module, child_json);
1902 }
1903 }
1904 }
1905}
1906
1907static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001908node_add_model_metadata(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001909{
1910 json_object *obj;
1911 char *str;
1912
1913 obj = json_object_new_object();
1914 node_metadata_model(module, obj);
1915 asprintf(&str, "$@@%s", module->name);
1916 json_object_object_add(parent, str, obj);
1917 free(str);
1918}
1919
1920static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001921node_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 +01001922{
Michal Vaskof35ea502016-02-24 10:44:54 +01001923 const struct lys_module *cur_module;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001924 struct lys_node *child;
1925 json_object *node_json;
1926 char *json_name;
1927
Michal Vaskoa45770b2015-11-23 15:49:41 +01001928 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1929 return;
1930 }
1931
1932 if (node->nodetype & LYS_USES) {
1933 cur_module = module;
1934 node_json = parent;
1935 goto children;
1936 }
1937
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001938 /* add node metadata */
1939 if (node_add_metadata(node, module, parent)) {
1940 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
1941 return;
1942 }
1943
Michal Vaskoa45770b2015-11-23 15:49:41 +01001944 /* no other metadata */
1945 if (!node->child) {
1946 return;
1947 }
1948
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001949 /* get node module */
1950 cur_module = node->module;
1951 if (cur_module->type) {
1952 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1953 }
1954
1955 /* create JSON object for child metadata */
1956 node_json = json_object_new_object();
1957 if (cur_module == module) {
1958 json_object_object_add(parent, node->name, node_json);
1959 } else {
1960 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
1961 json_object_object_add(parent, json_name, node_json);
1962 free(json_name);
1963 }
1964
Michal Vaskoa45770b2015-11-23 15:49:41 +01001965children:
Michal Vaskoea2ddd92016-03-17 15:43:58 +01001966 if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1967 LY_TREE_FOR(node->child, child) {
1968 node_add_children_with_metadata_recursive(child, cur_module, node_json);
1969 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001970 }
1971}
1972
1973static json_object *
1974libyang_query(unsigned int session_key, const char *filter, int load_children)
1975{
Michal Vaskof35ea502016-02-24 10:44:54 +01001976 const struct lys_node *node;
1977 const struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001978 struct session_with_mutex *locked_session;
1979 json_object *ret = NULL, *data;
1980
1981 locked_session = session_get_locked(session_key, &ret);
1982 if (!locked_session) {
1983 ERROR("Locking failed or session not found.");
1984 goto finish;
1985 }
1986
Michal Vaskof35ea502016-02-24 10:44:54 +01001987 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001988
Michal Vaskoa45770b2015-11-23 15:49:41 +01001989 if (filter[0] == '/') {
Michal Vasko56ec5952016-04-05 14:30:23 +02001990 node = ly_ctx_get_node(nc_session_get_ctx(locked_session->session), NULL, filter);
Michal Vaskoa45770b2015-11-23 15:49:41 +01001991 if (!node) {
1992 ret = create_error_reply("Failed to resolve XPath filter node.");
1993 goto finish;
1994 }
1995 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001996 module = ly_ctx_get_module(nc_session_get_ctx(locked_session->session), filter, NULL);
Michal Vaskoa45770b2015-11-23 15:49:41 +01001997 if (!module) {
1998 ret = create_error_reply("Failed to find model.");
1999 goto finish;
2000 }
2001 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002002
Michal Vaskoa45770b2015-11-23 15:49:41 +01002003 pthread_mutex_lock(&json_lock);
2004 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002005
Michal Vaskoa45770b2015-11-23 15:49:41 +01002006 if (module) {
2007 node_add_model_metadata(module, data);
2008 if (load_children) {
2009 LY_TREE_FOR(module->data, node) {
2010 node_add_children_with_metadata_recursive(node, NULL, data);
2011 }
2012 }
2013 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002014 if (load_children) {
2015 node_add_children_with_metadata_recursive(node, NULL, data);
2016 } else {
2017 node_add_metadata(node, NULL, data);
2018 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002019 }
2020
Michal Vaskoa45770b2015-11-23 15:49:41 +01002021 pthread_mutex_unlock(&json_lock);
2022 ret = create_data_reply(json_object_to_json_string(data));
2023 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002024
2025finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002026 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002027 return ret;
2028}
2029
2030static json_object *
2031libyang_merge(unsigned int session_key, const char *config)
2032{
2033 struct lyd_node *data_tree = NULL, *sibling;
2034 struct session_with_mutex *locked_session;
2035 json_object *ret = NULL, *data_json = NULL;
2036 enum json_tokener_error err = 0;
2037
2038 locked_session = session_get_locked(session_key, &ret);
2039 if (!locked_session) {
2040 ERROR("Locking failed or session not found.");
2041 goto finish;
2042 }
2043
Michal Vaskof35ea502016-02-24 10:44:54 +01002044 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002045
Michal Vaskof35ea502016-02-24 10:44:54 +01002046 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 +01002047 if (!data_tree) {
2048 ERROR("Creating data tree failed.");
2049 ret = create_error_reply("Failed to create data tree from JSON config.");
2050 session_unlock(locked_session);
2051 goto finish;
2052 }
2053
2054 session_unlock(locked_session);
2055
2056 pthread_mutex_lock(&json_lock);
2057 data_json = json_tokener_parse_verbose(config, &err);
2058 if (!data_json) {
2059 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2060 pthread_mutex_unlock(&json_lock);
2061 ret = create_error_reply(json_tokener_error_desc(err));
2062 goto finish;
2063 }
2064
2065 /* go simultaneously through both trees and add metadata */
2066 LY_TREE_FOR(data_tree, sibling) {
2067 node_add_metadata_recursive(sibling, NULL, data_json);
2068 }
2069 pthread_mutex_unlock(&json_lock);
2070 ret = create_data_reply(json_object_to_json_string(data_json));
2071
2072finish:
2073 LY_TREE_FOR(data_tree, sibling) {
2074 lyd_free(sibling);
2075 }
2076 json_object_put(data_json);
2077 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002078}
2079
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002080/**
2081 * @}
2082 *//* netconf_operations */
2083
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002084void
2085clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002086{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002087 switch (level) {
Michal Vasko56c02b22016-04-06 09:59:09 +02002088 case NC_VERB_ERROR:
2089 ERROR("ERROR: %s", msg);
2090 break;
2091 case NC_VERB_WARNING:
2092 ERROR("WARNING: %s", msg);
2093 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002094 case NC_VERB_VERBOSE:
Michal Vasko56c02b22016-04-06 09:59:09 +02002095 ERROR("VERBOSE: %s", msg);
2096 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002097 case NC_VERB_DEBUG:
2098 DEBUG("DEBUG: %s", msg);
2099 break;
2100 }
Michal Vasko56c02b22016-04-06 09:59:09 +02002101
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002102 if (level == NC_VERB_ERROR) {
2103 /* return global error */
Michal Vasko56c02b22016-04-06 09:59:09 +02002104 netconf_callback_error_process(msg);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002105 }
Radek Krejci469aab82012-07-22 18:42:20 +02002106}
2107
Tomas Cejka64b87482013-06-03 16:30:53 +02002108/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002109 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002110 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002111 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002112 * \return pointer to message
2113 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002114char *
2115get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002116{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002117 /* read json in chunked framing */
2118 unsigned int buffer_size = 0;
2119 ssize_t buffer_len = 0;
2120 char *buffer = NULL;
2121 char c;
2122 ssize_t ret;
2123 int i, chunk_len;
2124 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002125
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002126 while (1) {
2127 /* read chunk length */
2128 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2129 if (buffer != NULL) {
2130 free (buffer);
2131 buffer = NULL;
2132 }
2133 break;
2134 }
2135 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2136 if (buffer != NULL) {
2137 free (buffer);
2138 buffer = NULL;
2139 }
2140 break;
2141 }
2142 i=0;
2143 memset (chunk_len_str, 0, 12);
2144 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2145 if (i==0 && c == '#') {
2146 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2147 /* end but invalid */
2148 if (buffer != NULL) {
2149 free (buffer);
2150 buffer = NULL;
2151 }
2152 }
2153 /* end of message, double-loop break */
2154 goto msg_complete;
2155 }
2156 chunk_len_str[i++] = c;
2157 if (i==11) {
2158 ERROR("Message is too long, buffer for length is not big enought!!!!");
2159 break;
2160 }
2161 }
2162 if (c != '\n') {
2163 if (buffer != NULL) {
2164 free (buffer);
2165 buffer = NULL;
2166 }
2167 break;
2168 }
2169 chunk_len_str[i] = 0;
2170 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2171 if (buffer != NULL) {
2172 free (buffer);
2173 buffer = NULL;
2174 }
2175 break;
2176 }
2177 buffer_size += chunk_len+1;
2178 buffer = realloc (buffer, sizeof(char)*buffer_size);
2179 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2180 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2181 if (buffer != NULL) {
2182 free (buffer);
2183 buffer = NULL;
2184 }
2185 break;
2186 }
2187 buffer_len += ret;
2188 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002189msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002190 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002191}
2192
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002193NC_DATASTORE
2194parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002195{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002196 if (strcmp(ds, "running") == 0) {
2197 return NC_DATASTORE_RUNNING;
2198 } else if (strcmp(ds, "startup") == 0) {
2199 return NC_DATASTORE_STARTUP;
2200 } else if (strcmp(ds, "candidate") == 0) {
2201 return NC_DATASTORE_CANDIDATE;
2202 } else if (strcmp(ds, "url") == 0) {
2203 return NC_DATASTORE_URL;
2204 } else if (strcmp(ds, "config") == 0) {
2205 return NC_DATASTORE_CONFIG;
2206 }
2207 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002208}
2209
Michal Vaskof35ea502016-02-24 10:44:54 +01002210NC_RPC_EDIT_TESTOPT
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002211parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002212{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002213 if (strcmp(t, "notset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002214 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002215 } else if (strcmp(t, "testset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002216 return NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002217 } else if (strcmp(t, "set") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002218 return NC_RPC_EDIT_TESTOPT_SET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002219 } else if (strcmp(t, "test") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002220 return NC_RPC_EDIT_TESTOPT_TEST;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002221 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002222 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002223}
2224
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002225json_object *
2226create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002227{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002228 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002229
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002230 pthread_mutex_lock(&json_lock);
2231 reply = json_object_new_object();
2232 array = json_object_new_array();
2233 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2234 json_object_array_add(array, json_object_new_string(errmess));
2235 json_object_object_add(reply, "errors", array);
2236 pthread_mutex_unlock(&json_lock);
2237
2238 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002239}
2240
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002241json_object *
2242create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002243{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002244 pthread_mutex_lock(&json_lock);
2245 json_object *reply = json_object_new_object();
2246 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2247 json_object_object_add(reply, "data", json_object_new_string(data));
2248 pthread_mutex_unlock(&json_lock);
2249 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002250}
2251
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002252json_object *
2253create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002254{
Michal Vasko365dc4c2016-03-17 10:03:58 +01002255 json_object *reply;
2256
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002257 pthread_mutex_lock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002258 reply = json_object_new_object();
2259 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2260 pthread_mutex_unlock(&json_lock);
2261 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002262}
2263
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002264json_object *
2265create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002266{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002267 json_object *replies;
2268
2269 pthread_mutex_lock(&json_lock);
2270 replies = json_object_new_object();
2271 pthread_mutex_unlock(&json_lock);
2272
2273 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002274}
2275
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002276void
2277add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002278{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002279 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002280
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002281 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002282
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002283 pthread_mutex_lock(&json_lock);
2284 json_object_object_add(replies, str, reply);
2285 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002286
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002287 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002288}
2289
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002290char *
2291get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002292{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002293 json_object *js_tmp = NULL;
2294 char *res = NULL;
2295 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2296 res = strdup(json_object_get_string(js_tmp));
2297 }
2298 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002299}
2300
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002301json_object *
2302handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002303{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002304 char *host = NULL;
2305 char *port = NULL;
2306 char *user = NULL;
2307 char *pass = NULL;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002308 char *privkey = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002309 json_object *reply = NULL;
2310 unsigned int session_key = 0;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002311
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002312 DEBUG("Request: connect");
2313 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002314
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002315 host = get_param_string(request, "host");
2316 port = get_param_string(request, "port");
2317 user = get_param_string(request, "user");
2318 pass = get_param_string(request, "pass");
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002319 privkey = get_param_string(request, "privatekey");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002320
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002321 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002322
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002323 if (host == NULL) {
2324 host = "localhost";
2325 }
2326
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002327 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002328 if (user == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002329 ERROR("Cannot connect - insufficient input.");
2330 session_key = 0;
2331 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002332 session_key = netconf_connect(host, port, user, pass, privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002333 DEBUG("Session key: %u", session_key);
2334 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002335
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002336 GETSPEC_ERR_REPLY
2337
2338 pthread_mutex_lock(&json_lock);
2339 if (session_key == 0) {
2340 /* negative reply */
2341 if (err_reply == NULL) {
2342 reply = json_object_new_object();
2343 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2344 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2345 ERROR("Connection failed.");
2346 } else {
2347 /* use filled err_reply from libnetconf's callback */
2348 reply = err_reply;
2349 ERROR("Connect - error from libnetconf's callback.");
2350 }
2351 } else {
2352 /* positive reply */
2353 reply = json_object_new_object();
2354 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2355 json_object_object_add(reply, "session", json_object_new_int(session_key));
2356 }
2357 memset(pass, 0, strlen(pass));
2358 pthread_mutex_unlock(&json_lock);
2359 CHECK_AND_FREE(host);
2360 CHECK_AND_FREE(user);
2361 CHECK_AND_FREE(port);
2362 CHECK_AND_FREE(pass);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002363 CHECK_AND_FREE(privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002364 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002365}
2366
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002367json_object *
2368handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002369{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002370 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002371
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002372 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002373
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002374 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2375 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2376 } else {
2377 reply = create_ok_reply();
2378 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002379
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002380 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002381}
2382
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002383json_object *
2384handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002385{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002386 char *filter = NULL;
2387 char *data = NULL;
2388 json_object *reply = NULL, *obj;
2389 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002390
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002391 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002392
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002393 pthread_mutex_lock(&json_lock);
2394 filter = get_param_string(request, "filter");
2395 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2396 pthread_mutex_unlock(&json_lock);
2397 reply = create_error_reply("Missing strict parameter.");
2398 return reply;
2399 }
2400 strict = json_object_get_boolean(obj);
2401 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002402
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002403 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2404 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2405 } else {
2406 reply = create_data_reply(data);
2407 free(data);
2408 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002409
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002410 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002411}
2412
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002413json_object *
2414handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002415{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002416 NC_DATASTORE ds_type_s = -1;
2417 char *filter = NULL;
2418 char *data = NULL;
2419 char *source = NULL;
2420 json_object *reply = NULL, *obj;
2421 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002422
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002423 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002424
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002425 pthread_mutex_lock(&json_lock);
2426 filter = get_param_string(request, "filter");
2427 source = get_param_string(request, "source");
2428 if (source != NULL) {
2429 ds_type_s = parse_datastore(source);
2430 }
2431 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2432 pthread_mutex_unlock(&json_lock);
2433 reply = create_error_reply("Missing strict parameter.");
2434 return reply;
2435 }
2436 strict = json_object_get_boolean(obj);
2437 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002438
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002439 if ((int)ds_type_s == -1) {
2440 reply = create_error_reply("Invalid source repository type requested.");
2441 goto finalize;
2442 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002443
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002444 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2445 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2446 } else {
2447 reply = create_data_reply(data);
2448 free(data);
2449 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002450
Tomas Cejka09629492014-07-10 15:58:06 +02002451finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002452 CHECK_AND_FREE(filter);
2453 CHECK_AND_FREE(source);
2454 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002455}
2456
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002457json_object *
2458handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002459{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002460 NC_DATASTORE ds_type_t = -1;
Michal Vaskof35ea502016-02-24 10:44:54 +01002461 NC_RPC_EDIT_DFLTOP defop_type = 0;
2462 NC_RPC_EDIT_ERROPT erropt_type = 0;
2463 NC_RPC_EDIT_TESTOPT testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002464 char *defop = NULL;
2465 char *erropt = NULL;
2466 char *config = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002467 char *target = NULL;
2468 char *testopt = NULL;
2469 char *urisource = NULL;
2470 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002471 struct lyd_node *content;
2472 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002473
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002474 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002475
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002476 pthread_mutex_lock(&json_lock);
2477 /* get parameters */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002478 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2479 pthread_mutex_unlock(&json_lock);
2480 reply = create_error_reply("Missing configs parameter.");
2481 goto finalize;
2482 }
2483 obj = json_object_array_get_idx(configs, idx);
2484 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002485
Michal Vaskof35ea502016-02-24 10:44:54 +01002486 target = get_param_string(request, "target");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002487 defop = get_param_string(request, "default-operation");
2488 erropt = get_param_string(request, "error-option");
2489 urisource = get_param_string(request, "uri-source");
2490 testopt = get_param_string(request, "test-option");
2491 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002492
Michal Vaskob8f5d442016-04-05 14:48:37 +02002493 if (!target) {
2494 ERROR("Missing the target parameter.");
2495 goto finalize;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002496 }
Michal Vaskob8f5d442016-04-05 14:48:37 +02002497 ds_type_t = parse_datastore(target);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002498
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002499 if (defop != NULL) {
2500 if (strcmp(defop, "merge") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002501 defop_type = NC_RPC_EDIT_DFLTOP_MERGE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002502 } else if (strcmp(defop, "replace") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002503 defop_type = NC_RPC_EDIT_DFLTOP_REPLACE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002504 } else if (strcmp(defop, "none") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002505 defop_type = NC_RPC_EDIT_DFLTOP_NONE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002506 } else {
2507 reply = create_error_reply("Invalid default-operation parameter.");
2508 goto finalize;
2509 }
2510 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002511 defop_type = NC_RPC_EDIT_DFLTOP_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002512 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002513
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002514 if (erropt != NULL) {
2515 if (strcmp(erropt, "continue-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002516 erropt_type = NC_RPC_EDIT_ERROPT_CONTINUE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002517 } else if (strcmp(erropt, "stop-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002518 erropt_type = NC_RPC_EDIT_ERROPT_STOP;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002519 } else if (strcmp(erropt, "rollback-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002520 erropt_type = NC_RPC_EDIT_ERROPT_ROLLBACK;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002521 } else {
2522 reply = create_error_reply("Invalid error-option parameter.");
2523 goto finalize;
2524 }
2525 } else {
2526 erropt_type = 0;
2527 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002528
Michal Vaskof35ea502016-02-24 10:44:54 +01002529 if ((config && urisource) || (!config && !urisource)) {
2530 reply = create_error_reply("Invalid config and uri-source data parameters.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002531 goto finalize;
2532 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002533
2534 if (config) {
2535 locked_session = session_get_locked(session_key, NULL);
2536 if (!locked_session) {
2537 ERROR("Unknown session or locking failed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002538 goto finalize;
2539 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002540
2541 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_EDIT);
2542 session_unlock(locked_session);
2543
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002544 if (!content) {
2545 ERROR("Failed to parse edit-config content.");
2546 goto finalize;
2547 }
2548
Michal Vaskof35ea502016-02-24 10:44:54 +01002549 free(config);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002550 config = NULL;
2551
Michal Vaskof35ea502016-02-24 10:44:54 +01002552 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2553 lyd_free_withsiblings(content);
Michal Vaskoc7a8f3c2016-04-05 14:53:33 +02002554 if (!config) {
2555 ERROR("Failed to print edit-config content.");
2556 goto finalize;
2557 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002558 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002559 config = urisource;
2560 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002561
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002562 if (testopt != NULL) {
2563 testopt_type = parse_testopt(testopt);
2564 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002565 testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002566 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002567
Michal Vaskof35ea502016-02-24 10:44:54 +01002568 reply = netconf_editconfig(session_key, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002569
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002570 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002571
Tomas Cejka09629492014-07-10 15:58:06 +02002572finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002573 CHECK_AND_FREE(defop);
2574 CHECK_AND_FREE(erropt);
2575 CHECK_AND_FREE(config);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002576 CHECK_AND_FREE(urisource);
2577 CHECK_AND_FREE(target);
2578 CHECK_AND_FREE(testopt);
2579
2580 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002581}
2582
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002583json_object *
2584handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002585{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002586 NC_DATASTORE ds_type_s = -1;
2587 NC_DATASTORE ds_type_t = -1;
2588 char *config = NULL;
2589 char *target = NULL;
2590 char *source = NULL;
2591 char *uri_src = NULL;
2592 char *uri_trg = NULL;
2593 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002594 struct lyd_node *content;
2595 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002596
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002597 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002598
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002599 /* get parameters */
2600 pthread_mutex_lock(&json_lock);
2601 target = get_param_string(request, "target");
2602 source = get_param_string(request, "source");
2603 uri_src = get_param_string(request, "uri-source");
2604 uri_trg = get_param_string(request, "uri-target");
2605 if (!strcmp(source, "config")) {
2606 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2607 pthread_mutex_unlock(&json_lock);
2608 reply = create_error_reply("Missing configs parameter.");
2609 goto finalize;
2610 }
2611 obj = json_object_array_get_idx(configs, idx);
2612 if (!obj) {
2613 pthread_mutex_unlock(&json_lock);
2614 reply = create_error_reply("Configs array parameter shorter than sessions.");
2615 goto finalize;
2616 }
2617 config = strdup(json_object_get_string(obj));
2618 }
2619 pthread_mutex_unlock(&json_lock);
2620
2621 if (target != NULL) {
2622 ds_type_t = parse_datastore(target);
2623 }
2624 if (source != NULL) {
2625 ds_type_s = parse_datastore(source);
2626 }
2627
2628 if ((int)ds_type_s == -1) {
2629 /* invalid source datastore specified */
2630 reply = create_error_reply("Invalid source repository type requested.");
2631 goto finalize;
2632 }
2633
2634 if ((int)ds_type_t == -1) {
2635 /* invalid target datastore specified */
2636 reply = create_error_reply("Invalid target repository type requested.");
2637 goto finalize;
2638 }
2639
2640 if (ds_type_s == NC_DATASTORE_URL) {
2641 if (uri_src == NULL) {
2642 uri_src = "";
2643 }
2644 }
2645 if (ds_type_t == NC_DATASTORE_URL) {
2646 if (uri_trg == NULL) {
2647 uri_trg = "";
2648 }
2649 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002650
2651 if (config) {
2652 locked_session = session_get_locked(session_key, NULL);
2653 if (!locked_session) {
2654 ERROR("Unknown session or locking failed.");
2655 goto finalize;
2656 }
2657
2658 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_CONFIG);
2659 session_unlock(locked_session);
2660
2661 free(config);
2662 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2663 lyd_free_withsiblings(content);
2664 }
2665
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002666 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2667
2668 CHECK_ERR_SET_REPLY
2669
2670finalize:
2671 CHECK_AND_FREE(config);
2672 CHECK_AND_FREE(target);
2673 CHECK_AND_FREE(source);
2674 CHECK_AND_FREE(uri_src);
2675 CHECK_AND_FREE(uri_trg);
2676
2677 return reply;
2678}
2679
2680json_object *
2681handle_op_deleteconfig(json_object *request, unsigned int session_key)
2682{
2683 json_object *reply;
2684 NC_DATASTORE ds_type = -1;
2685 char *target, *url;
2686
2687 DEBUG("Request: delete-config (session %u)", session_key);
2688
2689 pthread_mutex_lock(&json_lock);
2690 target = get_param_string(request, "target");
2691 url = get_param_string(request, "url");
2692 pthread_mutex_unlock(&json_lock);
2693
2694 if (target != NULL) {
2695 ds_type = parse_datastore(target);
2696 }
2697 if ((int)ds_type == -1) {
2698 reply = create_error_reply("Invalid target repository type requested.");
2699 goto finalize;
2700 }
2701 if (ds_type == NC_DATASTORE_URL) {
2702 if (!url) {
2703 url = "";
2704 }
2705 }
2706
2707 reply = netconf_deleteconfig(session_key, ds_type, url);
2708
2709 CHECK_ERR_SET_REPLY
2710 if (reply == NULL) {
2711 reply = create_ok_reply();
2712 }
2713
2714finalize:
2715 CHECK_AND_FREE(target);
2716 CHECK_AND_FREE(url);
2717 return reply;
2718}
2719
2720json_object *
2721handle_op_lock(json_object *request, unsigned int session_key)
2722{
2723 json_object *reply;
2724 NC_DATASTORE ds_type = -1;
2725 char *target;
2726
2727 DEBUG("Request: lock (session %u)", session_key);
2728
2729 pthread_mutex_lock(&json_lock);
2730 target = get_param_string(request, "target");
2731 pthread_mutex_unlock(&json_lock);
2732
2733 if (target != NULL) {
2734 ds_type = parse_datastore(target);
2735 }
2736 if ((int)ds_type == -1) {
2737 reply = create_error_reply("Invalid target repository type requested.");
2738 goto finalize;
2739 }
2740
2741 reply = netconf_lock(session_key, ds_type);
2742
2743 CHECK_ERR_SET_REPLY
2744 if (reply == NULL) {
2745 reply = create_ok_reply();
2746 }
2747
2748finalize:
2749 CHECK_AND_FREE(target);
2750 return reply;
2751}
2752
2753json_object *
2754handle_op_unlock(json_object *request, unsigned int session_key)
2755{
2756 json_object *reply;
2757 NC_DATASTORE ds_type = -1;
2758 char *target;
2759
2760 DEBUG("Request: unlock (session %u)", session_key);
2761
2762 pthread_mutex_lock(&json_lock);
2763 target = get_param_string(request, "target");
2764 pthread_mutex_unlock(&json_lock);
2765
2766 if (target != NULL) {
2767 ds_type = parse_datastore(target);
2768 }
2769 if ((int)ds_type == -1) {
2770 reply = create_error_reply("Invalid target repository type requested.");
2771 goto finalize;
2772 }
2773
2774 reply = netconf_unlock(session_key, ds_type);
2775
2776 CHECK_ERR_SET_REPLY
2777 if (reply == NULL) {
2778 reply = create_ok_reply();
2779 }
2780
2781finalize:
2782 CHECK_AND_FREE(target);
2783 return reply;
2784}
2785
2786json_object *
2787handle_op_kill(json_object *request, unsigned int session_key)
2788{
2789 json_object *reply = NULL;
2790 char *sid = NULL;
2791
2792 DEBUG("Request: kill-session (session %u)", session_key);
2793
2794 pthread_mutex_lock(&json_lock);
2795 sid = get_param_string(request, "session-id");
2796 pthread_mutex_unlock(&json_lock);
2797
2798 if (sid == NULL) {
2799 reply = create_error_reply("Missing session-id parameter.");
2800 goto finalize;
2801 }
2802
2803 reply = netconf_killsession(session_key, sid);
2804
2805 CHECK_ERR_SET_REPLY
2806
2807finalize:
2808 CHECK_AND_FREE(sid);
2809 return reply;
2810}
2811
2812json_object *
2813handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2814{
2815 json_object *reply = NULL;
2816 struct session_with_mutex *locked_session = NULL;
2817 DEBUG("Request: get info about session %u", session_key);
2818
2819 DEBUG("LOCK wrlock %s", __func__);
2820 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2821 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2822 }
2823
2824 for (locked_session = netconf_sessions_list;
2825 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002826 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002827 if (locked_session != NULL) {
2828 DEBUG("LOCK mutex %s", __func__);
2829 pthread_mutex_lock(&locked_session->lock);
2830 DEBUG("UNLOCK wrlock %s", __func__);
2831 if (pthread_rwlock_unlock(&session_lock) != 0) {
2832 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2833 }
2834 if (locked_session->hello_message != NULL) {
2835 reply = locked_session->hello_message;
2836 } else {
2837 reply = create_error_reply("Invalid session identifier.");
2838 }
2839 DEBUG("UNLOCK mutex %s", __func__);
2840 pthread_mutex_unlock(&locked_session->lock);
2841 } else {
2842 DEBUG("UNLOCK wrlock %s", __func__);
2843 if (pthread_rwlock_unlock(&session_lock) != 0) {
2844 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2845 }
2846 reply = create_error_reply("Invalid session identifier.");
2847 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002848
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002849 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002850}
2851
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002852json_object *
2853handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002854{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002855 json_object *reply = NULL, *contents, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002856 char *content = NULL, *str;
2857 struct lyd_node *data = NULL, *node_content;
2858 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002859
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002860 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002861
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002862 pthread_mutex_lock(&json_lock);
2863 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2864 pthread_mutex_unlock(&json_lock);
2865 reply = create_error_reply("Missing contents parameter.");
2866 goto finalize;
2867 }
2868 obj = json_object_array_get_idx(contents, idx);
2869 if (!obj) {
2870 pthread_mutex_unlock(&json_lock);
2871 reply = create_error_reply("Contents array parameter shorter than sessions.");
2872 goto finalize;
2873 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002874 content = strdup(json_object_get_string(obj));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002875 pthread_mutex_unlock(&json_lock);
2876
Michal Vaskof35ea502016-02-24 10:44:54 +01002877 locked_session = session_get_locked(session_key, NULL);
2878 if (!locked_session) {
2879 ERROR("Unknown session or locking failed.");
2880 goto finalize;
2881 }
2882
2883 node_content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), content, LYD_JSON, LYD_OPT_RPC);
2884 session_unlock(locked_session);
2885
2886 free(content);
2887 lyd_print_mem(&content, node_content, LYD_XML, LYP_WITHSIBLINGS);
2888 lyd_free_withsiblings(node_content);
2889
2890 reply = netconf_generic(session_key, content, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002891 if (reply == NULL) {
2892 GETSPEC_ERR_REPLY
2893 if (err_reply != NULL) {
2894 /* use filled err_reply from libnetconf's callback */
2895 reply = err_reply;
2896 }
2897 } else {
2898 if (data == NULL) {
2899 pthread_mutex_lock(&json_lock);
2900 reply = json_object_new_object();
2901 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2902 pthread_mutex_unlock(&json_lock);
2903 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002904 lyd_print_mem(&str, data, LYD_JSON, LYP_WITHSIBLINGS);
2905 lyd_free_withsiblings(data);
2906 reply = create_data_reply(str);
2907 free(str);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002908 }
2909 }
2910
2911finalize:
Michal Vaskof35ea502016-02-24 10:44:54 +01002912 CHECK_AND_FREE(content);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002913 return reply;
2914}
2915
2916json_object *
2917handle_op_getschema(json_object *request, unsigned int session_key)
2918{
2919 char *data = NULL;
2920 char *identifier = NULL;
2921 char *version = NULL;
2922 char *format = NULL;
2923 json_object *reply = NULL;
2924
2925 DEBUG("Request: get-schema (session %u)", session_key);
2926
2927 pthread_mutex_lock(&json_lock);
2928 identifier = get_param_string(request, "identifier");
2929 version = get_param_string(request, "version");
2930 format = get_param_string(request, "format");
2931 pthread_mutex_unlock(&json_lock);
2932
2933 if (identifier == NULL) {
2934 reply = create_error_reply("No identifier for get-schema supplied.");
2935 goto finalize;
2936 }
2937
2938 DEBUG("get-schema(version: %s, format: %s)", version, format);
2939 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
2940 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
2941 } else {
2942 reply = create_data_reply(data);
2943 free(data);
2944 }
2945
2946finalize:
2947 CHECK_AND_FREE(identifier);
2948 CHECK_AND_FREE(version);
2949 CHECK_AND_FREE(format);
2950 return reply;
2951}
2952
2953json_object *
2954handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
2955{
2956 struct nc_session *temp_session = NULL;
2957 struct session_with_mutex * locked_session = NULL;
2958 json_object *reply = NULL;
2959
2960 DEBUG("Request: reload hello (session %u)", session_key);
2961
2962 DEBUG("LOCK wrlock %s", __func__);
2963 if (pthread_rwlock_wrlock(&session_lock) != 0) {
2964 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2965 return NULL;
2966 }
2967
2968 for (locked_session = netconf_sessions_list;
2969 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002970 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002971 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
2972 DEBUG("LOCK mutex %s", __func__);
2973 pthread_mutex_lock(&locked_session->lock);
2974 DEBUG("creating temporary NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01002975 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002976 if (temp_session != NULL) {
2977 prepare_status_message(locked_session, temp_session);
2978 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01002979 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002980 temp_session = NULL;
2981 } else {
2982 DEBUG("Reload hello failed due to channel establishment");
2983 reply = create_error_reply("Reload was unsuccessful, connection failed.");
2984 }
2985 DEBUG("UNLOCK mutex %s", __func__);
2986 pthread_mutex_unlock(&locked_session->lock);
2987 DEBUG("UNLOCK wrlock %s", __func__);
2988 if (pthread_rwlock_unlock(&session_lock) != 0) {
2989 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2990 }
2991 } else {
2992 DEBUG("UNLOCK wrlock %s", __func__);
2993 if (pthread_rwlock_unlock(&session_lock) != 0) {
2994 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2995 }
2996 reply = create_error_reply("Invalid session identifier.");
2997 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002998
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002999 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
3000 reply = locked_session->hello_message;
3001 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02003002
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003003 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02003004}
3005
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003006void
Michal Vaskof35ea502016-02-24 10:44:54 +01003007notification_history(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003008{
Michal Vaskof35ea502016-02-24 10:44:54 +01003009 time_t eventtime;
3010 char *content;
3011 (void)session;
3012
3013 eventtime = nc_datetime2time(notif->datetime);
3014
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003015 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
3016 if (notif_history_array == NULL) {
3017 ERROR("No list of notification history found.");
3018 return;
3019 }
3020 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
3021 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01003022 json_object *notif_obj = json_object_new_object();
3023 if (notif_obj == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003024 ERROR("Could not allocate memory for notification (json).");
3025 goto failed;
3026 }
Michal Vaskof35ea502016-02-24 10:44:54 +01003027 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
3028
3029 json_object_object_add(notif_obj, "eventtime", json_object_new_int64(eventtime));
3030 json_object_object_add(notif_obj, "content", json_object_new_string(content));
3031
3032 free(content);
3033
3034 json_object_array_add(notif_history_array, notif_obj);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003035failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003036 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003037}
3038
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003039json_object *
3040handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003041{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003042 json_object *reply = NULL;
3043 json_object *js_tmp = NULL;
3044 struct session_with_mutex *locked_session = NULL;
3045 struct nc_session *temp_session = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003046 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003047 time_t start = 0;
3048 time_t stop = 0;
3049 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003050
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003051 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003052
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003053 pthread_mutex_lock(&json_lock);
3054 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3055 from = json_object_get_int64(js_tmp);
3056 }
3057 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3058 to = json_object_get_int64(js_tmp);
3059 }
3060 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003061
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003062 start = time(NULL) + from;
3063 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003064
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003065 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003066
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003067 DEBUG("LOCK wrlock %s", __func__);
3068 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3069 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3070 reply = create_error_reply("Internal lock failed.");
3071 goto finalize;
3072 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003073
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003074 for (locked_session = netconf_sessions_list;
3075 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003076 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003077 if (locked_session != NULL) {
3078 DEBUG("LOCK mutex %s", __func__);
3079 pthread_mutex_lock(&locked_session->lock);
3080 DEBUG("UNLOCK wrlock %s", __func__);
3081 if (pthread_rwlock_unlock(&session_lock) != 0) {
3082 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3083 }
3084 DEBUG("creating temporal NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01003085 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003086 if (temp_session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003087 rpc = nc_rpc_subscribe(NULL, NULL, nc_time2datetime(start, NULL), nc_time2datetime(stop, NULL), NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003088 if (rpc == NULL) {
3089 DEBUG("UNLOCK mutex %s", __func__);
3090 pthread_mutex_unlock(&locked_session->lock);
3091 DEBUG("notifications: creating an rpc request failed.");
3092 reply = create_error_reply("notifications: creating an rpc request failed.");
3093 goto finalize;
3094 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003095
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003096 DEBUG("Send NC subscribe.");
3097 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3098 json_object *res = netconf_unlocked_op(temp_session, rpc);
3099 if (res != NULL) {
3100 DEBUG("UNLOCK mutex %s", __func__);
3101 pthread_mutex_unlock(&locked_session->lock);
3102 DEBUG("Subscription RPC failed.");
3103 reply = res;
3104 goto finalize;
3105 }
3106 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003107
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003108 DEBUG("UNLOCK mutex %s", __func__);
3109 pthread_mutex_unlock(&locked_session->lock);
3110 DEBUG("LOCK mutex %s", __func__);
3111 pthread_mutex_lock(&ntf_history_lock);
3112 pthread_mutex_lock(&json_lock);
3113 json_object *notif_history_array = json_object_new_array();
3114 pthread_mutex_unlock(&json_lock);
3115 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3116 ERROR("notif_history: cannot set thread-specific hash value.");
3117 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003118
Michal Vaskof35ea502016-02-24 10:44:54 +01003119 nc_recv_notif_dispatch(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003120
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003121 pthread_mutex_lock(&json_lock);
3122 reply = json_object_new_object();
3123 json_object_object_add(reply, "notifications", notif_history_array);
3124 //json_object_put(notif_history_array);
3125 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003126
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003127 DEBUG("UNLOCK mutex %s", __func__);
3128 pthread_mutex_unlock(&ntf_history_lock);
3129 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01003130 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003131 temp_session = NULL;
3132 } else {
3133 DEBUG("UNLOCK mutex %s", __func__);
3134 pthread_mutex_unlock(&locked_session->lock);
3135 DEBUG("Get history of notification failed due to channel establishment");
3136 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3137 }
3138 } else {
3139 DEBUG("UNLOCK wrlock %s", __func__);
3140 if (pthread_rwlock_unlock(&session_lock) != 0) {
3141 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3142 }
3143 reply = create_error_reply("Invalid session identifier.");
3144 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003145
Tomas Cejka09629492014-07-10 15:58:06 +02003146finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003147 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003148}
3149
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003150json_object *
3151handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003152{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003153 json_object *reply = NULL;
3154 char *target = NULL;
3155 char *url = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003156 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003157 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003158
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003159 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003160
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003161 pthread_mutex_lock(&json_lock);
3162 target = get_param_string(request, "target");
3163 url = get_param_string(request, "url");
3164 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003165
3166
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003167 if (target == NULL) {
3168 reply = create_error_reply("Missing target parameter.");
3169 goto finalize;
3170 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003171
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003172 /* validation */
3173 target_ds = parse_datastore(target);
Michal Vaskof35ea502016-02-24 10:44:54 +01003174 rpc = nc_rpc_validate(target_ds, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003175 if (rpc == NULL) {
3176 DEBUG("mod_netconf: creating rpc request failed");
3177 reply = create_error_reply("Creation of RPC request failed.");
3178 goto finalize;
3179 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003180
Michal Vaskof35ea502016-02-24 10:44:54 +01003181 if ((reply = netconf_op(session_key, rpc, 0, NULL)) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003182 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003183
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003184 if (reply == NULL) {
3185 DEBUG("Request: validation ok.");
3186 reply = create_ok_reply();
3187 }
3188 }
3189 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003190
Tomas Cejka09629492014-07-10 15:58:06 +02003191finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003192 CHECK_AND_FREE(target);
3193 CHECK_AND_FREE(url);
3194 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003195}
3196
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003197json_object *
3198handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003199{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003200 json_object *reply = NULL, *filters, *obj;
3201 char *filter = NULL;
3202 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003203
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003204 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003205
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003206 pthread_mutex_lock(&json_lock);
3207 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3208 pthread_mutex_unlock(&json_lock);
3209 reply = create_error_reply("Missing filters parameter.");
3210 goto finalize;
3211 }
3212 obj = json_object_array_get_idx(filters, idx);
3213 if (!obj) {
3214 pthread_mutex_unlock(&json_lock);
3215 reply = create_error_reply("Filters array parameter shorter than sessions.");
3216 goto finalize;
3217 }
3218 filter = strdup(json_object_get_string(obj));
3219 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3220 load_children = json_object_get_boolean(obj);
3221 }
3222 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003223
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003224 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003225
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003226 CHECK_ERR_SET_REPLY
3227 if (!reply) {
3228 reply = create_error_reply("Query failed.");
3229 }
David Kupka8e60a372012-09-04 09:15:20 +02003230
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003231finalize:
3232 CHECK_AND_FREE(filter);
3233 return reply;
3234}
David Kupka8e60a372012-09-04 09:15:20 +02003235
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003236json_object *
3237handle_op_merge(json_object *request, unsigned int session_key, int idx)
3238{
3239 json_object *reply = NULL, *configs, *obj;
3240 char *config = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003241 struct lyd_node *content;
3242 struct session_with_mutex *locked_session;
David Kupka8e60a372012-09-04 09:15:20 +02003243
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003244 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003245
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003246 pthread_mutex_lock(&json_lock);
3247 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3248 pthread_mutex_unlock(&json_lock);
3249 reply = create_error_reply("Missing configurations parameter.");
3250 goto finalize;
3251 }
3252 obj = json_object_array_get_idx(configs, idx);
3253 if (!obj) {
3254 pthread_mutex_unlock(&json_lock);
3255 reply = create_error_reply("Filters array parameter shorter than sessions.");
3256 goto finalize;
3257 }
3258 config = strdup(json_object_get_string(obj));
3259 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003260
Michal Vaskof35ea502016-02-24 10:44:54 +01003261 locked_session = session_get_locked(session_key, NULL);
3262 if (!locked_session) {
3263 ERROR("Unknown session or locking failed.");
3264 goto finalize;
3265 }
3266
3267 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_DATA);
3268 session_unlock(locked_session);
3269
3270 free(config);
3271 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
3272 lyd_free_withsiblings(content);
3273
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003274 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003275
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003276 CHECK_ERR_SET_REPLY
3277 if (!reply) {
3278 reply = create_error_reply("Merge failed.");
3279 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003280
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003281finalize:
3282 CHECK_AND_FREE(config);
3283 return reply;
3284}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003285
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003286void *
3287thread_routine(void *arg)
3288{
3289 void *retval = NULL;
3290 struct pollfd fds;
3291 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3292 json_object *js_tmp = NULL;
Michal Vaskodf280a22016-03-17 09:19:53 +01003293 int operation = (-1), count, i, sent;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003294 int status = 0;
3295 const char *msgtext;
3296 unsigned int session_key = 0;
3297 char *chunked_out_msg = NULL;
3298 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003299
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003300 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003301
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003302 /* init thread specific err_reply memory */
3303 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003304
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003305 while (!isterminated) {
3306 fds.fd = client;
3307 fds.events = POLLIN;
3308 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003309
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003310 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003311
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003312 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3313 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3314 continue;
3315 } else if (status < 0) {
3316 /* 0: poll time outed
3317 * close socket and ignore this request from the client, it can try it again
3318 * -1: poll failed
3319 * something wrong happend, close this socket and wait for another request
3320 */
3321 close(client);
3322 break;
3323 }
3324 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003325
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003326 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003327
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003328 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3329 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3330 /* close client's socket (it's probably already closed by client */
3331 close(client);
3332 break;
3333 }
Tomas Cejka09629492014-07-10 15:58:06 +02003334
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003335 DEBUG("Get framed message...");
3336 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02003337
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003338 DEBUG("Check read buffer.");
3339 if (buffer != NULL) {
Michal Vaskoe7d1bbc2016-04-05 16:09:29 +02003340 DEBUG("Received message:\n%s\n", buffer);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003341 enum json_tokener_error jerr;
3342 pthread_mutex_lock(&json_lock);
3343 request = json_tokener_parse_verbose(buffer, &jerr);
3344 if (jerr != json_tokener_success) {
3345 ERROR("JSON parsing error");
3346 pthread_mutex_unlock(&json_lock);
3347 continue;
3348 }
3349
3350 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3351 operation = json_object_get_int(js_tmp);
3352 }
3353 pthread_mutex_unlock(&json_lock);
3354 if (operation == -1) {
3355 replies = create_replies();
3356 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3357 goto send_reply;
3358 }
3359
3360 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3361 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3362 replies = create_replies();
3363 add_reply(replies, create_error_reply("Operation not supported."), 0);
3364 goto send_reply;
3365 }
3366
3367 DEBUG("operation %d", operation);
3368
3369 /* null global JSON error-reply */
3370 clean_err_reply();
3371
3372 /* clean replies envelope */
3373 if (replies != NULL) {
3374 pthread_mutex_lock(&json_lock);
3375 json_object_put(replies);
3376 pthread_mutex_unlock(&json_lock);
3377 }
3378 replies = create_replies();
3379
3380 if (operation == MSG_CONNECT) {
3381 count = 1;
3382 } else {
3383 pthread_mutex_lock(&json_lock);
3384 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
Michal Vasko642cad02016-03-17 12:31:18 +01003385 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003386 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3387 goto send_reply;
3388 }
3389 count = json_object_array_length(sessions);
3390 pthread_mutex_unlock(&json_lock);
3391 }
3392
3393 for (i = 0; i < count; ++i) {
3394 if (operation != MSG_CONNECT) {
3395 js_tmp = json_object_array_get_idx(sessions, i);
3396 session_key = json_object_get_int(js_tmp);
3397 }
3398
3399 /* process required operation */
Michal Vasko977bad92016-03-15 16:20:51 +01003400 reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003401 switch (operation) {
3402 case MSG_CONNECT:
3403 reply = handle_op_connect(request);
3404 break;
3405 case MSG_DISCONNECT:
3406 reply = handle_op_disconnect(request, session_key);
3407 break;
3408 case MSG_GET:
3409 reply = handle_op_get(request, session_key);
3410 break;
3411 case MSG_GETCONFIG:
3412 reply = handle_op_getconfig(request, session_key);
3413 break;
3414 case MSG_EDITCONFIG:
3415 reply = handle_op_editconfig(request, session_key, i);
3416 break;
3417 case MSG_COPYCONFIG:
3418 reply = handle_op_copyconfig(request, session_key, i);
3419 break;
3420 case MSG_DELETECONFIG:
3421 reply = handle_op_deleteconfig(request, session_key);
3422 break;
3423 case MSG_LOCK:
3424 reply = handle_op_lock(request, session_key);
3425 break;
3426 case MSG_UNLOCK:
3427 reply = handle_op_unlock(request, session_key);
3428 break;
3429 case MSG_KILL:
3430 reply = handle_op_kill(request, session_key);
3431 break;
3432 case MSG_INFO:
3433 reply = handle_op_info(request, session_key);
3434 break;
3435 case MSG_GENERIC:
3436 reply = handle_op_generic(request, session_key, i);
3437 break;
3438 case MSG_GETSCHEMA:
3439 reply = handle_op_getschema(request, session_key);
3440 break;
3441 case MSG_RELOADHELLO:
3442 reply = handle_op_reloadhello(request, session_key);
3443 break;
3444 case MSG_NTF_GETHISTORY:
3445 reply = handle_op_ntfgethistory(request, session_key);
3446 break;
3447 case MSG_VALIDATE:
3448 reply = handle_op_validate(request, session_key);
3449 break;
3450 case SCH_QUERY:
3451 reply = handle_op_query(request, session_key, i);
3452 break;
3453 case SCH_MERGE:
3454 reply = handle_op_merge(request, session_key, i);
3455 break;
3456 }
3457
3458 add_reply(replies, reply, session_key);
3459 }
3460
3461 /* free parameters */
3462 operation = (-1);
3463
3464 DEBUG("Clean request json object.");
3465 if (request != NULL) {
3466 pthread_mutex_lock(&json_lock);
3467 json_object_put(request);
3468 pthread_mutex_unlock(&json_lock);
Michal Vasko365dc4c2016-03-17 10:03:58 +01003469 request = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003470 }
3471 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02003472
3473send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003474 /* send reply to caller */
3475 if (replies) {
3476 pthread_mutex_lock(&json_lock);
3477 msgtext = json_object_to_json_string(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003478 pthread_mutex_unlock(&json_lock);
Michal Vaskof849e4e2016-04-06 10:00:08 +02003479 DEBUG("Sending message:\n%s\n", msgtext);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003480 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3481 if (buffer != NULL) {
3482 free(buffer);
3483 buffer = NULL;
3484 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003485 break;
3486 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003487
Michal Vaskodf280a22016-03-17 09:19:53 +01003488 i = 0;
3489 sent = 0;
3490 count = strlen(chunked_out_msg) + 1;
3491 while (count && ((i = send(client, chunked_out_msg + sent, count, 0)) != -1)) {
3492 sent += i;
3493 count -= i;
3494 }
3495 if (i == -1) {
3496 ERROR("Sending message failed (%s).", strerror(errno));
3497 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003498 DEBUG("Clean reply json object.");
3499 pthread_mutex_lock(&json_lock);
3500 json_object_put(replies);
Michal Vasko52c91772016-03-17 10:04:12 +01003501 pthread_mutex_unlock(&json_lock);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003502 replies = NULL;
3503 DEBUG("Clean message buffer.");
3504 CHECK_AND_FREE(chunked_out_msg);
3505 chunked_out_msg = NULL;
3506 if (buffer) {
3507 free(buffer);
3508 buffer = NULL;
3509 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003510 clean_err_reply();
3511 } else {
3512 ERROR("Reply is NULL, shouldn't be...");
3513 continue;
3514 }
3515 }
3516 }
3517 free(arg);
3518 free_err_reply();
Michal Vaskod0205992016-03-17 10:04:49 +01003519 nc_thread_destroy();
David Kupka8e60a372012-09-04 09:15:20 +02003520
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003521 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003522}
3523
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003524/**
3525 * \brief Close all open NETCONF sessions.
3526 *
3527 * During termination of mod_netconf, it is useful to close all remaining
3528 * sessions. This function iterates over the list of sessions and close them
3529 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003530 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003531static void
3532close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003533{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003534 struct session_with_mutex *locked_session, *next_session;
3535 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003536
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003537 /* get exclusive access to sessions_list (conns) */
3538 DEBUG("LOCK wrlock %s", __func__);
3539 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3540 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3541 return;
3542 }
3543 for (next_session = netconf_sessions_list; next_session;) {
3544 locked_session = next_session;
3545 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003546
Michal Vaskoc3146782015-11-04 14:46:41 +01003547 /* close_and_free_session handles locking on its own */
Michal Vaskof35ea502016-02-24 10:44:54 +01003548 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 +01003549 close_and_free_session(locked_session);
3550 }
3551 netconf_sessions_list = NULL;
3552
3553 /* get exclusive access to sessions_list (conns) */
3554 DEBUG("UNLOCK wrlock %s", __func__);
3555 if (pthread_rwlock_unlock (&session_lock) != 0) {
3556 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3557 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003558}
3559
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003560static void
3561check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003562{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003563 struct nc_session *ns = NULL;
3564 struct session_with_mutex *locked_session = NULL;
3565 time_t current_time = time(NULL);
3566 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003567
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003568 /* get exclusive access to sessions_list (conns) */
3569 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3570 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3571 return;
3572 }
3573 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3574 ns = locked_session->session;
3575 if (ns == NULL) {
3576 continue;
3577 }
3578 pthread_mutex_lock(&locked_session->lock);
3579 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003580 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 +02003581
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003582 /* close_and_free_session handles locking on its own */
3583 close_and_free_session(locked_session);
3584 } else {
3585 pthread_mutex_unlock(&locked_session->lock);
3586 }
3587 }
3588 /* get exclusive access to sessions_list (conns) */
3589 if (pthread_rwlock_unlock(&session_lock) != 0) {
3590 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3591 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003592}
3593
3594
3595/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003596 * This is actually implementation of NETCONF client
3597 * - requests are received from UNIX socket in the predefined format
3598 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003599 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003600 *
3601 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003602static void
3603forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003604{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003605 struct timeval tv;
3606 struct sockaddr_un local, remote;
3607 int lsock, client, ret, i, pthread_count = 0;
3608 unsigned int olds = 0, timediff = 0;
3609 socklen_t len;
3610 struct pass_to_thread *arg;
3611 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3612 struct timespec maxtime;
3613 pthread_rwlockattr_t lock_attrs;
3614 #ifdef WITH_NOTIFICATIONS
3615 char use_notifications = 0;
3616 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003617
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003618 /* wait at most 5 seconds for every thread to terminate */
3619 maxtime.tv_sec = 5;
3620 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003621
Tomas Cejka04e08f42014-03-27 19:52:34 +01003622#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003623 /* change uid and gid of process for security reasons */
3624 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003625#else
3626# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003627 if (strlen(SU_GROUP) > 0) {
3628 struct group *g = getgrnam(SU_GROUP);
3629 if (g == NULL) {
3630 ERROR("GID (%s) was not found.", SU_GROUP);
3631 return;
3632 }
3633 if (setgid(g->gr_gid) != 0) {
3634 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3635 return;
3636 }
3637 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003638# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003639 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003640# endif
3641# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003642 if (strlen(SU_USER) > 0) {
3643 struct passwd *p = getpwnam(SU_USER);
3644 if (p == NULL) {
3645 ERROR("UID (%s) was not found.", SU_USER);
3646 return;
3647 }
3648 if (setuid(p->pw_uid) != 0) {
3649 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3650 return;
3651 }
3652 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003653# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003654 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003655# endif
3656#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003657
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003658 /* try to remove if exists */
3659 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003660
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003661 /* create listening UNIX socket to accept incoming connections */
3662 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3663 ERROR("Creating socket failed (%s)", strerror(errno));
3664 goto error_exit;
3665 }
Radek Krejci469aab82012-07-22 18:42:20 +02003666
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003667 local.sun_family = AF_UNIX;
3668 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3669 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003670
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003671 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3672 if (errno == EADDRINUSE) {
3673 ERROR("mod_netconf socket address already in use");
3674 goto error_exit;
3675 }
3676 ERROR("Binding socket failed (%s)", strerror(errno));
3677 goto error_exit;
3678 }
Radek Krejci469aab82012-07-22 18:42:20 +02003679
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003680 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3681 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3682 goto error_exit;
3683 }
3684 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003685
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003686 uid_t user = -1;
3687 if (strlen(CHOWN_USER) > 0) {
3688 struct passwd *p = getpwnam(CHOWN_USER);
3689 if (p != NULL) {
3690 user = p->pw_uid;
3691 }
3692 }
3693 gid_t group = -1;
3694 if (strlen(CHOWN_GROUP) > 0) {
3695 struct group *g = getgrnam(CHOWN_GROUP);
3696 if (g != NULL) {
3697 group = g->gr_gid;
3698 }
3699 }
3700 if (chown(sockname, user, group) == -1) {
3701 ERROR("Chown on socket file failed (%s).", strerror(errno));
3702 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003703
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003704 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003705
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003706 #ifdef WITH_NOTIFICATIONS
3707 if (notification_init() == -1) {
3708 ERROR("libwebsockets initialization failed");
3709 use_notifications = 0;
3710 } else {
3711 use_notifications = 1;
3712 }
3713 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003714
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003715 /* setup libnetconf's callbacks */
Michal Vaskod0205992016-03-17 10:04:49 +01003716 nc_client_init();
Michal Vaskodedf91b2016-04-06 10:00:31 +02003717 nc_verbosity(NC_VERB_VERBOSE);
Michal Vaskof35ea502016-02-24 10:44:54 +01003718 nc_set_print_clb(clb_print);
3719 nc_client_ssh_set_auth_hostkey_check_clb(netconf_callback_ssh_hostkey_check);
3720 nc_client_ssh_set_auth_interactive_clb(netconf_callback_sshauth_interactive);
3721 nc_client_ssh_set_auth_password_clb(netconf_callback_sshauth_password);
3722 nc_client_ssh_set_auth_privkey_passphrase_clb(netconf_callback_sshauth_passphrase);
Radek Krejci469aab82012-07-22 18:42:20 +02003723
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003724 /* disable publickey authentication */
Michal Vaskof35ea502016-02-24 10:44:54 +01003725 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003726
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003727 /* create mutex protecting session list */
3728 pthread_rwlockattr_init(&lock_attrs);
3729 /* rwlock is shared only with threads in this process */
3730 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3731 /* create rw lock */
3732 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3733 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3734 goto error_exit;
3735 }
3736 pthread_mutex_init(&ntf_history_lock, NULL);
3737 pthread_mutex_init(&json_lock, NULL);
3738 DEBUG("Initialization of notification history.");
3739 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3740 ERROR("Initialization of notification history failed.");
3741 }
3742 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3743 ERROR("Initialization of reply key failed.");
3744 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003745
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003746 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3747 while (isterminated == 0) {
3748 gettimeofday(&tv, NULL);
3749 timediff = (unsigned int)tv.tv_sec - olds;
3750 #ifdef WITH_NOTIFICATIONS
3751 if (use_notifications == 1) {
3752 notification_handle();
3753 }
3754 #endif
3755 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3756 check_timeout_and_close();
3757 }
Radek Krejci469aab82012-07-22 18:42:20 +02003758
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003759 /* open incoming connection if any */
3760 len = sizeof(remote);
3761 client = accept(lsock, (struct sockaddr *) &remote, &len);
3762 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3763 usleep(SLEEP_TIME * 1000);
3764 continue;
3765 } else if (client == -1 && (errno == EINTR)) {
3766 continue;
3767 } else if (client == -1) {
3768 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3769 continue;
3770 }
Radek Krejci469aab82012-07-22 18:42:20 +02003771
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003772 /* set client's socket as non-blocking */
3773 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003774
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003775 arg = malloc(sizeof(struct pass_to_thread));
3776 arg->client = client;
3777 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003778
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003779 /* start new thread. It will serve this particular request and then terminate */
3780 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3781 ERROR("Creating POSIX thread failed: %d\n", ret);
3782 } else {
3783 DEBUG("Thread %lu created", ptids[pthread_count]);
3784 pthread_count++;
3785 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3786 ptids[pthread_count] = 0;
3787 }
Radek Krejci469aab82012-07-22 18:42:20 +02003788
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003789 /* check if some thread already terminated, free some resources by joining it */
3790 for (i = 0; i < pthread_count; i++) {
3791 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3792 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3793 pthread_count--;
3794 if (pthread_count > 0) {
3795 /* place last Thread ID on the place of joined one */
3796 ptids[i] = ptids[pthread_count];
3797 }
3798 }
3799 }
3800 DEBUG("Running %d threads", pthread_count);
3801 }
Radek Krejci469aab82012-07-22 18:42:20 +02003802
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003803 DEBUG("mod_netconf terminating...");
3804 /* join all threads */
3805 for (i = 0; i < pthread_count; i++) {
3806 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3807 }
Radek Krejci469aab82012-07-22 18:42:20 +02003808
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003809 #ifdef WITH_NOTIFICATIONS
3810 notification_close();
3811 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003812
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003813 /* close all NETCONF sessions */
3814 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003815
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003816 /* destroy rwlock */
3817 pthread_rwlock_destroy(&session_lock);
3818 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003819
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003820 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003821
Michal Vaskod0205992016-03-17 10:04:49 +01003822 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003823 free(ptids);
3824 close(lsock);
3825 exit(0);
3826 return;
3827
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003828error_exit:
Michal Vaskod0205992016-03-17 10:04:49 +01003829 nc_client_destroy();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003830 close(lsock);
3831 free(ptids);
3832 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003833}
3834
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003835int
3836main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003837{
Michal Vaskoc3146782015-11-04 14:46:41 +01003838 struct sigaction action;
3839 sigset_t block_mask;
Michal Vaskoa53ef882015-11-24 11:02:01 +01003840 int daemonize = 0, i;
Michal Vaskoc3146782015-11-04 14:46:41 +01003841
Michal Vaskoa53ef882015-11-24 11:02:01 +01003842 if (argc > 3) {
3843 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3844 return 1;
3845 }
3846
3847 sockname = SOCKET_FILENAME;
3848 for (i = 1; i < argc; ++i) {
3849 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
3850 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3851 return 0;
3852 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon")) {
3853 daemonize = 1;
3854 } else {
3855 sockname = argv[i];
3856 }
3857 }
3858
3859 if (daemonize && (daemon(0, 0) == -1)) {
3860 ERROR("daemon() failed (%s)", strerror(errno));
3861 return 1;
Michal Vaskoc3146782015-11-04 14:46:41 +01003862 }
3863
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003864 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003865 action.sa_handler = signal_handler;
3866 action.sa_mask = block_mask;
3867 action.sa_flags = 0;
3868 sigaction(SIGINT, &action, NULL);
3869 sigaction(SIGTERM, &action, NULL);
3870
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003871 forked_proc();
3872 DEBUG("Terminated");
3873 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003874}