blob: a1c28d667d1092c6cba7d07932f89efeb0ddebd4 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file session.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - input/output functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#include <assert.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27#include <pthread.h>
28#include <unistd.h>
29
30#include <libyang/libyang.h>
31
32#include "config.h"
33#include "libnetconf.h"
34#include "messages_p.h"
35#include "session_p.h"
36
37#define TIMEOUT_STEP 50
38
Radek Krejci5686ff72015-10-09 13:33:56 +020039/*
40 * @return 0 - success
41 * -1 - timeout
42 * >0 - error
43 */
44static int
45session_ti_lock(struct nc_session *session, int timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020046{
47 int r;
Radek Krejci206fcd62015-10-07 15:42:48 +020048
49 if (timeout >= 0) {
50 /* limited waiting for lock */
51 do {
52 r = pthread_mutex_trylock(&session->ti_lock);
53 if (r == EBUSY) {
54 /* try later until timeout passes */
55 usleep(TIMEOUT_STEP);
56 timeout = timeout - TIMEOUT_STEP;
57 continue;
58 } else if (r) {
59 /* error */
60 ERR("Acquiring session (%u) TI lock failed (%s).", session->id, strerror(r));
Radek Krejci5686ff72015-10-09 13:33:56 +020061 return r;
Radek Krejci206fcd62015-10-07 15:42:48 +020062 } else {
63 /* lock acquired */
Radek Krejci5686ff72015-10-09 13:33:56 +020064 return 0;
Radek Krejci206fcd62015-10-07 15:42:48 +020065 }
66 } while(timeout > 0);
67
Radek Krejci5686ff72015-10-09 13:33:56 +020068 /* timeout has passed */
69 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +020070 } else {
71 /* infinite waiting for lock */
Radek Krejci5686ff72015-10-09 13:33:56 +020072 return pthread_mutex_lock(&session->ti_lock);
73 }
74}
75
76static int
77session_ti_unlock(struct nc_session *session)
78{
79 return pthread_mutex_unlock(&session->ti_lock);
80}
81
82API NC_MSG_TYPE
83nc_recv_rpc(struct nc_session *session, int timeout, struct nc_rpc **rpc)
84{
85 int r;
86 struct lyxml_elem *xml = NULL;
87 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
88
89 if (!session || !rpc) {
90 ERR("%s: Invalid parameter", __func__);
91 return NC_MSG_ERROR;
92 } else if (session->side != NC_SIDE_SERVER) {
93 ERR("%s: only servers are allowed to receive RPCs.", __func__);
94 return NC_MSG_ERROR;
95 }
96
97 r = session_ti_lock(session, timeout);
98 if (r > 0) {
99 /* error */
100 return NC_MSG_ERROR;
101 } else if (r < 0) {
102 /* timeout */
103 return NC_MSG_WOULDBLOCK;
Radek Krejci206fcd62015-10-07 15:42:48 +0200104 }
105
106 msgtype = nc_read_msg(session, timeout, &xml);
Radek Krejci5686ff72015-10-09 13:33:56 +0200107 session_ti_unlock(session);
Radek Krejci206fcd62015-10-07 15:42:48 +0200108
Radek Krejci5686ff72015-10-09 13:33:56 +0200109 switch(msgtype) {
110 case NC_MSG_RPC:
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200111 *rpc = malloc(sizeof **rpc);
112 (*rpc)->ctx = session->ctx;
Radek Krejci5686ff72015-10-09 13:33:56 +0200113 (*rpc)->tree = lyd_parse_xml(session->ctx, xml, 0);
114 (*rpc)->root = xml;
115 break;
116 case NC_MSG_HELLO:
117 ERR("SESSION %u: Received another <hello> message.", session->id);
118 goto error;
119 case NC_MSG_REPLY:
120 ERR("SESSION %u: Received <rpc-reply> from NETCONF client.", session->id);
121 goto error;
122 case NC_MSG_NOTIF:
123 ERR("SESSION %u: Received <notification> from NETCONF client.", session->id);
124 goto error;
125 default:
126 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
127 * NC_MSG_NONE is not returned by nc_read_msg()
128 */
129 break;
130 }
Radek Krejci0dc69f72015-10-08 15:32:09 +0200131
Radek Krejci206fcd62015-10-07 15:42:48 +0200132 return msgtype;
Radek Krejci5686ff72015-10-09 13:33:56 +0200133
134error:
135
136 /* cleanup */
137 lyxml_free_elem(session->ctx, xml);
138
139 return NC_MSG_ERROR;
140}
141
142API NC_MSG_TYPE
143nc_recv_reply(struct nc_session* session, int timeout, struct nc_reply **reply)
144{
145 int r;
146 struct lyxml_elem *xml;
147 struct nc_reply_cont *cont_r;
148 struct nc_notif_cont **cont_n;
149 struct nc_notif *notif;
150 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
151
152 if (!session || !reply) {
153 ERR("%s: Invalid parameter", __func__);
154 return NC_MSG_ERROR;
155 } else if (session->side != NC_SIDE_CLIENT) {
156 ERR("%s: only clients are allowed to receive RPC replies.", __func__);
157 return NC_MSG_ERROR;
158 }
159
160 do {
161 if (msgtype && session->notif) {
162 /* second run, wait and give a chance to nc_recv_notif() */
163 usleep(TIMEOUT_STEP);
164 timeout = timeout - (TIMEOUT_STEP);
165 }
166 r = session_ti_lock(session, timeout);
167 if (r > 0) {
168 /* error */
169 return NC_MSG_ERROR;
170 } else if (r < 0) {
171 /* timeout */
172 return NC_MSG_WOULDBLOCK;
173 }
174
175 /* try to get message from the session's queue */
176 if (session->notifs) {
177 cont_r = session->replies;
178 session->replies = cont_r->next;
179
180 session_ti_unlock(session);
181
182 *reply = cont_r->msg;
183 free(cont_r);
184
185 return NC_MSG_REPLY;
186 }
187
188 /* read message from wire */
189 msgtype = nc_read_msg(session, timeout, &xml);
190 if (msgtype == NC_MSG_NOTIF) {
191 if (!session->notif) {
192 session_ti_unlock(session);
193 ERR("SESSION %u: Received Notification but session is not subscribed.", session->id);
194 goto error;
195 }
196
197 /* create notification object */
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200198 notif = malloc(sizeof *notif);
199 notif->ctx = session->ctx;
Radek Krejci5686ff72015-10-09 13:33:56 +0200200 notif->tree = lyd_parse_xml(session->ctx, xml, 0);
201 notif->root = xml;
202
203 /* store the message for nc_recv_notif() */
204 cont_n = &session->notifs;
205 while(*cont_n) {
206 cont_n = &((*cont_n)->next);
207 }
208 *cont_n = malloc(sizeof **cont_n);
209 (*cont_n)->msg = notif;
210 (*cont_n)->next = NULL;
211 }
212
213 session_ti_unlock(session);
214
215 switch(msgtype) {
216 case NC_MSG_REPLY:
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200217 *reply = malloc(sizeof **reply);
218 (*reply)->ctx = session->ctx;
Radek Krejci5686ff72015-10-09 13:33:56 +0200219 (*reply)->tree = lyd_parse_xml(session->ctx, xml, 0);
220 (*reply)->root = xml;
221 break;
222 case NC_MSG_HELLO:
223 ERR("SESSION %u: Received another <hello> message.", session->id);
224 goto error;
225 case NC_MSG_RPC:
226 ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
227 goto error;
228 default:
229 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
230 * NC_MSG_NOTIF already handled before the switch;
231 * NC_MSG_NONE is not returned by nc_read_msg()
232 */
233 break;
234 }
235
236 } while(msgtype == NC_MSG_NOTIF);
237
238 return msgtype;
239
240error:
241
242 /* cleanup */
243 lyxml_free_elem(session->ctx, xml);
244
245 return NC_MSG_ERROR;
246}
247
248API NC_MSG_TYPE
249nc_recv_notif(struct nc_session* session, int timeout, struct nc_notif **notif)
250{
251 int r;
252 struct lyxml_elem *xml;
253 struct nc_notif_cont *cont_n;
254 struct nc_reply_cont **cont_r;
255 struct nc_reply *reply;
256 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
257
258 if (!session || !notif) {
259 ERR("%s: Invalid parameter", __func__);
260 return NC_MSG_ERROR;
261 } else if (session->side != NC_SIDE_CLIENT) {
262 ERR("%s: only clients are allowed to receive Notifications.", __func__);
263 return NC_MSG_ERROR;
264 }
265
266 do {
267 if (msgtype) {
268 /* second run, wait and give a chance to nc_recv_reply() */
269 usleep(TIMEOUT_STEP);
270 timeout = timeout - (TIMEOUT_STEP);
271 }
272 r = session_ti_lock(session, timeout);
273 if (r > 0) {
274 /* error */
275 return NC_MSG_ERROR;
276 } else if (r < 0) {
277 /* timeout */
278 return NC_MSG_WOULDBLOCK;
279 }
280
281 /* try to get message from the session's queue */
282 if (session->notifs) {
283 cont_n = session->notifs;
284 session->notifs = cont_n->next;
285
286 session_ti_unlock(session);
287
288 *notif = cont_n->msg;
289 free(cont_n);
290
291 return NC_MSG_NOTIF;
292 }
293
294 /* read message from wire */
295 msgtype = nc_read_msg(session, timeout, &xml);
296 if (msgtype == NC_MSG_REPLY) {
297 /* create reply object */
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200298 reply = malloc(sizeof *reply);
299 reply->ctx = session->ctx;
Radek Krejci5686ff72015-10-09 13:33:56 +0200300 reply->tree = lyd_parse_xml(session->ctx, xml, 0);
301 reply->root = xml;
302
303 /* store the message for nc_recv_reply() */
304 cont_r = &session->replies;
305 while(*cont_r) {
306 cont_r = &((*cont_r)->next);
307 }
308 *cont_r = malloc(sizeof **cont_r);
309 (*cont_r)->msg = reply;
310 (*cont_r)->next = NULL;
311 }
312
313 session_ti_unlock(session);
314
315 switch(msgtype) {
316 case NC_MSG_NOTIF:
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200317 *notif = malloc(sizeof **notif);
318 (*notif)->ctx = session->ctx;
Radek Krejci5686ff72015-10-09 13:33:56 +0200319 (*notif)->tree = lyd_parse_xml(session->ctx, xml, 0);
320 (*notif)->root = xml;
321 break;
322 case NC_MSG_HELLO:
323 ERR("SESSION %u: Received another <hello> message.", session->id);
324 goto error;
325 case NC_MSG_RPC:
326 ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
327 goto error;
328 default:
329 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
330 * NC_MSG_REPLY already handled before the switch;
331 * NC_MSG_NONE is not returned by nc_read_msg()
332 */
333 break;
334 }
335
336 } while(msgtype == NC_MSG_REPLY);
337
338 return msgtype;
339
340error:
341
342 /* cleanup */
343 lyxml_free_elem(session->ctx, xml);
344
345 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200346}
Radek Krejcife0b3472015-10-12 13:43:42 +0200347
348API NC_MSG_TYPE
349nc_send_rpc(struct nc_session* session, struct lyd_node *op, const char *attrs)
350{
351 int r;
352
353 if (!session || !op) {
354 ERR("%s: Invalid parameter", __func__);
355 return NC_MSG_ERROR;
356 } else if (session->side != NC_SIDE_CLIENT) {
357 ERR("%s: only clients are allowed to send RPCs.", __func__);
358 return NC_MSG_ERROR;
359 }
360
361 r = session_ti_lock(session, 0);
362 if (r != 0) {
363 /* error or blocking */
364 return NC_MSG_WOULDBLOCK;
365 }
366
367 r = nc_write_msg(session, NC_MSG_RPC, op, attrs);
368
369 session_ti_unlock(session);
370
371 if (r) {
372 return NC_MSG_ERROR;
373 } else {
374 return NC_MSG_RPC;
375 }
376}
377