blob: 9f0ac4b0fed6974a071d154a62f2929f2b12a205 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020014#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010015
16#include <stdint.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20#include <poll.h>
21#include <sys/types.h>
22#include <sys/socket.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020023#include <sys/un.h>
24#include <netinet/in.h>
25#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010026#include <arpa/inet.h>
27#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010028#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010029#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010030#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010031#include <signal.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033
Michal Vasko1a38c862016-01-15 15:50:07 +010034#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010035#include "session_server.h"
36
Michal Vaskob48aa812016-01-18 14:13:09 +010037struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010038#ifdef NC_ENABLED_SSH
39 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
40#endif
41 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020042 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
43 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010044};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010045
fanchanghu966f2de2016-07-21 02:28:57 -040046static nc_rpc_clb global_rpc_clb = NULL;
47
Michal Vasko3031aae2016-01-27 16:07:18 +010048struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010049nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010050{
51 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010052 struct nc_endpt *endpt = NULL;
53
Michal Vaskoddce1212019-05-24 09:58:49 +020054 if (!name) {
55 ERRARG("endpt_name");
56 return NULL;
57 }
58
Michal Vaskoade892d2017-02-22 13:40:35 +010059 /* WRITE LOCK */
60 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010061
62 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020063 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010064 endpt = &server_opts.endpts[i];
65 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010066 }
67 }
68
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010069 if (!endpt) {
70 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010071 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020072 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010073 return NULL;
74 }
75
Michal Vaskoe2713da2016-08-22 16:06:40 +020076 if (idx) {
77 *idx = i;
78 }
79
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010080 return endpt;
81}
82
Michal Vasko2e6defd2016-10-07 15:48:15 +020083struct nc_ch_client *
84nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010085{
Michal Vasko2e6defd2016-10-07 15:48:15 +020086 uint16_t i;
87 struct nc_ch_client *client = NULL;
88
Michal Vaskoddce1212019-05-24 09:58:49 +020089 if (!name) {
90 ERRARG("client_name");
91 return NULL;
92 }
93
Michal Vasko2e6defd2016-10-07 15:48:15 +020094 /* READ LOCK */
95 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
96
97 for (i = 0; i < server_opts.ch_client_count; ++i) {
98 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
99 client = &server_opts.ch_clients[i];
100 break;
101 }
102 }
103
104 if (!client) {
105 ERR("Call Home client \"%s\" was not found.", name);
106 /* READ UNLOCK */
107 pthread_rwlock_unlock(&server_opts.ch_client_lock);
108 return NULL;
109 }
110
111 /* CH CLIENT LOCK */
112 pthread_mutex_lock(&client->lock);
113
114 if (idx) {
115 *idx = i;
116 }
117
118 return client;
119}
120
121void
122nc_server_ch_client_unlock(struct nc_ch_client *client)
123{
124 /* CH CLIENT UNLOCK */
125 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100126
127 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200128 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100129}
Michal Vasko086311b2016-01-08 09:53:11 +0100130
Michal Vasko1a38c862016-01-15 15:50:07 +0100131API void
132nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
133{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200134 if (!session) {
135 ERRARG("session");
136 return;
137 } else if (!reason) {
138 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100139 return;
140 }
141
Michal Vasko142cfea2017-08-07 10:12:11 +0200142 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
143 session->killed_by = 0;
144 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100145 session->term_reason = reason;
146}
147
Michal Vasko142cfea2017-08-07 10:12:11 +0200148API void
149nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
150{
151 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
152 ERRARG("session");
153 return;
154 } else if (!sid) {
155 ERRARG("sid");
156 return;
157 }
158
159 session->killed_by = sid;
160}
161
162API void
163nc_session_set_status(struct nc_session *session, NC_STATUS status)
164{
165 if (!session) {
166 ERRARG("session");
167 return;
168 } else if (!status) {
169 ERRARG("status");
170 return;
171 }
172
173 session->status = status;
174}
175
Michal Vasko086311b2016-01-08 09:53:11 +0100176int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200177nc_sock_listen_inet(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100178{
Michal Vasko06c860d2018-07-09 16:08:52 +0200179 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100180 int is_ipv4, sock;
181 struct sockaddr_storage saddr;
182
183 struct sockaddr_in *saddr4;
184 struct sockaddr_in6 *saddr6;
185
186
187 if (!strchr(address, ':')) {
188 is_ipv4 = 1;
189 } else {
190 is_ipv4 = 0;
191 }
192
193 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
194 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100195 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100196 goto fail;
197 }
198
Michal Vaskobe52dc22018-10-17 09:28:17 +0200199 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200200 opt = 1;
201 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
202 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
203 goto fail;
204 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100205 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
206 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
207 goto fail;
208 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200209
210 if (nc_sock_enable_keepalive(sock)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100211 goto fail;
212 }
213
214 bzero(&saddr, sizeof(struct sockaddr_storage));
215 if (is_ipv4) {
216 saddr4 = (struct sockaddr_in *)&saddr;
217
218 saddr4->sin_family = AF_INET;
219 saddr4->sin_port = htons(port);
220
221 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100222 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100223 goto fail;
224 }
225
226 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100227 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100228 goto fail;
229 }
230
231 } else {
232 saddr6 = (struct sockaddr_in6 *)&saddr;
233
234 saddr6->sin6_family = AF_INET6;
235 saddr6->sin6_port = htons(port);
236
237 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100238 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100239 goto fail;
240 }
241
242 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100243 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100244 goto fail;
245 }
246 }
247
Michal Vaskofb89d772016-01-08 12:25:35 +0100248 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100249 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100250 goto fail;
251 }
252
253 return sock;
254
255fail:
256 if (sock > -1) {
257 close(sock);
258 }
259
260 return -1;
261}
262
263int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200264nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
265{
266 struct sockaddr_un sun;
267 int sock = -1;
268
269 sock = socket(AF_UNIX, SOCK_STREAM, 0);
270 if (sock == -1) {
271 ERR("Failed to create socket (%s).", strerror(errno));
272 goto fail;
273 }
274
275 memset(&sun, 0, sizeof(sun));
276 sun.sun_family = AF_UNIX;
277 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
278
279 unlink(sun.sun_path);
280 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
281 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
282 goto fail;
283 }
284
285 if (opts->mode != (mode_t)-1) {
286 if (chmod(sun.sun_path, opts->mode) < 0) {
287 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
288 goto fail;
289 }
290 }
291
292 if (opts->uid != (uid_t)-1 || opts->gid != (gid_t)-1) {
293 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
294 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
295 goto fail;
296 }
297 }
298
299 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
300 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
301 goto fail;
302 }
303
304 return sock;
305
306fail:
307 if (sock > -1) {
308 close(sock);
309 }
310
311 return -1;
312}
313
314int
Michal Vasko3031aae2016-01-27 16:07:18 +0100315nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100316{
Michal Vaskof54cd352017-02-22 13:42:02 +0100317 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100318 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100319 struct pollfd *pfd;
320 struct sockaddr_storage saddr;
321 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100322 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100323
324 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100325 if (!pfd) {
326 ERRMEM;
327 return -1;
328 }
329
Michal Vaskoac2f6182017-01-30 14:32:03 +0100330 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200331 if (binds[i].sock < 0) {
332 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200333 continue;
334 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200335 if (binds[i].pollin) {
336 binds[i].pollin = 0;
337 /* leftover pollin */
338 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100339 break;
340 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100341 pfd[pfd_count].fd = binds[i].sock;
342 pfd[pfd_count].events = POLLIN;
343 pfd[pfd_count].revents = 0;
344
345 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100346 }
347
Michal Vasko0a3f3752016-10-13 14:58:38 +0200348 if (sock == -1) {
349 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100350 sigfillset(&sigmask);
351 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100352 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100353 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
354
Michal Vasko0a3f3752016-10-13 14:58:38 +0200355 if (!ret) {
356 /* we timeouted */
357 free(pfd);
358 return 0;
359 } else if (ret == -1) {
360 ERR("Poll failed (%s).", strerror(errno));
361 free(pfd);
362 return -1;
363 }
Michal Vasko086311b2016-01-08 09:53:11 +0100364
Michal Vaskoac2f6182017-01-30 14:32:03 +0100365 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
366 /* adjust i so that indices in binds and pfd always match */
367 while (binds[i].sock != pfd[j].fd) {
368 ++i;
369 }
370
371 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200372 --ret;
373
374 if (!ret) {
375 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100376 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200377 break;
378 } else {
379 /* just remember the event for next time */
380 binds[i].pollin = 1;
381 }
382 }
Michal Vasko086311b2016-01-08 09:53:11 +0100383 }
384 }
385 free(pfd);
386
387 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100388 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100389 return -1;
390 }
391
392 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100393 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100394 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100395 return -1;
396 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200397 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100398
Michal Vasko0190bc32016-03-02 15:47:49 +0100399 /* make the socket non-blocking */
400 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
401 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100402 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100403 return -1;
404 }
405
Michal Vasko3031aae2016-01-27 16:07:18 +0100406 if (idx) {
407 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100408 }
409
Michal Vasko086311b2016-01-08 09:53:11 +0100410 /* host was requested */
411 if (host) {
412 if (saddr.ss_family == AF_INET) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000413 *host = malloc(INET_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100414 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000415 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100416 ERR("inet_ntop failed (%s).", strerror(errno));
417 free(*host);
418 *host = NULL;
419 }
Michal Vasko086311b2016-01-08 09:53:11 +0100420
Michal Vasko4eb3c312016-03-01 14:09:37 +0100421 if (port) {
422 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
423 }
424 } else {
425 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100426 }
427 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100428 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100429 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000430 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100431 ERR("inet_ntop failed (%s).", strerror(errno));
432 free(*host);
433 *host = NULL;
434 }
Michal Vasko086311b2016-01-08 09:53:11 +0100435
Michal Vasko4eb3c312016-03-01 14:09:37 +0100436 if (port) {
437 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
438 }
439 } else {
440 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100441 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200442 } else if (saddr.ss_family == AF_UNIX) {
443 *host = strdup(((struct sockaddr_un *)&saddr)->sun_path);
444 if (*host) {
445 if (port) {
446 *port = 0;
447 }
448 } else {
449 ERRMEM;
450 }
Michal Vasko086311b2016-01-08 09:53:11 +0100451 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100452 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100453 }
454 }
455
456 return ret;
457}
458
Michal Vasko05ba9df2016-01-13 14:40:27 +0100459static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100460nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100461{
462 const char *identifier = NULL, *version = NULL, *format = NULL;
463 char *model_data = NULL;
464 const struct lys_module *module;
465 struct nc_server_error *err;
466 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200467 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100468
469 LY_TREE_FOR(rpc->child, child) {
470 if (!strcmp(child->schema->name, "identifier")) {
471 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
472 } else if (!strcmp(child->schema->name, "version")) {
473 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500474 if (version && version[0] == '\0') {
475 version = NULL;
476 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100477 } else if (!strcmp(child->schema->name, "format")) {
478 format = ((struct lyd_node_leaf_list *)child)->value_str;
479 }
480 }
Michal Vaskoddce1212019-05-24 09:58:49 +0200481 VRB("Schema \"%s\" was requested.", identifier);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100482
483 /* check version */
484 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100485 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
486 nc_err_set_msg(err, "The requested version is not supported.", "en");
487 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100488 }
489
490 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200491 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100492 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200493 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
494 }
495 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100496 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
497 nc_err_set_msg(err, "The requested schema was not found.", "en");
498 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100499 }
500
501 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100502 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100503 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100504 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100505 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100506 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100507 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
508 nc_err_set_msg(err, "The requested format is not supported.", "en");
509 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100510 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200511 if (!model_data) {
512 ERRINT;
513 return NULL;
514 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100515
Michal Vasko88639e92017-08-03 14:38:10 +0200516 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
517 if (!sdata) {
518 ERRINT;
519 free(model_data);
520 return NULL;
521 }
522
Radek Krejci539efb62016-08-24 15:05:16 +0200523 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
524 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100525 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100526 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200527 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100528 return NULL;
529 }
530
Radek Krejci36dfdb32016-09-01 16:56:35 +0200531 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100532}
533
534static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100535nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100536{
Michal Vasko428087d2016-01-14 16:04:28 +0100537 session->term_reason = NC_SESSION_TERM_CLOSED;
538 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100539}
540
Michal Vasko086311b2016-01-08 09:53:11 +0100541API int
542nc_server_init(struct ly_ctx *ctx)
543{
Michal Vasko88639e92017-08-03 14:38:10 +0200544 const struct lys_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000545 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100546
Michal Vasko086311b2016-01-08 09:53:11 +0100547 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200548 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100549 return -1;
550 }
551
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100552 nc_init();
553
Michal Vasko05ba9df2016-01-13 14:40:27 +0100554 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200555 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
556 if (rpc && !rpc->priv) {
557 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100558 }
559
560 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200561 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
562 if (rpc && !rpc->priv) {
563 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100564 }
565
Michal Vasko086311b2016-01-08 09:53:11 +0100566 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100567
568 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500569 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100570
Frank Rimpler9f838b02018-07-25 06:44:03 +0000571 errno=0;
572
573 if (pthread_rwlockattr_init(&attr) == 0) {
574 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
575 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
576 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
577 }
578 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
579 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
580 }
581 } else {
582 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
583 }
584 pthread_rwlockattr_destroy(&attr);
585 } else {
586 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
587 }
Michal Vasko086311b2016-01-08 09:53:11 +0100588 return 0;
589}
590
Michal Vaskob48aa812016-01-18 14:13:09 +0100591API void
592nc_server_destroy(void)
593{
Radek Krejci658782b2016-12-04 22:04:55 +0100594 unsigned int i;
595
596 for (i = 0; i < server_opts.capabilities_count; i++) {
597 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
598 }
599 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200600 server_opts.capabilities = NULL;
601 server_opts.capabilities_count = 0;
602
Radek Krejci53691be2016-02-22 13:58:37 +0100603#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100604 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100605#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100606#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100607 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
608 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
609 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200610 server_opts.passwd_auth_data = NULL;
611 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100612
Michal Vasko17dfda92016-12-01 14:06:16 +0100613 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100614
615 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
616 server_opts.hostkey_data_free(server_opts.hostkey_data);
617 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200618 server_opts.hostkey_data = NULL;
619 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100620#endif
621#ifdef NC_ENABLED_TLS
622 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
623 server_opts.server_cert_data_free(server_opts.server_cert_data);
624 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200625 server_opts.server_cert_data = NULL;
626 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100627 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
628 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
629 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200630 server_opts.trusted_cert_list_data = NULL;
631 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100632#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100633 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100634}
635
Michal Vasko086311b2016-01-08 09:53:11 +0100636API int
637nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
638{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200639 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
640 ERRARG("basic_mode");
641 return -1;
642 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
643 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100644 return -1;
645 }
646
647 server_opts.wd_basic_mode = basic_mode;
648 server_opts.wd_also_supported = also_supported;
649 return 0;
650}
651
Michal Vasko1a38c862016-01-15 15:50:07 +0100652API void
Michal Vasko55f03972016-04-13 08:56:01 +0200653nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
654{
655 if (!basic_mode && !also_supported) {
656 ERRARG("basic_mode and also_supported");
657 return;
658 }
659
660 if (basic_mode) {
661 *basic_mode = server_opts.wd_basic_mode;
662 }
663 if (also_supported) {
664 *also_supported = server_opts.wd_also_supported;
665 }
666}
667
Michal Vasko55f03972016-04-13 08:56:01 +0200668API int
Radek Krejci658782b2016-12-04 22:04:55 +0100669nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200670{
Radek Krejci658782b2016-12-04 22:04:55 +0100671 const char **new;
672
673 if (!value || !value[0]) {
674 ERRARG("value must not be empty");
675 return EXIT_FAILURE;
676 }
677
678 server_opts.capabilities_count++;
679 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
680 if (!new) {
681 ERRMEM;
682 return EXIT_FAILURE;
683 }
684 server_opts.capabilities = new;
685 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
686
687 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200688}
689
Michal Vasko1a38c862016-01-15 15:50:07 +0100690API void
Michal Vasko086311b2016-01-08 09:53:11 +0100691nc_server_set_hello_timeout(uint16_t hello_timeout)
692{
Michal Vasko086311b2016-01-08 09:53:11 +0100693 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100694}
695
Michal Vasko55f03972016-04-13 08:56:01 +0200696API uint16_t
697nc_server_get_hello_timeout(void)
698{
699 return server_opts.hello_timeout;
700}
701
Michal Vasko1a38c862016-01-15 15:50:07 +0100702API void
Michal Vasko086311b2016-01-08 09:53:11 +0100703nc_server_set_idle_timeout(uint16_t idle_timeout)
704{
Michal Vasko086311b2016-01-08 09:53:11 +0100705 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100706}
707
Michal Vasko55f03972016-04-13 08:56:01 +0200708API uint16_t
709nc_server_get_idle_timeout(void)
710{
711 return server_opts.idle_timeout;
712}
713
Michal Vasko71090fc2016-05-24 16:37:28 +0200714API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100715nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100716{
Michal Vasko71090fc2016-05-24 16:37:28 +0200717 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200718 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200719
Michal Vasko45e53ae2016-04-07 11:46:03 +0200720 if (!server_opts.ctx) {
721 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200722 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200723 } else if (fdin < 0) {
724 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200725 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200726 } else if (fdout < 0) {
727 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200728 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200729 } else if (!username) {
730 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200731 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200732 } else if (!session) {
733 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200734 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100735 }
736
737 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200738 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100739 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100740 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200741 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100742 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100743 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100744
Michal Vasko086311b2016-01-08 09:53:11 +0100745 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100746 (*session)->ti_type = NC_TI_FD;
747 (*session)->ti.fd.in = fdin;
748 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100749
750 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100751 (*session)->flags = NC_SESSION_SHAREDCTX;
752 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100753
Michal Vaskob48aa812016-01-18 14:13:09 +0100754 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +0100755 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100756
Michal Vasko086311b2016-01-08 09:53:11 +0100757 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200758 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200759 if (msgtype != NC_MSG_HELLO) {
760 nc_session_free(*session, NULL);
761 *session = NULL;
762 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100763 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200764
765 nc_gettimespec_mono(&ts_cur);
766 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
767 nc_gettimespec_real(&ts_cur);
768 (*session)->opts.server.session_start = ts_cur.tv_sec;
769
Michal Vasko1a38c862016-01-15 15:50:07 +0100770 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100771
Michal Vasko71090fc2016-05-24 16:37:28 +0200772 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100773}
Michal Vasko9e036d52016-01-08 10:49:26 +0100774
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200775static int
776nc_accept_unix(struct nc_session *session, int sock)
777{
778 const struct passwd *pw;
779 struct ucred ucred;
780 char *username;
781 socklen_t len;
782
783 session->ti_type = NC_TI_UNIX;
784
785 len = sizeof(ucred);
786 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
787 ERR("Failed to get credentials from unix socket (%s).",
788 strerror(errno));
789 close(sock);
790 return -1;
791 }
792
793 pw = getpwuid(ucred.uid);
794 if (pw == NULL) {
795 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
796 strerror(errno));
797 close(sock);
798 return -1;
799 }
800
801 username = strdup(pw->pw_name);
802 if (username == NULL) {
803 ERRMEM;
804 close(sock);
805 return -1;
806 }
807 session->username = lydict_insert_zc(server_opts.ctx, username);
808
809 session->ti.unixsock.sock = sock;
810
811 return 1;
812}
813
Michal Vaskob30b99c2016-07-26 11:35:43 +0200814static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100815nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
816{
817 uint8_t q_last;
818
819 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
820 ERRINT;
821 return;
822 }
823
824 /* get a unique queue value (by adding 1 to the last added value, if any) */
825 if (ps->queue_len) {
826 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
827 *id = ps->queue[q_last] + 1;
828 } else {
829 *id = 0;
830 }
831
832 /* add the id into the queue */
833 ++ps->queue_len;
834 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
835 ps->queue[q_last] = *id;
836}
837
838static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200839nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
840{
Michal Vasko74c345f2018-02-07 10:37:11 +0100841 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200842
843 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100844 /* get the actual queue idx */
845 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200846
847 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100848 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200849 /* another equal value, simply cannot be */
850 ERRINT;
851 }
Michal Vaskod8340032018-02-12 14:41:00 +0100852 if (found == 2) {
853 /* move the following values */
854 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
855 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100856 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200857 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100858 if (i == 0) {
859 found = 1;
860 } else {
861 /* this is not okay, our id is in the middle of the queue */
862 found = 2;
863 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200864 }
865 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200866 if (!found) {
867 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100868 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200869 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100870
Michal Vasko103fe632018-02-12 16:37:45 +0100871 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100872 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100873 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100874 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
875 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200876}
877
Michal Vaskof04a52a2016-04-07 10:52:10 +0200878int
Michal Vasko26043172016-07-26 14:08:59 +0200879nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200880{
881 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200882 struct timespec ts;
883
Michal Vasko77a6abe2017-10-05 10:02:20 +0200884 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100885 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200886
887 /* LOCK */
888 ret = pthread_mutex_timedlock(&ps->lock, &ts);
889 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200890 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200891 return -1;
892 }
893
Michal Vasko74c345f2018-02-07 10:37:11 +0100894 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100895 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100896 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100897 pthread_mutex_unlock(&ps->lock);
898 return -1;
899 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100900
901 /* add ourselves into the queue */
902 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200903
904 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200905 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200906 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100907 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200908
909 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
910 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530911 /**
912 * This may happen when another thread releases the lock and broadcasts the condition
913 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
914 * but when actually this thread was ready for condition.
915 */
preetbhansali629dfc42018-12-17 16:04:40 +0530916 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530917 break;
918 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100919
Michal Vasko26043172016-07-26 14:08:59 +0200920 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200921 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200922 nc_ps_queue_remove_id(ps, *id);
923 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200924 return -1;
925 }
926 }
927
Michal Vaskobe86fe32016-04-07 10:43:03 +0200928 /* UNLOCK */
929 pthread_mutex_unlock(&ps->lock);
930
931 return 0;
932}
933
Michal Vaskof04a52a2016-04-07 10:52:10 +0200934int
Michal Vasko26043172016-07-26 14:08:59 +0200935nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200936{
937 int ret;
938 struct timespec ts;
939
Michal Vasko77a6abe2017-10-05 10:02:20 +0200940 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100941 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200942
943 /* LOCK */
944 ret = pthread_mutex_timedlock(&ps->lock, &ts);
945 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200946 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200947 ret = -1;
948 }
949
Michal Vaskob30b99c2016-07-26 11:35:43 +0200950 /* we must be the first, it was our turn after all, right? */
951 if (ps->queue[ps->queue_begin] != id) {
952 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200953 /* UNLOCK */
954 if (!ret) {
955 pthread_mutex_unlock(&ps->lock);
956 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200957 return -1;
958 }
959
Michal Vaskobe86fe32016-04-07 10:43:03 +0200960 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200961 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200962
963 /* broadcast to all other threads that the queue moved */
964 pthread_cond_broadcast(&ps->cond);
965
Michal Vaskobe86fe32016-04-07 10:43:03 +0200966 /* UNLOCK */
967 if (!ret) {
968 pthread_mutex_unlock(&ps->lock);
969 }
970
971 return ret;
972}
973
Michal Vasko428087d2016-01-14 16:04:28 +0100974API struct nc_pollsession *
975nc_ps_new(void)
976{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100977 struct nc_pollsession *ps;
978
979 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100980 if (!ps) {
981 ERRMEM;
982 return NULL;
983 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200984 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100985 pthread_mutex_init(&ps->lock, NULL);
986
987 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100988}
989
990API void
991nc_ps_free(struct nc_pollsession *ps)
992{
fanchanghu3d4e7212017-08-09 09:42:30 +0800993 uint16_t i;
994
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100995 if (!ps) {
996 return;
997 }
998
Michal Vaskobe86fe32016-04-07 10:43:03 +0200999 if (ps->queue_len) {
1000 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
1001 }
1002
fanchanghu3d4e7212017-08-09 09:42:30 +08001003 for (i = 0; i < ps->session_count; i++) {
1004 free(ps->sessions[i]);
1005 }
1006
Michal Vasko428087d2016-01-14 16:04:28 +01001007 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001008 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001009 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001010
Michal Vasko428087d2016-01-14 16:04:28 +01001011 free(ps);
1012}
1013
1014API int
1015nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1016{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001017 uint8_t q_id;
1018
Michal Vasko45e53ae2016-04-07 11:46:03 +02001019 if (!ps) {
1020 ERRARG("ps");
1021 return -1;
1022 } else if (!session) {
1023 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001024 return -1;
1025 }
1026
Michal Vasko48a63ed2016-03-01 09:48:21 +01001027 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001028 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001029 return -1;
1030 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001031
Michal Vasko428087d2016-01-14 16:04:28 +01001032 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001033 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001034 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001035 ERRMEM;
1036 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001037 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001038 return -1;
1039 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001040 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1041 if (!ps->sessions[ps->session_count - 1]) {
1042 ERRMEM;
1043 --ps->session_count;
1044 /* UNLOCK */
1045 nc_ps_unlock(ps, q_id, __func__);
1046 return -1;
1047 }
1048 ps->sessions[ps->session_count - 1]->session = session;
1049 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001050
Michal Vasko48a63ed2016-03-01 09:48:21 +01001051 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001052 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001053}
1054
Michal Vasko48a63ed2016-03-01 09:48:21 +01001055static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001056_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001057{
1058 uint16_t i;
1059
Radek Krejcid5f978f2016-03-03 13:14:45 +01001060 if (index >= 0) {
1061 i = (uint16_t)index;
1062 goto remove;
1063 }
Michal Vasko428087d2016-01-14 16:04:28 +01001064 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001065 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001066remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001067 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001068 if (i <= ps->session_count) {
1069 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001070 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001071 }
1072 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001073 free(ps->sessions);
1074 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001075 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001076 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001077 return 0;
1078 }
1079 }
1080
Michal Vaskof0537d82016-01-29 14:42:38 +01001081 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001082}
1083
Michal Vasko48a63ed2016-03-01 09:48:21 +01001084API int
1085nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1086{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001087 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001088 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001089
Michal Vasko45e53ae2016-04-07 11:46:03 +02001090 if (!ps) {
1091 ERRARG("ps");
1092 return -1;
1093 } else if (!session) {
1094 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001095 return -1;
1096 }
1097
1098 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001099 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001100 return -1;
1101 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001102
Radek Krejcid5f978f2016-03-03 13:14:45 +01001103 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001104
1105 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001106 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001107
Michal Vaskobe86fe32016-04-07 10:43:03 +02001108 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001109}
1110
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001111API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001112nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001113{
1114 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001115 struct nc_session *ret = NULL;
1116
1117 if (!ps) {
1118 ERRARG("ps");
1119 return NULL;
1120 }
1121
1122 /* LOCK */
1123 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1124 return NULL;
1125 }
1126
Michal Vasko4871c9d2017-10-09 14:48:39 +02001127 if (idx < ps->session_count) {
1128 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001129 }
1130
1131 /* UNLOCK */
1132 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1133
1134 return ret;
1135}
1136
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001137API uint16_t
1138nc_ps_session_count(struct nc_pollsession *ps)
1139{
Michal Vasko47003942019-03-14 12:25:23 +01001140 uint8_t q_id;
1141 uint16_t session_count;
1142
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001143 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001144 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001145 return 0;
1146 }
1147
Michal Vasko47003942019-03-14 12:25:23 +01001148 /* LOCK (just for memory barrier so that we read the current value) */
1149 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1150 return 0;
1151 }
1152
1153 session_count = ps->session_count;
1154
1155 /* UNLOCK */
1156 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1157
1158 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001159}
1160
Michal Vasko131120a2018-05-29 15:44:02 +02001161/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001162 * returns: NC_PSPOLL_ERROR,
1163 * NC_PSPOLL_BAD_RPC,
1164 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1165 * NC_PSPOLL_RPC
1166 */
1167static int
Michal Vasko131120a2018-05-29 15:44:02 +02001168nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001169{
1170 struct lyxml_elem *xml = NULL;
1171 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001172 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001173 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001174
Michal Vasko45e53ae2016-04-07 11:46:03 +02001175 if (!session) {
1176 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001177 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001178 } else if (!rpc) {
1179 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001180 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001181 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001182 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001183 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001184 }
1185
Michal Vasko131120a2018-05-29 15:44:02 +02001186 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001187
1188 switch (msgtype) {
1189 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001190 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001191 if (!*rpc) {
1192 ERRMEM;
1193 goto error;
1194 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001195
Radek Krejcif93c7d42016-04-06 13:41:15 +02001196 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001197 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1198 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001199 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001200 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001201 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001202 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001203 nc_server_reply_free(reply);
1204 if (ret == -1) {
1205 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001206 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001207 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1208 } else {
1209 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001210 }
Michal Vasko428087d2016-01-14 16:04:28 +01001211 (*rpc)->root = xml;
1212 break;
1213 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001214 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001215 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001216 goto error;
1217 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001218 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001219 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001220 goto error;
1221 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001222 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001223 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001224 goto error;
1225 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001226 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001227 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001228 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001229 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001230 break;
1231 }
1232
Michal Vasko71090fc2016-05-24 16:37:28 +02001233 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001234
1235error:
1236 /* cleanup */
1237 lyxml_free(server_opts.ctx, xml);
1238
Michal Vasko71090fc2016-05-24 16:37:28 +02001239 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001240}
1241
fanchanghu966f2de2016-07-21 02:28:57 -04001242API void
1243nc_set_global_rpc_clb(nc_rpc_clb clb)
1244{
1245 global_rpc_clb = clb;
1246}
1247
Radek Krejci93e80222016-10-03 13:34:25 +02001248API NC_MSG_TYPE
1249nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1250{
Michal Vasko131120a2018-05-29 15:44:02 +02001251 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001252
1253 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001254 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001255 ERRARG("session");
1256 return NC_MSG_ERROR;
1257 } else if (!notif || !notif->tree || !notif->eventtime) {
1258 ERRARG("notif");
1259 return NC_MSG_ERROR;
1260 }
1261
Michal Vasko131120a2018-05-29 15:44:02 +02001262 /* we do not need RPC lock for this, IO lock will be acquired properly */
1263 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1264 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001265 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001266 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001267
Michal Vasko131120a2018-05-29 15:44:02 +02001268 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001269}
1270
Michal Vasko131120a2018-05-29 15:44:02 +02001271/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001272 * returns: NC_PSPOLL_ERROR,
1273 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1274 * NC_PSPOLL_REPLY_ERROR,
1275 * 0
1276 */
1277static int
Michal Vasko131120a2018-05-29 15:44:02 +02001278nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001279{
1280 nc_rpc_clb clb;
1281 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001282 struct lys_node *rpc_act = NULL;
1283 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001284 int ret = 0;
1285 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001286
Michal Vasko4a827e52016-03-03 10:59:00 +01001287 if (!rpc) {
1288 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001289 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001290 }
1291
Michal Vasko90e8e692016-07-13 12:27:57 +02001292 if (rpc->tree->schema->nodetype == LYS_RPC) {
1293 /* RPC */
1294 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001295 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001296 /* action */
1297 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1298 if (elem->schema->nodetype == LYS_ACTION) {
1299 rpc_act = elem->schema;
1300 break;
1301 }
1302 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001303 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001304 if (!rpc_act) {
1305 ERRINT;
1306 return NC_PSPOLL_ERROR;
1307 }
1308 }
1309
1310 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001311 if (!global_rpc_clb) {
1312 /* no callback, reply with a not-implemented error */
1313 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1314 } else {
1315 reply = global_rpc_clb(rpc->tree, session);
1316 }
Michal Vasko428087d2016-01-14 16:04:28 +01001317 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001318 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001319 reply = clb(rpc->tree, session);
1320 }
1321
1322 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001323 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001324 }
Michal Vasko131120a2018-05-29 15:44:02 +02001325 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001326 if (reply->type == NC_RPL_ERROR) {
1327 ret |= NC_PSPOLL_REPLY_ERROR;
1328 }
1329 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001330
Michal Vasko131120a2018-05-29 15:44:02 +02001331 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001332 ERR("Session %u: failed to write reply.", session->id);
1333 ret |= NC_PSPOLL_ERROR;
1334 }
Michal Vasko428087d2016-01-14 16:04:28 +01001335
1336 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1337 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1338 session->status = NC_STATUS_INVALID;
1339 }
1340
Michal Vasko71090fc2016-05-24 16:37:28 +02001341 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001342}
1343
Michal Vasko131120a2018-05-29 15:44:02 +02001344/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001345 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1346 * NC_PSPOLL_ERROR, (msg filled)
1347 * NC_PSPOLL_TIMEOUT,
1348 * NC_PSPOLL_RPC (some application data available),
1349 * NC_PSPOLL_SSH_CHANNEL,
1350 * NC_PSPOLL_SSH_MSG
1351 */
1352static int
Michal Vasko131120a2018-05-29 15:44:02 +02001353nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001354{
Michal Vasko9a327362017-01-11 11:31:46 +01001355 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001356 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001357#ifdef NC_ENABLED_SSH
1358 struct nc_session *new;
1359#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001360
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001361 /* check timeout first */
1362 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001363 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001364 sprintf(msg, "session idle timeout elapsed");
1365 session->status = NC_STATUS_INVALID;
1366 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1367 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1368 }
1369
Michal Vasko131120a2018-05-29 15:44:02 +02001370 r = nc_session_io_lock(session, io_timeout, __func__);
1371 if (r < 0) {
1372 sprintf(msg, "session IO lock failed to be acquired");
1373 return NC_PSPOLL_ERROR;
1374 } else if (!r) {
1375 return NC_PSPOLL_TIMEOUT;
1376 }
1377
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001378 switch (session->ti_type) {
1379#ifdef NC_ENABLED_SSH
1380 case NC_TI_LIBSSH:
1381 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001382 if (r == SSH_EOF) {
1383 sprintf(msg, "SSH channel unexpected EOF");
1384 session->status = NC_STATUS_INVALID;
1385 session->term_reason = NC_SESSION_TERM_DROPPED;
1386 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1387 } else if (r == SSH_ERROR) {
1388 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001389 session->status = NC_STATUS_INVALID;
1390 session->term_reason = NC_SESSION_TERM_OTHER;
1391 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001392 } else if (!r) {
1393 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1394 /* new SSH message */
1395 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1396 if (session->ti.libssh.next) {
1397 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1398 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1399 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1400 /* new NETCONF SSH channel */
1401 ret = NC_PSPOLL_SSH_CHANNEL;
1402 break;
1403 }
1404 }
1405 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001406 break;
1407 }
1408 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001409
Michal Vasko8dcaa882017-10-19 14:28:42 +02001410 /* just some SSH message */
1411 ret = NC_PSPOLL_SSH_MSG;
1412 } else {
1413 ret = NC_PSPOLL_TIMEOUT;
1414 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001415 } else {
1416 /* we have some application data */
1417 ret = NC_PSPOLL_RPC;
1418 }
1419 break;
1420#endif
1421#ifdef NC_ENABLED_TLS
1422 case NC_TI_OPENSSL:
1423 r = SSL_pending(session->ti.tls);
1424 if (!r) {
1425 /* no data pending in the SSL buffer, poll fd */
1426 pfd.fd = SSL_get_rfd(session->ti.tls);
1427 if (pfd.fd < 0) {
1428 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1429 ret = NC_PSPOLL_ERROR;
1430 break;
1431 }
1432 pfd.events = POLLIN;
1433 pfd.revents = 0;
1434 r = poll(&pfd, 1, 0);
1435
1436 if ((r < 0) && (errno != EINTR)) {
1437 sprintf(msg, "poll failed (%s)", strerror(errno));
1438 session->status = NC_STATUS_INVALID;
1439 ret = NC_PSPOLL_ERROR;
1440 } else if (r > 0) {
1441 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1442 sprintf(msg, "communication socket unexpectedly closed");
1443 session->status = NC_STATUS_INVALID;
1444 session->term_reason = NC_SESSION_TERM_DROPPED;
1445 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1446 } else if (pfd.revents & POLLERR) {
1447 sprintf(msg, "communication socket error");
1448 session->status = NC_STATUS_INVALID;
1449 session->term_reason = NC_SESSION_TERM_OTHER;
1450 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1451 } else {
1452 ret = NC_PSPOLL_RPC;
1453 }
1454 } else {
1455 ret = NC_PSPOLL_TIMEOUT;
1456 }
1457 } else {
1458 ret = NC_PSPOLL_RPC;
1459 }
1460 break;
1461#endif
1462 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001463 case NC_TI_UNIX:
1464 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001465 pfd.events = POLLIN;
1466 pfd.revents = 0;
1467 r = poll(&pfd, 1, 0);
1468
1469 if ((r < 0) && (errno != EINTR)) {
1470 sprintf(msg, "poll failed (%s)", strerror(errno));
1471 session->status = NC_STATUS_INVALID;
1472 ret = NC_PSPOLL_ERROR;
1473 } else if (r > 0) {
1474 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1475 sprintf(msg, "communication socket unexpectedly closed");
1476 session->status = NC_STATUS_INVALID;
1477 session->term_reason = NC_SESSION_TERM_DROPPED;
1478 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1479 } else if (pfd.revents & POLLERR) {
1480 sprintf(msg, "communication socket error");
1481 session->status = NC_STATUS_INVALID;
1482 session->term_reason = NC_SESSION_TERM_OTHER;
1483 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1484 } else {
1485 ret = NC_PSPOLL_RPC;
1486 }
1487 } else {
1488 ret = NC_PSPOLL_TIMEOUT;
1489 }
1490 break;
1491 case NC_TI_NONE:
1492 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1493 ret = NC_PSPOLL_ERROR;
1494 break;
1495 }
1496
Michal Vasko131120a2018-05-29 15:44:02 +02001497 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001498 return ret;
1499}
1500
1501API int
1502nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1503{
1504 int ret, r;
1505 uint8_t q_id;
1506 uint16_t i, j;
1507 char msg[256];
1508 struct timespec ts_timeout, ts_cur;
1509 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001510 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001511 struct nc_server_rpc *rpc = NULL;
1512
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001513 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001514 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001515 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001516 }
1517
Michal Vaskoade892d2017-02-22 13:40:35 +01001518 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001519 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001520 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001521 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001522
Michal Vaskoade892d2017-02-22 13:40:35 +01001523 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001524 nc_ps_unlock(ps, q_id, __func__);
1525 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001526 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001527
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001528 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001529 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001530 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001531 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001532 nc_addtimespec(&ts_timeout, timeout);
1533 }
1534
1535 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001536 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001537 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001538 if (ps->last_event_session == ps->session_count - 1) {
1539 i = j = 0;
1540 } else {
1541 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001542 }
Michal Vasko9a327362017-01-11 11:31:46 +01001543 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001544 cur_ps_session = ps->sessions[i];
1545 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001546
Michal Vasko131120a2018-05-29 15:44:02 +02001547 /* SESSION RPC LOCK */
1548 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001549 if (r == -1) {
1550 ret = NC_PSPOLL_ERROR;
1551 } else if (r == 1) {
1552 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001553 switch (cur_ps_session->state) {
1554 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001555 if (cur_session->status == NC_STATUS_RUNNING) {
1556 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001557 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001558
Michal Vasko131120a2018-05-29 15:44:02 +02001559 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001560 switch (ret) {
1561 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1562 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001563 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001564 break;
1565 case NC_PSPOLL_ERROR:
1566 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001567 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001568 break;
1569 case NC_PSPOLL_TIMEOUT:
1570#ifdef NC_ENABLED_SSH
1571 case NC_PSPOLL_SSH_CHANNEL:
1572 case NC_PSPOLL_SSH_MSG:
1573#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001574 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001575 break;
1576 case NC_PSPOLL_RPC:
1577 /* let's keep the state busy, we are not done with this session */
1578 break;
1579 }
1580 } else {
1581 /* session is not fine, let the caller know */
1582 ret = NC_PSPOLL_SESSION_TERM;
1583 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1584 ret |= NC_PSPOLL_SESSION_ERROR;
1585 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001586 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001587 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001588 break;
1589 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001590 /* it definitely should not be busy because we have the lock */
1591 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001592 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001593 break;
1594 case NC_PS_STATE_INVALID:
1595 /* we got it locked, but it will be freed, let it be */
1596 ret = NC_PSPOLL_TIMEOUT;
1597 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001598 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001599
Michal Vasko131120a2018-05-29 15:44:02 +02001600 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001601 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001602 /* SESSION RPC UNLOCK */
1603 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001604 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001605 } else {
1606 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001607 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001608 }
Michal Vasko428087d2016-01-14 16:04:28 +01001609
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001610 /* something happened */
1611 if (ret != NC_PSPOLL_TIMEOUT) {
1612 break;
1613 }
1614
Michal Vasko9a327362017-01-11 11:31:46 +01001615 if (i == ps->session_count - 1) {
1616 i = 0;
1617 } else {
1618 ++i;
1619 }
1620 } while (i != j);
1621
Michal Vaskoade892d2017-02-22 13:40:35 +01001622 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001623 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001624 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001625 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001626 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001627
1628 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1629 /* final timeout */
1630 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001631 }
Michal Vasko428087d2016-01-14 16:04:28 +01001632 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001633 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001634
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001635 /* do we want to return the session? */
1636 switch (ret) {
1637 case NC_PSPOLL_RPC:
1638 case NC_PSPOLL_SESSION_TERM:
1639 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1640#ifdef NC_ENABLED_SSH
1641 case NC_PSPOLL_SSH_CHANNEL:
1642 case NC_PSPOLL_SSH_MSG:
1643#endif
1644 if (session) {
1645 *session = cur_session;
1646 }
1647 ps->last_event_session = i;
1648 break;
1649 default:
1650 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001651 }
Michal Vasko428087d2016-01-14 16:04:28 +01001652
Michal Vaskoade892d2017-02-22 13:40:35 +01001653 /* PS UNLOCK */
1654 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001655
Michal Vasko131120a2018-05-29 15:44:02 +02001656 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001657 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001658 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001659 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1660 if (cur_session->status != NC_STATUS_RUNNING) {
1661 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001662 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001663 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001664 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001665 }
1666 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001667 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001668
Michal Vasko7f1ee932018-10-11 09:41:42 +02001669 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001670 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001671 if (cur_session->status != NC_STATUS_RUNNING) {
1672 ret |= NC_PSPOLL_SESSION_TERM;
1673 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1674 ret |= NC_PSPOLL_SESSION_ERROR;
1675 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001676 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001677 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001678 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001679 }
Michal Vasko428087d2016-01-14 16:04:28 +01001680 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001681 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001682
Michal Vasko131120a2018-05-29 15:44:02 +02001683 /* SESSION RPC UNLOCK */
1684 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001685 }
1686
Michal Vasko48a63ed2016-03-01 09:48:21 +01001687 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001688}
1689
Michal Vaskod09eae62016-02-01 10:32:52 +01001690API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001691nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001692{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001693 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001694 uint16_t i;
1695 struct nc_session *session;
1696
Michal Vasko9a25e932016-02-01 10:36:42 +01001697 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001698 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001699 return;
1700 }
1701
Michal Vasko48a63ed2016-03-01 09:48:21 +01001702 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001703 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001704 return;
1705 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001706
Michal Vasko48a63ed2016-03-01 09:48:21 +01001707 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001708 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001709 nc_session_free(ps->sessions[i]->session, data_free);
1710 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001711 }
1712 free(ps->sessions);
1713 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001714 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001715 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001716 } else {
1717 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001718 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1719 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001720 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001721 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001722 continue;
1723 }
1724
1725 ++i;
1726 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001727 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001728
1729 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001730 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001731}
1732
Radek Krejci53691be2016-02-22 13:58:37 +01001733#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001734
Michal Vaskoe2713da2016-08-22 16:06:40 +02001735API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001736nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001737{
Michal Vasko3031aae2016-01-27 16:07:18 +01001738 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001739 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001740
Michal Vasko45e53ae2016-04-07 11:46:03 +02001741 if (!name) {
1742 ERRARG("name");
1743 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001744 }
1745
Michal Vaskoade892d2017-02-22 13:40:35 +01001746 /* BIND LOCK */
1747 pthread_mutex_lock(&server_opts.bind_lock);
1748
1749 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001750 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001751
1752 /* check name uniqueness */
1753 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001754 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001755 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001756 ret = -1;
1757 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001758 }
1759 }
1760
Michal Vasko3031aae2016-01-27 16:07:18 +01001761 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001762 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001763 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001764 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001765 ret = -1;
1766 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001767 }
1768 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001769 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001770
Michal Vaskoe2713da2016-08-22 16:06:40 +02001771 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001772 if (!server_opts.binds) {
1773 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001774 ret = -1;
1775 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001776 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001777
Michal Vasko2e6defd2016-10-07 15:48:15 +02001778 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1779 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1780 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001781 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001782
1783 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001784#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001785 case NC_TI_LIBSSH:
1786 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1787 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1788 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001789 ret = -1;
1790 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001791 }
1792 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1793 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1794 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1795 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1796 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001797#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001798#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001799 case NC_TI_OPENSSL:
1800 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1801 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1802 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001803 ret = -1;
1804 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001805 }
1806 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001807#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001808 case NC_TI_UNIX:
1809 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1810 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1811 ERRMEM;
1812 ret = -1;
1813 goto cleanup;
1814 }
1815 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1816 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1817 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1818 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001819 default:
1820 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001821 ret = -1;
1822 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001823 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001824
Michal Vaskoade892d2017-02-22 13:40:35 +01001825cleanup:
1826 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001827 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001828
Michal Vaskoade892d2017-02-22 13:40:35 +01001829 /* BIND UNLOCK */
1830 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001831
Michal Vaskoade892d2017-02-22 13:40:35 +01001832 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001833}
1834
Michal Vasko3031aae2016-01-27 16:07:18 +01001835int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001836nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001837{
1838 struct nc_endpt *endpt;
1839 struct nc_bind *bind = NULL;
1840 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001841 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001842
Michal Vasko45e53ae2016-04-07 11:46:03 +02001843 if (!endpt_name) {
1844 ERRARG("endpt_name");
1845 return -1;
1846 } else if ((!address && !port) || (address && port)) {
1847 ERRARG("address and port");
1848 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001849 }
1850
Michal Vaskoe2713da2016-08-22 16:06:40 +02001851 if (address) {
1852 set_addr = 1;
1853 } else {
1854 set_addr = 0;
1855 }
1856
Michal Vaskoade892d2017-02-22 13:40:35 +01001857 /* BIND LOCK */
1858 pthread_mutex_lock(&server_opts.bind_lock);
1859
1860 /* ENDPT LOCK */
1861 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001862 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001863 /* BIND UNLOCK */
1864 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001865 return -1;
1866 }
1867
Michal Vaskoe2713da2016-08-22 16:06:40 +02001868 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001869
Michal Vaskoe2713da2016-08-22 16:06:40 +02001870 if (set_addr) {
1871 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001872 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001873 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001874 }
1875
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001876 if (!set_addr && endpt->ti == NC_TI_UNIX) {
1877 ret = -1;
1878 goto cleanup;
1879 }
1880
Michal Vaskoe2713da2016-08-22 16:06:40 +02001881 /* we have all the information we need to create a listening socket */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001882 if (address && (port || endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001883 /* create new socket, close the old one */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001884 if (endpt->ti == NC_TI_UNIX)
1885 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
1886 else
1887 sock = nc_sock_listen_inet(address, port);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001888 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001889 ret = -1;
1890 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001891 }
1892
1893 if (bind->sock > -1) {
1894 close(bind->sock);
1895 }
1896 bind->sock = sock;
1897 } /* else we are just setting address or port */
1898
1899 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001900 lydict_remove(server_opts.ctx, bind->address);
1901 bind->address = lydict_insert(server_opts.ctx, address, 0);
1902 } else {
1903 bind->port = port;
1904 }
1905
Michal Vaskoe2713da2016-08-22 16:06:40 +02001906 if (sock > -1) {
1907#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001908 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001909#elif defined(NC_ENABLED_SSH)
1910 VRB("Listening on %s:%u for SSH connections.", address, port);
1911#else
1912 VRB("Listening on %s:%u for TLS connections.", address, port);
1913#endif
1914 }
1915
Michal Vasko4c1fb492017-01-30 14:31:07 +01001916cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001917 /* ENDPT UNLOCK */
1918 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001919
Michal Vaskoade892d2017-02-22 13:40:35 +01001920 /* BIND UNLOCK */
1921 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001922
Michal Vasko4c1fb492017-01-30 14:31:07 +01001923 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001924}
1925
Michal Vaskoe2713da2016-08-22 16:06:40 +02001926API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001927nc_server_endpt_set_address(const char *endpt_name, const char *address)
1928{
1929 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1930}
1931
1932API int
1933nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1934{
1935 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1936}
1937
1938API int
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001939nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
1940{
1941 struct nc_endpt *endpt;
1942 uint16_t i;
1943 int ret = 0;
1944
1945 if (!endpt_name) {
1946 ERRARG("endpt_name");
1947 return -1;
1948 } else if (mode == 0) {
1949 ERRARG("mode");
1950 return -1;
1951 }
1952
1953 /* ENDPT LOCK */
1954 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
1955 if (!endpt)
1956 return -1;
1957
1958 if (endpt->ti != NC_TI_UNIX) {
1959 ret = -1;
1960 goto cleanup;
1961 }
1962
1963 endpt->opts.unixsock->mode = mode;
1964 endpt->opts.unixsock->uid = uid;
1965 endpt->opts.unixsock->gid = gid;
1966
1967cleanup:
1968 /* ENDPT UNLOCK */
1969 pthread_rwlock_unlock(&server_opts.endpt_lock);
1970
1971 return ret;
1972}
1973
1974API int
Michal Vasko59050372016-11-22 14:33:55 +01001975nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001976{
1977 uint32_t i;
1978 int ret = -1;
1979
Michal Vaskoade892d2017-02-22 13:40:35 +01001980 /* BIND LOCK */
1981 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001982
Michal Vaskoade892d2017-02-22 13:40:35 +01001983 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001984 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001985
Michal Vasko59050372016-11-22 14:33:55 +01001986 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001987 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001988 for (i = 0; i < server_opts.endpt_count; ++i) {
1989 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001990 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001991#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001992 case NC_TI_LIBSSH:
1993 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1994 free(server_opts.endpts[i].opts.ssh);
1995 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001996#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001997#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001998 case NC_TI_OPENSSL:
1999 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2000 free(server_opts.endpts[i].opts.tls);
2001 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002002#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002003 case NC_TI_UNIX:
2004 free(server_opts.endpts[i].opts.unixsock);
2005 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002006 default:
2007 ERRINT;
2008 /* won't get here ...*/
2009 break;
2010 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002011 ret = 0;
2012 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002013 free(server_opts.endpts);
2014 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002015
2016 /* remove all binds */
2017 for (i = 0; i < server_opts.endpt_count; ++i) {
2018 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2019 if (server_opts.binds[i].sock > -1) {
2020 close(server_opts.binds[i].sock);
2021 }
2022 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002023 free(server_opts.binds);
2024 server_opts.binds = NULL;
2025
Michal Vasko3031aae2016-01-27 16:07:18 +01002026 server_opts.endpt_count = 0;
2027
Michal Vasko1a38c862016-01-15 15:50:07 +01002028 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002029 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002030 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002031 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002032 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002033 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002034 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002035#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002036 case NC_TI_LIBSSH:
2037 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2038 free(server_opts.endpts[i].opts.ssh);
2039 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002040#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002041#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002042 case NC_TI_OPENSSL:
2043 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2044 free(server_opts.endpts[i].opts.tls);
2045 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002046#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002047 case NC_TI_UNIX:
2048 free(server_opts.endpts[i].opts.unixsock);
2049 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002050 default:
2051 ERRINT;
2052 break;
2053 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002054
Michal Vaskoe2713da2016-08-22 16:06:40 +02002055 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002056 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2057 if (server_opts.binds[i].sock > -1) {
2058 close(server_opts.binds[i].sock);
2059 }
2060
2061 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002062 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002063 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002064 free(server_opts.binds);
2065 server_opts.binds = NULL;
2066 free(server_opts.endpts);
2067 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002068 } else if (i < server_opts.endpt_count) {
2069 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2070 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002071 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002072
2073 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002074 if (name) {
2075 break;
2076 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002077 }
2078 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002079 }
2080
Michal Vaskoade892d2017-02-22 13:40:35 +01002081 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002082 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002083
Michal Vaskoade892d2017-02-22 13:40:35 +01002084 /* BIND UNLOCK */
2085 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002086
2087 return ret;
2088}
2089
Michal Vasko71090fc2016-05-24 16:37:28 +02002090API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002091nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002092{
Michal Vasko71090fc2016-05-24 16:37:28 +02002093 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002094 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002095 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002096 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002097 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002098
Michal Vasko45e53ae2016-04-07 11:46:03 +02002099 if (!server_opts.ctx) {
2100 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002101 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002102 } else if (!session) {
2103 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002104 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002105 }
2106
Michal Vaskoade892d2017-02-22 13:40:35 +01002107 /* BIND LOCK */
2108 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002109
2110 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002111 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002112 /* BIND UNLOCK */
2113 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002114 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002115 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002116
Michal Vaskoe2713da2016-08-22 16:06:40 +02002117 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002118 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002119 /* BIND UNLOCK */
2120 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002121 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002122 if (!ret) {
2123 return NC_MSG_WOULDBLOCK;
2124 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002125 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002126 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002127
2128 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2129 /* ENDPT READ LOCK */
2130 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2131
2132 /* BIND UNLOCK */
2133 pthread_mutex_unlock(&server_opts.bind_lock);
2134
Michal Vaskob48aa812016-01-18 14:13:09 +01002135 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002136
Michal Vasko131120a2018-05-29 15:44:02 +02002137 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002138 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002139 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002140 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002141 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002142 msgtype = NC_MSG_ERROR;
2143 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002144 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002145 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002146 (*session)->ctx = server_opts.ctx;
2147 (*session)->flags = NC_SESSION_SHAREDCTX;
2148 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2149 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002150
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002151 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002152#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002153 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2154 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002155 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002156 if (ret < 0) {
2157 msgtype = NC_MSG_ERROR;
2158 goto cleanup;
2159 } else if (!ret) {
2160 msgtype = NC_MSG_WOULDBLOCK;
2161 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002162 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002163 } else
2164#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002165#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002166 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2167 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002168 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002169 if (ret < 0) {
2170 msgtype = NC_MSG_ERROR;
2171 goto cleanup;
2172 } else if (!ret) {
2173 msgtype = NC_MSG_WOULDBLOCK;
2174 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002175 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002176 } else
2177#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002178 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2179 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2180 ret = nc_accept_unix(*session, sock);
2181 if (ret < 0) {
2182 msgtype = NC_MSG_ERROR;
2183 goto cleanup;
2184 }
2185 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002186 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002187 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002188 msgtype = NC_MSG_ERROR;
2189 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002190 }
2191
Michal Vasko2cc4c682016-03-01 09:16:48 +01002192 (*session)->data = NULL;
2193
Michal Vaskoade892d2017-02-22 13:40:35 +01002194 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002195 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002196
Michal Vaskob48aa812016-01-18 14:13:09 +01002197 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002198 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002199
Michal Vasko9e036d52016-01-08 10:49:26 +01002200 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002201 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002202 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002203 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002204 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002205 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002206 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002207
2208 nc_gettimespec_mono(&ts_cur);
2209 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2210 nc_gettimespec_real(&ts_cur);
2211 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002212 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002213
Michal Vasko71090fc2016-05-24 16:37:28 +02002214 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002215
Michal Vasko71090fc2016-05-24 16:37:28 +02002216cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002217 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002218 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002219
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002220 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002221 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002222 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002223}
2224
Michal Vasko2e6defd2016-10-07 15:48:15 +02002225API int
2226nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
2227{
2228 uint16_t i;
2229
2230 if (!name) {
2231 ERRARG("name");
2232 return -1;
2233 } else if (!ti) {
2234 ERRARG("ti");
2235 return -1;
2236 }
2237
2238 /* WRITE LOCK */
2239 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2240
2241 /* check name uniqueness */
2242 for (i = 0; i < server_opts.ch_client_count; ++i) {
2243 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2244 ERR("Call Home client \"%s\" already exists.", name);
2245 /* WRITE UNLOCK */
2246 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2247 return -1;
2248 }
2249 }
2250
2251 ++server_opts.ch_client_count;
2252 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2253 if (!server_opts.ch_clients) {
2254 ERRMEM;
2255 /* WRITE UNLOCK */
2256 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2257 return -1;
2258 }
2259 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko69a3ff62018-11-09 09:31:48 +01002260 server_opts.ch_clients[server_opts.ch_client_count - 1].id = ATOMIC_INC(&server_opts.new_client_id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002261 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002262 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2263 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002264
2265 switch (ti) {
2266#ifdef NC_ENABLED_SSH
2267 case NC_TI_LIBSSH:
2268 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2269 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2270 ERRMEM;
2271 /* WRITE UNLOCK */
2272 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2273 return -1;
2274 }
2275 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2276 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2277 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2278 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2279 break;
2280#endif
2281#ifdef NC_ENABLED_TLS
2282 case NC_TI_OPENSSL:
2283 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2284 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2285 ERRMEM;
2286 /* WRITE UNLOCK */
2287 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2288 return -1;
2289 }
2290 break;
2291#endif
2292 default:
2293 ERRINT;
2294 /* WRITE UNLOCK */
2295 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2296 return -1;
2297 }
2298
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002299 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2300
Michal Vasko2e6defd2016-10-07 15:48:15 +02002301 /* set CH default options */
2302 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2303 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2304
2305 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2306
2307 /* WRITE UNLOCK */
2308 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2309
2310 return 0;
2311}
2312
2313API int
Michal Vasko59050372016-11-22 14:33:55 +01002314nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002315{
2316 uint16_t i, j;
2317 int ret = -1;
2318
2319 /* WRITE LOCK */
2320 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2321
Michal Vasko59050372016-11-22 14:33:55 +01002322 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002323 /* remove all CH clients */
2324 for (i = 0; i < server_opts.ch_client_count; ++i) {
2325 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2326
2327 /* remove all endpoints */
2328 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2329 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2330 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2331 }
2332 free(server_opts.ch_clients[i].ch_endpts);
2333
2334 switch (server_opts.ch_clients[i].ti) {
2335#ifdef NC_ENABLED_SSH
2336 case NC_TI_LIBSSH:
2337 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2338 free(server_opts.ch_clients[i].opts.ssh);
2339 break;
2340#endif
2341#ifdef NC_ENABLED_TLS
2342 case NC_TI_OPENSSL:
2343 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2344 free(server_opts.ch_clients[i].opts.tls);
2345 break;
2346#endif
2347 default:
2348 ERRINT;
2349 /* won't get here ...*/
2350 break;
2351 }
2352
2353 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2354
2355 ret = 0;
2356 }
2357 free(server_opts.ch_clients);
2358 server_opts.ch_clients = NULL;
2359
2360 server_opts.ch_client_count = 0;
2361
2362 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002363 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002364 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002365 if ((name && !strcmp(server_opts.ch_clients[i].name, name)) || (!name && (server_opts.ch_clients[i].ti == ti))) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002366 /* remove endpt */
2367 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2368
2369 switch (server_opts.ch_clients[i].ti) {
2370#ifdef NC_ENABLED_SSH
2371 case NC_TI_LIBSSH:
2372 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2373 free(server_opts.ch_clients[i].opts.ssh);
2374 break;
2375#endif
2376#ifdef NC_ENABLED_TLS
2377 case NC_TI_OPENSSL:
2378 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2379 free(server_opts.ch_clients[i].opts.tls);
2380 break;
2381#endif
2382 default:
2383 ERRINT;
2384 break;
2385 }
2386
2387 /* remove all endpoints */
2388 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2389 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2390 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2391 }
2392 free(server_opts.ch_clients[i].ch_endpts);
2393
2394 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2395
2396 /* move last client and endpoint(s) to the empty space */
2397 --server_opts.ch_client_count;
2398 if (i < server_opts.ch_client_count) {
2399 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2400 sizeof *server_opts.ch_clients);
2401 } else if (!server_opts.ch_client_count) {
2402 free(server_opts.ch_clients);
2403 server_opts.ch_clients = NULL;
2404 }
2405
2406 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002407 if (name) {
2408 break;
2409 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002410 }
2411 }
2412 }
2413
2414 /* WRITE UNLOCK */
2415 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2416
2417 return ret;
2418}
2419
2420API int
2421nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2422{
2423 uint16_t i;
2424 struct nc_ch_client *client;
2425
2426 if (!client_name) {
2427 ERRARG("client_name");
2428 return -1;
2429 } else if (!endpt_name) {
2430 ERRARG("endpt_name");
2431 return -1;
2432 }
2433
2434 /* LOCK */
2435 client = nc_server_ch_client_lock(client_name, 0, NULL);
2436 if (!client) {
2437 return -1;
2438 }
2439
2440 for (i = 0; i < client->ch_endpt_count; ++i) {
2441 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2442 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2443 /* UNLOCK */
2444 nc_server_ch_client_unlock(client);
2445 return -1;
2446 }
2447 }
2448
2449 ++client->ch_endpt_count;
2450 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2451 if (!client->ch_endpts) {
2452 ERRMEM;
2453 /* UNLOCK */
2454 nc_server_ch_client_unlock(client);
2455 return -1;
2456 }
2457
2458 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2459 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2460 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +00002461 client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002462
2463 /* UNLOCK */
2464 nc_server_ch_client_unlock(client);
2465
2466 return 0;
2467}
2468
2469API int
2470nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2471{
2472 uint16_t i;
2473 int ret = -1;
2474 struct nc_ch_client *client;
2475
2476 if (!client_name) {
2477 ERRARG("client_name");
2478 return -1;
2479 }
2480
2481 /* LOCK */
2482 client = nc_server_ch_client_lock(client_name, 0, NULL);
2483 if (!client) {
2484 return -1;
2485 }
2486
2487 if (!endpt_name) {
2488 /* remove all endpoints */
2489 for (i = 0; i < client->ch_endpt_count; ++i) {
2490 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2491 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Frank Rimpler9f838b02018-07-25 06:44:03 +00002492 if (client->ch_endpts[i].sock_pending != -1) {
2493 close(client->ch_endpts[i].sock_pending);
2494 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002495 }
2496 free(client->ch_endpts);
2497 client->ch_endpts = NULL;
2498 client->ch_endpt_count = 0;
2499
2500 ret = 0;
2501 } else {
2502 for (i = 0; i < client->ch_endpt_count; ++i) {
2503 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2504 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2505 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002506
Michal Vasko4f921012016-10-20 14:07:45 +02002507 /* move last endpoint to the empty space */
2508 --client->ch_endpt_count;
2509 if (i < client->ch_endpt_count) {
2510 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2511 } else if (!server_opts.ch_client_count) {
2512 free(server_opts.ch_clients);
2513 server_opts.ch_clients = NULL;
2514 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002515
Michal Vasko4f921012016-10-20 14:07:45 +02002516 ret = 0;
2517 break;
2518 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002519 }
2520 }
2521
2522 /* UNLOCK */
2523 nc_server_ch_client_unlock(client);
2524
2525 return ret;
2526}
2527
2528API int
2529nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2530{
2531 uint16_t i;
2532 int ret = -1;
2533 struct nc_ch_client *client;
2534
2535 if (!client_name) {
2536 ERRARG("client_name");
2537 return -1;
2538 } else if (!endpt_name) {
2539 ERRARG("endpt_name");
2540 return -1;
2541 } else if (!address) {
2542 ERRARG("address");
2543 return -1;
2544 }
2545
2546 /* LOCK */
2547 client = nc_server_ch_client_lock(client_name, 0, NULL);
2548 if (!client) {
2549 return -1;
2550 }
2551
2552 for (i = 0; i < client->ch_endpt_count; ++i) {
2553 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2554 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2555 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2556
2557 ret = 0;
2558 break;
2559 }
2560 }
2561
2562 /* UNLOCK */
2563 nc_server_ch_client_unlock(client);
2564
2565 if (ret == -1) {
2566 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2567 }
2568
2569 return ret;
2570}
2571
2572API int
2573nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2574{
2575 uint16_t i;
2576 int ret = -1;
2577 struct nc_ch_client *client;
2578
2579 if (!client_name) {
2580 ERRARG("client_name");
2581 return -1;
2582 } else if (!endpt_name) {
2583 ERRARG("endpt_name");
2584 return -1;
2585 } else if (!port) {
2586 ERRARG("port");
2587 return -1;
2588 }
2589
2590 /* LOCK */
2591 client = nc_server_ch_client_lock(client_name, 0, NULL);
2592 if (!client) {
2593 return -1;
2594 }
2595
2596 for (i = 0; i < client->ch_endpt_count; ++i) {
2597 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2598 client->ch_endpts[i].port = port;
2599
2600 ret = 0;
2601 break;
2602 }
2603 }
2604
2605 /* UNLOCK */
2606 nc_server_ch_client_unlock(client);
2607
2608 if (ret == -1) {
2609 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2610 }
2611
2612 return ret;
2613}
2614
2615API int
2616nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2617{
2618 struct nc_ch_client *client;
2619
2620 if (!client_name) {
2621 ERRARG("client_name");
2622 return -1;
2623 } else if (!conn_type) {
2624 ERRARG("conn_type");
2625 return -1;
2626 }
2627
2628 /* LOCK */
2629 client = nc_server_ch_client_lock(client_name, 0, NULL);
2630 if (!client) {
2631 return -1;
2632 }
2633
2634 if (client->conn_type != conn_type) {
2635 client->conn_type = conn_type;
2636
2637 /* set default options */
2638 switch (conn_type) {
2639 case NC_CH_PERSIST:
2640 client->conn.persist.idle_timeout = 86400;
2641 client->conn.persist.ka_max_wait = 30;
2642 client->conn.persist.ka_max_attempts = 3;
2643 break;
2644 case NC_CH_PERIOD:
2645 client->conn.period.idle_timeout = 300;
2646 client->conn.period.reconnect_timeout = 60;
2647 break;
2648 default:
2649 ERRINT;
2650 break;
2651 }
2652 }
2653
2654 /* UNLOCK */
2655 nc_server_ch_client_unlock(client);
2656
2657 return 0;
2658}
2659
2660API int
2661nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2662{
2663 struct nc_ch_client *client;
2664
2665 if (!client_name) {
2666 ERRARG("client_name");
2667 return -1;
2668 }
2669
2670 /* LOCK */
2671 client = nc_server_ch_client_lock(client_name, 0, NULL);
2672 if (!client) {
2673 return -1;
2674 }
2675
2676 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002677 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002678 /* UNLOCK */
2679 nc_server_ch_client_unlock(client);
2680 return -1;
2681 }
2682
2683 client->conn.persist.idle_timeout = idle_timeout;
2684
2685 /* UNLOCK */
2686 nc_server_ch_client_unlock(client);
2687
2688 return 0;
2689}
2690
2691API int
2692nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2693{
2694 struct nc_ch_client *client;
2695
2696 if (!client_name) {
2697 ERRARG("client_name");
2698 return -1;
2699 } else if (!max_wait) {
2700 ERRARG("max_wait");
2701 return -1;
2702 }
2703
2704 /* LOCK */
2705 client = nc_server_ch_client_lock(client_name, 0, NULL);
2706 if (!client) {
2707 return -1;
2708 }
2709
2710 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002711 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712 /* UNLOCK */
2713 nc_server_ch_client_unlock(client);
2714 return -1;
2715 }
2716
2717 client->conn.persist.ka_max_wait = max_wait;
2718
2719 /* UNLOCK */
2720 nc_server_ch_client_unlock(client);
2721
2722 return 0;
2723}
2724
2725API int
2726nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2727{
2728 struct nc_ch_client *client;
2729
2730 if (!client_name) {
2731 ERRARG("client_name");
2732 return -1;
2733 }
2734
2735 /* LOCK */
2736 client = nc_server_ch_client_lock(client_name, 0, NULL);
2737 if (!client) {
2738 return -1;
2739 }
2740
2741 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002742 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002743 /* UNLOCK */
2744 nc_server_ch_client_unlock(client);
2745 return -1;
2746 }
2747
2748 client->conn.persist.ka_max_attempts = max_attempts;
2749
2750 /* UNLOCK */
2751 nc_server_ch_client_unlock(client);
2752
2753 return 0;
2754}
2755
2756API int
2757nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2758{
2759 struct nc_ch_client *client;
2760
2761 if (!client_name) {
2762 ERRARG("client_name");
2763 return -1;
2764 }
2765
2766 /* LOCK */
2767 client = nc_server_ch_client_lock(client_name, 0, NULL);
2768 if (!client) {
2769 return -1;
2770 }
2771
2772 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002773 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002774 /* UNLOCK */
2775 nc_server_ch_client_unlock(client);
2776 return -1;
2777 }
2778
2779 client->conn.period.idle_timeout = idle_timeout;
2780
2781 /* UNLOCK */
2782 nc_server_ch_client_unlock(client);
2783
2784 return 0;
2785}
2786
2787API int
2788nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2789{
2790 struct nc_ch_client *client;
2791
2792 if (!client_name) {
2793 ERRARG("client_name");
2794 return -1;
2795 } else if (!reconnect_timeout) {
2796 ERRARG("reconnect_timeout");
2797 return -1;
2798 }
2799
2800 /* LOCK */
2801 client = nc_server_ch_client_lock(client_name, 0, NULL);
2802 if (!client) {
2803 return -1;
2804 }
2805
2806 if (client->conn_type != NC_CH_PERIOD) {
2807 ERR("Call Home client \"%s\" is not of periodic connection type.");
2808 /* UNLOCK */
2809 nc_server_ch_client_unlock(client);
2810 return -1;
2811 }
2812
2813 client->conn.period.reconnect_timeout = reconnect_timeout;
2814
2815 /* UNLOCK */
2816 nc_server_ch_client_unlock(client);
2817
2818 return 0;
2819}
2820
2821API int
2822nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2823{
2824 struct nc_ch_client *client;
2825
2826 if (!client_name) {
2827 ERRARG("client_name");
2828 return -1;
2829 }
2830
2831 /* LOCK */
2832 client = nc_server_ch_client_lock(client_name, 0, NULL);
2833 if (!client) {
2834 return -1;
2835 }
2836
2837 client->start_with = start_with;
2838
2839 /* UNLOCK */
2840 nc_server_ch_client_unlock(client);
2841
2842 return 0;
2843}
2844
2845API int
2846nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2847{
2848 struct nc_ch_client *client;
2849
2850 if (!client_name) {
2851 ERRARG("client_name");
2852 return -1;
2853 } else if (!max_attempts) {
2854 ERRARG("max_attempts");
2855 return -1;
2856 }
2857
2858 /* LOCK */
2859 client = nc_server_ch_client_lock(client_name, 0, NULL);
2860 if (!client) {
2861 return -1;
2862 }
2863
2864 client->max_attempts = max_attempts;
2865
2866 /* UNLOCK */
2867 nc_server_ch_client_unlock(client);
2868
2869 return 0;
2870}
2871
2872/* client lock is expected to be held */
2873static NC_MSG_TYPE
2874nc_connect_ch_client_endpt(struct nc_ch_client *client, struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002875{
Michal Vasko71090fc2016-05-24 16:37:28 +02002876 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002877 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002878 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002879 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002880
Michal Vasko66032bc2019-01-22 15:03:12 +01002881 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002882 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002883 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002884 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002885 /* no need to store the socket as pending any longer */
2886 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002887
Michal Vasko131120a2018-05-29 15:44:02 +02002888 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002889 if (!(*session)) {
2890 ERRMEM;
2891 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002892 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002893 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002894 }
2895 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002896 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002897 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002898 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002899 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002900
Michal Vaskob05053d2016-01-22 16:12:06 +01002901 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002902#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002903 if (client->ti == NC_TI_LIBSSH) {
2904 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002905 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002906 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002907
Michal Vasko71090fc2016-05-24 16:37:28 +02002908 if (ret < 0) {
2909 msgtype = NC_MSG_ERROR;
2910 goto fail;
2911 } else if (!ret) {
2912 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002913 goto fail;
2914 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002915 } else
2916#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002917#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002918 if (client->ti == NC_TI_OPENSSL) {
2919 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002920 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002921 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002922
Michal Vasko71090fc2016-05-24 16:37:28 +02002923 if (ret < 0) {
2924 msgtype = NC_MSG_ERROR;
2925 goto fail;
2926 } else if (!ret) {
2927 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002928 goto fail;
2929 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002930 } else
2931#endif
2932 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002933 ERRINT;
2934 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002935 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002936 goto fail;
2937 }
2938
2939 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002940 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01002941
2942 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002943 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002944 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002945 goto fail;
2946 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002947
2948 nc_gettimespec_mono(&ts_cur);
2949 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2950 nc_gettimespec_real(&ts_cur);
2951 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002952 (*session)->status = NC_STATUS_RUNNING;
2953
Michal Vasko71090fc2016-05-24 16:37:28 +02002954 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002955
2956fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002957 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002958 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002959 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002960}
2961
Michal Vasko2e6defd2016-10-07 15:48:15 +02002962struct nc_ch_client_thread_arg {
2963 char *client_name;
2964 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2965};
2966
2967static struct nc_ch_client *
2968nc_server_ch_client_with_endpt_lock(const char *name)
2969{
2970 struct nc_ch_client *client;
2971
2972 while (1) {
2973 /* LOCK */
2974 client = nc_server_ch_client_lock(name, 0, NULL);
2975 if (!client) {
2976 return NULL;
2977 }
2978 if (client->ch_endpt_count) {
2979 return client;
2980 }
2981 /* no endpoints defined yet */
2982
2983 /* UNLOCK */
2984 nc_server_ch_client_unlock(client);
2985
2986 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2987 }
2988
2989 return NULL;
2990}
2991
2992static int
2993nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2994{
Michal Vasko3f05a092018-03-13 10:39:49 +01002995 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002996 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002997 struct timespec ts;
2998 struct nc_ch_client *client;
2999
3000 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01003001 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
3002 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003003 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
3004 ERRMEM;
3005 nc_session_free(session, NULL);
3006 return -1;
3007 }
3008 pthread_mutex_init(session->opts.server.ch_lock, NULL);
3009 pthread_cond_init(session->opts.server.ch_cond, NULL);
3010
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003011 session->flags |= NC_SESSION_CALLHOME;
3012
Michal Vasko2e6defd2016-10-07 15:48:15 +02003013 /* CH LOCK */
3014 pthread_mutex_lock(session->opts.server.ch_lock);
3015
3016 /* give the session to the user */
3017 data->session_clb(data->client_name, session);
3018
3019 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003020 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003021 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003022
Michal Vasko3f05a092018-03-13 10:39:49 +01003023 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3024 if (!r) {
3025 /* we were woken up, something probably happened */
3026 if (session->status != NC_STATUS_RUNNING) {
3027 break;
3028 }
3029 } else if (r != ETIMEDOUT) {
3030 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3031 ret = -1;
3032 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003033 }
3034
Michal Vasko2e6defd2016-10-07 15:48:15 +02003035 /* check whether the client was not removed */
3036 /* LOCK */
3037 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
3038 if (!client) {
3039 /* client was removed, finish thread */
3040 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
3041 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003042 ret = 1;
3043 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003044 }
3045
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003046 if (client->conn_type == NC_CH_PERSIST) {
3047 /* TODO keep-alives */
3048 idle_timeout = client->conn.persist.idle_timeout;
3049 } else {
3050 idle_timeout = client->conn.period.idle_timeout;
3051 }
3052
Michal Vasko9fb42272017-10-05 13:50:05 +02003053 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003054 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003055 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3056 session->status = NC_STATUS_INVALID;
3057 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3058 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003059
3060 /* UNLOCK */
3061 nc_server_ch_client_unlock(client);
3062
3063 } while (session->status == NC_STATUS_RUNNING);
3064
Michal Vasko27377422018-03-15 08:59:35 +01003065 /* CH UNLOCK */
3066 pthread_mutex_unlock(session->opts.server.ch_lock);
3067
Michal Vasko3f05a092018-03-13 10:39:49 +01003068 if (session->status == NC_STATUS_CLOSING) {
3069 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3070 session->flags &= ~NC_SESSION_CALLHOME;
3071 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003072
Michal Vasko3f05a092018-03-13 10:39:49 +01003073 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003074}
3075
3076static void *
3077nc_ch_client_thread(void *arg)
3078{
3079 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3080 NC_MSG_TYPE msgtype;
3081 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003082 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003083 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003084 struct nc_ch_endpt *cur_endpt;
3085 struct nc_session *session;
3086 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003087 uint32_t client_id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003088
3089 /* LOCK */
3090 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3091 if (!client) {
3092 goto cleanup;
3093 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003094 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003095
3096 cur_endpt = &client->ch_endpts[0];
3097 cur_endpt_name = strdup(cur_endpt->name);
3098
Michal Vasko29af44b2016-10-13 10:59:55 +02003099 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003100 while (1) {
3101 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
3102
3103 if (msgtype == NC_MSG_HELLO) {
3104 /* UNLOCK */
3105 nc_server_ch_client_unlock(client);
3106
Michal Vasko29af44b2016-10-13 10:59:55 +02003107 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003108 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3109 goto cleanup;
3110 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003111 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003112
3113 /* LOCK */
3114 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3115 if (!client) {
3116 goto cleanup;
3117 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003118 if (client->id != client_id) {
3119 nc_server_ch_client_unlock(client);
3120 goto cleanup;
3121 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003122
3123 /* session changed status -> it was disconnected for whatever reason,
3124 * persistent connection immediately tries to reconnect, periodic waits some first */
3125 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003126 /* UNLOCK */
3127 nc_server_ch_client_unlock(client);
3128
3129 /* TODO wake up sometimes to check for new notifications */
3130 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
3131
3132 /* LOCK */
3133 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3134 if (!client) {
3135 goto cleanup;
3136 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003137 if (client->id != client_id) {
3138 nc_server_ch_client_unlock(client);
3139 goto cleanup;
3140 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003141 }
3142
3143 /* set next endpoint to try */
3144 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003145 next_endpt_index = 0;
Michal Vasko2a225342018-09-05 08:38:34 +02003146 } else {
Peter Feiged05f2252018-09-03 08:09:47 +00003147 /* we keep the current one but due to unlock/lock we have to find it again */
3148 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3149 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3150 break;
3151 }
3152 }
3153 if (next_endpt_index >= client->ch_endpt_count) {
3154 /* endpoint was removed, start with the first one */
3155 next_endpt_index = 0;
3156 }
3157 }
3158
Michal Vasko2e6defd2016-10-07 15:48:15 +02003159 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003160 /* UNLOCK */
3161 nc_server_ch_client_unlock(client);
3162
Michal Vasko2e6defd2016-10-07 15:48:15 +02003163 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003164 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3165
Michal Vasko6bb116b2016-10-26 13:53:46 +02003166 /* LOCK */
3167 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3168 if (!client) {
3169 goto cleanup;
3170 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003171 if (client->id != client_id) {
3172 nc_server_ch_client_unlock(client);
3173 goto cleanup;
3174 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003175
Michal Vasko2e6defd2016-10-07 15:48:15 +02003176 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003177
3178 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003179 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3180 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003181 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003182 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003183 }
3184
Peter Feiged05f2252018-09-03 08:09:47 +00003185 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003186 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003187 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003188 cur_attempts = 0;
3189 } else if (cur_attempts == client->max_attempts) {
3190 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003191 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003192 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003193 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003194 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003195 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003196 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003197 }
3198
3199 cur_attempts = 0;
3200 } /* else we keep the current one */
3201 }
Peter Feiged05f2252018-09-03 08:09:47 +00003202
3203 cur_endpt = &client->ch_endpts[next_endpt_index];
3204 free(cur_endpt_name);
3205 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003206 }
3207
3208cleanup:
3209 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003210 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003211 free(data->client_name);
3212 free(data);
3213 return NULL;
3214}
3215
3216API int
3217nc_connect_ch_client_dispatch(const char *client_name,
Michal Vasko3f05a092018-03-13 10:39:49 +01003218 void (*session_clb)(const char *client_name, struct nc_session *new_session))
3219{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003220 int ret;
3221 pthread_t tid;
3222 struct nc_ch_client_thread_arg *arg;
3223
3224 if (!client_name) {
3225 ERRARG("client_name");
3226 return -1;
3227 } else if (!session_clb) {
3228 ERRARG("session_clb");
3229 return -1;
3230 }
3231
3232 arg = malloc(sizeof *arg);
3233 if (!arg) {
3234 ERRMEM;
3235 return -1;
3236 }
3237 arg->client_name = strdup(client_name);
3238 if (!arg->client_name) {
3239 ERRMEM;
3240 free(arg);
3241 return -1;
3242 }
3243 arg->session_clb = session_clb;
3244
3245 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3246 if (ret) {
3247 ERR("Creating a new thread failed (%s).", strerror(ret));
3248 free(arg->client_name);
3249 free(arg);
3250 return -1;
3251 }
3252 /* the thread now manages arg */
3253
3254 pthread_detach(tid);
3255
3256 return 0;
3257}
3258
Radek Krejci53691be2016-02-22 13:58:37 +01003259#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003260
Michal Vaskoe8e07702017-03-15 10:19:30 +01003261API int
3262nc_server_endpt_count(void)
3263{
3264 return server_opts.endpt_count;
3265}
3266
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003267API time_t
3268nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003269{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003270 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003271 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003272 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003273 }
3274
Michal Vasko2e6defd2016-10-07 15:48:15 +02003275 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003276}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003277
3278API void
3279nc_session_set_notif_status(struct nc_session *session, int notif_status)
3280{
3281 if (!session || (session->side != NC_SERVER)) {
3282 ERRARG("session");
3283 return;
3284 }
3285
3286 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3287}
3288
3289API int
3290nc_session_get_notif_status(const struct nc_session *session)
3291{
3292 if (!session || (session->side != NC_SERVER)) {
3293 ERRARG("session");
3294 return 0;
3295 }
3296
3297 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003298}
Michal Vasko8f430592019-02-26 08:32:54 +01003299
3300API int
3301nc_session_is_callhome(const struct nc_session *session)
3302{
3303 if (!session || (session->side != NC_SERVER)) {
3304 ERRARG("session");
3305 return 0;
3306 }
3307
3308 if (session->flags & NC_SESSION_CALLHOME) {
3309 return 1;
3310 }
3311
3312 return 0;
3313}