blob: 901e7e94d48cc944aef01d82f2c428fc79abe8db [file] [log] [blame]
Radek Krejci469aab82012-07-22 18:42:20 +02001/*!
Michal Vasko30aa3822015-11-25 10:41:54 +01002 * \file netopeerguid.c
3 * \brief NetopeerGUI daemon
Radek Krejci469aab82012-07-22 18:42:20 +02004 * \author Tomas Cejka <cejkat@cesnet.cz>
5 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskoa53ef882015-11-24 11:02:01 +01006 * \author Michal Vasko <mvasko@cesnet.cz>
Radek Krejci469aab82012-07-22 18:42:20 +02007 * \date 2011
8 * \date 2012
Tomas Cejka94da2c52013-01-08 18:20:30 +01009 * \date 2013
Michal Vaskoa53ef882015-11-24 11:02:01 +010010 * \date 2015
Radek Krejci469aab82012-07-22 18:42:20 +020011 */
12/*
Michal Vaskoa53ef882015-11-24 11:02:01 +010013 * Copyright (C) 2011-2015 CESNET
Radek Krejci469aab82012-07-22 18:42:20 +020014 *
15 * LICENSE TERMS
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in
24 * the documentation and/or other materials provided with the
25 * distribution.
26 * 3. Neither the name of the Company nor the names of its contributors
27 * may be used to endorse or promote products derived from this
28 * software without specific prior written permission.
29 *
30 * ALTERNATIVELY, provided that this notice is retained in full, this
31 * product may be distributed under the terms of the GNU General Public
32 * License (GPL) version 2 or later, in which case the provisions
33 * of the GPL apply INSTEAD OF those given above.
34 *
35 * This software is provided ``as is'', and any express or implied
36 * warranties, including, but not limited to, the implied warranties of
37 * merchantability and fitness for a particular purpose are disclaimed.
38 * In no event shall the company or contributors be liable for any
39 * direct, indirect, incidental, special, exemplary, or consequential
40 * damages (including, but not limited to, procurement of substitute
41 * goods or services; loss of use, data, or profits; or business
42 * interruption) however caused and on any theory of liability, whether
43 * in contract, strict liability, or tort (including negligence or
44 * otherwise) arising in any way out of the use of this software, even
45 * if advised of the possibility of such damage.
46 *
47 */
Michal Vaskoc3146782015-11-04 14:46:41 +010048#define _GNU_SOURCE
Radek Krejci469aab82012-07-22 18:42:20 +020049
Radek Krejci7b4ddd02012-07-30 08:09:58 +020050#include <unistd.h>
51#include <poll.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010052#include <time.h>
Michal Vaskob69dde22016-03-09 11:42:20 +010053#include <fcntl.h>
Radek Krejci469aab82012-07-22 18:42:20 +020054#include <sys/types.h>
55#include <sys/socket.h>
56#include <sys/un.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010057#include <sys/stat.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010058#include <pwd.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010059#include <errno.h>
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010060#include <limits.h>
Tomas Cejka04e08f42014-03-27 19:52:34 +010061#include <grp.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010062#include <signal.h>
David Kupka8e60a372012-09-04 09:15:20 +020063#include <pthread.h>
64#include <ctype.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020065
Michal Vaskof35ea502016-02-24 10:44:54 +010066#include <nc_client.h>
Radek Krejci7b4ddd02012-07-30 08:09:58 +020067
Tomas Cejka04e08f42014-03-27 19:52:34 +010068#include "../config.h"
69
Tomas Cejkad340dbf2013-03-24 20:36:57 +010070#ifdef WITH_NOTIFICATIONS
Michal Vaskoa53ef882015-11-24 11:02:01 +010071#include "notification_server.h"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010072#endif
73
Tomas Cejka94da2c52013-01-08 18:20:30 +010074#include "message_type.h"
Michal Vaskoa53ef882015-11-24 11:02:01 +010075#include "netopeerguid.h"
Radek Krejci469aab82012-07-22 18:42:20 +020076
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010077#define SCHEMA_DIR "/tmp/yang_models"
Radek Krejci469aab82012-07-22 18:42:20 +020078#define MAX_PROCS 5
Michal Vaskoa53ef882015-11-24 11:02:01 +010079#define SOCKET_FILENAME "/var/run/netopeerguid.sock"
Radek Krejci469aab82012-07-22 18:42:20 +020080#define MAX_SOCKET_CL 10
81#define BUFFER_SIZE 4096
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010082#define ACTIVITY_CHECK_INTERVAL 10 /**< timeout in seconds, how often activity is checked */
83#define ACTIVITY_TIMEOUT (60*60) /**< timeout in seconds, after this time, session is automaticaly closed. */
Radek Krejci469aab82012-07-22 18:42:20 +020084
Michal Vasko7732dee2015-11-05 10:22:15 +010085/* sleep in master process for non-blocking socket reading, in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020086#define SLEEP_TIME 200
87
88#ifndef offsetof
89#define offsetof(type, member) ((size_t) ((type *) 0)->member)
90#endif
91
Tomas Cejka027f3bc2012-11-10 20:28:36 +010092/* timeout in msec */
Radek Krejci469aab82012-07-22 18:42:20 +020093struct timeval timeout = { 1, 0 };
94
Michal Vaskoda0ab5c2015-11-13 13:24:51 +010095#define NCWITHDEFAULTS NCWD_MODE_NOTSET
Tomas Cejka5064c232013-01-17 09:30:58 +010096
Radek Krejci469aab82012-07-22 18:42:20 +020097#define MSG_OK 0
98#define MSG_OPEN 1
99#define MSG_DATA 2
100#define MSG_CLOSE 3
101#define MSG_ERROR 4
102#define MSG_UNKNOWN 5
103
Tomas Cejka47387fd2013-06-10 20:37:46 +0200104pthread_rwlock_t session_lock; /**< mutex protecting netconf_sessions_list from multiple access errors */
Tomas Cejka6b886e02013-07-05 09:53:17 +0200105pthread_mutex_t ntf_history_lock; /**< mutex protecting notification history list */
Tomas Cejkaef531ee2013-11-12 16:07:00 +0100106pthread_mutex_t ntf_hist_clbc_mutex; /**< mutex protecting notification history list */
Tomas Cejka9a23f6e2014-03-27 14:57:00 +0100107pthread_mutex_t json_lock; /**< mutex for protecting json-c calls */
108
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100109unsigned int session_key_generator = 1;
Michal Vaskoc3146782015-11-04 14:46:41 +0100110struct session_with_mutex *netconf_sessions_list = NULL;
Michal Vaskoc3146782015-11-04 14:46:41 +0100111static const char *sockname;
Tomas Cejkad016f9c2013-07-10 09:16:16 +0200112static pthread_key_t notif_history_key;
Tomas Cejka442258e2014-04-01 18:17:18 +0200113pthread_key_t err_reply_key;
Radek Krejci469aab82012-07-22 18:42:20 +0200114volatile int isterminated = 0;
Radek Krejci469aab82012-07-22 18:42:20 +0200115static char* password;
116
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100117json_object *create_ok_reply(void);
118json_object *create_data_reply(const char *data);
119static char *netconf_getschema(unsigned int session_key, const char *identifier, const char *version,
120 const char *format, json_object **err);
Michal Vaskof35ea502016-02-24 10:44:54 +0100121static void node_add_metadata_recursive(struct lyd_node *data_tree, const struct lys_module *module,
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100122 json_object *data_json_parent);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100123static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100124
125static void
126signal_handler(int sign)
Radek Krejci469aab82012-07-22 18:42:20 +0200127{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100128 switch (sign) {
129 case SIGINT:
130 case SIGTERM:
131 isterminated = 1;
132 break;
133 }
Radek Krejci469aab82012-07-22 18:42:20 +0200134}
135
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100136int
137netconf_callback_ssh_hostkey_check(const char* UNUSED(hostname), ssh_session UNUSED(session))
Radek Krejci469aab82012-07-22 18:42:20 +0200138{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100139 /* always approve */
140 return (EXIT_SUCCESS);
Radek Krejci469aab82012-07-22 18:42:20 +0200141}
142
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100143char *
Michal Vaskof35ea502016-02-24 10:44:54 +0100144netconf_callback_sshauth_passphrase(const char *UNUSED(priv_key_file))
Radek Krejci469aab82012-07-22 18:42:20 +0200145{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100146 char *buf;
147 buf = strdup(password);
148 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200149}
150
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100151char *
152netconf_callback_sshauth_password(const char *UNUSED(username), const char *UNUSED(hostname))
Radek Krejci469aab82012-07-22 18:42:20 +0200153{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100154 char *buf;
155 buf = strdup(password);
156 return (buf);
Radek Krejci469aab82012-07-22 18:42:20 +0200157}
158
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100159char *
160netconf_callback_sshauth_interactive(const char *UNUSED(name), const char *UNUSED(instruction),
161 const char *UNUSED(prompt), int UNUSED(echo))
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200162{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100163 char *buf;
164 buf = strdup(password);
165 return (buf);
Tomas Cejkab34b7b12015-06-21 22:54:11 +0200166}
167
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100168void
169netconf_callback_error_process(const char *UNUSED(tag),
170 const char *UNUSED(type),
171 const char *UNUSED(severity),
172 const char *UNUSED(apptag),
173 const char *UNUSED(path),
174 const char *message,
175 const char *UNUSED(attribute),
176 const char *UNUSED(element),
177 const char *UNUSED(ns),
178 const char *UNUSED(sid))
Radek Krejcic11fd862012-07-26 12:41:21 +0200179{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100180 json_object **err_reply_p = (json_object **) pthread_getspecific(err_reply_key);
181 if (err_reply_p == NULL) {
182 ERROR("Error message was not allocated. %s", __func__);
183 return;
184 }
185 json_object *err_reply = *err_reply_p;
Tomas Cejkaedb3ab42014-03-27 15:04:00 +0100186
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100187 json_object *array = NULL;
188 if (err_reply == NULL) {
189 ERROR("error calback: empty error list");
190 pthread_mutex_lock(&json_lock);
191 err_reply = json_object_new_object();
192 array = json_object_new_array();
193 json_object_object_add(err_reply, "type", json_object_new_int(REPLY_ERROR));
194 json_object_object_add(err_reply, "errors", array);
195 if (message != NULL) {
196 json_object_array_add(array, json_object_new_string(message));
197 }
198 pthread_mutex_unlock(&json_lock);
199 (*err_reply_p) = err_reply;
200 } else {
201 ERROR("error calback: nonempty error list");
202 pthread_mutex_lock(&json_lock);
203 if (json_object_object_get_ex(err_reply, "errors", &array) == TRUE) {
204 if (message != NULL) {
205 json_object_array_add(array, json_object_new_string(message));
206 }
207 }
208 pthread_mutex_unlock(&json_lock);
209 }
210 pthread_setspecific(err_reply_key, err_reply_p);
211 return;
Radek Krejcic11fd862012-07-26 12:41:21 +0200212}
213
Tomas Cejka47387fd2013-06-10 20:37:46 +0200214/**
215 * should be used in locked area
216 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100217void
218prepare_status_message(struct session_with_mutex *s, struct nc_session *session)
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200219{
Michal Vaskof35ea502016-02-24 10:44:54 +0100220 int i;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100221 json_object *json_obj = NULL;
222 json_object *js_tmp = NULL;
223 char *old_sid = NULL;
224 const char *j_old_sid = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +0100225 char str_port[6];
226 const char **cpblts;
227 struct lyd_node *yanglib, *module, *node;
Tomas Cejkaf38a54c2013-05-27 21:57:35 +0200228
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100229 if (s == NULL) {
230 ERROR("No session given.");
231 return;
232 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +0200233
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100234 pthread_mutex_lock(&json_lock);
235 if (s->hello_message != NULL) {
236 ERROR("clean previous hello message");
237 if (json_object_object_get_ex(s->hello_message, "sid", &js_tmp) == TRUE) {
238 j_old_sid = json_object_get_string(js_tmp);
239 if (j_old_sid != NULL) {
240 old_sid = strdup(j_old_sid);
241 }
242 }
243 json_object_put(s->hello_message);
244 s->hello_message = NULL;
245 }
246 s->hello_message = json_object_new_object();
247 if (session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100248 if (!old_sid) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100249 /* we don't have old sid */
Michal Vaskof35ea502016-02-24 10:44:54 +0100250 asprintf(&old_sid, "%u", nc_session_get_id(session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100251 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100252 json_object_object_add(s->hello_message, "sid", json_object_new_string(old_sid));
253 free(old_sid);
254 old_sid = NULL;
255
256 json_object_object_add(s->hello_message, "version", json_object_new_string((nc_session_get_version(session) ? "1.1":"1.0")));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100257 json_object_object_add(s->hello_message, "host", json_object_new_string(nc_session_get_host(session)));
Michal Vaskof35ea502016-02-24 10:44:54 +0100258 sprintf(str_port, "%u", nc_session_get_port(session));
259 json_object_object_add(s->hello_message, "port", json_object_new_string(str_port));
260 json_object_object_add(s->hello_message, "user", json_object_new_string(nc_session_get_username(session)));
261 cpblts = nc_session_get_cpblts(session);
262 if (cpblts) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100263 json_obj = json_object_new_array();
Michal Vaskof35ea502016-02-24 10:44:54 +0100264 for (i = 0; cpblts[i]; ++i) {
265 json_object_array_add(json_obj, json_object_new_string(cpblts[i]));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100266 }
267 json_object_object_add(s->hello_message, "capabilities", json_obj);
268 }
Michal Vaskof35ea502016-02-24 10:44:54 +0100269
270 yanglib = ly_ctx_info(nc_session_get_ctx(session));
271 if (yanglib) {
272 json_obj = json_object_new_array();
273 LY_TREE_FOR(yanglib->child, module) {
274 if (!strcmp(module->schema->name, "module")) {
275 LY_TREE_FOR(module->child, node) {
276 if (!strcmp(node->schema->name, "name")) {
277 json_object_array_add(json_obj, json_object_new_string(((struct lyd_node_leaf_list *)node)->value_str));
278 break;
279 }
280 }
281 }
282 }
283 json_object_object_add(s->hello_message, "models", json_obj);
284
Michal Vaskoc04900a2016-03-15 16:20:24 +0100285 lyd_free(yanglib);
Michal Vaskof35ea502016-02-24 10:44:54 +0100286 }
287
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100288 DEBUG("%s", json_object_to_json_string(s->hello_message));
289 } else {
290 ERROR("Session was not given.");
291 json_object_object_add(s->hello_message, "type", json_object_new_int(REPLY_ERROR));
292 json_object_object_add(s->hello_message, "error-message", json_object_new_string("Invalid session identifier."));
293 }
294 DEBUG("Status info from hello message prepared");
295 pthread_mutex_unlock(&json_lock);
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200296}
297
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100298void
299create_err_reply_p()
Tomas Cejka442258e2014-04-01 18:17:18 +0200300{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100301 json_object **err_reply = calloc(1, sizeof(json_object **));
302 if (err_reply == NULL) {
303 ERROR("Allocation of err_reply storage failed!");
304 return;
305 }
306 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
307 ERROR("cannot set thread-specific value.");
308 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200309}
310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100311void
312clean_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200313{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100314 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
315 if (err_reply != NULL) {
316 if (*err_reply != NULL) {
317 pthread_mutex_lock(&json_lock);
318 json_object_put(*err_reply);
319 pthread_mutex_unlock(&json_lock);
320 }
321 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
322 ERROR("Cannot set thread-specific hash value.");
323 }
324 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200325}
326
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100327void
328free_err_reply()
Tomas Cejka442258e2014-04-01 18:17:18 +0200329{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100330 json_object **err_reply = (json_object **) pthread_getspecific(err_reply_key);
331 if (err_reply != NULL) {
332 if (*err_reply != NULL) {
333 pthread_mutex_lock(&json_lock);
334 json_object_put(*err_reply);
335 pthread_mutex_unlock(&json_lock);
336 }
337 free(err_reply);
338 err_reply = NULL;
339 if (pthread_setspecific(err_reply_key, err_reply) != 0) {
340 ERROR("Cannot set thread-specific hash value.");
341 }
342 }
343}
344
345static struct session_with_mutex *
346session_get_locked(unsigned int session_key, json_object **err)
347{
348 struct session_with_mutex *locked_session;
349
350 /* get non-exclusive (read) access to sessions_list (conns) */
351 DEBUG("LOCK wrlock %s", __func__);
352 if (pthread_rwlock_rdlock(&session_lock) != 0) {
353 if (*err) {
354 *err = create_error_reply("Locking failed.");
355 }
356 return NULL;
357 }
358 /* get session where send the RPC */
359 for (locked_session = netconf_sessions_list;
360 locked_session && (locked_session->session_key != session_key);
361 locked_session = locked_session->next);
362 if (!locked_session) {
363 if (*err) {
364 *err = create_error_reply("Session not found.");
365 }
366 return NULL;
367 }
368
369 /* get exclusive access to session */
370 DEBUG("LOCK mutex %s", __func__);
371 if (pthread_mutex_lock(&locked_session->lock) != 0) {
372 if (*err) {
373 *err = create_error_reply("Locking failed.");
374 }
375 goto wrlock_fail;
376 }
377 return locked_session;
378
379wrlock_fail:
380 DEBUG("UNLOCK wrlock %s", __func__);
381 pthread_rwlock_unlock(&session_lock);
382 return NULL;
383}
384
385static void
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100386session_user_activity(const char *username)
387{
388 struct session_with_mutex *sess;
389
390 for (sess = netconf_sessions_list; sess; sess = sess->next) {
Michal Vaskof35ea502016-02-24 10:44:54 +0100391 if (!strcmp(nc_session_get_username(sess->session), username)) {
Michal Vaskoe32bcba2015-11-24 09:05:51 +0100392 sess->last_activity = time(NULL);
393 }
394 }
395}
396
397static void
Michal Vaskoda0ab5c2015-11-13 13:24:51 +0100398session_unlock(struct session_with_mutex *locked_session)
399{
400 DEBUG("UNLOCK mutex %s", __func__);
401 pthread_mutex_unlock(&locked_session->lock);
402 DEBUG("UNLOCK wrlock %s", __func__);
403 pthread_rwlock_unlock(&session_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +0200404}
Tomas Cejka45ab59f2013-05-15 00:10:49 +0200405
Michal Vasko3fda9a92015-11-23 10:10:57 +0100406static void
407node_metadata_text(const char *text, const char *name, json_object *parent)
408{
409 json_object *obj;
410
411 if (!text) {
412 return;
413 }
414
415 obj = json_object_new_string(text);
416 json_object_object_add(parent, name, obj);
417}
418
419static void
420node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
421{
422 json_object *obj;
423
424 if (!restr) {
425 return;
426 }
427
428 obj = json_object_new_string(restr->expr);
429 json_object_object_add(parent, name, obj);
430}
431
432static void
433node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
434{
435 uint8_t i;
436 json_object *array, *obj;
437
438 if (!must_size || !must) {
439 return;
440 }
441
442 array = json_object_new_array();
443
444 for (i = 0; i < must_size; ++i) {
445 obj = json_object_new_string(must[i].expr);
446 json_object_array_add(array, obj);
447 }
448
449 json_object_object_add(parent, "must", array);
450}
451
452static void
453node_metadata_basic(struct lys_node *node, json_object *parent)
454{
455 json_object *obj;
456
457 /* description */
458 node_metadata_text(node->dsc, "description", parent);
459
460 /* reference */
461 node_metadata_text(node->ref, "reference", parent);
462
463 /* config */
464 if (node->flags & LYS_CONFIG_R) {
465 obj = json_object_new_boolean(0);
466 } else {
467 obj = json_object_new_boolean(1);
468 }
469 json_object_object_add(parent, "config", obj);
470
471 /* status */
472 if (node->flags & LYS_STATUS_DEPRC) {
473 obj = json_object_new_string("deprecated");
474 } else if (node->flags & LYS_STATUS_OBSLT) {
475 obj = json_object_new_string("obsolete");
476 } else {
477 obj = json_object_new_string("current");
478 }
479 json_object_object_add(parent, "status", obj);
480
481 /* mandatory */
482 if (node->flags & LYS_MAND_TRUE) {
483 obj = json_object_new_boolean(1);
484 } else {
485 obj = json_object_new_boolean(0);
486 }
487 json_object_object_add(parent, "mandatory", obj);
488
489 /* NACM extensions */
490 if (node->nacm) {
491 if (node->nacm & LYS_NACM_DENYW) {
492 obj = json_object_new_string("default-deny-write");
493 } else {
494 obj = json_object_new_string("default-deny-all");
495 }
496 json_object_object_add(parent, "ext", obj);
497 }
498}
499
500static void
501node_metadata_when(struct lys_when *when, json_object *parent)
502{
503 json_object *obj;
504
505 if (!when) {
506 return;
507 }
508
509 obj = json_object_new_string(when->cond);
510 json_object_object_add(parent, "when", obj);
511}
512
513static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100514node_metadata_children_recursive(struct lys_node *node, json_object **child_array, json_object **choice_array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100515{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100516 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100517 struct lys_node *child;
518
519 if (!node->child) {
520 return;
521 }
522
523 LY_TREE_FOR(node->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100524 if (child->nodetype == LYS_USES) {
525 node_metadata_children_recursive(child, child_array, choice_array);
526 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100527 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100528 if (!*child_array) {
529 *child_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100530 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100531 json_object_array_add(*child_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100532 } else if (child->nodetype == LYS_CHOICE) {
533 obj = json_object_new_string(child->name);
Michal Vaskoa45770b2015-11-23 15:49:41 +0100534 if (!*choice_array) {
535 *choice_array = json_object_new_array();
Michal Vasko3fda9a92015-11-23 10:10:57 +0100536 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100537 json_object_array_add(*choice_array, obj);
Michal Vasko3fda9a92015-11-23 10:10:57 +0100538 }
539 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100540}
541
542static void
Michal Vaskoa45770b2015-11-23 15:49:41 +0100543node_metadata_cases_recursive(struct lys_node_choice *choice, json_object *array)
Michal Vasko3fda9a92015-11-23 10:10:57 +0100544{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100545 json_object *obj;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100546 struct lys_node *child;
547
548 if (!choice->child) {
549 return;
550 }
551
Michal Vasko3fda9a92015-11-23 10:10:57 +0100552 LY_TREE_FOR(choice->child, child) {
Michal Vaskoa45770b2015-11-23 15:49:41 +0100553 if (child->nodetype == LYS_USES) {
554 node_metadata_cases_recursive((struct lys_node_choice *)child, array);
555 } else if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
Michal Vasko3fda9a92015-11-23 10:10:57 +0100556 obj = json_object_new_string(child->name);
557 json_object_array_add(array, obj);
558 }
559 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100560}
561
562static void
563node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
564{
565 json_object *obj;
566
567 if (min) {
568 obj = json_object_new_int(min);
569 json_object_object_add(parent, "min-elements", obj);
570 }
571
572 if (max) {
573 obj = json_object_new_int(max);
574 json_object_object_add(parent, "max-elements", obj);
575 }
576}
577
578static void
579node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
580{
581 struct lys_ident_der *cur;
582 json_object *obj;
583
584 if (!ident) {
585 return;
586 }
587
588 obj = json_object_new_string(ident->name);
589 json_object_array_add(array, obj);
590
591 for (cur = ident->der; cur; cur = cur->next) {
592 node_metadata_ident_recursive(cur->ident, array);
593 }
594}
595
596static void
597node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
598{
599 json_object *obj, *array, *item;
600 char *str;
601 int i;
602
603 /* built-in YANG type */
604 if (!type->der->module) {
605 switch (type->base) {
606 case LY_TYPE_BINARY:
607 node_metadata_text("binary", "type", parent);
608 node_metadata_restr(type->info.binary.length, "length", parent);
609 break;
610 case LY_TYPE_BITS:
611 node_metadata_text("bits", "type", parent);
612
613 array = json_object_new_array();
614 for (i = 0; i < type->info.bits.count; ++i) {
615 item = json_object_new_object();
616 obj = json_object_new_string(type->info.bits.bit[i].name);
617 json_object_object_add(item, "name", obj);
618 obj = json_object_new_int(type->info.bits.bit[i].pos);
619 json_object_object_add(item, "position", obj);
620 json_object_array_add(array, item);
621 }
622 json_object_object_add(parent, "bits", array);
623 break;
624 case LY_TYPE_BOOL:
625 node_metadata_text("bool", "type", parent);
626 break;
627 case LY_TYPE_DEC64:
628 node_metadata_text("decimal64", "type", parent);
629 node_metadata_restr(type->info.dec64.range, "range", parent);
630 obj = json_object_new_int(type->info.dec64.dig);
631 json_object_object_add(parent, "fraction-digits", obj);
632 break;
633 case LY_TYPE_EMPTY:
634 node_metadata_text("empty", "type", parent);
635 break;
636 case LY_TYPE_ENUM:
637 node_metadata_text("enumeration", "type", parent);
638
639 array = json_object_new_array();
640 for (i = 0; i < type->info.enums.count; ++i) {
641 obj = json_object_new_string(type->info.enums.enm[i].name);
642 json_object_array_add(array, obj);
643 }
644 json_object_object_add(parent, "enumval", array);
645 break;
646 case LY_TYPE_IDENT:
647 node_metadata_text("identityref", "type", parent);
648
649 array = json_object_new_array();
650 node_metadata_ident_recursive(type->info.ident.ref, array);
651 json_object_object_add(parent, "identityval", array);
652 break;
653 case LY_TYPE_INST:
654 node_metadata_text("instance-identifier", "type", parent);
655 if (type->info.inst.req == -1) {
656 obj = json_object_new_boolean(0);
657 } else {
658 obj = json_object_new_boolean(1);
659 }
660 json_object_object_add(parent, "require-instance", obj);
661 break;
662 case LY_TYPE_LEAFREF:
663 node_metadata_text("leafref", "type", parent);
664 node_metadata_text(type->info.lref.path, "path", parent);
665 break;
666 case LY_TYPE_STRING:
667 node_metadata_text("string", "type", parent);
668 node_metadata_restr(type->info.str.length, "length", parent);
669 if (type->info.str.pat_count) {
670 array = json_object_new_array();
671 for (i = 0; i < type->info.str.pat_count; ++i) {
672 obj = json_object_new_string(type->info.str.patterns[i].expr);
673 json_object_array_add(array, obj);
674 }
675 json_object_object_add(parent, "pattern", array);
676 }
677 break;
678 case LY_TYPE_UNION:
679 node_metadata_text("union", "type", parent);
680 array = json_object_new_array();
681 for (i = 0; i < type->info.uni.count; ++i) {
682 obj = json_object_new_object();
683 node_metadata_type(&type->info.uni.types[i], module, obj);
684 json_object_array_add(array, obj);
685 }
686 json_object_object_add(parent, "types", array);
687 break;
688 case LY_TYPE_INT8:
689 node_metadata_text("int8", "type", parent);
690 node_metadata_restr(type->info.num.range, "range", parent);
691 break;
692 case LY_TYPE_UINT8:
693 node_metadata_text("uint8", "type", parent);
694 node_metadata_restr(type->info.num.range, "range", parent);
695 break;
696 case LY_TYPE_INT16:
697 node_metadata_text("int16", "type", parent);
698 node_metadata_restr(type->info.num.range, "range", parent);
699 break;
700 case LY_TYPE_UINT16:
701 node_metadata_text("uint16", "type", parent);
702 node_metadata_restr(type->info.num.range, "range", parent);
703 break;
704 case LY_TYPE_INT32:
705 node_metadata_text("int32", "type", parent);
706 node_metadata_restr(type->info.num.range, "range", parent);
707 break;
708 case LY_TYPE_UINT32:
709 node_metadata_text("uint32", "type", parent);
710 node_metadata_restr(type->info.num.range, "range", parent);
711 break;
712 case LY_TYPE_INT64:
713 node_metadata_text("int64", "type", parent);
714 node_metadata_restr(type->info.num.range, "range", parent);
715 break;
716 case LY_TYPE_UINT64:
717 node_metadata_text("uint64", "type", parent);
718 node_metadata_restr(type->info.num.range, "range", parent);
719 break;
720 default:
721 ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
722 break;
723 }
724
725 /* typedef */
726 } else {
727 if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
728 node_metadata_text(type->der->name, "type", parent);
729 } else {
730 asprintf(&str, "%s:%s", type->module_name, type->der->name);
731 node_metadata_text(str, "type", parent);
732 free(str);
733 }
734 obj = json_object_new_object();
735 node_metadata_typedef(type->der, obj);
736 json_object_object_add(parent, "typedef", obj);
737 }
738}
739
740static void
741node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
742{
743 json_object *obj;
744
745 /* description */
746 node_metadata_text(tpdf->dsc, "description", parent);
747
748 /* reference */
749 node_metadata_text(tpdf->ref, "reference", parent);
750
751 /* status */
752 if (tpdf->flags & LYS_STATUS_DEPRC) {
753 obj = json_object_new_string("deprecated");
754 } else if (tpdf->flags & LYS_STATUS_OBSLT) {
755 obj = json_object_new_string("obsolete");
756 } else {
757 obj = json_object_new_string("current");
758 }
759 json_object_object_add(parent, "status", obj);
760
761 /* type */
762 node_metadata_type(&tpdf->type, tpdf->module, parent);
763
764 /* units */
765 node_metadata_text(tpdf->units, "units", parent);
766
767 /* default */
768 node_metadata_text(tpdf->dflt, "default", parent);
769}
770
771static void
772node_metadata_container(struct lys_node_container *cont, json_object *parent)
773{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100774 json_object *obj, *child_array = NULL, *choice_array = NULL;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100775
776 /* element type */
777 obj = json_object_new_string("container");
778 json_object_object_add(parent, "eltype", obj);
779
780 /* shared info */
781 node_metadata_basic((struct lys_node *)cont, parent);
782
783 /* must */
784 node_metadata_must(cont->must_size, cont->must, parent);
785
786 /* presence */
787 node_metadata_text(cont->presence, "presence", parent);
788
789 /* when */
790 node_metadata_when(cont->when, parent);
791
792 /* children & choice */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100793 node_metadata_children_recursive((struct lys_node *)cont, &child_array, &choice_array);
794 if (child_array) {
795 json_object_object_add(parent, "children", child_array);
796 }
797 if (choice_array) {
798 json_object_object_add(parent, "choice", choice_array);
799 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100800}
801
802static void
803node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
804{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100805 json_object *obj, *array;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100806
807 /* element type */
808 obj = json_object_new_string("choice");
809 json_object_object_add(parent, "eltype", obj);
810
811 /* shared info */
812 node_metadata_basic((struct lys_node *)choice, parent);
813
814 /* default */
815 node_metadata_text(choice->dflt->name, "default", parent);
816
817 /* when */
818 node_metadata_when(choice->when, parent);
819
820 /* cases */
Michal Vaskoa45770b2015-11-23 15:49:41 +0100821 if (choice->child) {
822 array = json_object_new_array();
823 node_metadata_cases_recursive(choice, array);
824 json_object_object_add(parent, "cases", array);
825 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100826}
827
828static void
829node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
830{
831 json_object *obj;
832 struct lys_node_list *list;
833 int is_key, i;
834
835 /* element type */
836 obj = json_object_new_string("leaf");
837 json_object_object_add(parent, "eltype", obj);
838
839 /* shared info */
840 node_metadata_basic((struct lys_node *)leaf, parent);
841
842 /* type */
843 node_metadata_type(&leaf->type, leaf->module, parent);
844
845 /* units */
846 node_metadata_text(leaf->units, "units", parent);
847
848 /* default */
849 node_metadata_text(leaf->dflt, "default", parent);
850
851 /* must */
852 node_metadata_must(leaf->must_size, leaf->must, parent);
853
854 /* when */
855 node_metadata_when(leaf->when, parent);
856
857 /* iskey */
858 is_key = 0;
859 list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
860 if (list && (list->nodetype == LYS_LIST)) {
861 for (i = 0; i < list->keys_size; ++i) {
862 if (list->keys[i] == leaf) {
863 is_key = 1;
864 break;
865 }
866 }
867 }
868 obj = json_object_new_boolean(is_key);
869 json_object_object_add(parent, "iskey", obj);
870}
871
872static void
873node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
874{
875 json_object *obj;
876
877 /* element type */
878 obj = json_object_new_string("leaf-list");
879 json_object_object_add(parent, "eltype", obj);
880
881 /* shared info */
882 node_metadata_basic((struct lys_node *)llist, parent);
883
884 /* type */
885 node_metadata_type(&llist->type, llist->module, parent);
886
887 /* units */
888 node_metadata_text(llist->units, "units", parent);
889
890 /* must */
891 node_metadata_must(llist->must_size, llist->must, parent);
892
893 /* when */
894 node_metadata_when(llist->when, parent);
895
896 /* min/max-elements */
897 node_metadata_min_max(llist->min, llist->max, parent);
898}
899
900static void
901node_metadata_list(struct lys_node_list *list, json_object *parent)
902{
Michal Vaskoa45770b2015-11-23 15:49:41 +0100903 json_object *obj, *array, *child_array = NULL, *choice_array = NULL;;
Michal Vasko3fda9a92015-11-23 10:10:57 +0100904 int i;
905 unsigned int j;
906
907 /* element type */
908 obj = json_object_new_string("list");
909 json_object_object_add(parent, "eltype", obj);
910
911 /* shared info */
912 node_metadata_basic((struct lys_node *)list, parent);
913
914 /* must */
915 node_metadata_must(list->must_size, list->must, parent);
916
917 /* when */
918 node_metadata_when(list->when, parent);
919
920 /* min/max-elements */
921 node_metadata_min_max(list->min, list->max, parent);
922
923 /* keys */
924 if (list->keys_size) {
925 array = json_object_new_array();
926 for (i = 0; i < list->keys_size; ++i) {
927 obj = json_object_new_string(list->keys[i]->name);
928 json_object_array_add(array, obj);
929 }
930 json_object_object_add(parent, "keys", array);
931 }
932
933 /* unique */
934 if (list->unique_size) {
935 array = json_object_new_array();
936 for (i = 0; i < list->unique_size; ++i) {
937 for (j = 0; j < list->unique[i].expr_size; ++j) {
938 obj = json_object_new_string(list->unique[i].expr[j]);
939 json_object_array_add(array, obj);
940 }
941 }
942 json_object_object_add(parent, "unique", array);
943 }
Michal Vaskoa45770b2015-11-23 15:49:41 +0100944
945 /* children & choice */
946 node_metadata_children_recursive((struct lys_node *)list, &child_array, &choice_array);
947 if (child_array) {
948 json_object_object_add(parent, "children", child_array);
949 }
950 if (choice_array) {
951 json_object_object_add(parent, "choice", choice_array);
952 }
Michal Vasko3fda9a92015-11-23 10:10:57 +0100953}
954
955static void
956node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
957{
958 json_object *obj;
959
960 /* element type */
961 obj = json_object_new_string("anyxml");
962 json_object_object_add(parent, "eltype", obj);
963
964 /* shared info */
965 node_metadata_basic((struct lys_node *)anyxml, parent);
966
967 /* must */
968 node_metadata_must(anyxml->must_size, anyxml->must, parent);
969
970 /* when */
971 node_metadata_when(anyxml->when, parent);
972
973}
974
975static void
976node_metadata_case(struct lys_node_case *cas, json_object *parent)
977{
978 json_object *obj;
979
980 /* element type */
981 obj = json_object_new_string("case");
982 json_object_object_add(parent, "eltype", obj);
983
984 /* shared info */
985 node_metadata_basic((struct lys_node *)cas, parent);
986
987 /* when */
988 node_metadata_when(cas->when, parent);
989}
990
Michal Vaskoa45770b2015-11-23 15:49:41 +0100991static void
992node_metadata_rpc(struct lys_node_rpc *rpc, json_object *parent)
993{
994 json_object *obj;
995
996 /* element type */
997 obj = json_object_new_string("rpc");
998 json_object_object_add(parent, "eltype", obj);
999
1000 /* description */
1001 node_metadata_text(rpc->dsc, "description", parent);
1002
1003 /* reference */
1004 node_metadata_text(rpc->ref, "reference", parent);
1005
1006 /* status */
1007 if (rpc->flags & LYS_STATUS_DEPRC) {
1008 obj = json_object_new_string("deprecated");
1009 } else if (rpc->flags & LYS_STATUS_OBSLT) {
1010 obj = json_object_new_string("obsolete");
1011 } else {
1012 obj = json_object_new_string("current");
1013 }
1014 json_object_object_add(parent, "status", obj);
1015}
1016
1017static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001018node_metadata_model(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001019{
1020 json_object *obj, *array, *item;
1021 int i;
1022
1023 /* yang-version */
1024 if (module->version == 2) {
1025 obj = json_object_new_string("1.1");
1026 } else {
1027 obj = json_object_new_string("1.0");
1028 }
1029 json_object_object_add(parent, "yang-version", obj);
1030
1031 /* namespace */
1032 node_metadata_text(module->ns, "namespace", parent);
1033
1034 /* prefix */
1035 node_metadata_text(module->prefix, "prefix", parent);
1036
1037 /* contact */
1038 node_metadata_text(module->contact, "contact", parent);
1039
1040 /* organization */
1041 node_metadata_text(module->org, "organization", parent);
1042
1043 /* revision */
1044 if (module->rev_size) {
1045 node_metadata_text(module->rev[0].date, "revision", parent);
1046 }
1047
1048 /* description */
1049 node_metadata_text(module->dsc, "description", parent);
1050
1051 /* import */
1052 if (module->imp_size) {
1053 array = json_object_new_array();
1054 for (i = 0; i < module->imp_size; ++i) {
1055 item = json_object_new_object();
1056
1057 node_metadata_text(module->imp[i].module->name, "name", item);
1058 node_metadata_text(module->imp[i].prefix, "prefix", item);
1059 if (module->imp[i].rev && module->imp[i].rev[0]) {
1060 node_metadata_text(module->imp[i].rev, "revision", item);
1061 }
1062
1063 json_object_array_add(array, item);
1064 }
1065 json_object_object_add(parent, "imports", array);
1066 }
1067
1068 /* include */
1069 if (module->inc_size) {
1070 array = json_object_new_array();
1071 for (i = 0; i < module->inc_size; ++i) {
1072 item = json_object_new_object();
1073
1074 node_metadata_text(module->inc[i].submodule->name, "name", item);
1075 if (module->inc[i].rev && module->inc[i].rev[0]) {
1076 node_metadata_text(module->inc[i].rev, "revision", item);
1077 }
1078
1079 json_object_array_add(array, item);
1080 }
1081 json_object_object_add(parent, "includes", array);
1082 }
1083}
1084
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001085/**
1086 * \defgroup netconf_operations NETCONF operations
1087 * The list of NETCONF operations that mod_netconf supports.
1088 * @{
1089 */
1090
1091/**
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001092 * \brief Send RPC and wait for reply with timeout.
1093 *
1094 * \param[in] session libnetconf session
1095 * \param[in] rpc prepared RPC message
1096 * \param[in] timeout timeout in miliseconds, -1 for blocking, 0 for non-blocking
1097 * \param[out] reply reply from the server
Michal Vaskof35ea502016-02-24 10:44:54 +01001098 * \return NC_MSG_WOULDBLOCK or NC_MSG_ERROR.
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001099 * On success, it returns NC_MSG_REPLY.
1100 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001101NC_MSG_TYPE
Michal Vaskof35ea502016-02-24 10:44:54 +01001102netconf_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 +02001103{
Michal Vaskof35ea502016-02-24 10:44:54 +01001104 uint64_t msgid;
1105 NC_MSG_TYPE ret;
1106 ret = nc_send_rpc(session, rpc, timeout, &msgid);
1107 if (ret != NC_MSG_RPC) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001108 return ret;
1109 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001110
1111 while ((ret = nc_recv_reply(session, rpc, msgid, timeout, (strict ? LYD_OPT_STRICT : 0), reply)) == NC_MSG_NOTIF);
1112
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001113 return ret;
1114}
1115
Tomas Cejka8ce138c2015-04-27 23:25:25 +02001116/**
Tomas Cejka0a4bba82013-04-19 11:51:28 +02001117 * \brief Connect to NETCONF server
1118 *
1119 * \warning Session_key hash is not bound with caller identification. This could be potential security risk.
1120 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001121static unsigned int
Michal Vaskof35ea502016-02-24 10:44:54 +01001122netconf_connect(const char *host, const char *port, const char *user, const char *pass, const char *privkey)
Radek Krejci469aab82012-07-22 18:42:20 +02001123{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001124 struct nc_session* session = NULL;
1125 struct session_with_mutex *locked_session, *last_session;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001126 char *pubkey;
Radek Krejci469aab82012-07-22 18:42:20 +02001127
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001128 /* connect to the requested NETCONF server */
1129 password = (char*)pass;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001130 if (privkey) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001131 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 3);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001132 asprintf(&pubkey, "%s.pub", privkey);
Michal Vaskof35ea502016-02-24 10:44:54 +01001133 nc_client_ssh_add_keypair(pubkey, privkey);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01001134 free(pubkey);
1135 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001136 nc_client_ssh_set_username(user);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001137 DEBUG("prepare to connect %s@%s:%s", user, host, port);
Michal Vaskof35ea502016-02-24 10:44:54 +01001138 session = nc_connect_ssh(host, (unsigned short)atoi(port), NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001139 DEBUG("nc_session_connect done");
David Kupka8e60a372012-09-04 09:15:20 +02001140
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001141 /* if connected successful, add session to the list */
1142 if (session != NULL) {
1143 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 +01001144 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001145 session = NULL;
1146 free(locked_session);
1147 locked_session = NULL;
1148 ERROR("Creating structure session_with_mutex failed %d (%s)", errno, strerror(errno));
1149 return 0;
1150 }
1151 locked_session->session = session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001152 locked_session->hello_message = NULL;
1153 locked_session->closed = 0;
1154 pthread_mutex_init(&locked_session->lock, NULL);
1155 DEBUG("Before session_lock");
1156 /* get exclusive access to sessions_list (conns) */
1157 DEBUG("LOCK wrlock %s", __func__);
1158 if (pthread_rwlock_wrlock(&session_lock) != 0) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001159 nc_session_free(session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001160 free(locked_session);
1161 ERROR("Error while locking rwlock: %d (%s)", errno, strerror(errno));
1162 return 0;
1163 }
1164 locked_session->ntfc_subscribed = 0;
1165 DEBUG("Add connection to the list");
1166 if (!netconf_sessions_list) {
Michal Vaskoc3146782015-11-04 14:46:41 +01001167 netconf_sessions_list = locked_session;
1168 } else {
1169 for (last_session = netconf_sessions_list; last_session->next; last_session = last_session->next);
1170 last_session->next = locked_session;
1171 locked_session->prev = last_session;
1172 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001173 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejka45ab59f2013-05-15 00:10:49 +02001174
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001175 /* no need to lock session, noone can read it while we have wrlock */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001176
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001177 /* store information about session from hello message for future usage */
1178 prepare_status_message(locked_session, session);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001179
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001180 DEBUG("NETCONF session established");
1181 locked_session->session_key = session_key_generator;
1182 ++session_key_generator;
1183 if (session_key_generator == UINT_MAX) {
1184 session_key_generator = 1;
1185 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001186
Michal Vaskoe32bcba2015-11-24 09:05:51 +01001187 DEBUG("Before session_unlock");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001188 /* unlock session list */
1189 DEBUG("UNLOCK wrlock %s", __func__);
1190 if (pthread_rwlock_unlock(&session_lock) != 0) {
1191 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
1192 }
Radek Krejci469aab82012-07-22 18:42:20 +02001193
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001194 return locked_session->session_key;
1195 }
1196
1197 ERROR("Connection could not be established");
1198 return 0;
Radek Krejci469aab82012-07-22 18:42:20 +02001199}
1200
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001201static int
1202close_and_free_session(struct session_with_mutex *locked_session)
Radek Krejci469aab82012-07-22 18:42:20 +02001203{
Michal Vaskoa53ef882015-11-24 11:02:01 +01001204 int i;
1205
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001206 DEBUG("lock private lock.");
1207 DEBUG("LOCK mutex %s", __func__);
1208 if (pthread_mutex_lock(&locked_session->lock) != 0) {
1209 ERROR("Error while locking rwlock");
1210 }
1211 locked_session->ntfc_subscribed = 0;
1212 locked_session->closed = 1;
1213 if (locked_session->session != NULL) {
Michal Vaskoa9590052016-03-08 10:12:24 +01001214 nc_session_free(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001215 locked_session->session = NULL;
1216 }
1217 DEBUG("session closed.");
1218 DEBUG("unlock private lock.");
1219 DEBUG("UNLOCK mutex %s", __func__);
1220 if (pthread_mutex_unlock(&locked_session->lock) != 0) {
1221 ERROR("Error while locking rwlock");
1222 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001223
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001224 DEBUG("unlock session lock.");
1225 DEBUG("closed session, disabled notif(?), wait 0.5s");
1226 usleep(500000); /* let notification thread stop */
Tomas Cejka47387fd2013-06-10 20:37:46 +02001227
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001228 /* session shouldn't be used by now */
Michal Vaskoa53ef882015-11-24 11:02:01 +01001229 for (i = 0; i < locked_session->notif_count; ++i) {
1230 free(locked_session->notifications[i].content);
1231 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001232 free(locked_session->notifications);
1233 pthread_mutex_destroy(&locked_session->lock);
1234 if (locked_session->hello_message != NULL) {
1235 json_object_put(locked_session->hello_message);
1236 locked_session->hello_message = NULL;
1237 }
1238 locked_session->session = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001239 free(locked_session);
1240 locked_session = NULL;
1241 DEBUG("NETCONF session closed, everything cleared.");
1242 return (EXIT_SUCCESS);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001243}
1244
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001245static int
1246netconf_close(unsigned int session_key, json_object **reply)
Tomas Cejka47387fd2013-06-10 20:37:46 +02001247{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001248 struct session_with_mutex *locked_session;
Radek Krejci469aab82012-07-22 18:42:20 +02001249
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001250 DEBUG("Session to close: %u", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001251
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001252 /* get exclusive (write) access to sessions_list (conns) */
1253 DEBUG("lock session lock.");
1254 DEBUG("LOCK wrlock %s", __func__);
1255 if (pthread_rwlock_wrlock (&session_lock) != 0) {
1256 ERROR("Error while locking rwlock");
1257 (*reply) = create_error_reply("Internal: Error while locking.");
1258 return EXIT_FAILURE;
1259 }
1260 /* remove session from the active sessions list -> nobody new can now work with session */
1261 for (locked_session = netconf_sessions_list;
1262 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01001263 locked_session = locked_session->next);
1264
1265 if (!locked_session) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001266 ERROR("Could not find the session %u to close.", session_key);
1267 (*reply) = create_error_reply("Internal: Error while finding a session.");
Michal Vaskoc3146782015-11-04 14:46:41 +01001268 return EXIT_FAILURE;
1269 }
1270
1271 if (!locked_session->prev) {
1272 netconf_sessions_list = netconf_sessions_list->next;
Michal Vaskof0b6fbb2016-03-08 12:04:34 +01001273 if (netconf_sessions_list) {
1274 netconf_sessions_list->prev = NULL;
1275 }
Michal Vaskoc3146782015-11-04 14:46:41 +01001276 } else {
1277 locked_session->prev->next = locked_session->next;
1278 if (locked_session->next) {
1279 locked_session->next->prev = locked_session->prev;
1280 }
1281 }
Tomas Cejkabdedcd32013-06-09 11:54:53 +02001282
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001283 DEBUG("UNLOCK wrlock %s", __func__);
1284 if (pthread_rwlock_unlock (&session_lock) != 0) {
1285 ERROR("Error while unlocking rwlock");
1286 (*reply) = create_error_reply("Internal: Error while unlocking.");
1287 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02001288
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001289 if ((locked_session != NULL) && (locked_session->session != NULL)) {
1290 return close_and_free_session(locked_session);
1291 } else {
1292 ERROR("Unknown session to close");
1293 (*reply) = create_error_reply("Internal: Unkown session to close.");
1294 return (EXIT_FAILURE);
1295 }
1296 (*reply) = NULL;
Radek Krejci469aab82012-07-22 18:42:20 +02001297}
1298
Tomas Cejkac7929632013-10-24 19:25:15 +02001299/**
1300 * Test reply message type and return error message.
1301 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001302 * \param[in] session nc_session internal struct
Michal Vaskoc3146782015-11-04 14:46:41 +01001303 * \param[in] session_key session ID, 0 to disable disconnect on error
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001304 * \param[in] msgt RPC-REPLY message type
Tomas Cejkac7929632013-10-24 19:25:15 +02001305 * \param[out] data
1306 * \return NULL on success
1307 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001308json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001309netconf_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 +02001310{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001311 json_object *err = NULL;
Tomas Cejkac7929632013-10-24 19:25:15 +02001312
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001313 /* process the result of the operation */
1314 switch (msgt) {
Michal Vaskof35ea502016-02-24 10:44:54 +01001315 case NC_MSG_ERROR:
1316 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001317 ERROR("mod_netconf: receiving rpc-reply failed");
1318 if (session_key) {
1319 netconf_close(session_key, &err);
1320 }
1321 if (err != NULL) {
1322 return err;
1323 }
1324 return create_error_reply("Internal: Receiving RPC-REPLY failed.");
1325 }
1326 case NC_MSG_NONE:
1327 /* there is error handled by callback */
1328 if (data != NULL) {
1329 free(*data);
1330 (*data) = NULL;
1331 }
1332 return NULL;
1333 case NC_MSG_REPLY:
Michal Vaskof35ea502016-02-24 10:44:54 +01001334 switch (reply->type) {
1335 case NC_RPL_OK:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001336 if ((data != NULL) && (*data != NULL)) {
1337 free(*data);
1338 (*data) = NULL;
1339 }
1340 return create_ok_reply();
Michal Vaskof35ea502016-02-24 10:44:54 +01001341 case NC_RPL_DATA:
1342 if (((*data) = ((struct nc_reply_data *)reply)->data) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001343 ERROR("mod_netconf: no data from reply");
1344 return create_error_reply("Internal: No data from reply received.");
1345 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001346 ((struct nc_reply_data *)reply)->data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001347 return NULL;
1348 }
1349 break;
Michal Vaskof35ea502016-02-24 10:44:54 +01001350 case NC_RPL_ERROR:
1351 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001352 if (data != NULL) {
1353 free(*data);
1354 (*data) = NULL;
1355 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001356 return create_error_reply(((struct nc_reply_error *)reply)->err[0].message);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001357 default:
Michal Vaskof35ea502016-02-24 10:44:54 +01001358 ERROR("mod_netconf: unexpected rpc-reply (%d)", reply->type);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001359 if (data != NULL) {
1360 free(*data);
1361 (*data) = NULL;
1362 }
1363 return create_error_reply("Unknown type of NETCONF reply.");
1364 }
1365 break;
1366 default:
1367 ERROR("mod_netconf: unexpected reply message received (%d)", msgt);
1368 if (data != NULL) {
1369 free(*data);
1370 (*data) = NULL;
1371 }
1372 return create_error_reply("Internal: Unexpected RPC-REPLY message type.");
1373 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001374}
1375
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001376json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001377netconf_unlocked_op(struct nc_session *session, struct nc_rpc *rpc)
Tomas Cejka6b886e02013-07-05 09:53:17 +02001378{
Michal Vaskof35ea502016-02-24 10:44:54 +01001379 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001380 NC_MSG_TYPE msgt;
Tomas Cejka6b886e02013-07-05 09:53:17 +02001381
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001382 /* check requests */
1383 if (rpc == NULL) {
1384 ERROR("mod_netconf: rpc is not created");
1385 return create_error_reply("Internal error: RPC is not created");
1386 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001387
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001388 if (session != NULL) {
1389 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001390 msgt = netconf_send_recv_timed(session, rpc, 50000, 0, &reply);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001391 /* process the result of the operation */
1392 return netconf_test_reply(session, 0, msgt, reply, NULL);
1393 } else {
1394 ERROR("Unknown session to process.");
1395 return create_error_reply("Internal error: Unknown session to process.");
1396 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02001397}
1398
Tomas Cejkac7929632013-10-24 19:25:15 +02001399/**
1400 * Perform RPC method that returns data.
1401 *
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001402 * \param[in] session_id session identifier
1403 * \param[in] rpc RPC message to perform
1404 * \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 +02001405 * \return NULL on success, json object with error otherwise
1406 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001407static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001408netconf_op(unsigned int session_key, struct nc_rpc *rpc, int strict, struct lyd_node **received_data)
Radek Krejci469aab82012-07-22 18:42:20 +02001409{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001410 struct session_with_mutex * locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001411 struct nc_reply* reply = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001412 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001413 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001414 NC_MSG_TYPE msgt;
Radek Krejci035bf4e2012-07-25 10:59:09 +02001415
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001416 /* check requests */
1417 if (rpc == NULL) {
1418 ERROR("mod_netconf: rpc is not created");
1419 res = create_error_reply("Internal: RPC could not be created.");
1420 data = NULL;
1421 goto finished;
1422 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001423
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001424 locked_session = session_get_locked(session_key, &res);
1425 if (!locked_session) {
1426 ERROR("Unknown session or locking failed.");
1427 goto finished;
1428 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001429
Michal Vaskof35ea502016-02-24 10:44:54 +01001430 session_user_activity(nc_session_get_username(locked_session->session));
Tomas Cejkac7929632013-10-24 19:25:15 +02001431
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001432 /* send the request and get the reply */
Michal Vaskof35ea502016-02-24 10:44:54 +01001433 msgt = netconf_send_recv_timed(locked_session->session, rpc, 2000000, strict, &reply);
Tomas Cejka47387fd2013-06-10 20:37:46 +02001434
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001435 session_unlock(locked_session);
Tomas Cejkac7929632013-10-24 19:25:15 +02001436
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001437 res = netconf_test_reply(locked_session->session, session_key, msgt, reply, &data);
Radek Krejcia332b692012-11-12 16:15:54 +01001438
Tomas Cejkac7929632013-10-24 19:25:15 +02001439finished:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001440 nc_reply_free(reply);
1441 if (received_data != NULL) {
1442 (*received_data) = data;
1443 } else {
1444 if (data != NULL) {
1445 free(data);
1446 data = NULL;
1447 }
1448 }
1449 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001450}
1451
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001452static char *
1453netconf_getconfig(unsigned int session_key, NC_DATASTORE source, const char *filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001454{
Michal Vaskof35ea502016-02-24 10:44:54 +01001455 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001456 struct session_with_mutex *locked_session;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001457 json_object *res = NULL, *data_cjson;
1458 enum json_tokener_error tok_err;
Michal Vaskof35ea502016-02-24 10:44:54 +01001459 char *data_json;
1460 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001461
Michal Vaskof35ea502016-02-24 10:44:54 +01001462 /* tell server to show all elements even if they have default values */
1463#ifdef HAVE_WITHDEFAULTS_TAGGED
1464 rpc = nc_rpc_getconfig(source, filter, NC_WD_MODE_ALL_TAG, NC_PARAMTYPE_CONST);
1465#else
1466 rpc = nc_rpc_getconfig(source, filter, 0, NC_PARAMTYPE_CONST);
1467#endif
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001468 if (rpc == NULL) {
1469 ERROR("mod_netconf: creating rpc request failed");
1470 return (NULL);
1471 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001472
Michal Vaskof35ea502016-02-24 10:44:54 +01001473 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001474 nc_rpc_free(rpc);
1475 if (res != NULL) {
1476 (*err) = res;
1477 } else {
1478 (*err) = NULL;
1479 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001480
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001481 if (data) {
1482 for (locked_session = netconf_sessions_list;
1483 locked_session && (locked_session->session_key != session_key);
1484 locked_session = locked_session->next);
1485 /* won't fail */
1486
Michal Vaskof35ea502016-02-24 10:44:54 +01001487 /* print data into JSON */
1488 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001489 ERROR("Printing JSON <get-config> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001490 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001491 return NULL;
1492 }
1493
1494 /* parse JSON data into cjson */
1495 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001496 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001497 if (!data_cjson) {
1498 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1499 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001500 lyd_free_withsiblings(data);
1501 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001502 return NULL;
1503 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001504 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001505
1506 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001507 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001508 node_add_metadata_recursive(sibling, NULL, data_cjson);
1509 lyd_free(sibling);
1510 }
1511
Michal Vaskof35ea502016-02-24 10:44:54 +01001512 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001513 json_object_put(data_cjson);
1514 pthread_mutex_unlock(&json_lock);
1515 }
1516
Michal Vaskof35ea502016-02-24 10:44:54 +01001517 return (data_json);
Radek Krejci8e4632a2012-07-26 13:40:34 +02001518}
1519
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001520static char *
1521netconf_getschema(unsigned int session_key, const char *identifier, const char *version, const char *format, json_object **err)
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001522{
Michal Vaskof35ea502016-02-24 10:44:54 +01001523 struct nc_rpc *rpc;
1524 struct lyd_node *data = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001525 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001526 char *model_data = NULL, *anyxml, *ptr, *ptr2;
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001527
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001528 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001529 rpc = nc_rpc_getschema(identifier, version, format, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001530 if (rpc == NULL) {
1531 ERROR("mod_netconf: creating rpc request failed");
1532 return (NULL);
1533 }
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001534
Michal Vaskof35ea502016-02-24 10:44:54 +01001535 res = netconf_op(session_key, rpc, 0, &data);
1536 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001537 if (res != NULL) {
1538 (*err) = res;
1539 } else {
1540 (*err) = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001541
1542 if (data) {
1543 lyxml_print_mem(&anyxml, ((struct lyd_node_anyxml *)data)->value, 0);
1544
1545 /* it's with the data root node, remove it */
1546 if (anyxml) {
1547 ptr = strchr(anyxml, '>');
1548 ++ptr;
1549
1550 ptr2 = strrchr(anyxml, '<');
1551
1552 model_data = strndup(ptr, strlen(ptr) - strlen(ptr2));
1553 free(anyxml);
1554 }
1555 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001556 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001557
Michal Vaskof35ea502016-02-24 10:44:54 +01001558 return (model_data);
Tomas Cejka0aeca8b2012-12-22 19:56:03 +01001559}
1560
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001561static char *
1562netconf_get(unsigned int session_key, const char* filter, int strict, json_object **err)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001563{
Michal Vaskof35ea502016-02-24 10:44:54 +01001564 struct nc_rpc* rpc;
1565 char* data_json = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001566 json_object *res = NULL, *data_cjson;
1567 enum json_tokener_error tok_err;
1568 struct session_with_mutex *locked_session;
Michal Vaskof35ea502016-02-24 10:44:54 +01001569 struct lyd_node *data, *sibling, *next;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001570
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001571 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001572 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001573 if (rpc == NULL) {
1574 ERROR("mod_netconf: creating rpc request failed");
1575 return (NULL);
1576 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001577
Michal Vaskof35ea502016-02-24 10:44:54 +01001578 res = netconf_op(session_key, rpc, strict, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001579 nc_rpc_free(rpc);
1580 if (res != NULL) {
1581 (*err) = res;
1582 } else {
1583 (*err) = NULL;
1584 }
Tomas Cejkac7929632013-10-24 19:25:15 +02001585
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001586 if (data) {
1587 for (locked_session = netconf_sessions_list;
1588 locked_session && (locked_session->session_key != session_key);
1589 locked_session = locked_session->next);
1590 /* won't fail */
1591
Michal Vaskof35ea502016-02-24 10:44:54 +01001592 /* print JSON data */
1593 if (lyd_print_mem(&data_json, data, LYD_JSON, LYP_WITHSIBLINGS)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001594 ERROR("Printing JSON <get> data failed.");
Michal Vaskof35ea502016-02-24 10:44:54 +01001595 lyd_free_withsiblings(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001596 return NULL;
1597 }
1598
1599 /* parse JSON data into cjson */
1600 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001601 data_cjson = json_tokener_parse_verbose(data_json, &tok_err);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001602 if (!data_cjson) {
1603 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(tok_err));
1604 pthread_mutex_unlock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01001605 lyd_free_withsiblings(data);
1606 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001607 return NULL;
1608 }
Michal Vaskof35ea502016-02-24 10:44:54 +01001609 free(data_json);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001610
1611 /* go simultaneously through both trees and add metadata */
Michal Vaskof35ea502016-02-24 10:44:54 +01001612 LY_TREE_FOR_SAFE(data, next, sibling) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001613 node_add_metadata_recursive(sibling, NULL, data_cjson);
1614 lyd_free(sibling);
1615 }
1616
Michal Vaskof35ea502016-02-24 10:44:54 +01001617 data_json = strdup(json_object_to_json_string_ext(data_cjson, 0));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001618 json_object_put(data_cjson);
1619 pthread_mutex_unlock(&json_lock);
1620 }
1621
Michal Vaskof35ea502016-02-24 10:44:54 +01001622 return data_json;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001623}
1624
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001625static json_object *
1626netconf_copyconfig(unsigned int session_key, NC_DATASTORE source, NC_DATASTORE target, const char *config,
1627 const char *uri_src, const char *uri_trg)
Radek Krejci8e4632a2012-07-26 13:40:34 +02001628{
Michal Vaskof35ea502016-02-24 10:44:54 +01001629 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001630 json_object *res = NULL;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001631
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001632 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001633 rpc = nc_rpc_copy(target, uri_trg, source, (config ? config : uri_src), 0, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001634 if (rpc == NULL) {
1635 ERROR("mod_netconf: creating rpc request failed");
1636 return create_error_reply("Internal: Creating rpc request failed");
1637 }
Radek Krejci8e4632a2012-07-26 13:40:34 +02001638
Michal Vaskof35ea502016-02-24 10:44:54 +01001639 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001640 nc_rpc_free(rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001641
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001642 return res;
Radek Krejci8e4632a2012-07-26 13:40:34 +02001643}
Radek Krejci035bf4e2012-07-25 10:59:09 +02001644
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001645static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001646netconf_editconfig(unsigned int session_key, NC_DATASTORE target, NC_RPC_EDIT_DFLTOP defop,
1647 NC_RPC_EDIT_ERROPT erropt, NC_RPC_EDIT_TESTOPT testopt, const char *config_or_url)
Radek Krejci62ab34b2012-07-26 13:42:05 +02001648{
Michal Vaskof35ea502016-02-24 10:44:54 +01001649 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001650 json_object *res = NULL;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001651
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001652 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001653 rpc = nc_rpc_edit(target, defop, testopt, erropt, config_or_url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001654 if (rpc == NULL) {
1655 ERROR("mod_netconf: creating rpc request failed");
1656 return create_error_reply("Internal: Creating rpc request failed");
1657 }
Radek Krejci62ab34b2012-07-26 13:42:05 +02001658
Michal Vaskof35ea502016-02-24 10:44:54 +01001659 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001660 nc_rpc_free (rpc);
Tomas Cejkac7929632013-10-24 19:25:15 +02001661
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001662 return res;
Radek Krejci62ab34b2012-07-26 13:42:05 +02001663}
1664
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001665static json_object *
1666netconf_killsession(unsigned int session_key, const char *sid)
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001667{
Michal Vaskof35ea502016-02-24 10:44:54 +01001668 struct nc_rpc *rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001669 json_object *res = NULL;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001670
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001671 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001672 rpc = nc_rpc_kill(atoi(sid));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001673 if (rpc == NULL) {
1674 ERROR("mod_netconf: creating rpc request failed");
1675 return create_error_reply("Internal: Creating rpc request failed");
1676 }
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001677
Michal Vaskof35ea502016-02-24 10:44:54 +01001678 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001679 nc_rpc_free(rpc);
1680 return res;
Radek Krejcie34d3eb2012-07-26 15:05:53 +02001681}
1682
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001683static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001684netconf_onlytargetop(unsigned int session_key, NC_DATASTORE target, struct nc_rpc *(*op_func)(NC_DATASTORE))
Radek Krejci2f318372012-07-26 14:22:35 +02001685{
Michal Vaskof35ea502016-02-24 10:44:54 +01001686 struct nc_rpc* rpc;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001687 json_object *res = NULL;
Radek Krejci2f318372012-07-26 14:22:35 +02001688
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001689 /* create requests */
1690 rpc = op_func(target);
1691 if (rpc == NULL) {
1692 ERROR("mod_netconf: creating rpc request failed");
1693 return create_error_reply("Internal: Creating rpc request failed");
1694 }
Radek Krejci2f318372012-07-26 14:22:35 +02001695
Michal Vaskof35ea502016-02-24 10:44:54 +01001696 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001697 nc_rpc_free (rpc);
1698 return res;
Radek Krejci2f318372012-07-26 14:22:35 +02001699}
1700
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001701static json_object *
1702netconf_deleteconfig(unsigned int session_key, NC_DATASTORE target, const char *url)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001703{
Michal Vaskof35ea502016-02-24 10:44:54 +01001704 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001705 json_object *res = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01001706 rpc = nc_rpc_delete(target, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001707 if (rpc == NULL) {
1708 ERROR("mod_netconf: creating rpc request failed");
1709 return create_error_reply("Internal: Creating rpc request failed");
1710 }
Tomas Cejka404d37e2013-04-13 02:31:35 +02001711
Michal Vaskof35ea502016-02-24 10:44:54 +01001712 res = netconf_op(session_key, rpc, 0, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001713 nc_rpc_free (rpc);
1714 return res;
Radek Krejci5cd7d422012-07-26 14:50:29 +02001715}
1716
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001717static json_object *
1718netconf_lock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001719{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001720 return (netconf_onlytargetop(session_key, target, nc_rpc_lock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001721}
1722
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001723static json_object *
1724netconf_unlock(unsigned int session_key, NC_DATASTORE target)
Radek Krejci5cd7d422012-07-26 14:50:29 +02001725{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001726 return (netconf_onlytargetop(session_key, target, nc_rpc_unlock));
Radek Krejci5cd7d422012-07-26 14:50:29 +02001727}
1728
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001729static json_object *
Michal Vaskof35ea502016-02-24 10:44:54 +01001730netconf_generic(unsigned int session_key, const char *xml_content, struct lyd_node **data)
Radek Krejci80c10d92012-07-30 08:38:50 +02001731{
Michal Vaskof35ea502016-02-24 10:44:54 +01001732 struct nc_rpc* rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001733 json_object *res = NULL;
Radek Krejci80c10d92012-07-30 08:38:50 +02001734
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001735 /* create requests */
Michal Vaskof35ea502016-02-24 10:44:54 +01001736 rpc = nc_rpc_generic_xml(xml_content, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001737 if (rpc == NULL) {
1738 ERROR("mod_netconf: creating rpc request failed");
1739 return create_error_reply("Internal: Creating rpc request failed");
1740 }
Radek Krejci80c10d92012-07-30 08:38:50 +02001741
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001742 /* get session where send the RPC */
Michal Vaskof35ea502016-02-24 10:44:54 +01001743 res = netconf_op(session_key, rpc, 0, data);
1744 nc_rpc_free(rpc);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001745 return res;
1746}
1747
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001748static int
Michal Vaskof35ea502016-02-24 10:44:54 +01001749node_add_metadata(const struct lys_node *node, const struct lys_module *module, json_object *parent)
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001750{
1751 struct lys_module *cur_module;
1752 json_object *meta_obj;
1753 char *obj_name;
1754
Michal Vaskoa45770b2015-11-23 15:49:41 +01001755 if (node->nodetype == LYS_INPUT) {
1756 /* silently skipped */
1757 return 0;
1758 }
1759
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001760 cur_module = node->module;
1761 if (cur_module->type) {
1762 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1763 }
1764 if (cur_module == module) {
1765 asprintf(&obj_name, "$@%s", node->name);
1766 } else {
1767 asprintf(&obj_name, "$@%s:%s", cur_module->name, node->name);
1768 }
1769
1770 /* in (leaf-)lists the metadata could have already been added */
Michal Vaskoa45770b2015-11-23 15:49:41 +01001771 if ((node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && (json_object_object_get_ex(parent, obj_name, NULL) == TRUE)) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001772 free(obj_name);
1773 return 1;
1774 }
1775
1776 meta_obj = json_object_new_object();
1777
1778 switch (node->nodetype) {
1779 case LYS_CONTAINER:
1780 node_metadata_container((struct lys_node_container *)node, meta_obj);
1781 break;
Michal Vasko3fda9a92015-11-23 10:10:57 +01001782 case LYS_CHOICE:
1783 node_metadata_choice((struct lys_node_choice *)node, meta_obj);
1784 break;
1785 case LYS_LEAF:
1786 node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
1787 break;
1788 case LYS_LEAFLIST:
1789 node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
1790 break;
1791 case LYS_LIST:
1792 node_metadata_list((struct lys_node_list *)node, meta_obj);
1793 break;
1794 case LYS_ANYXML:
1795 node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
1796 break;
1797 case LYS_CASE:
1798 node_metadata_case((struct lys_node_case *)node, meta_obj);
1799 break;
Michal Vaskoa45770b2015-11-23 15:49:41 +01001800 case LYS_RPC:
1801 node_metadata_rpc((struct lys_node_rpc *)node, meta_obj);
1802 break;
1803 default: /* LYS_OUTPUT */
Michal Vasko3fda9a92015-11-23 10:10:57 +01001804 ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
1805 break;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001806 }
1807
1808 /* just a precaution */
1809 if (json_object_get_type(parent) != json_type_object) {
1810 ERROR("Internal: wrong JSON type (%s:%d)", __FILE__, __LINE__);
1811 free(obj_name);
1812 return 1;
1813 }
1814
1815 json_object_object_add(parent, obj_name, meta_obj);
1816 free(obj_name);
1817 return 0;
1818}
1819
1820static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001821node_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 +01001822{
1823 struct lys_module *cur_module;
1824 struct lys_node *list_schema;
1825 struct lyd_node *child, *list_item;
1826 json_object *child_json, *list_child_json;
1827 char *child_name;
1828 int list_idx;
1829
Michal Vaskoa45770b2015-11-23 15:49:41 +01001830 if (data_tree->schema->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1831 return;
1832 }
1833
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001834 /* add data_tree metadata */
1835 if (node_add_metadata(data_tree->schema, module, data_json_parent)) {
1836 return;
1837 }
1838
1839 /* get data_tree module */
1840 cur_module = data_tree->schema->module;
1841 if (cur_module->type) {
1842 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1843 }
1844
1845 if (!(data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) {
1846 /* print correct data_tree JSON name */
1847 if (cur_module == module) {
1848 asprintf(&child_name, "%s", data_tree->schema->name);
1849 } else {
1850 asprintf(&child_name, "%s:%s", cur_module->name, data_tree->schema->name);
1851 }
1852
1853 /* go down in JSON object */
1854 if (json_object_object_get_ex(data_json_parent, child_name, &child_json) == FALSE) {
1855 ERROR("Internal: failed to get JSON object \"%s\".", child_name);
1856 free(child_name);
1857 return;
1858 }
1859 free(child_name);
1860
1861 if (data_tree->schema->nodetype == LYS_LIST) {
1862 if (json_object_get_type(child_json) != json_type_array) {
1863 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1864 return;
1865 }
1866 /* go down in data tree for every item, we process them all now, skip later
1867 * (metadata duplicate will be detected at the beginning of this function) */
1868 list_idx = 0;
1869 list_schema = data_tree->schema;
1870
1871 LY_TREE_FOR(data_tree, list_item) {
1872 /* another list member */
1873 if (list_item->schema == list_schema) {
1874 list_child_json = json_object_array_get_idx(child_json, list_idx);
1875 if (!list_child_json) {
1876 ERROR("Internal: list \"%s\" idx out-of-bounds", list_schema->name);
1877 return;
1878 }
1879 LY_TREE_FOR(list_item->child, child) {
1880 node_add_metadata_recursive(child, cur_module, list_child_json);
1881 }
1882
1883 ++list_idx;
1884 }
1885 }
1886 } else {
1887 if (json_object_get_type(child_json) != json_type_object) {
1888 ERROR("Internal: type mismatch (%s:%d)", __FILE__, __LINE__);
1889 return;
1890 }
1891 /* go down in data tree */
1892 LY_TREE_FOR(data_tree->child, child) {
1893 node_add_metadata_recursive(child, cur_module, child_json);
1894 }
1895 }
1896 }
1897}
1898
1899static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001900node_add_model_metadata(const struct lys_module *module, json_object *parent)
Michal Vaskoa45770b2015-11-23 15:49:41 +01001901{
1902 json_object *obj;
1903 char *str;
1904
1905 obj = json_object_new_object();
1906 node_metadata_model(module, obj);
1907 asprintf(&str, "$@@%s", module->name);
1908 json_object_object_add(parent, str, obj);
1909 free(str);
1910}
1911
1912static void
Michal Vaskof35ea502016-02-24 10:44:54 +01001913node_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 +01001914{
Michal Vaskof35ea502016-02-24 10:44:54 +01001915 const struct lys_module *cur_module;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001916 struct lys_node *child;
1917 json_object *node_json;
1918 char *json_name;
1919
Michal Vaskoa45770b2015-11-23 15:49:41 +01001920 if (node->nodetype & (LYS_OUTPUT | LYS_GROUPING)) {
1921 return;
1922 }
1923
1924 if (node->nodetype & LYS_USES) {
1925 cur_module = module;
1926 node_json = parent;
1927 goto children;
1928 }
1929
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001930 /* add node metadata */
1931 if (node_add_metadata(node, module, parent)) {
1932 ERROR("Internal: metadata duplicate for \"%s\".", node->name);
1933 return;
1934 }
1935
Michal Vaskoa45770b2015-11-23 15:49:41 +01001936 /* no other metadata */
1937 if (!node->child) {
1938 return;
1939 }
1940
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001941 /* get node module */
1942 cur_module = node->module;
1943 if (cur_module->type) {
1944 cur_module = ((struct lys_submodule *)cur_module)->belongsto;
1945 }
1946
1947 /* create JSON object for child metadata */
1948 node_json = json_object_new_object();
1949 if (cur_module == module) {
1950 json_object_object_add(parent, node->name, node_json);
1951 } else {
1952 asprintf(&json_name, "%s:%s", cur_module->name, node->name);
1953 json_object_object_add(parent, json_name, node_json);
1954 free(json_name);
1955 }
1956
Michal Vaskoa45770b2015-11-23 15:49:41 +01001957children:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001958 LY_TREE_FOR(node->child, child) {
1959 node_add_children_with_metadata_recursive(child, cur_module, node_json);
1960 }
1961}
1962
1963static json_object *
1964libyang_query(unsigned int session_key, const char *filter, int load_children)
1965{
Michal Vaskof35ea502016-02-24 10:44:54 +01001966 const struct lys_node *node;
1967 const struct lys_module *module = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001968 struct session_with_mutex *locked_session;
1969 json_object *ret = NULL, *data;
1970
1971 locked_session = session_get_locked(session_key, &ret);
1972 if (!locked_session) {
1973 ERROR("Locking failed or session not found.");
1974 goto finish;
1975 }
1976
Michal Vaskof35ea502016-02-24 10:44:54 +01001977 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001978
Michal Vaskoa45770b2015-11-23 15:49:41 +01001979 if (filter[0] == '/') {
Michal Vaskof35ea502016-02-24 10:44:54 +01001980 node = ly_ctx_get_node(nc_session_get_ctx(locked_session->session), filter);
Michal Vaskoa45770b2015-11-23 15:49:41 +01001981 if (!node) {
1982 ret = create_error_reply("Failed to resolve XPath filter node.");
1983 goto finish;
1984 }
1985 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01001986 module = ly_ctx_get_module(nc_session_get_ctx(locked_session->session), filter, NULL);
Michal Vaskoa45770b2015-11-23 15:49:41 +01001987 if (!module) {
1988 ret = create_error_reply("Failed to find model.");
1989 goto finish;
1990 }
1991 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001992
Michal Vaskoa45770b2015-11-23 15:49:41 +01001993 pthread_mutex_lock(&json_lock);
1994 data = json_object_new_object();
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01001995
Michal Vaskoa45770b2015-11-23 15:49:41 +01001996 if (module) {
1997 node_add_model_metadata(module, data);
1998 if (load_children) {
1999 LY_TREE_FOR(module->data, node) {
2000 node_add_children_with_metadata_recursive(node, NULL, data);
2001 }
2002 }
2003 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002004 if (load_children) {
2005 node_add_children_with_metadata_recursive(node, NULL, data);
2006 } else {
2007 node_add_metadata(node, NULL, data);
2008 }
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002009 }
2010
Michal Vaskoa45770b2015-11-23 15:49:41 +01002011 pthread_mutex_unlock(&json_lock);
2012 ret = create_data_reply(json_object_to_json_string(data));
2013 json_object_put(data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002014
2015finish:
Michal Vaskoa45770b2015-11-23 15:49:41 +01002016 session_unlock(locked_session);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002017 return ret;
2018}
2019
2020static json_object *
2021libyang_merge(unsigned int session_key, const char *config)
2022{
2023 struct lyd_node *data_tree = NULL, *sibling;
2024 struct session_with_mutex *locked_session;
2025 json_object *ret = NULL, *data_json = NULL;
2026 enum json_tokener_error err = 0;
2027
2028 locked_session = session_get_locked(session_key, &ret);
2029 if (!locked_session) {
2030 ERROR("Locking failed or session not found.");
2031 goto finish;
2032 }
2033
Michal Vaskof35ea502016-02-24 10:44:54 +01002034 session_user_activity(nc_session_get_username(locked_session->session));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002035
Michal Vaskof35ea502016-02-24 10:44:54 +01002036 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 +01002037 if (!data_tree) {
2038 ERROR("Creating data tree failed.");
2039 ret = create_error_reply("Failed to create data tree from JSON config.");
2040 session_unlock(locked_session);
2041 goto finish;
2042 }
2043
2044 session_unlock(locked_session);
2045
2046 pthread_mutex_lock(&json_lock);
2047 data_json = json_tokener_parse_verbose(config, &err);
2048 if (!data_json) {
2049 ERROR("Parsing JSON config failed (%s).", json_tokener_error_desc(err));
2050 pthread_mutex_unlock(&json_lock);
2051 ret = create_error_reply(json_tokener_error_desc(err));
2052 goto finish;
2053 }
2054
2055 /* go simultaneously through both trees and add metadata */
2056 LY_TREE_FOR(data_tree, sibling) {
2057 node_add_metadata_recursive(sibling, NULL, data_json);
2058 }
2059 pthread_mutex_unlock(&json_lock);
2060 ret = create_data_reply(json_object_to_json_string(data_json));
2061
2062finish:
2063 LY_TREE_FOR(data_tree, sibling) {
2064 lyd_free(sibling);
2065 }
2066 json_object_put(data_json);
2067 return ret;
Radek Krejci80c10d92012-07-30 08:38:50 +02002068}
2069
Tomas Cejka0a4bba82013-04-19 11:51:28 +02002070/**
2071 * @}
2072 *//* netconf_operations */
2073
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002074void
2075clb_print(NC_VERB_LEVEL level, const char *msg)
Radek Krejci469aab82012-07-22 18:42:20 +02002076{
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002077#define FOREACH(I) \
Tomas Cejkacf44e522015-04-24 17:29:21 +02002078 I(NC_VERB_ERROR) I(NC_VERB_WARNING)
2079
2080#define CASE(VAL) case VAL: ERROR("%s: %s", #VAL, msg); \
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002081 break;
Tomas Cejka8a86cc22014-09-18 15:35:07 +02002082
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002083 switch (level) {
2084 FOREACH(CASE);
2085 case NC_VERB_VERBOSE:
2086 case NC_VERB_DEBUG:
2087 DEBUG("DEBUG: %s", msg);
2088 break;
2089 }
2090 if (level == NC_VERB_ERROR) {
2091 /* return global error */
2092 netconf_callback_error_process(NULL /* tag */, NULL /* type */,
2093 NULL /* severity */, NULL /* apptag */,
2094 NULL /* path */, msg, NULL /* attribute */,
2095 NULL /* element */, NULL /* ns */, NULL /* sid */);
2096 }
Radek Krejci469aab82012-07-22 18:42:20 +02002097}
2098
Tomas Cejka64b87482013-06-03 16:30:53 +02002099/**
Tomas Cejka6e8f4262013-07-10 09:20:19 +02002100 * Receive message from client over UNIX socket and return pointer to it.
Tomas Cejka64b87482013-06-03 16:30:53 +02002101 * Caller should free message memory.
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002102 * \param[in] client socket descriptor of client
Tomas Cejka64b87482013-06-03 16:30:53 +02002103 * \return pointer to message
2104 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002105char *
2106get_framed_message(int client)
Tomas Cejka64b87482013-06-03 16:30:53 +02002107{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002108 /* read json in chunked framing */
2109 unsigned int buffer_size = 0;
2110 ssize_t buffer_len = 0;
2111 char *buffer = NULL;
2112 char c;
2113 ssize_t ret;
2114 int i, chunk_len;
2115 char chunk_len_str[12];
Tomas Cejka64b87482013-06-03 16:30:53 +02002116
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002117 while (1) {
2118 /* read chunk length */
2119 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '\n') {
2120 if (buffer != NULL) {
2121 free (buffer);
2122 buffer = NULL;
2123 }
2124 break;
2125 }
2126 if ((ret = recv (client, &c, 1, 0)) != 1 || c != '#') {
2127 if (buffer != NULL) {
2128 free (buffer);
2129 buffer = NULL;
2130 }
2131 break;
2132 }
2133 i=0;
2134 memset (chunk_len_str, 0, 12);
2135 while ((ret = recv (client, &c, 1, 0) == 1 && (isdigit(c) || c == '#'))) {
2136 if (i==0 && c == '#') {
2137 if (recv (client, &c, 1, 0) != 1 || c != '\n') {
2138 /* end but invalid */
2139 if (buffer != NULL) {
2140 free (buffer);
2141 buffer = NULL;
2142 }
2143 }
2144 /* end of message, double-loop break */
2145 goto msg_complete;
2146 }
2147 chunk_len_str[i++] = c;
2148 if (i==11) {
2149 ERROR("Message is too long, buffer for length is not big enought!!!!");
2150 break;
2151 }
2152 }
2153 if (c != '\n') {
2154 if (buffer != NULL) {
2155 free (buffer);
2156 buffer = NULL;
2157 }
2158 break;
2159 }
2160 chunk_len_str[i] = 0;
2161 if ((chunk_len = atoi (chunk_len_str)) == 0) {
2162 if (buffer != NULL) {
2163 free (buffer);
2164 buffer = NULL;
2165 }
2166 break;
2167 }
2168 buffer_size += chunk_len+1;
2169 buffer = realloc (buffer, sizeof(char)*buffer_size);
2170 memset(buffer + (buffer_size-chunk_len-1), 0, chunk_len+1);
2171 if ((ret = recv (client, buffer+buffer_len, chunk_len, 0)) == -1 || ret != chunk_len) {
2172 if (buffer != NULL) {
2173 free (buffer);
2174 buffer = NULL;
2175 }
2176 break;
2177 }
2178 buffer_len += ret;
2179 }
Tomas Cejka64b87482013-06-03 16:30:53 +02002180msg_complete:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002181 return buffer;
Tomas Cejka64b87482013-06-03 16:30:53 +02002182}
2183
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002184NC_DATASTORE
2185parse_datastore(const char *ds)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002186{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002187 if (strcmp(ds, "running") == 0) {
2188 return NC_DATASTORE_RUNNING;
2189 } else if (strcmp(ds, "startup") == 0) {
2190 return NC_DATASTORE_STARTUP;
2191 } else if (strcmp(ds, "candidate") == 0) {
2192 return NC_DATASTORE_CANDIDATE;
2193 } else if (strcmp(ds, "url") == 0) {
2194 return NC_DATASTORE_URL;
2195 } else if (strcmp(ds, "config") == 0) {
2196 return NC_DATASTORE_CONFIG;
2197 }
2198 return -1;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002199}
2200
Michal Vaskof35ea502016-02-24 10:44:54 +01002201NC_RPC_EDIT_TESTOPT
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002202parse_testopt(const char *t)
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002203{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002204 if (strcmp(t, "notset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002205 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002206 } else if (strcmp(t, "testset") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002207 return NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002208 } else if (strcmp(t, "set") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002209 return NC_RPC_EDIT_TESTOPT_SET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002210 } else if (strcmp(t, "test") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002211 return NC_RPC_EDIT_TESTOPT_TEST;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002212 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002213 return NC_RPC_EDIT_TESTOPT_UNKNOWN;
Tomas Cejka5ae8dfb2014-02-14 23:42:17 +01002214}
2215
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002216json_object *
2217create_error_reply(const char *errmess)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002218{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002219 json_object *reply, *array;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002220
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002221 pthread_mutex_lock(&json_lock);
2222 reply = json_object_new_object();
2223 array = json_object_new_array();
2224 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2225 json_object_array_add(array, json_object_new_string(errmess));
2226 json_object_object_add(reply, "errors", array);
2227 pthread_mutex_unlock(&json_lock);
2228
2229 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002230}
2231
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002232json_object *
2233create_data_reply(const char *data)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002234{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002235 pthread_mutex_lock(&json_lock);
2236 json_object *reply = json_object_new_object();
2237 json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
2238 json_object_object_add(reply, "data", json_object_new_string(data));
2239 pthread_mutex_unlock(&json_lock);
2240 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002241}
2242
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002243json_object *
2244create_ok_reply(void)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002245{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002246 pthread_mutex_lock(&json_lock);
2247 json_object *reply = json_object_new_object();
2248 reply = json_object_new_object();
2249 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2250 pthread_mutex_unlock(&json_lock);
2251 return reply;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01002252}
2253
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002254json_object *
2255create_replies(void)
Tomas Cejka09629492014-07-10 15:58:06 +02002256{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002257 json_object *replies;
2258
2259 pthread_mutex_lock(&json_lock);
2260 replies = json_object_new_object();
2261 pthread_mutex_unlock(&json_lock);
2262
2263 return replies;
Tomas Cejka09629492014-07-10 15:58:06 +02002264}
2265
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002266void
2267add_reply(json_object *replies, json_object *reply, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002268{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002269 char *str;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002270
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002271 asprintf(&str, "%u", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002272
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002273 pthread_mutex_lock(&json_lock);
2274 json_object_object_add(replies, str, reply);
2275 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002276
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002277 free(str);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002278}
2279
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002280char *
2281get_param_string(json_object *data, const char *name)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002282{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002283 json_object *js_tmp = NULL;
2284 char *res = NULL;
2285 if (json_object_object_get_ex(data, name, &js_tmp) == TRUE) {
2286 res = strdup(json_object_get_string(js_tmp));
2287 }
2288 return res;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002289}
2290
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002291json_object *
2292handle_op_connect(json_object *request)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002293{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002294 char *host = NULL;
2295 char *port = NULL;
2296 char *user = NULL;
2297 char *pass = NULL;
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002298 char *privkey = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002299 json_object *reply = NULL;
2300 unsigned int session_key = 0;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002301
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002302 DEBUG("Request: connect");
2303 pthread_mutex_lock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002304
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002305 host = get_param_string(request, "host");
2306 port = get_param_string(request, "port");
2307 user = get_param_string(request, "user");
2308 pass = get_param_string(request, "pass");
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002309 privkey = get_param_string(request, "privatekey");
Tomas Cejkad5b53772013-06-08 23:01:07 +02002310
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002311 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002312
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002313 if (host == NULL) {
2314 host = "localhost";
2315 }
2316
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002317 DEBUG("host: %s, port: %s, user: %s", host, port, user);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002318 if (user == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002319 ERROR("Cannot connect - insufficient input.");
2320 session_key = 0;
2321 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002322 session_key = netconf_connect(host, port, user, pass, privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002323 DEBUG("Session key: %u", session_key);
2324 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002325
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002326 GETSPEC_ERR_REPLY
2327
2328 pthread_mutex_lock(&json_lock);
2329 if (session_key == 0) {
2330 /* negative reply */
2331 if (err_reply == NULL) {
2332 reply = json_object_new_object();
2333 json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
2334 json_object_object_add(reply, "error-message", json_object_new_string("Connecting NETCONF server failed."));
2335 ERROR("Connection failed.");
2336 } else {
2337 /* use filled err_reply from libnetconf's callback */
2338 reply = err_reply;
2339 ERROR("Connect - error from libnetconf's callback.");
2340 }
2341 } else {
2342 /* positive reply */
2343 reply = json_object_new_object();
2344 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2345 json_object_object_add(reply, "session", json_object_new_int(session_key));
2346 }
2347 memset(pass, 0, strlen(pass));
2348 pthread_mutex_unlock(&json_lock);
2349 CHECK_AND_FREE(host);
2350 CHECK_AND_FREE(user);
2351 CHECK_AND_FREE(port);
2352 CHECK_AND_FREE(pass);
Michal Vaskoda5ccc82015-11-25 10:42:49 +01002353 CHECK_AND_FREE(privkey);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002354 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002355}
2356
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002357json_object *
2358handle_op_disconnect(json_object *UNUSED(request), unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002359{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002360 json_object *reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002361
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002362 DEBUG("Request: disconnect (session %u)", session_key);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002363
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002364 if (netconf_close(session_key, &reply) != EXIT_SUCCESS) {
2365 CHECK_ERR_SET_REPLY_ERR("Get configuration information from device failed.")
2366 } else {
2367 reply = create_ok_reply();
2368 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002369
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002370 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002371}
2372
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002373json_object *
2374handle_op_get(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002375{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002376 char *filter = NULL;
2377 char *data = NULL;
2378 json_object *reply = NULL, *obj;
2379 int strict;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002380
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002381 DEBUG("Request: get (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002382
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002383 pthread_mutex_lock(&json_lock);
2384 filter = get_param_string(request, "filter");
2385 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2386 pthread_mutex_unlock(&json_lock);
2387 reply = create_error_reply("Missing strict parameter.");
2388 return reply;
2389 }
2390 strict = json_object_get_boolean(obj);
2391 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02002392
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002393 if ((data = netconf_get(session_key, filter, strict, &reply)) == NULL) {
2394 CHECK_ERR_SET_REPLY_ERR("Get information failed.")
2395 } else {
2396 reply = create_data_reply(data);
2397 free(data);
2398 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002399
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002400 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002401}
2402
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002403json_object *
2404handle_op_getconfig(json_object *request, unsigned int session_key)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002405{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002406 NC_DATASTORE ds_type_s = -1;
2407 char *filter = NULL;
2408 char *data = NULL;
2409 char *source = NULL;
2410 json_object *reply = NULL, *obj;
2411 int strict;
Tomas Cejkab4d05872014-02-14 22:44:38 +01002412
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002413 DEBUG("Request: get-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002414
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002415 pthread_mutex_lock(&json_lock);
2416 filter = get_param_string(request, "filter");
2417 source = get_param_string(request, "source");
2418 if (source != NULL) {
2419 ds_type_s = parse_datastore(source);
2420 }
2421 if (json_object_object_get_ex(request, "strict", &obj) == FALSE) {
2422 pthread_mutex_unlock(&json_lock);
2423 reply = create_error_reply("Missing strict parameter.");
2424 return reply;
2425 }
2426 strict = json_object_get_boolean(obj);
2427 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002428
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002429 if ((int)ds_type_s == -1) {
2430 reply = create_error_reply("Invalid source repository type requested.");
2431 goto finalize;
2432 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01002433
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002434 if ((data = netconf_getconfig(session_key, ds_type_s, filter, strict, &reply)) == NULL) {
2435 CHECK_ERR_SET_REPLY_ERR("Get configuration operation failed.")
2436 } else {
2437 reply = create_data_reply(data);
2438 free(data);
2439 }
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002440
Tomas Cejka09629492014-07-10 15:58:06 +02002441finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002442 CHECK_AND_FREE(filter);
2443 CHECK_AND_FREE(source);
2444 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002445}
2446
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002447json_object *
2448handle_op_editconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002449{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002450 NC_DATASTORE ds_type_t = -1;
Michal Vaskof35ea502016-02-24 10:44:54 +01002451 NC_RPC_EDIT_DFLTOP defop_type = 0;
2452 NC_RPC_EDIT_ERROPT erropt_type = 0;
2453 NC_RPC_EDIT_TESTOPT testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002454 char *defop = NULL;
2455 char *erropt = NULL;
2456 char *config = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002457 char *target = NULL;
2458 char *testopt = NULL;
2459 char *urisource = NULL;
2460 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002461 struct lyd_node *content;
2462 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002463
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002464 DEBUG("Request: edit-config (session %u)", session_key);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002465
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002466 pthread_mutex_lock(&json_lock);
2467 /* get parameters */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002468 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2469 pthread_mutex_unlock(&json_lock);
2470 reply = create_error_reply("Missing configs parameter.");
2471 goto finalize;
2472 }
2473 obj = json_object_array_get_idx(configs, idx);
2474 config = strdup(json_object_get_string(obj));
Tomas Cejkad5b53772013-06-08 23:01:07 +02002475
Michal Vaskof35ea502016-02-24 10:44:54 +01002476 target = get_param_string(request, "target");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002477 defop = get_param_string(request, "default-operation");
2478 erropt = get_param_string(request, "error-option");
2479 urisource = get_param_string(request, "uri-source");
2480 testopt = get_param_string(request, "test-option");
2481 pthread_mutex_unlock(&json_lock);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002482
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002483 if (target != NULL) {
2484 ds_type_t = parse_datastore(target);
2485 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002486
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002487 if (defop != NULL) {
2488 if (strcmp(defop, "merge") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002489 defop_type = NC_RPC_EDIT_DFLTOP_MERGE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002490 } else if (strcmp(defop, "replace") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002491 defop_type = NC_RPC_EDIT_DFLTOP_REPLACE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002492 } else if (strcmp(defop, "none") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002493 defop_type = NC_RPC_EDIT_DFLTOP_NONE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002494 } else {
2495 reply = create_error_reply("Invalid default-operation parameter.");
2496 goto finalize;
2497 }
2498 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002499 defop_type = NC_RPC_EDIT_DFLTOP_UNKNOWN;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002500 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002501
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002502 if (erropt != NULL) {
2503 if (strcmp(erropt, "continue-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002504 erropt_type = NC_RPC_EDIT_ERROPT_CONTINUE;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002505 } else if (strcmp(erropt, "stop-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002506 erropt_type = NC_RPC_EDIT_ERROPT_STOP;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002507 } else if (strcmp(erropt, "rollback-on-error") == 0) {
Michal Vaskof35ea502016-02-24 10:44:54 +01002508 erropt_type = NC_RPC_EDIT_ERROPT_ROLLBACK;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002509 } else {
2510 reply = create_error_reply("Invalid error-option parameter.");
2511 goto finalize;
2512 }
2513 } else {
2514 erropt_type = 0;
2515 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002516
Michal Vaskof35ea502016-02-24 10:44:54 +01002517 if ((config && urisource) || (!config && !urisource)) {
2518 reply = create_error_reply("Invalid config and uri-source data parameters.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002519 goto finalize;
2520 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002521
2522 if (config) {
2523 locked_session = session_get_locked(session_key, NULL);
2524 if (!locked_session) {
2525 ERROR("Unknown session or locking failed.");
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002526 goto finalize;
2527 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002528
2529 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_EDIT);
2530 session_unlock(locked_session);
2531
2532 free(config);
2533 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2534 lyd_free_withsiblings(content);
2535 } else {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002536 config = urisource;
2537 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002538
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002539 if (testopt != NULL) {
2540 testopt_type = parse_testopt(testopt);
2541 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002542 testopt_type = NC_RPC_EDIT_TESTOPT_TESTSET;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002543 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002544
Michal Vaskof35ea502016-02-24 10:44:54 +01002545 reply = netconf_editconfig(session_key, ds_type_t, defop_type, erropt_type, testopt_type, config);
Tomas Cejkad5b53772013-06-08 23:01:07 +02002546
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002547 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01002548
Tomas Cejka09629492014-07-10 15:58:06 +02002549finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002550 CHECK_AND_FREE(defop);
2551 CHECK_AND_FREE(erropt);
2552 CHECK_AND_FREE(config);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002553 CHECK_AND_FREE(urisource);
2554 CHECK_AND_FREE(target);
2555 CHECK_AND_FREE(testopt);
2556
2557 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002558}
2559
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002560json_object *
2561handle_op_copyconfig(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002562{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002563 NC_DATASTORE ds_type_s = -1;
2564 NC_DATASTORE ds_type_t = -1;
2565 char *config = NULL;
2566 char *target = NULL;
2567 char *source = NULL;
2568 char *uri_src = NULL;
2569 char *uri_trg = NULL;
2570 json_object *reply = NULL, *configs, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002571 struct lyd_node *content;
2572 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002573
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002574 DEBUG("Request: copy-config (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002575
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002576 /* get parameters */
2577 pthread_mutex_lock(&json_lock);
2578 target = get_param_string(request, "target");
2579 source = get_param_string(request, "source");
2580 uri_src = get_param_string(request, "uri-source");
2581 uri_trg = get_param_string(request, "uri-target");
2582 if (!strcmp(source, "config")) {
2583 if (json_object_object_get_ex(request, "configs", &configs) == FALSE) {
2584 pthread_mutex_unlock(&json_lock);
2585 reply = create_error_reply("Missing configs parameter.");
2586 goto finalize;
2587 }
2588 obj = json_object_array_get_idx(configs, idx);
2589 if (!obj) {
2590 pthread_mutex_unlock(&json_lock);
2591 reply = create_error_reply("Configs array parameter shorter than sessions.");
2592 goto finalize;
2593 }
2594 config = strdup(json_object_get_string(obj));
2595 }
2596 pthread_mutex_unlock(&json_lock);
2597
2598 if (target != NULL) {
2599 ds_type_t = parse_datastore(target);
2600 }
2601 if (source != NULL) {
2602 ds_type_s = parse_datastore(source);
2603 }
2604
2605 if ((int)ds_type_s == -1) {
2606 /* invalid source datastore specified */
2607 reply = create_error_reply("Invalid source repository type requested.");
2608 goto finalize;
2609 }
2610
2611 if ((int)ds_type_t == -1) {
2612 /* invalid target datastore specified */
2613 reply = create_error_reply("Invalid target repository type requested.");
2614 goto finalize;
2615 }
2616
2617 if (ds_type_s == NC_DATASTORE_URL) {
2618 if (uri_src == NULL) {
2619 uri_src = "";
2620 }
2621 }
2622 if (ds_type_t == NC_DATASTORE_URL) {
2623 if (uri_trg == NULL) {
2624 uri_trg = "";
2625 }
2626 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002627
2628 if (config) {
2629 locked_session = session_get_locked(session_key, NULL);
2630 if (!locked_session) {
2631 ERROR("Unknown session or locking failed.");
2632 goto finalize;
2633 }
2634
2635 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_CONFIG);
2636 session_unlock(locked_session);
2637
2638 free(config);
2639 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
2640 lyd_free_withsiblings(content);
2641 }
2642
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002643 reply = netconf_copyconfig(session_key, ds_type_s, ds_type_t, config, uri_src, uri_trg);
2644
2645 CHECK_ERR_SET_REPLY
2646
2647finalize:
2648 CHECK_AND_FREE(config);
2649 CHECK_AND_FREE(target);
2650 CHECK_AND_FREE(source);
2651 CHECK_AND_FREE(uri_src);
2652 CHECK_AND_FREE(uri_trg);
2653
2654 return reply;
2655}
2656
2657json_object *
2658handle_op_deleteconfig(json_object *request, unsigned int session_key)
2659{
2660 json_object *reply;
2661 NC_DATASTORE ds_type = -1;
2662 char *target, *url;
2663
2664 DEBUG("Request: delete-config (session %u)", session_key);
2665
2666 pthread_mutex_lock(&json_lock);
2667 target = get_param_string(request, "target");
2668 url = get_param_string(request, "url");
2669 pthread_mutex_unlock(&json_lock);
2670
2671 if (target != NULL) {
2672 ds_type = parse_datastore(target);
2673 }
2674 if ((int)ds_type == -1) {
2675 reply = create_error_reply("Invalid target repository type requested.");
2676 goto finalize;
2677 }
2678 if (ds_type == NC_DATASTORE_URL) {
2679 if (!url) {
2680 url = "";
2681 }
2682 }
2683
2684 reply = netconf_deleteconfig(session_key, ds_type, url);
2685
2686 CHECK_ERR_SET_REPLY
2687 if (reply == NULL) {
2688 reply = create_ok_reply();
2689 }
2690
2691finalize:
2692 CHECK_AND_FREE(target);
2693 CHECK_AND_FREE(url);
2694 return reply;
2695}
2696
2697json_object *
2698handle_op_lock(json_object *request, unsigned int session_key)
2699{
2700 json_object *reply;
2701 NC_DATASTORE ds_type = -1;
2702 char *target;
2703
2704 DEBUG("Request: lock (session %u)", session_key);
2705
2706 pthread_mutex_lock(&json_lock);
2707 target = get_param_string(request, "target");
2708 pthread_mutex_unlock(&json_lock);
2709
2710 if (target != NULL) {
2711 ds_type = parse_datastore(target);
2712 }
2713 if ((int)ds_type == -1) {
2714 reply = create_error_reply("Invalid target repository type requested.");
2715 goto finalize;
2716 }
2717
2718 reply = netconf_lock(session_key, ds_type);
2719
2720 CHECK_ERR_SET_REPLY
2721 if (reply == NULL) {
2722 reply = create_ok_reply();
2723 }
2724
2725finalize:
2726 CHECK_AND_FREE(target);
2727 return reply;
2728}
2729
2730json_object *
2731handle_op_unlock(json_object *request, unsigned int session_key)
2732{
2733 json_object *reply;
2734 NC_DATASTORE ds_type = -1;
2735 char *target;
2736
2737 DEBUG("Request: unlock (session %u)", session_key);
2738
2739 pthread_mutex_lock(&json_lock);
2740 target = get_param_string(request, "target");
2741 pthread_mutex_unlock(&json_lock);
2742
2743 if (target != NULL) {
2744 ds_type = parse_datastore(target);
2745 }
2746 if ((int)ds_type == -1) {
2747 reply = create_error_reply("Invalid target repository type requested.");
2748 goto finalize;
2749 }
2750
2751 reply = netconf_unlock(session_key, ds_type);
2752
2753 CHECK_ERR_SET_REPLY
2754 if (reply == NULL) {
2755 reply = create_ok_reply();
2756 }
2757
2758finalize:
2759 CHECK_AND_FREE(target);
2760 return reply;
2761}
2762
2763json_object *
2764handle_op_kill(json_object *request, unsigned int session_key)
2765{
2766 json_object *reply = NULL;
2767 char *sid = NULL;
2768
2769 DEBUG("Request: kill-session (session %u)", session_key);
2770
2771 pthread_mutex_lock(&json_lock);
2772 sid = get_param_string(request, "session-id");
2773 pthread_mutex_unlock(&json_lock);
2774
2775 if (sid == NULL) {
2776 reply = create_error_reply("Missing session-id parameter.");
2777 goto finalize;
2778 }
2779
2780 reply = netconf_killsession(session_key, sid);
2781
2782 CHECK_ERR_SET_REPLY
2783
2784finalize:
2785 CHECK_AND_FREE(sid);
2786 return reply;
2787}
2788
2789json_object *
2790handle_op_info(json_object *UNUSED(request), unsigned int session_key)
2791{
2792 json_object *reply = NULL;
2793 struct session_with_mutex *locked_session = NULL;
2794 DEBUG("Request: get info about session %u", session_key);
2795
2796 DEBUG("LOCK wrlock %s", __func__);
2797 if (pthread_rwlock_rdlock(&session_lock) != 0) {
2798 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2799 }
2800
2801 for (locked_session = netconf_sessions_list;
2802 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002803 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002804 if (locked_session != NULL) {
2805 DEBUG("LOCK mutex %s", __func__);
2806 pthread_mutex_lock(&locked_session->lock);
2807 DEBUG("UNLOCK wrlock %s", __func__);
2808 if (pthread_rwlock_unlock(&session_lock) != 0) {
2809 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2810 }
2811 if (locked_session->hello_message != NULL) {
2812 reply = locked_session->hello_message;
2813 } else {
2814 reply = create_error_reply("Invalid session identifier.");
2815 }
2816 DEBUG("UNLOCK mutex %s", __func__);
2817 pthread_mutex_unlock(&locked_session->lock);
2818 } else {
2819 DEBUG("UNLOCK wrlock %s", __func__);
2820 if (pthread_rwlock_unlock(&session_lock) != 0) {
2821 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2822 }
2823 reply = create_error_reply("Invalid session identifier.");
2824 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002825
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002826 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002827}
2828
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002829json_object *
2830handle_op_generic(json_object *request, unsigned int session_key, int idx)
Tomas Cejkad5b53772013-06-08 23:01:07 +02002831{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002832 json_object *reply = NULL, *contents, *obj;
Michal Vaskof35ea502016-02-24 10:44:54 +01002833 char *content = NULL, *str;
2834 struct lyd_node *data = NULL, *node_content;
2835 struct session_with_mutex *locked_session;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002836
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002837 DEBUG("Request: generic request (session %u)", session_key);
Tomas Cejka47387fd2013-06-10 20:37:46 +02002838
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002839 pthread_mutex_lock(&json_lock);
2840 if (json_object_object_get_ex(request, "contents", &contents) == FALSE) {
2841 pthread_mutex_unlock(&json_lock);
2842 reply = create_error_reply("Missing contents parameter.");
2843 goto finalize;
2844 }
2845 obj = json_object_array_get_idx(contents, idx);
2846 if (!obj) {
2847 pthread_mutex_unlock(&json_lock);
2848 reply = create_error_reply("Contents array parameter shorter than sessions.");
2849 goto finalize;
2850 }
Michal Vaskof35ea502016-02-24 10:44:54 +01002851 content = strdup(json_object_get_string(obj));
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002852 pthread_mutex_unlock(&json_lock);
2853
Michal Vaskof35ea502016-02-24 10:44:54 +01002854 locked_session = session_get_locked(session_key, NULL);
2855 if (!locked_session) {
2856 ERROR("Unknown session or locking failed.");
2857 goto finalize;
2858 }
2859
2860 node_content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), content, LYD_JSON, LYD_OPT_RPC);
2861 session_unlock(locked_session);
2862
2863 free(content);
2864 lyd_print_mem(&content, node_content, LYD_XML, LYP_WITHSIBLINGS);
2865 lyd_free_withsiblings(node_content);
2866
2867 reply = netconf_generic(session_key, content, &data);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002868 if (reply == NULL) {
2869 GETSPEC_ERR_REPLY
2870 if (err_reply != NULL) {
2871 /* use filled err_reply from libnetconf's callback */
2872 reply = err_reply;
2873 }
2874 } else {
2875 if (data == NULL) {
2876 pthread_mutex_lock(&json_lock);
2877 reply = json_object_new_object();
2878 json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
2879 pthread_mutex_unlock(&json_lock);
2880 } else {
Michal Vaskof35ea502016-02-24 10:44:54 +01002881 lyd_print_mem(&str, data, LYD_JSON, LYP_WITHSIBLINGS);
2882 lyd_free_withsiblings(data);
2883 reply = create_data_reply(str);
2884 free(str);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002885 }
2886 }
2887
2888finalize:
Michal Vaskof35ea502016-02-24 10:44:54 +01002889 CHECK_AND_FREE(content);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002890 return reply;
2891}
2892
2893json_object *
2894handle_op_getschema(json_object *request, unsigned int session_key)
2895{
2896 char *data = NULL;
2897 char *identifier = NULL;
2898 char *version = NULL;
2899 char *format = NULL;
2900 json_object *reply = NULL;
2901
2902 DEBUG("Request: get-schema (session %u)", session_key);
2903
2904 pthread_mutex_lock(&json_lock);
2905 identifier = get_param_string(request, "identifier");
2906 version = get_param_string(request, "version");
2907 format = get_param_string(request, "format");
2908 pthread_mutex_unlock(&json_lock);
2909
2910 if (identifier == NULL) {
2911 reply = create_error_reply("No identifier for get-schema supplied.");
2912 goto finalize;
2913 }
2914
2915 DEBUG("get-schema(version: %s, format: %s)", version, format);
2916 if ((data = netconf_getschema(session_key, identifier, version, format, &reply)) == NULL) {
2917 CHECK_ERR_SET_REPLY_ERR("Get models operation failed.")
2918 } else {
2919 reply = create_data_reply(data);
2920 free(data);
2921 }
2922
2923finalize:
2924 CHECK_AND_FREE(identifier);
2925 CHECK_AND_FREE(version);
2926 CHECK_AND_FREE(format);
2927 return reply;
2928}
2929
2930json_object *
2931handle_op_reloadhello(json_object *UNUSED(request), unsigned int session_key)
2932{
2933 struct nc_session *temp_session = NULL;
2934 struct session_with_mutex * locked_session = NULL;
2935 json_object *reply = NULL;
2936
2937 DEBUG("Request: reload hello (session %u)", session_key);
2938
2939 DEBUG("LOCK wrlock %s", __func__);
2940 if (pthread_rwlock_wrlock(&session_lock) != 0) {
2941 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2942 return NULL;
2943 }
2944
2945 for (locked_session = netconf_sessions_list;
2946 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01002947 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002948 if ((locked_session != NULL) && (locked_session->hello_message != NULL)) {
2949 DEBUG("LOCK mutex %s", __func__);
2950 pthread_mutex_lock(&locked_session->lock);
2951 DEBUG("creating temporary NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01002952 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002953 if (temp_session != NULL) {
2954 prepare_status_message(locked_session, temp_session);
2955 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01002956 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002957 temp_session = NULL;
2958 } else {
2959 DEBUG("Reload hello failed due to channel establishment");
2960 reply = create_error_reply("Reload was unsuccessful, connection failed.");
2961 }
2962 DEBUG("UNLOCK mutex %s", __func__);
2963 pthread_mutex_unlock(&locked_session->lock);
2964 DEBUG("UNLOCK wrlock %s", __func__);
2965 if (pthread_rwlock_unlock(&session_lock) != 0) {
2966 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2967 }
2968 } else {
2969 DEBUG("UNLOCK wrlock %s", __func__);
2970 if (pthread_rwlock_unlock(&session_lock) != 0) {
2971 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
2972 }
2973 reply = create_error_reply("Invalid session identifier.");
2974 }
Tomas Cejkad5b53772013-06-08 23:01:07 +02002975
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002976 if ((reply == NULL) && (locked_session->hello_message != NULL)) {
2977 reply = locked_session->hello_message;
2978 }
Tomas Cejka47387fd2013-06-10 20:37:46 +02002979
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002980 return reply;
Tomas Cejkad5b53772013-06-08 23:01:07 +02002981}
2982
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002983void
Michal Vaskof35ea502016-02-24 10:44:54 +01002984notification_history(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejka6b886e02013-07-05 09:53:17 +02002985{
Michal Vaskof35ea502016-02-24 10:44:54 +01002986 time_t eventtime;
2987 char *content;
2988 (void)session;
2989
2990 eventtime = nc_datetime2time(notif->datetime);
2991
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01002992 json_object *notif_history_array = (json_object *)pthread_getspecific(notif_history_key);
2993 if (notif_history_array == NULL) {
2994 ERROR("No list of notification history found.");
2995 return;
2996 }
2997 DEBUG("Got notification from history %lu.", (long unsigned)eventtime);
2998 pthread_mutex_lock(&json_lock);
Michal Vaskof35ea502016-02-24 10:44:54 +01002999 json_object *notif_obj = json_object_new_object();
3000 if (notif_obj == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003001 ERROR("Could not allocate memory for notification (json).");
3002 goto failed;
3003 }
Michal Vaskof35ea502016-02-24 10:44:54 +01003004 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
3005
3006 json_object_object_add(notif_obj, "eventtime", json_object_new_int64(eventtime));
3007 json_object_object_add(notif_obj, "content", json_object_new_string(content));
3008
3009 free(content);
3010
3011 json_object_array_add(notif_history_array, notif_obj);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003012failed:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003013 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003014}
3015
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003016json_object *
3017handle_op_ntfgethistory(json_object *request, unsigned int session_key)
Tomas Cejka6b886e02013-07-05 09:53:17 +02003018{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003019 json_object *reply = NULL;
3020 json_object *js_tmp = NULL;
3021 struct session_with_mutex *locked_session = NULL;
3022 struct nc_session *temp_session = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003023 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003024 time_t start = 0;
3025 time_t stop = 0;
3026 int64_t from = 0, to = 0;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003027
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003028 DEBUG("Request: get notification history (session %u)", session_key);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003029
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003030 pthread_mutex_lock(&json_lock);
3031 if (json_object_object_get_ex(request, "from", &js_tmp) == TRUE) {
3032 from = json_object_get_int64(js_tmp);
3033 }
3034 if (json_object_object_get_ex(request, "to", &js_tmp) == TRUE) {
3035 to = json_object_get_int64(js_tmp);
3036 }
3037 pthread_mutex_unlock(&json_lock);
Tomas Cejka09629492014-07-10 15:58:06 +02003038
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003039 start = time(NULL) + from;
3040 stop = time(NULL) + to;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003041
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003042 DEBUG("notification history interval %li %li", (long int)from, (long int)to);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003043
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003044 DEBUG("LOCK wrlock %s", __func__);
3045 if (pthread_rwlock_rdlock(&session_lock) != 0) {
3046 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3047 reply = create_error_reply("Internal lock failed.");
3048 goto finalize;
3049 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003050
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003051 for (locked_session = netconf_sessions_list;
3052 locked_session && (locked_session->session_key != session_key);
Michal Vaskoc3146782015-11-04 14:46:41 +01003053 locked_session = locked_session->next);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003054 if (locked_session != NULL) {
3055 DEBUG("LOCK mutex %s", __func__);
3056 pthread_mutex_lock(&locked_session->lock);
3057 DEBUG("UNLOCK wrlock %s", __func__);
3058 if (pthread_rwlock_unlock(&session_lock) != 0) {
3059 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3060 }
3061 DEBUG("creating temporal NC session.");
Michal Vaskof35ea502016-02-24 10:44:54 +01003062 temp_session = nc_connect_ssh_channel(locked_session->session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003063 if (temp_session != NULL) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003064 rpc = nc_rpc_subscribe(NULL, NULL, nc_time2datetime(start, NULL), nc_time2datetime(stop, NULL), NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003065 if (rpc == NULL) {
3066 DEBUG("UNLOCK mutex %s", __func__);
3067 pthread_mutex_unlock(&locked_session->lock);
3068 DEBUG("notifications: creating an rpc request failed.");
3069 reply = create_error_reply("notifications: creating an rpc request failed.");
3070 goto finalize;
3071 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003072
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003073 DEBUG("Send NC subscribe.");
3074 /** \todo replace with sth like netconf_op(http_server, session_hash, rpc) */
3075 json_object *res = netconf_unlocked_op(temp_session, rpc);
3076 if (res != NULL) {
3077 DEBUG("UNLOCK mutex %s", __func__);
3078 pthread_mutex_unlock(&locked_session->lock);
3079 DEBUG("Subscription RPC failed.");
3080 reply = res;
3081 goto finalize;
3082 }
3083 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejka6b886e02013-07-05 09:53:17 +02003084
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003085 DEBUG("UNLOCK mutex %s", __func__);
3086 pthread_mutex_unlock(&locked_session->lock);
3087 DEBUG("LOCK mutex %s", __func__);
3088 pthread_mutex_lock(&ntf_history_lock);
3089 pthread_mutex_lock(&json_lock);
3090 json_object *notif_history_array = json_object_new_array();
3091 pthread_mutex_unlock(&json_lock);
3092 if (pthread_setspecific(notif_history_key, notif_history_array) != 0) {
3093 ERROR("notif_history: cannot set thread-specific hash value.");
3094 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003095
Michal Vaskof35ea502016-02-24 10:44:54 +01003096 nc_recv_notif_dispatch(temp_session, notification_history);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003097
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003098 pthread_mutex_lock(&json_lock);
3099 reply = json_object_new_object();
3100 json_object_object_add(reply, "notifications", notif_history_array);
3101 //json_object_put(notif_history_array);
3102 pthread_mutex_unlock(&json_lock);
Tomas Cejka6b886e02013-07-05 09:53:17 +02003103
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003104 DEBUG("UNLOCK mutex %s", __func__);
3105 pthread_mutex_unlock(&ntf_history_lock);
3106 DEBUG("closing temporal NC session.");
Michal Vaskoa9590052016-03-08 10:12:24 +01003107 nc_session_free(temp_session, NULL);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003108 temp_session = NULL;
3109 } else {
3110 DEBUG("UNLOCK mutex %s", __func__);
3111 pthread_mutex_unlock(&locked_session->lock);
3112 DEBUG("Get history of notification failed due to channel establishment");
3113 reply = create_error_reply("Get history of notification was unsuccessful, connection failed.");
3114 }
3115 } else {
3116 DEBUG("UNLOCK wrlock %s", __func__);
3117 if (pthread_rwlock_unlock(&session_lock) != 0) {
3118 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3119 }
3120 reply = create_error_reply("Invalid session identifier.");
3121 }
Tomas Cejka6b886e02013-07-05 09:53:17 +02003122
Tomas Cejka09629492014-07-10 15:58:06 +02003123finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003124 return reply;
Tomas Cejka4003a702013-10-01 00:02:45 +02003125}
3126
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003127json_object *
3128handle_op_validate(json_object *request, unsigned int session_key)
Tomas Cejka4003a702013-10-01 00:02:45 +02003129{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003130 json_object *reply = NULL;
3131 char *target = NULL;
3132 char *url = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003133 struct nc_rpc *rpc = NULL;
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003134 NC_DATASTORE target_ds;
Tomas Cejka4003a702013-10-01 00:02:45 +02003135
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003136 DEBUG("Request: validate datastore (session %u)", session_key);
Tomas Cejka4003a702013-10-01 00:02:45 +02003137
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003138 pthread_mutex_lock(&json_lock);
3139 target = get_param_string(request, "target");
3140 url = get_param_string(request, "url");
3141 pthread_mutex_unlock(&json_lock);
Tomas Cejka4003a702013-10-01 00:02:45 +02003142
3143
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003144 if (target == NULL) {
3145 reply = create_error_reply("Missing target parameter.");
3146 goto finalize;
3147 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003148
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003149 /* validation */
3150 target_ds = parse_datastore(target);
Michal Vaskof35ea502016-02-24 10:44:54 +01003151 rpc = nc_rpc_validate(target_ds, url, NC_PARAMTYPE_CONST);
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003152 if (rpc == NULL) {
3153 DEBUG("mod_netconf: creating rpc request failed");
3154 reply = create_error_reply("Creation of RPC request failed.");
3155 goto finalize;
3156 }
Tomas Cejka4003a702013-10-01 00:02:45 +02003157
Michal Vaskof35ea502016-02-24 10:44:54 +01003158 if ((reply = netconf_op(session_key, rpc, 0, NULL)) == NULL) {
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003159 CHECK_ERR_SET_REPLY
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003160
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003161 if (reply == NULL) {
3162 DEBUG("Request: validation ok.");
3163 reply = create_ok_reply();
3164 }
3165 }
3166 nc_rpc_free (rpc);
Tomas Cejkaedb3ab42014-03-27 15:04:00 +01003167
Tomas Cejka09629492014-07-10 15:58:06 +02003168finalize:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003169 CHECK_AND_FREE(target);
3170 CHECK_AND_FREE(url);
3171 return reply;
Tomas Cejka6b886e02013-07-05 09:53:17 +02003172}
3173
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003174json_object *
3175handle_op_query(json_object *request, unsigned int session_key, int idx)
David Kupka8e60a372012-09-04 09:15:20 +02003176{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003177 json_object *reply = NULL, *filters, *obj;
3178 char *filter = NULL;
3179 int load_children = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003180
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003181 DEBUG("Request: query (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003182
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003183 pthread_mutex_lock(&json_lock);
3184 if (json_object_object_get_ex(request, "filters", &filters) == FALSE) {
3185 pthread_mutex_unlock(&json_lock);
3186 reply = create_error_reply("Missing filters parameter.");
3187 goto finalize;
3188 }
3189 obj = json_object_array_get_idx(filters, idx);
3190 if (!obj) {
3191 pthread_mutex_unlock(&json_lock);
3192 reply = create_error_reply("Filters array parameter shorter than sessions.");
3193 goto finalize;
3194 }
3195 filter = strdup(json_object_get_string(obj));
3196 if (json_object_object_get_ex(request, "load_children", &obj) == TRUE) {
3197 load_children = json_object_get_boolean(obj);
3198 }
3199 pthread_mutex_unlock(&json_lock);
Tomas Cejka442258e2014-04-01 18:17:18 +02003200
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003201 reply = libyang_query(session_key, filter, load_children);
David Kupka8e60a372012-09-04 09:15:20 +02003202
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003203 CHECK_ERR_SET_REPLY
3204 if (!reply) {
3205 reply = create_error_reply("Query failed.");
3206 }
David Kupka8e60a372012-09-04 09:15:20 +02003207
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003208finalize:
3209 CHECK_AND_FREE(filter);
3210 return reply;
3211}
David Kupka8e60a372012-09-04 09:15:20 +02003212
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003213json_object *
3214handle_op_merge(json_object *request, unsigned int session_key, int idx)
3215{
3216 json_object *reply = NULL, *configs, *obj;
3217 char *config = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +01003218 struct lyd_node *content;
3219 struct session_with_mutex *locked_session;
David Kupka8e60a372012-09-04 09:15:20 +02003220
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003221 DEBUG("Request: merge (session %u)", session_key);
David Kupka8e60a372012-09-04 09:15:20 +02003222
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003223 pthread_mutex_lock(&json_lock);
3224 if (json_object_object_get_ex(request, "configurations", &configs) == FALSE) {
3225 pthread_mutex_unlock(&json_lock);
3226 reply = create_error_reply("Missing configurations parameter.");
3227 goto finalize;
3228 }
3229 obj = json_object_array_get_idx(configs, idx);
3230 if (!obj) {
3231 pthread_mutex_unlock(&json_lock);
3232 reply = create_error_reply("Filters array parameter shorter than sessions.");
3233 goto finalize;
3234 }
3235 config = strdup(json_object_get_string(obj));
3236 pthread_mutex_unlock(&json_lock);
David Kupka8e60a372012-09-04 09:15:20 +02003237
Michal Vaskof35ea502016-02-24 10:44:54 +01003238 locked_session = session_get_locked(session_key, NULL);
3239 if (!locked_session) {
3240 ERROR("Unknown session or locking failed.");
3241 goto finalize;
3242 }
3243
3244 content = lyd_parse_mem(nc_session_get_ctx(locked_session->session), config, LYD_JSON, LYD_OPT_DATA);
3245 session_unlock(locked_session);
3246
3247 free(config);
3248 lyd_print_mem(&config, content, LYD_XML, LYP_WITHSIBLINGS);
3249 lyd_free_withsiblings(content);
3250
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003251 reply = libyang_merge(session_key, config);
David Kupka8e60a372012-09-04 09:15:20 +02003252
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003253 CHECK_ERR_SET_REPLY
3254 if (!reply) {
3255 reply = create_error_reply("Merge failed.");
3256 }
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003257
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003258finalize:
3259 CHECK_AND_FREE(config);
3260 return reply;
3261}
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003262
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003263void *
3264thread_routine(void *arg)
3265{
3266 void *retval = NULL;
3267 struct pollfd fds;
3268 json_object *request = NULL, *replies = NULL, *reply, *sessions = NULL;
3269 json_object *js_tmp = NULL;
3270 int operation = (-1), count, i;
3271 int status = 0;
3272 const char *msgtext;
3273 unsigned int session_key = 0;
3274 char *chunked_out_msg = NULL;
3275 int client = ((struct pass_to_thread *)arg)->client;
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003276
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003277 char *buffer = NULL;
David Kupka8e60a372012-09-04 09:15:20 +02003278
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003279 /* init thread specific err_reply memory */
3280 create_err_reply_p();
David Kupka8e60a372012-09-04 09:15:20 +02003281
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003282 while (!isterminated) {
3283 fds.fd = client;
3284 fds.events = POLLIN;
3285 fds.revents = 0;
David Kupka8e60a372012-09-04 09:15:20 +02003286
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003287 status = poll(&fds, 1, 1000);
Tomas Cejkad5b53772013-06-08 23:01:07 +02003288
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003289 if (status == 0 || (status == -1 && (errno == EAGAIN || (errno == EINTR && isterminated == 0)))) {
3290 /* poll was interrupted - check if the isterminated is set and if not, try poll again */
3291 continue;
3292 } else if (status < 0) {
3293 /* 0: poll time outed
3294 * close socket and ignore this request from the client, it can try it again
3295 * -1: poll failed
3296 * something wrong happend, close this socket and wait for another request
3297 */
3298 close(client);
3299 break;
3300 }
3301 /* status > 0 */
David Kupka8e60a372012-09-04 09:15:20 +02003302
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003303 /* check the status of the socket */
David Kupka8e60a372012-09-04 09:15:20 +02003304
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003305 /* if nothing to read and POLLHUP (EOF) or POLLERR set */
3306 if ((fds.revents & POLLHUP) || (fds.revents & POLLERR)) {
3307 /* close client's socket (it's probably already closed by client */
3308 close(client);
3309 break;
3310 }
Tomas Cejka09629492014-07-10 15:58:06 +02003311
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003312 DEBUG("Get framed message...");
3313 buffer = get_framed_message(client);
Tomas Cejka09629492014-07-10 15:58:06 +02003314
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003315 DEBUG("Check read buffer.");
3316 if (buffer != NULL) {
3317 enum json_tokener_error jerr;
3318 pthread_mutex_lock(&json_lock);
3319 request = json_tokener_parse_verbose(buffer, &jerr);
3320 if (jerr != json_tokener_success) {
3321 ERROR("JSON parsing error");
3322 pthread_mutex_unlock(&json_lock);
3323 continue;
3324 }
3325
3326 if (json_object_object_get_ex(request, "type", &js_tmp) == TRUE) {
3327 operation = json_object_get_int(js_tmp);
3328 }
3329 pthread_mutex_unlock(&json_lock);
3330 if (operation == -1) {
3331 replies = create_replies();
3332 add_reply(replies, create_error_reply("Missing operation type from frontend."), 0);
3333 goto send_reply;
3334 }
3335
3336 if ((operation < 4) || ((operation > 19) && (operation < 100)) || (operation > 101)) {
3337 DEBUG("Unknown mod_netconf operation requested (%d)", operation);
3338 replies = create_replies();
3339 add_reply(replies, create_error_reply("Operation not supported."), 0);
3340 goto send_reply;
3341 }
3342
3343 DEBUG("operation %d", operation);
3344
3345 /* null global JSON error-reply */
3346 clean_err_reply();
3347
3348 /* clean replies envelope */
3349 if (replies != NULL) {
3350 pthread_mutex_lock(&json_lock);
3351 json_object_put(replies);
3352 pthread_mutex_unlock(&json_lock);
3353 }
3354 replies = create_replies();
3355
3356 if (operation == MSG_CONNECT) {
3357 count = 1;
3358 } else {
3359 pthread_mutex_lock(&json_lock);
3360 if (json_object_object_get_ex(request, "sessions", &sessions) == FALSE) {
3361 add_reply(replies, create_error_reply("Operation missing \"sessions\" arg"), 0);
3362 goto send_reply;
3363 }
3364 count = json_object_array_length(sessions);
3365 pthread_mutex_unlock(&json_lock);
3366 }
3367
3368 for (i = 0; i < count; ++i) {
3369 if (operation != MSG_CONNECT) {
3370 js_tmp = json_object_array_get_idx(sessions, i);
3371 session_key = json_object_get_int(js_tmp);
3372 }
3373
3374 /* process required operation */
3375 switch (operation) {
3376 case MSG_CONNECT:
3377 reply = handle_op_connect(request);
3378 break;
3379 case MSG_DISCONNECT:
3380 reply = handle_op_disconnect(request, session_key);
3381 break;
3382 case MSG_GET:
3383 reply = handle_op_get(request, session_key);
3384 break;
3385 case MSG_GETCONFIG:
3386 reply = handle_op_getconfig(request, session_key);
3387 break;
3388 case MSG_EDITCONFIG:
3389 reply = handle_op_editconfig(request, session_key, i);
3390 break;
3391 case MSG_COPYCONFIG:
3392 reply = handle_op_copyconfig(request, session_key, i);
3393 break;
3394 case MSG_DELETECONFIG:
3395 reply = handle_op_deleteconfig(request, session_key);
3396 break;
3397 case MSG_LOCK:
3398 reply = handle_op_lock(request, session_key);
3399 break;
3400 case MSG_UNLOCK:
3401 reply = handle_op_unlock(request, session_key);
3402 break;
3403 case MSG_KILL:
3404 reply = handle_op_kill(request, session_key);
3405 break;
3406 case MSG_INFO:
3407 reply = handle_op_info(request, session_key);
3408 break;
3409 case MSG_GENERIC:
3410 reply = handle_op_generic(request, session_key, i);
3411 break;
3412 case MSG_GETSCHEMA:
3413 reply = handle_op_getschema(request, session_key);
3414 break;
3415 case MSG_RELOADHELLO:
3416 reply = handle_op_reloadhello(request, session_key);
3417 break;
3418 case MSG_NTF_GETHISTORY:
3419 reply = handle_op_ntfgethistory(request, session_key);
3420 break;
3421 case MSG_VALIDATE:
3422 reply = handle_op_validate(request, session_key);
3423 break;
3424 case SCH_QUERY:
3425 reply = handle_op_query(request, session_key, i);
3426 break;
3427 case SCH_MERGE:
3428 reply = handle_op_merge(request, session_key, i);
3429 break;
3430 }
3431
3432 add_reply(replies, reply, session_key);
3433 }
3434
3435 /* free parameters */
3436 operation = (-1);
3437
3438 DEBUG("Clean request json object.");
3439 if (request != NULL) {
3440 pthread_mutex_lock(&json_lock);
3441 json_object_put(request);
3442 pthread_mutex_unlock(&json_lock);
3443 }
3444 DEBUG("Send reply json object.");
Tomas Cejka09629492014-07-10 15:58:06 +02003445
3446send_reply:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003447 /* send reply to caller */
3448 if (replies) {
3449 pthread_mutex_lock(&json_lock);
3450 msgtext = json_object_to_json_string(replies);
3451 if (asprintf(&chunked_out_msg, "\n#%d\n%s\n##\n", (int)strlen(msgtext), msgtext) == -1) {
3452 if (buffer != NULL) {
3453 free(buffer);
3454 buffer = NULL;
3455 }
3456 pthread_mutex_unlock(&json_lock);
3457 break;
3458 }
3459 pthread_mutex_unlock(&json_lock);
Tomas Cejka9a23f6e2014-03-27 14:57:00 +01003460
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003461 DEBUG("Send framed reply json object.");
3462 send(client, chunked_out_msg, strlen(chunked_out_msg) + 1, 0);
3463 DEBUG("Clean reply json object.");
3464 pthread_mutex_lock(&json_lock);
3465 json_object_put(replies);
3466 replies = NULL;
3467 DEBUG("Clean message buffer.");
3468 CHECK_AND_FREE(chunked_out_msg);
3469 chunked_out_msg = NULL;
3470 if (buffer) {
3471 free(buffer);
3472 buffer = NULL;
3473 }
3474 pthread_mutex_unlock(&json_lock);
3475 clean_err_reply();
3476 } else {
3477 ERROR("Reply is NULL, shouldn't be...");
3478 continue;
3479 }
3480 }
3481 }
3482 free(arg);
3483 free_err_reply();
David Kupka8e60a372012-09-04 09:15:20 +02003484
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003485 return retval;
David Kupka8e60a372012-09-04 09:15:20 +02003486}
3487
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003488/**
3489 * \brief Close all open NETCONF sessions.
3490 *
3491 * During termination of mod_netconf, it is useful to close all remaining
3492 * sessions. This function iterates over the list of sessions and close them
3493 * all.
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003494 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003495static void
3496close_all_nc_sessions(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003497{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003498 struct session_with_mutex *locked_session, *next_session;
3499 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003500
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003501 /* get exclusive access to sessions_list (conns) */
3502 DEBUG("LOCK wrlock %s", __func__);
3503 if ((ret = pthread_rwlock_wrlock (&session_lock)) != 0) {
3504 ERROR("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3505 return;
3506 }
3507 for (next_session = netconf_sessions_list; next_session;) {
3508 locked_session = next_session;
3509 next_session = locked_session->next;
Tomas Cejka47387fd2013-06-10 20:37:46 +02003510
Michal Vaskoc3146782015-11-04 14:46:41 +01003511 /* close_and_free_session handles locking on its own */
Michal Vaskof35ea502016-02-24 10:44:54 +01003512 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 +01003513 close_and_free_session(locked_session);
3514 }
3515 netconf_sessions_list = NULL;
3516
3517 /* get exclusive access to sessions_list (conns) */
3518 DEBUG("UNLOCK wrlock %s", __func__);
3519 if (pthread_rwlock_unlock (&session_lock) != 0) {
3520 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3521 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003522}
3523
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003524static void
3525check_timeout_and_close(void)
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003526{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003527 struct nc_session *ns = NULL;
3528 struct session_with_mutex *locked_session = NULL;
3529 time_t current_time = time(NULL);
3530 int ret;
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003531
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003532 /* get exclusive access to sessions_list (conns) */
3533 if ((ret = pthread_rwlock_wrlock(&session_lock)) != 0) {
3534 DEBUG("Error while locking rwlock: %d (%s)", ret, strerror(ret));
3535 return;
3536 }
3537 for (locked_session = netconf_sessions_list; locked_session; locked_session = locked_session->next) {
3538 ns = locked_session->session;
3539 if (ns == NULL) {
3540 continue;
3541 }
3542 pthread_mutex_lock(&locked_session->lock);
3543 if ((current_time - locked_session->last_activity) > ACTIVITY_TIMEOUT) {
Michal Vaskof35ea502016-02-24 10:44:54 +01003544 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 +02003545
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003546 /* close_and_free_session handles locking on its own */
3547 close_and_free_session(locked_session);
3548 } else {
3549 pthread_mutex_unlock(&locked_session->lock);
3550 }
3551 }
3552 /* get exclusive access to sessions_list (conns) */
3553 if (pthread_rwlock_unlock(&session_lock) != 0) {
3554 ERROR("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
3555 }
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003556}
3557
3558
3559/**
Radek Krejcif23850c2012-07-23 16:14:17 +02003560 * This is actually implementation of NETCONF client
3561 * - requests are received from UNIX socket in the predefined format
3562 * - results are replied through the same way
Michal Vaskoc3146782015-11-04 14:46:41 +01003563 * - the daemon run as a separate process
Radek Krejcif23850c2012-07-23 16:14:17 +02003564 *
3565 */
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003566static void
3567forked_proc(void)
Radek Krejci469aab82012-07-22 18:42:20 +02003568{
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003569 struct timeval tv;
3570 struct sockaddr_un local, remote;
3571 int lsock, client, ret, i, pthread_count = 0;
3572 unsigned int olds = 0, timediff = 0;
3573 socklen_t len;
3574 struct pass_to_thread *arg;
3575 pthread_t *ptids = calloc(1, sizeof(pthread_t));
3576 struct timespec maxtime;
3577 pthread_rwlockattr_t lock_attrs;
3578 #ifdef WITH_NOTIFICATIONS
3579 char use_notifications = 0;
3580 #endif
David Kupka8e60a372012-09-04 09:15:20 +02003581
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003582 /* wait at most 5 seconds for every thread to terminate */
3583 maxtime.tv_sec = 5;
3584 maxtime.tv_nsec = 0;
Radek Krejci469aab82012-07-22 18:42:20 +02003585
Tomas Cejka04e08f42014-03-27 19:52:34 +01003586#ifdef HAVE_UNIXD_SETUP_CHILD
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003587 /* change uid and gid of process for security reasons */
3588 unixd_setup_child();
Tomas Cejka04e08f42014-03-27 19:52:34 +01003589#else
3590# ifdef SU_GROUP
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003591 if (strlen(SU_GROUP) > 0) {
3592 struct group *g = getgrnam(SU_GROUP);
3593 if (g == NULL) {
3594 ERROR("GID (%s) was not found.", SU_GROUP);
3595 return;
3596 }
3597 if (setgid(g->gr_gid) != 0) {
3598 ERROR("Switching to %s GID failed. (%s)", SU_GROUP, strerror(errno));
3599 return;
3600 }
3601 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003602# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003603 DEBUG("no SU_GROUP");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003604# endif
3605# ifdef SU_USER
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003606 if (strlen(SU_USER) > 0) {
3607 struct passwd *p = getpwnam(SU_USER);
3608 if (p == NULL) {
3609 ERROR("UID (%s) was not found.", SU_USER);
3610 return;
3611 }
3612 if (setuid(p->pw_uid) != 0) {
3613 ERROR("Switching to UID %s failed. (%s)", SU_USER, strerror(errno));
3614 return;
3615 }
3616 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003617# else
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003618 DEBUG("no SU_USER");
Tomas Cejka04e08f42014-03-27 19:52:34 +01003619# endif
3620#endif
Radek Krejci469aab82012-07-22 18:42:20 +02003621
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003622 /* try to remove if exists */
3623 unlink(sockname);
Tomas Cejka04e08f42014-03-27 19:52:34 +01003624
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003625 /* create listening UNIX socket to accept incoming connections */
3626 if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
3627 ERROR("Creating socket failed (%s)", strerror(errno));
3628 goto error_exit;
3629 }
Radek Krejci469aab82012-07-22 18:42:20 +02003630
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003631 local.sun_family = AF_UNIX;
3632 strncpy(local.sun_path, sockname, sizeof(local.sun_path));
3633 len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
Radek Krejci469aab82012-07-22 18:42:20 +02003634
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003635 if (bind(lsock, (struct sockaddr *)&local, len) == -1) {
3636 if (errno == EADDRINUSE) {
3637 ERROR("mod_netconf socket address already in use");
3638 goto error_exit;
3639 }
3640 ERROR("Binding socket failed (%s)", strerror(errno));
3641 goto error_exit;
3642 }
Radek Krejci469aab82012-07-22 18:42:20 +02003643
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003644 if (listen(lsock, MAX_SOCKET_CL) == -1) {
3645 ERROR("Setting up listen socket failed (%s)", strerror(errno));
3646 goto error_exit;
3647 }
3648 chmod(sockname, S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH);
Radek Krejci469aab82012-07-22 18:42:20 +02003649
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003650 uid_t user = -1;
3651 if (strlen(CHOWN_USER) > 0) {
3652 struct passwd *p = getpwnam(CHOWN_USER);
3653 if (p != NULL) {
3654 user = p->pw_uid;
3655 }
3656 }
3657 gid_t group = -1;
3658 if (strlen(CHOWN_GROUP) > 0) {
3659 struct group *g = getgrnam(CHOWN_GROUP);
3660 if (g != NULL) {
3661 group = g->gr_gid;
3662 }
3663 }
3664 if (chown(sockname, user, group) == -1) {
3665 ERROR("Chown on socket file failed (%s).", strerror(errno));
3666 }
Tomas Cejka04e08f42014-03-27 19:52:34 +01003667
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003668 /* prepare internal lists */
Tomas Cejkaba21b382013-04-13 02:37:32 +02003669
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003670 #ifdef WITH_NOTIFICATIONS
3671 if (notification_init() == -1) {
3672 ERROR("libwebsockets initialization failed");
3673 use_notifications = 0;
3674 } else {
3675 use_notifications = 1;
3676 }
3677 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003678
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003679 /* setup libnetconf's callbacks */
3680 nc_verbosity(NC_VERB_DEBUG);
Michal Vaskof35ea502016-02-24 10:44:54 +01003681 nc_set_print_clb(clb_print);
3682 nc_client_ssh_set_auth_hostkey_check_clb(netconf_callback_ssh_hostkey_check);
3683 nc_client_ssh_set_auth_interactive_clb(netconf_callback_sshauth_interactive);
3684 nc_client_ssh_set_auth_password_clb(netconf_callback_sshauth_password);
3685 nc_client_ssh_set_auth_privkey_passphrase_clb(netconf_callback_sshauth_passphrase);
Radek Krejci469aab82012-07-22 18:42:20 +02003686
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003687 /* disable publickey authentication */
Michal Vaskof35ea502016-02-24 10:44:54 +01003688 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
Radek Krejci469aab82012-07-22 18:42:20 +02003689
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003690 /* create mutex protecting session list */
3691 pthread_rwlockattr_init(&lock_attrs);
3692 /* rwlock is shared only with threads in this process */
3693 pthread_rwlockattr_setpshared(&lock_attrs, PTHREAD_PROCESS_PRIVATE);
3694 /* create rw lock */
3695 if (pthread_rwlock_init(&session_lock, &lock_attrs) != 0) {
3696 ERROR("Initialization of mutex failed: %d (%s)", errno, strerror(errno));
3697 goto error_exit;
3698 }
3699 pthread_mutex_init(&ntf_history_lock, NULL);
3700 pthread_mutex_init(&json_lock, NULL);
3701 DEBUG("Initialization of notification history.");
3702 if (pthread_key_create(&notif_history_key, NULL) != 0) {
3703 ERROR("Initialization of notification history failed.");
3704 }
3705 if (pthread_key_create(&err_reply_key, NULL) != 0) {
3706 ERROR("Initialization of reply key failed.");
3707 }
Tomas Cejka8a82dab2013-05-30 23:37:23 +02003708
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003709 fcntl(lsock, F_SETFL, fcntl(lsock, F_GETFL, 0) | O_NONBLOCK);
3710 while (isterminated == 0) {
3711 gettimeofday(&tv, NULL);
3712 timediff = (unsigned int)tv.tv_sec - olds;
3713 #ifdef WITH_NOTIFICATIONS
3714 if (use_notifications == 1) {
3715 notification_handle();
3716 }
3717 #endif
3718 if (timediff > ACTIVITY_CHECK_INTERVAL) {
3719 check_timeout_and_close();
3720 }
Radek Krejci469aab82012-07-22 18:42:20 +02003721
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003722 /* open incoming connection if any */
3723 len = sizeof(remote);
3724 client = accept(lsock, (struct sockaddr *) &remote, &len);
3725 if (client == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
3726 usleep(SLEEP_TIME * 1000);
3727 continue;
3728 } else if (client == -1 && (errno == EINTR)) {
3729 continue;
3730 } else if (client == -1) {
3731 ERROR("Accepting mod_netconf client connection failed (%s)", strerror(errno));
3732 continue;
3733 }
Radek Krejci469aab82012-07-22 18:42:20 +02003734
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003735 /* set client's socket as non-blocking */
3736 //fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK);
Radek Krejci469aab82012-07-22 18:42:20 +02003737
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003738 arg = malloc(sizeof(struct pass_to_thread));
3739 arg->client = client;
3740 arg->netconf_sessions_list = netconf_sessions_list;
Radek Krejci469aab82012-07-22 18:42:20 +02003741
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003742 /* start new thread. It will serve this particular request and then terminate */
3743 if ((ret = pthread_create (&ptids[pthread_count], NULL, thread_routine, (void *)arg)) != 0) {
3744 ERROR("Creating POSIX thread failed: %d\n", ret);
3745 } else {
3746 DEBUG("Thread %lu created", ptids[pthread_count]);
3747 pthread_count++;
3748 ptids = realloc (ptids, sizeof(pthread_t) * (pthread_count+1));
3749 ptids[pthread_count] = 0;
3750 }
Radek Krejci469aab82012-07-22 18:42:20 +02003751
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003752 /* check if some thread already terminated, free some resources by joining it */
3753 for (i = 0; i < pthread_count; i++) {
3754 if (pthread_tryjoin_np(ptids[i], (void **)&arg) == 0) {
3755 DEBUG("Thread %lu joined with retval %p", ptids[i], arg);
3756 pthread_count--;
3757 if (pthread_count > 0) {
3758 /* place last Thread ID on the place of joined one */
3759 ptids[i] = ptids[pthread_count];
3760 }
3761 }
3762 }
3763 DEBUG("Running %d threads", pthread_count);
3764 }
Radek Krejci469aab82012-07-22 18:42:20 +02003765
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003766 DEBUG("mod_netconf terminating...");
3767 /* join all threads */
3768 for (i = 0; i < pthread_count; i++) {
3769 pthread_timedjoin_np(ptids[i], (void **)&arg, &maxtime);
3770 }
Radek Krejci469aab82012-07-22 18:42:20 +02003771
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003772 #ifdef WITH_NOTIFICATIONS
3773 notification_close();
3774 #endif
Tomas Cejkad340dbf2013-03-24 20:36:57 +01003775
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003776 /* close all NETCONF sessions */
3777 close_all_nc_sessions();
Tomas Cejkaaf7a1562013-04-13 02:27:43 +02003778
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003779 /* destroy rwlock */
3780 pthread_rwlock_destroy(&session_lock);
3781 pthread_rwlockattr_destroy(&lock_attrs);
David Kupka8e60a372012-09-04 09:15:20 +02003782
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003783 DEBUG("Exiting from the mod_netconf daemon");
Radek Krejci469aab82012-07-22 18:42:20 +02003784
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003785 free(ptids);
3786 close(lsock);
3787 exit(0);
3788 return;
3789
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003790error_exit:
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003791 close(lsock);
3792 free(ptids);
3793 return;
Radek Krejci469aab82012-07-22 18:42:20 +02003794}
3795
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003796int
3797main(int argc, char **argv)
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003798{
Michal Vaskoc3146782015-11-04 14:46:41 +01003799 struct sigaction action;
3800 sigset_t block_mask;
Michal Vaskoa53ef882015-11-24 11:02:01 +01003801 int daemonize = 0, i;
Michal Vaskoc3146782015-11-04 14:46:41 +01003802
Michal Vaskoa53ef882015-11-24 11:02:01 +01003803 if (argc > 3) {
3804 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3805 return 1;
3806 }
3807
3808 sockname = SOCKET_FILENAME;
3809 for (i = 1; i < argc; ++i) {
3810 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
3811 printf("Usage: [--(h)elp] [--(d)aemon] [socket-path]\n");
3812 return 0;
3813 } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--daemon")) {
3814 daemonize = 1;
3815 } else {
3816 sockname = argv[i];
3817 }
3818 }
3819
3820 if (daemonize && (daemon(0, 0) == -1)) {
3821 ERROR("daemon() failed (%s)", strerror(errno));
3822 return 1;
Michal Vaskoc3146782015-11-04 14:46:41 +01003823 }
3824
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003825 sigfillset(&block_mask);
Michal Vaskoc3146782015-11-04 14:46:41 +01003826 action.sa_handler = signal_handler;
3827 action.sa_mask = block_mask;
3828 action.sa_flags = 0;
3829 sigaction(SIGINT, &action, NULL);
3830 sigaction(SIGTERM, &action, NULL);
3831
Michal Vaskoda0ab5c2015-11-13 13:24:51 +01003832 forked_proc();
3833 DEBUG("Terminated");
3834 return 0;
Tomas Cejkaef531ee2013-11-12 16:07:00 +01003835}