blob: 885ef3a4fade28e25f517f691f1a68706a4685f7 [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 */
Miroslav Mareš9563b812017-08-19 17:45:36 +020014#define _GNU_SOURCE /* signals, threads */
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>
23#include <netinet/in.h>
Michal Vasko06c860d2018-07-09 16:08:52 +020024#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010025#include <arpa/inet.h>
26#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010027#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010028#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010029#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010030#include <signal.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031
Michal Vasko1a38c862016-01-15 15:50:07 +010032#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010033#include "session_server.h"
34
Michal Vaskob48aa812016-01-18 14:13:09 +010035struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010036#ifdef NC_ENABLED_SSH
37 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
38#endif
39 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020040 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
41 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010042};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010043
fanchanghu966f2de2016-07-21 02:28:57 -040044static nc_rpc_clb global_rpc_clb = NULL;
45
Michal Vasko3031aae2016-01-27 16:07:18 +010046struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010047nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010048{
49 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010050 struct nc_endpt *endpt = NULL;
51
Michal Vaskoade892d2017-02-22 13:40:35 +010052 /* WRITE LOCK */
53 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010054
55 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020056 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010057 endpt = &server_opts.endpts[i];
58 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010059 }
60 }
61
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010062 if (!endpt) {
63 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010064 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020065 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010066 return NULL;
67 }
68
Michal Vaskoe2713da2016-08-22 16:06:40 +020069 if (idx) {
70 *idx = i;
71 }
72
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010073 return endpt;
74}
75
Michal Vasko2e6defd2016-10-07 15:48:15 +020076struct nc_ch_client *
77nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010078{
Michal Vasko2e6defd2016-10-07 15:48:15 +020079 uint16_t i;
80 struct nc_ch_client *client = NULL;
81
82 /* READ LOCK */
83 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
84
85 for (i = 0; i < server_opts.ch_client_count; ++i) {
86 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
87 client = &server_opts.ch_clients[i];
88 break;
89 }
90 }
91
92 if (!client) {
93 ERR("Call Home client \"%s\" was not found.", name);
94 /* READ UNLOCK */
95 pthread_rwlock_unlock(&server_opts.ch_client_lock);
96 return NULL;
97 }
98
99 /* CH CLIENT LOCK */
100 pthread_mutex_lock(&client->lock);
101
102 if (idx) {
103 *idx = i;
104 }
105
106 return client;
107}
108
109void
110nc_server_ch_client_unlock(struct nc_ch_client *client)
111{
112 /* CH CLIENT UNLOCK */
113 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100114
115 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200116 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100117}
Michal Vasko086311b2016-01-08 09:53:11 +0100118
Michal Vasko1a38c862016-01-15 15:50:07 +0100119API void
120nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
121{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200122 if (!session) {
123 ERRARG("session");
124 return;
125 } else if (!reason) {
126 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100127 return;
128 }
129
Michal Vasko142cfea2017-08-07 10:12:11 +0200130 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
131 session->killed_by = 0;
132 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100133 session->term_reason = reason;
134}
135
Michal Vasko142cfea2017-08-07 10:12:11 +0200136API void
137nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
138{
139 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
140 ERRARG("session");
141 return;
142 } else if (!sid) {
143 ERRARG("sid");
144 return;
145 }
146
147 session->killed_by = sid;
148}
149
150API void
151nc_session_set_status(struct nc_session *session, NC_STATUS status)
152{
153 if (!session) {
154 ERRARG("session");
155 return;
156 } else if (!status) {
157 ERRARG("status");
158 return;
159 }
160
161 session->status = status;
162}
163
Michal Vasko086311b2016-01-08 09:53:11 +0100164int
Michal Vaskof05562c2016-01-20 12:06:43 +0100165nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100166{
Michal Vasko06c860d2018-07-09 16:08:52 +0200167 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100168 int is_ipv4, sock;
169 struct sockaddr_storage saddr;
170
171 struct sockaddr_in *saddr4;
172 struct sockaddr_in6 *saddr6;
173
174
175 if (!strchr(address, ':')) {
176 is_ipv4 = 1;
177 } else {
178 is_ipv4 = 0;
179 }
180
181 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
182 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100183 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100184 goto fail;
185 }
186
Michal Vasko06c860d2018-07-09 16:08:52 +0200187 opt = 1;
188 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
189 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
190 goto fail;
191 }
192 opt = 1;
193 if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof opt) == -1) {
194 ERR("Could not set SO_KEEPALIVE option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100195 goto fail;
196 }
197
198 bzero(&saddr, sizeof(struct sockaddr_storage));
199 if (is_ipv4) {
200 saddr4 = (struct sockaddr_in *)&saddr;
201
202 saddr4->sin_family = AF_INET;
203 saddr4->sin_port = htons(port);
204
205 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100206 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100207 goto fail;
208 }
209
210 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100211 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100212 goto fail;
213 }
214
215 } else {
216 saddr6 = (struct sockaddr_in6 *)&saddr;
217
218 saddr6->sin6_family = AF_INET6;
219 saddr6->sin6_port = htons(port);
220
221 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100222 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100223 goto fail;
224 }
225
226 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -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
Michal Vaskofb89d772016-01-08 12:25:35 +0100232 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100233 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100234 goto fail;
235 }
236
237 return sock;
238
239fail:
240 if (sock > -1) {
241 close(sock);
242 }
243
244 return -1;
245}
246
247int
Michal Vasko3031aae2016-01-27 16:07:18 +0100248nc_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 +0100249{
Michal Vaskof54cd352017-02-22 13:42:02 +0100250 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100251 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100252 struct pollfd *pfd;
253 struct sockaddr_storage saddr;
254 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100255 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100256
257 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100258 if (!pfd) {
259 ERRMEM;
260 return -1;
261 }
262
Michal Vaskoac2f6182017-01-30 14:32:03 +0100263 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200264 if (binds[i].sock < 0) {
265 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200266 continue;
267 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200268 if (binds[i].pollin) {
269 binds[i].pollin = 0;
270 /* leftover pollin */
271 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100272 break;
273 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100274 pfd[pfd_count].fd = binds[i].sock;
275 pfd[pfd_count].events = POLLIN;
276 pfd[pfd_count].revents = 0;
277
278 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100279 }
280
Michal Vasko0a3f3752016-10-13 14:58:38 +0200281 if (sock == -1) {
282 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100283 sigfillset(&sigmask);
284 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100285 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100286 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
287
Michal Vasko0a3f3752016-10-13 14:58:38 +0200288 if (!ret) {
289 /* we timeouted */
290 free(pfd);
291 return 0;
292 } else if (ret == -1) {
293 ERR("Poll failed (%s).", strerror(errno));
294 free(pfd);
295 return -1;
296 }
Michal Vasko086311b2016-01-08 09:53:11 +0100297
Michal Vaskoac2f6182017-01-30 14:32:03 +0100298 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
299 /* adjust i so that indices in binds and pfd always match */
300 while (binds[i].sock != pfd[j].fd) {
301 ++i;
302 }
303
304 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200305 --ret;
306
307 if (!ret) {
308 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100309 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200310 break;
311 } else {
312 /* just remember the event for next time */
313 binds[i].pollin = 1;
314 }
315 }
Michal Vasko086311b2016-01-08 09:53:11 +0100316 }
317 }
318 free(pfd);
319
320 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100321 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100322 return -1;
323 }
324
325 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100326 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100327 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100328 return -1;
329 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200330 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100331
Michal Vasko0190bc32016-03-02 15:47:49 +0100332 /* make the socket non-blocking */
333 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
334 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100335 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100336 return -1;
337 }
338
Michal Vasko7d965dc2018-03-12 14:39:23 +0100339 /* enable keep-alive */
Michal Vaskof952be22018-07-09 16:25:44 +0200340#ifdef TCP_KEEPIDLE
Michal Vasko7d965dc2018-03-12 14:39:23 +0100341 flags = 1;
Michal Vasko06c860d2018-07-09 16:08:52 +0200342 if (setsockopt(ret, IPPROTO_TCP, TCP_KEEPIDLE, &flags, sizeof flags) == -1) {
343 ERR("Setsockopt failed (%s).", strerror(errno));
344 close(ret);
345 return -1;
346 }
Michal Vaskof952be22018-07-09 16:25:44 +0200347#endif
348#ifdef TCP_KEEPINTVL
Michal Vasko06c860d2018-07-09 16:08:52 +0200349 flags = 5;
350 if (setsockopt(ret, IPPROTO_TCP, TCP_KEEPINTVL, &flags, sizeof flags) == -1) {
351 ERR("Setsockopt failed (%s).", strerror(errno));
352 close(ret);
353 return -1;
354 }
Michal Vaskof952be22018-07-09 16:25:44 +0200355#endif
356#ifdef TCP_KEEPCNT
Michal Vasko06c860d2018-07-09 16:08:52 +0200357 flags = 10;
358 if (setsockopt(ret, IPPROTO_TCP, TCP_KEEPCNT, &flags, sizeof flags) == -1) {
Michal Vasko7d965dc2018-03-12 14:39:23 +0100359 ERR("Setsockopt failed (%s).", strerror(errno));
360 close(ret);
361 return -1;
362 }
Michal Vaskof952be22018-07-09 16:25:44 +0200363#endif
Michal Vasko7d965dc2018-03-12 14:39:23 +0100364
Michal Vasko3031aae2016-01-27 16:07:18 +0100365 if (idx) {
366 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100367 }
368
Michal Vasko086311b2016-01-08 09:53:11 +0100369 /* host was requested */
370 if (host) {
371 if (saddr.ss_family == AF_INET) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000372 *host = malloc(INET_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100373 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000374 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100375 ERR("inet_ntop failed (%s).", strerror(errno));
376 free(*host);
377 *host = NULL;
378 }
Michal Vasko086311b2016-01-08 09:53:11 +0100379
Michal Vasko4eb3c312016-03-01 14:09:37 +0100380 if (port) {
381 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
382 }
383 } else {
384 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100385 }
386 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100387 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100388 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000389 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100390 ERR("inet_ntop failed (%s).", strerror(errno));
391 free(*host);
392 *host = NULL;
393 }
Michal Vasko086311b2016-01-08 09:53:11 +0100394
Michal Vasko4eb3c312016-03-01 14:09:37 +0100395 if (port) {
396 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
397 }
398 } else {
399 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100400 }
401 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100402 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100403 }
404 }
405
406 return ret;
407}
408
Michal Vasko05ba9df2016-01-13 14:40:27 +0100409static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100410nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100411{
412 const char *identifier = NULL, *version = NULL, *format = NULL;
413 char *model_data = NULL;
414 const struct lys_module *module;
415 struct nc_server_error *err;
416 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200417 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100418
419 LY_TREE_FOR(rpc->child, child) {
420 if (!strcmp(child->schema->name, "identifier")) {
421 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
422 } else if (!strcmp(child->schema->name, "version")) {
423 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500424 if (version && version[0] == '\0') {
425 version = NULL;
426 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 } else if (!strcmp(child->schema->name, "format")) {
428 format = ((struct lyd_node_leaf_list *)child)->value_str;
429 }
430 }
431
432 /* check version */
433 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100434 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
435 nc_err_set_msg(err, "The requested version is not supported.", "en");
436 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100437 }
438
439 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200440 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100441 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200442 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
443 }
444 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100445 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
446 nc_err_set_msg(err, "The requested schema was not found.", "en");
447 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100448 }
449
450 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100451 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100452 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100453 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100454 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100455 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100456 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
457 nc_err_set_msg(err, "The requested format is not supported.", "en");
458 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100459 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200460 if (!model_data) {
461 ERRINT;
462 return NULL;
463 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100464
Michal Vasko88639e92017-08-03 14:38:10 +0200465 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
466 if (!sdata) {
467 ERRINT;
468 free(model_data);
469 return NULL;
470 }
471
Radek Krejci539efb62016-08-24 15:05:16 +0200472 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
473 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100474 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100475 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200476 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100477 return NULL;
478 }
479
Radek Krejci36dfdb32016-09-01 16:56:35 +0200480 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100481}
482
483static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100484nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100485{
Michal Vasko428087d2016-01-14 16:04:28 +0100486 session->term_reason = NC_SESSION_TERM_CLOSED;
487 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100488}
489
Michal Vasko086311b2016-01-08 09:53:11 +0100490API int
491nc_server_init(struct ly_ctx *ctx)
492{
Michal Vasko88639e92017-08-03 14:38:10 +0200493 const struct lys_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000494 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100495
Michal Vasko086311b2016-01-08 09:53:11 +0100496 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200497 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100498 return -1;
499 }
500
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100501 nc_init();
502
Michal Vasko05ba9df2016-01-13 14:40:27 +0100503 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200504 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
505 if (rpc && !rpc->priv) {
506 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100507 }
508
509 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200510 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
511 if (rpc && !rpc->priv) {
512 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100513 }
514
Michal Vasko086311b2016-01-08 09:53:11 +0100515 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100516
517 server_opts.new_session_id = 1;
518 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
519
Frank Rimpler9f838b02018-07-25 06:44:03 +0000520 errno=0;
521
522 if (pthread_rwlockattr_init(&attr) == 0) {
523 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
524 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
525 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
526 }
527 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
528 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
529 }
530 } else {
531 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
532 }
533 pthread_rwlockattr_destroy(&attr);
534 } else {
535 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
536 }
Michal Vasko086311b2016-01-08 09:53:11 +0100537 return 0;
538}
539
Michal Vaskob48aa812016-01-18 14:13:09 +0100540API void
541nc_server_destroy(void)
542{
Radek Krejci658782b2016-12-04 22:04:55 +0100543 unsigned int i;
544
545 for (i = 0; i < server_opts.capabilities_count; i++) {
546 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
547 }
548 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200549 server_opts.capabilities = NULL;
550 server_opts.capabilities_count = 0;
551
Michal Vaskob48aa812016-01-18 14:13:09 +0100552 pthread_spin_destroy(&server_opts.sid_lock);
553
Radek Krejci53691be2016-02-22 13:58:37 +0100554#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100555 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100556#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100557#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100558 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
559 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
560 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200561 server_opts.passwd_auth_data = NULL;
562 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100563
Michal Vasko17dfda92016-12-01 14:06:16 +0100564 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100565
566 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
567 server_opts.hostkey_data_free(server_opts.hostkey_data);
568 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200569 server_opts.hostkey_data = NULL;
570 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100571#endif
572#ifdef NC_ENABLED_TLS
573 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
574 server_opts.server_cert_data_free(server_opts.server_cert_data);
575 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200576 server_opts.server_cert_data = NULL;
577 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100578 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
579 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
580 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200581 server_opts.trusted_cert_list_data = NULL;
582 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100583#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100584 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100585}
586
Michal Vasko086311b2016-01-08 09:53:11 +0100587API int
588nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
589{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200590 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
591 ERRARG("basic_mode");
592 return -1;
593 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
594 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100595 return -1;
596 }
597
598 server_opts.wd_basic_mode = basic_mode;
599 server_opts.wd_also_supported = also_supported;
600 return 0;
601}
602
Michal Vasko1a38c862016-01-15 15:50:07 +0100603API void
Michal Vasko55f03972016-04-13 08:56:01 +0200604nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
605{
606 if (!basic_mode && !also_supported) {
607 ERRARG("basic_mode and also_supported");
608 return;
609 }
610
611 if (basic_mode) {
612 *basic_mode = server_opts.wd_basic_mode;
613 }
614 if (also_supported) {
615 *also_supported = server_opts.wd_also_supported;
616 }
617}
618
Michal Vasko55f03972016-04-13 08:56:01 +0200619API int
Radek Krejci658782b2016-12-04 22:04:55 +0100620nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200621{
Radek Krejci658782b2016-12-04 22:04:55 +0100622 const char **new;
623
624 if (!value || !value[0]) {
625 ERRARG("value must not be empty");
626 return EXIT_FAILURE;
627 }
628
629 server_opts.capabilities_count++;
630 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
631 if (!new) {
632 ERRMEM;
633 return EXIT_FAILURE;
634 }
635 server_opts.capabilities = new;
636 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
637
638 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200639}
640
Michal Vasko1a38c862016-01-15 15:50:07 +0100641API void
Michal Vasko086311b2016-01-08 09:53:11 +0100642nc_server_set_hello_timeout(uint16_t hello_timeout)
643{
Michal Vasko086311b2016-01-08 09:53:11 +0100644 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100645}
646
Michal Vasko55f03972016-04-13 08:56:01 +0200647API uint16_t
648nc_server_get_hello_timeout(void)
649{
650 return server_opts.hello_timeout;
651}
652
Michal Vasko1a38c862016-01-15 15:50:07 +0100653API void
Michal Vasko086311b2016-01-08 09:53:11 +0100654nc_server_set_idle_timeout(uint16_t idle_timeout)
655{
Michal Vasko086311b2016-01-08 09:53:11 +0100656 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100657}
658
Michal Vasko55f03972016-04-13 08:56:01 +0200659API uint16_t
660nc_server_get_idle_timeout(void)
661{
662 return server_opts.idle_timeout;
663}
664
Michal Vasko71090fc2016-05-24 16:37:28 +0200665API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100666nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100667{
Michal Vasko71090fc2016-05-24 16:37:28 +0200668 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200669 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200670
Michal Vasko45e53ae2016-04-07 11:46:03 +0200671 if (!server_opts.ctx) {
672 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200673 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200674 } else if (fdin < 0) {
675 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200676 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200677 } else if (fdout < 0) {
678 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200679 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200680 } else if (!username) {
681 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200682 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200683 } else if (!session) {
684 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200685 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100686 }
687
688 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200689 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100690 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100691 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200692 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100693 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100694 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100695
Michal Vasko086311b2016-01-08 09:53:11 +0100696 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100697 (*session)->ti_type = NC_TI_FD;
698 (*session)->ti.fd.in = fdin;
699 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100700
701 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100702 (*session)->flags = NC_SESSION_SHAREDCTX;
703 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100704
Michal Vaskob48aa812016-01-18 14:13:09 +0100705 /* assign new SID atomically */
706 pthread_spin_lock(&server_opts.sid_lock);
707 (*session)->id = server_opts.new_session_id++;
708 pthread_spin_unlock(&server_opts.sid_lock);
709
Michal Vasko086311b2016-01-08 09:53:11 +0100710 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200711 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200712 if (msgtype != NC_MSG_HELLO) {
713 nc_session_free(*session, NULL);
714 *session = NULL;
715 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100716 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200717
718 nc_gettimespec_mono(&ts_cur);
719 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
720 nc_gettimespec_real(&ts_cur);
721 (*session)->opts.server.session_start = ts_cur.tv_sec;
722
Michal Vasko1a38c862016-01-15 15:50:07 +0100723 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100724
Michal Vasko71090fc2016-05-24 16:37:28 +0200725 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100726}
Michal Vasko9e036d52016-01-08 10:49:26 +0100727
Michal Vaskob30b99c2016-07-26 11:35:43 +0200728static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100729nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
730{
731 uint8_t q_last;
732
733 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
734 ERRINT;
735 return;
736 }
737
738 /* get a unique queue value (by adding 1 to the last added value, if any) */
739 if (ps->queue_len) {
740 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
741 *id = ps->queue[q_last] + 1;
742 } else {
743 *id = 0;
744 }
745
746 /* add the id into the queue */
747 ++ps->queue_len;
748 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
749 ps->queue[q_last] = *id;
750}
751
752static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200753nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
754{
Michal Vasko74c345f2018-02-07 10:37:11 +0100755 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200756
757 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100758 /* get the actual queue idx */
759 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200760
761 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100762 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200763 /* another equal value, simply cannot be */
764 ERRINT;
765 }
Michal Vaskod8340032018-02-12 14:41:00 +0100766 if (found == 2) {
767 /* move the following values */
768 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
769 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100770 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200771 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100772 if (i == 0) {
773 found = 1;
774 } else {
775 /* this is not okay, our id is in the middle of the queue */
776 found = 2;
777 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200778 }
779 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200780 if (!found) {
781 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100782 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200783 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100784
Michal Vasko103fe632018-02-12 16:37:45 +0100785 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100786 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100787 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100788 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
789 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200790}
791
Michal Vaskof04a52a2016-04-07 10:52:10 +0200792int
Michal Vasko26043172016-07-26 14:08:59 +0200793nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200794{
795 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200796 struct timespec ts;
797
Michal Vasko77a6abe2017-10-05 10:02:20 +0200798 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100799 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200800
801 /* LOCK */
802 ret = pthread_mutex_timedlock(&ps->lock, &ts);
803 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200804 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200805 return -1;
806 }
807
Michal Vasko74c345f2018-02-07 10:37:11 +0100808 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100809 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100810 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100811 pthread_mutex_unlock(&ps->lock);
812 return -1;
813 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100814
815 /* add ourselves into the queue */
816 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200817
818 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200819 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200820 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100821 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200822
823 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
824 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200825 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200826 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200827 nc_ps_queue_remove_id(ps, *id);
828 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200829 return -1;
830 }
831 }
832
Michal Vaskobe86fe32016-04-07 10:43:03 +0200833 /* UNLOCK */
834 pthread_mutex_unlock(&ps->lock);
835
836 return 0;
837}
838
Michal Vaskof04a52a2016-04-07 10:52:10 +0200839int
Michal Vasko26043172016-07-26 14:08:59 +0200840nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200841{
842 int ret;
843 struct timespec ts;
844
Michal Vasko77a6abe2017-10-05 10:02:20 +0200845 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100846 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200847
848 /* LOCK */
849 ret = pthread_mutex_timedlock(&ps->lock, &ts);
850 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200851 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200852 ret = -1;
853 }
854
Michal Vaskob30b99c2016-07-26 11:35:43 +0200855 /* we must be the first, it was our turn after all, right? */
856 if (ps->queue[ps->queue_begin] != id) {
857 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200858 /* UNLOCK */
859 if (!ret) {
860 pthread_mutex_unlock(&ps->lock);
861 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200862 return -1;
863 }
864
Michal Vaskobe86fe32016-04-07 10:43:03 +0200865 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200866 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200867
868 /* broadcast to all other threads that the queue moved */
869 pthread_cond_broadcast(&ps->cond);
870
Michal Vaskobe86fe32016-04-07 10:43:03 +0200871 /* UNLOCK */
872 if (!ret) {
873 pthread_mutex_unlock(&ps->lock);
874 }
875
876 return ret;
877}
878
Michal Vasko428087d2016-01-14 16:04:28 +0100879API struct nc_pollsession *
880nc_ps_new(void)
881{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100882 struct nc_pollsession *ps;
883
884 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100885 if (!ps) {
886 ERRMEM;
887 return NULL;
888 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200889 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100890 pthread_mutex_init(&ps->lock, NULL);
891
892 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100893}
894
895API void
896nc_ps_free(struct nc_pollsession *ps)
897{
fanchanghu3d4e7212017-08-09 09:42:30 +0800898 uint16_t i;
899
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100900 if (!ps) {
901 return;
902 }
903
Michal Vaskobe86fe32016-04-07 10:43:03 +0200904 if (ps->queue_len) {
905 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
906 }
907
fanchanghu3d4e7212017-08-09 09:42:30 +0800908 for (i = 0; i < ps->session_count; i++) {
909 free(ps->sessions[i]);
910 }
911
Michal Vasko428087d2016-01-14 16:04:28 +0100912 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100913 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200914 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100915
Michal Vasko428087d2016-01-14 16:04:28 +0100916 free(ps);
917}
918
919API int
920nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
921{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200922 uint8_t q_id;
923
Michal Vasko45e53ae2016-04-07 11:46:03 +0200924 if (!ps) {
925 ERRARG("ps");
926 return -1;
927 } else if (!session) {
928 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100929 return -1;
930 }
931
Michal Vasko48a63ed2016-03-01 09:48:21 +0100932 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200933 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200934 return -1;
935 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100936
Michal Vasko428087d2016-01-14 16:04:28 +0100937 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100938 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100939 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100940 ERRMEM;
941 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200942 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100943 return -1;
944 }
fanchanghu3d4e7212017-08-09 09:42:30 +0800945 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
946 if (!ps->sessions[ps->session_count - 1]) {
947 ERRMEM;
948 --ps->session_count;
949 /* UNLOCK */
950 nc_ps_unlock(ps, q_id, __func__);
951 return -1;
952 }
953 ps->sessions[ps->session_count - 1]->session = session;
954 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +0100955
Michal Vasko48a63ed2016-03-01 09:48:21 +0100956 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200957 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100958}
959
Michal Vasko48a63ed2016-03-01 09:48:21 +0100960static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100961_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100962{
963 uint16_t i;
964
Radek Krejcid5f978f2016-03-03 13:14:45 +0100965 if (index >= 0) {
966 i = (uint16_t)index;
967 goto remove;
968 }
Michal Vasko428087d2016-01-14 16:04:28 +0100969 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +0800970 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100971remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100972 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +0800973 if (i <= ps->session_count) {
974 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +0100975 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +0800976 }
977 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +0100978 free(ps->sessions);
979 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100980 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100981 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100982 return 0;
983 }
984 }
985
Michal Vaskof0537d82016-01-29 14:42:38 +0100986 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100987}
988
Michal Vasko48a63ed2016-03-01 09:48:21 +0100989API int
990nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
991{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200992 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200993 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100994
Michal Vasko45e53ae2016-04-07 11:46:03 +0200995 if (!ps) {
996 ERRARG("ps");
997 return -1;
998 } else if (!session) {
999 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001000 return -1;
1001 }
1002
1003 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001004 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001005 return -1;
1006 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001007
Radek Krejcid5f978f2016-03-03 13:14:45 +01001008 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001009
1010 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001011 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001012
Michal Vaskobe86fe32016-04-07 10:43:03 +02001013 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001014}
1015
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001016API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001017nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001018{
1019 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001020 struct nc_session *ret = NULL;
1021
1022 if (!ps) {
1023 ERRARG("ps");
1024 return NULL;
1025 }
1026
1027 /* LOCK */
1028 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1029 return NULL;
1030 }
1031
Michal Vasko4871c9d2017-10-09 14:48:39 +02001032 if (idx < ps->session_count) {
1033 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001034 }
1035
1036 /* UNLOCK */
1037 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1038
1039 return ret;
1040}
1041
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001042API uint16_t
1043nc_ps_session_count(struct nc_pollsession *ps)
1044{
1045 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001046 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001047 return 0;
1048 }
1049
Michal Vaskof4462fd2017-02-15 14:29:05 +01001050 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001051}
1052
Michal Vasko131120a2018-05-29 15:44:02 +02001053/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001054 * returns: NC_PSPOLL_ERROR,
1055 * NC_PSPOLL_BAD_RPC,
1056 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1057 * NC_PSPOLL_RPC
1058 */
1059static int
Michal Vasko131120a2018-05-29 15:44:02 +02001060nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001061{
1062 struct lyxml_elem *xml = NULL;
1063 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001064 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001065 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001066
Michal Vasko45e53ae2016-04-07 11:46:03 +02001067 if (!session) {
1068 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001069 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001070 } else if (!rpc) {
1071 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001072 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001073 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001074 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001075 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001076 }
1077
Michal Vasko131120a2018-05-29 15:44:02 +02001078 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001079
1080 switch (msgtype) {
1081 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001082 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001083 if (!*rpc) {
1084 ERRMEM;
1085 goto error;
1086 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001087
Radek Krejcif93c7d42016-04-06 13:41:15 +02001088 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001089 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1090 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001091 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001092 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001093 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001094 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001095 nc_server_reply_free(reply);
1096 if (ret == -1) {
1097 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001098 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001099 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1100 } else {
1101 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001102 }
Michal Vasko428087d2016-01-14 16:04:28 +01001103 (*rpc)->root = xml;
1104 break;
1105 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001106 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001107 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001108 goto error;
1109 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001110 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001111 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001112 goto error;
1113 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001114 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001115 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001116 goto error;
1117 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001118 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001119 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001120 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001121 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001122 break;
1123 }
1124
Michal Vasko71090fc2016-05-24 16:37:28 +02001125 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001126
1127error:
1128 /* cleanup */
1129 lyxml_free(server_opts.ctx, xml);
1130
Michal Vasko71090fc2016-05-24 16:37:28 +02001131 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001132}
1133
fanchanghu966f2de2016-07-21 02:28:57 -04001134API void
1135nc_set_global_rpc_clb(nc_rpc_clb clb)
1136{
1137 global_rpc_clb = clb;
1138}
1139
Radek Krejci93e80222016-10-03 13:34:25 +02001140API NC_MSG_TYPE
1141nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1142{
Michal Vasko131120a2018-05-29 15:44:02 +02001143 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001144
1145 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001146 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001147 ERRARG("session");
1148 return NC_MSG_ERROR;
1149 } else if (!notif || !notif->tree || !notif->eventtime) {
1150 ERRARG("notif");
1151 return NC_MSG_ERROR;
1152 }
1153
Michal Vasko131120a2018-05-29 15:44:02 +02001154 /* we do not need RPC lock for this, IO lock will be acquired properly */
1155 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1156 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001157 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001158 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001159
Michal Vasko131120a2018-05-29 15:44:02 +02001160 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001161}
1162
Michal Vasko131120a2018-05-29 15:44:02 +02001163/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 * returns: NC_PSPOLL_ERROR,
1165 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1166 * NC_PSPOLL_REPLY_ERROR,
1167 * 0
1168 */
1169static int
Michal Vasko131120a2018-05-29 15:44:02 +02001170nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001171{
1172 nc_rpc_clb clb;
1173 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001174 struct lys_node *rpc_act = NULL;
1175 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001176 int ret = 0;
1177 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001178
Michal Vasko4a827e52016-03-03 10:59:00 +01001179 if (!rpc) {
1180 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001181 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001182 }
1183
Michal Vasko90e8e692016-07-13 12:27:57 +02001184 if (rpc->tree->schema->nodetype == LYS_RPC) {
1185 /* RPC */
1186 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001187 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001188 /* action */
1189 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1190 if (elem->schema->nodetype == LYS_ACTION) {
1191 rpc_act = elem->schema;
1192 break;
1193 }
1194 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001195 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001196 if (!rpc_act) {
1197 ERRINT;
1198 return NC_PSPOLL_ERROR;
1199 }
1200 }
1201
1202 if (!rpc_act->priv) {
1203 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001204 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001205 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001206 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001207 reply = clb(rpc->tree, session);
1208 }
1209
1210 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001211 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001212 }
Michal Vasko131120a2018-05-29 15:44:02 +02001213 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001214 if (reply->type == NC_RPL_ERROR) {
1215 ret |= NC_PSPOLL_REPLY_ERROR;
1216 }
1217 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001218
Michal Vasko131120a2018-05-29 15:44:02 +02001219 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001220 ERR("Session %u: failed to write reply.", session->id);
1221 ret |= NC_PSPOLL_ERROR;
1222 }
Michal Vasko428087d2016-01-14 16:04:28 +01001223
1224 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1225 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1226 session->status = NC_STATUS_INVALID;
1227 }
1228
Michal Vasko71090fc2016-05-24 16:37:28 +02001229 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001230}
1231
Michal Vasko131120a2018-05-29 15:44:02 +02001232/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001233 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1234 * NC_PSPOLL_ERROR, (msg filled)
1235 * NC_PSPOLL_TIMEOUT,
1236 * NC_PSPOLL_RPC (some application data available),
1237 * NC_PSPOLL_SSH_CHANNEL,
1238 * NC_PSPOLL_SSH_MSG
1239 */
1240static int
Michal Vasko131120a2018-05-29 15:44:02 +02001241nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001242{
Michal Vasko9a327362017-01-11 11:31:46 +01001243 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001244 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001245#ifdef NC_ENABLED_SSH
1246 struct nc_session *new;
1247#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001248
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001249 /* check timeout first */
1250 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001251 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001252 sprintf(msg, "session idle timeout elapsed");
1253 session->status = NC_STATUS_INVALID;
1254 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1255 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1256 }
1257
Michal Vasko131120a2018-05-29 15:44:02 +02001258 r = nc_session_io_lock(session, io_timeout, __func__);
1259 if (r < 0) {
1260 sprintf(msg, "session IO lock failed to be acquired");
1261 return NC_PSPOLL_ERROR;
1262 } else if (!r) {
1263 return NC_PSPOLL_TIMEOUT;
1264 }
1265
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001266 switch (session->ti_type) {
1267#ifdef NC_ENABLED_SSH
1268 case NC_TI_LIBSSH:
1269 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001270 if (r == SSH_EOF) {
1271 sprintf(msg, "SSH channel unexpected EOF");
1272 session->status = NC_STATUS_INVALID;
1273 session->term_reason = NC_SESSION_TERM_DROPPED;
1274 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1275 } else if (r == SSH_ERROR) {
1276 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001277 session->status = NC_STATUS_INVALID;
1278 session->term_reason = NC_SESSION_TERM_OTHER;
1279 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001280 } else if (!r) {
1281 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1282 /* new SSH message */
1283 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1284 if (session->ti.libssh.next) {
1285 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1286 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1287 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1288 /* new NETCONF SSH channel */
1289 ret = NC_PSPOLL_SSH_CHANNEL;
1290 break;
1291 }
1292 }
1293 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001294 break;
1295 }
1296 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001297
Michal Vasko8dcaa882017-10-19 14:28:42 +02001298 /* just some SSH message */
1299 ret = NC_PSPOLL_SSH_MSG;
1300 } else {
1301 ret = NC_PSPOLL_TIMEOUT;
1302 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001303 } else {
1304 /* we have some application data */
1305 ret = NC_PSPOLL_RPC;
1306 }
1307 break;
1308#endif
1309#ifdef NC_ENABLED_TLS
1310 case NC_TI_OPENSSL:
1311 r = SSL_pending(session->ti.tls);
1312 if (!r) {
1313 /* no data pending in the SSL buffer, poll fd */
1314 pfd.fd = SSL_get_rfd(session->ti.tls);
1315 if (pfd.fd < 0) {
1316 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1317 ret = NC_PSPOLL_ERROR;
1318 break;
1319 }
1320 pfd.events = POLLIN;
1321 pfd.revents = 0;
1322 r = poll(&pfd, 1, 0);
1323
1324 if ((r < 0) && (errno != EINTR)) {
1325 sprintf(msg, "poll failed (%s)", strerror(errno));
1326 session->status = NC_STATUS_INVALID;
1327 ret = NC_PSPOLL_ERROR;
1328 } else if (r > 0) {
1329 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1330 sprintf(msg, "communication socket unexpectedly closed");
1331 session->status = NC_STATUS_INVALID;
1332 session->term_reason = NC_SESSION_TERM_DROPPED;
1333 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1334 } else if (pfd.revents & POLLERR) {
1335 sprintf(msg, "communication socket error");
1336 session->status = NC_STATUS_INVALID;
1337 session->term_reason = NC_SESSION_TERM_OTHER;
1338 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1339 } else {
1340 ret = NC_PSPOLL_RPC;
1341 }
1342 } else {
1343 ret = NC_PSPOLL_TIMEOUT;
1344 }
1345 } else {
1346 ret = NC_PSPOLL_RPC;
1347 }
1348 break;
1349#endif
1350 case NC_TI_FD:
1351 pfd.fd = session->ti.fd.in;
1352 pfd.events = POLLIN;
1353 pfd.revents = 0;
1354 r = poll(&pfd, 1, 0);
1355
1356 if ((r < 0) && (errno != EINTR)) {
1357 sprintf(msg, "poll failed (%s)", strerror(errno));
1358 session->status = NC_STATUS_INVALID;
1359 ret = NC_PSPOLL_ERROR;
1360 } else if (r > 0) {
1361 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1362 sprintf(msg, "communication socket unexpectedly closed");
1363 session->status = NC_STATUS_INVALID;
1364 session->term_reason = NC_SESSION_TERM_DROPPED;
1365 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1366 } else if (pfd.revents & POLLERR) {
1367 sprintf(msg, "communication socket error");
1368 session->status = NC_STATUS_INVALID;
1369 session->term_reason = NC_SESSION_TERM_OTHER;
1370 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1371 } else {
1372 ret = NC_PSPOLL_RPC;
1373 }
1374 } else {
1375 ret = NC_PSPOLL_TIMEOUT;
1376 }
1377 break;
1378 case NC_TI_NONE:
1379 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1380 ret = NC_PSPOLL_ERROR;
1381 break;
1382 }
1383
Michal Vasko131120a2018-05-29 15:44:02 +02001384 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001385 return ret;
1386}
1387
1388API int
1389nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1390{
1391 int ret, r;
1392 uint8_t q_id;
1393 uint16_t i, j;
1394 char msg[256];
1395 struct timespec ts_timeout, ts_cur;
1396 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001397 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001398 struct nc_server_rpc *rpc = NULL;
1399
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001400 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001401 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001402 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001403 }
1404
Michal Vaskoade892d2017-02-22 13:40:35 +01001405 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001406 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001407 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001408 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001409
Michal Vaskoade892d2017-02-22 13:40:35 +01001410 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001411 nc_ps_unlock(ps, q_id, __func__);
1412 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001413 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001414
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001415 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001416 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001417 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001418 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001419 nc_addtimespec(&ts_timeout, timeout);
1420 }
1421
1422 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001423 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001424 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001425 if (ps->last_event_session == ps->session_count - 1) {
1426 i = j = 0;
1427 } else {
1428 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001429 }
Michal Vasko9a327362017-01-11 11:31:46 +01001430 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001431 cur_ps_session = ps->sessions[i];
1432 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001433
Michal Vasko131120a2018-05-29 15:44:02 +02001434 /* SESSION RPC LOCK */
1435 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001436 if (r == -1) {
1437 ret = NC_PSPOLL_ERROR;
1438 } else if (r == 1) {
1439 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001440 switch (cur_ps_session->state) {
1441 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001442 if (cur_session->status == NC_STATUS_RUNNING) {
1443 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001444 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001445
Michal Vasko131120a2018-05-29 15:44:02 +02001446 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001447 switch (ret) {
1448 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1449 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001450 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001451 break;
1452 case NC_PSPOLL_ERROR:
1453 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001454 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001455 break;
1456 case NC_PSPOLL_TIMEOUT:
1457#ifdef NC_ENABLED_SSH
1458 case NC_PSPOLL_SSH_CHANNEL:
1459 case NC_PSPOLL_SSH_MSG:
1460#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001461 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001462 break;
1463 case NC_PSPOLL_RPC:
1464 /* let's keep the state busy, we are not done with this session */
1465 break;
1466 }
1467 } else {
1468 /* session is not fine, let the caller know */
1469 ret = NC_PSPOLL_SESSION_TERM;
1470 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1471 ret |= NC_PSPOLL_SESSION_ERROR;
1472 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001473 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001474 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001475 break;
1476 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001477 /* it definitely should not be busy because we have the lock */
1478 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001479 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001480 break;
1481 case NC_PS_STATE_INVALID:
1482 /* we got it locked, but it will be freed, let it be */
1483 ret = NC_PSPOLL_TIMEOUT;
1484 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001485 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001486
Michal Vasko131120a2018-05-29 15:44:02 +02001487 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001488 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001489 /* SESSION RPC UNLOCK */
1490 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001491 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001492 } else {
1493 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001494 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001495 }
Michal Vasko428087d2016-01-14 16:04:28 +01001496
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001497 /* something happened */
1498 if (ret != NC_PSPOLL_TIMEOUT) {
1499 break;
1500 }
1501
Michal Vasko9a327362017-01-11 11:31:46 +01001502 if (i == ps->session_count - 1) {
1503 i = 0;
1504 } else {
1505 ++i;
1506 }
1507 } while (i != j);
1508
Michal Vaskoade892d2017-02-22 13:40:35 +01001509 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001510 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001511 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001512 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001513 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001514
1515 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1516 /* final timeout */
1517 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001518 }
Michal Vasko428087d2016-01-14 16:04:28 +01001519 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001520 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001521
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001522 /* do we want to return the session? */
1523 switch (ret) {
1524 case NC_PSPOLL_RPC:
1525 case NC_PSPOLL_SESSION_TERM:
1526 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1527#ifdef NC_ENABLED_SSH
1528 case NC_PSPOLL_SSH_CHANNEL:
1529 case NC_PSPOLL_SSH_MSG:
1530#endif
1531 if (session) {
1532 *session = cur_session;
1533 }
1534 ps->last_event_session = i;
1535 break;
1536 default:
1537 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001538 }
Michal Vasko428087d2016-01-14 16:04:28 +01001539
Michal Vaskoade892d2017-02-22 13:40:35 +01001540 /* PS UNLOCK */
1541 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001542
Michal Vasko131120a2018-05-29 15:44:02 +02001543 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001544 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001545 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001546 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1547 if (cur_session->status != NC_STATUS_RUNNING) {
1548 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001549 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001550 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001551 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001552 }
1553 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001554 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001555
1556 /* process RPC, not needed afterwards */
Michal Vasko131120a2018-05-29 15:44:02 +02001557 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001558 nc_server_rpc_free(rpc, server_opts.ctx);
1559
1560 if (cur_session->status != NC_STATUS_RUNNING) {
1561 ret |= NC_PSPOLL_SESSION_TERM;
1562 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1563 ret |= NC_PSPOLL_SESSION_ERROR;
1564 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001565 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001566 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001567 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001568 }
Michal Vasko428087d2016-01-14 16:04:28 +01001569 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001570
Michal Vasko131120a2018-05-29 15:44:02 +02001571 /* SESSION RPC UNLOCK */
1572 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001573 }
1574
Michal Vasko48a63ed2016-03-01 09:48:21 +01001575 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001576}
1577
Michal Vaskod09eae62016-02-01 10:32:52 +01001578API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001579nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001580{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001581 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001582 uint16_t i;
1583 struct nc_session *session;
1584
Michal Vasko9a25e932016-02-01 10:36:42 +01001585 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001586 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001587 return;
1588 }
1589
Michal Vasko48a63ed2016-03-01 09:48:21 +01001590 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001591 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001592 return;
1593 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001594
Michal Vasko48a63ed2016-03-01 09:48:21 +01001595 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001596 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001597 nc_session_free(ps->sessions[i]->session, data_free);
1598 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001599 }
1600 free(ps->sessions);
1601 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001602 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001603 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001604 } else {
1605 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001606 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1607 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001608 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001609 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001610 continue;
1611 }
1612
1613 ++i;
1614 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001615 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001616
1617 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001618 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001619}
1620
Radek Krejci53691be2016-02-22 13:58:37 +01001621#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001622
Michal Vaskoe2713da2016-08-22 16:06:40 +02001623API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001624nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001625{
Michal Vasko3031aae2016-01-27 16:07:18 +01001626 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001627 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001628
Michal Vasko45e53ae2016-04-07 11:46:03 +02001629 if (!name) {
1630 ERRARG("name");
1631 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001632 }
1633
Michal Vaskoade892d2017-02-22 13:40:35 +01001634 /* BIND LOCK */
1635 pthread_mutex_lock(&server_opts.bind_lock);
1636
1637 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001638 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001639
1640 /* check name uniqueness */
1641 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001642 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001643 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001644 ret = -1;
1645 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001646 }
1647 }
1648
Michal Vasko3031aae2016-01-27 16:07:18 +01001649 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001650 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001651 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001652 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001653 ret = -1;
1654 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001655 }
1656 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001657 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001658
Michal Vaskoe2713da2016-08-22 16:06:40 +02001659 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001660 if (!server_opts.binds) {
1661 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001662 ret = -1;
1663 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001664 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001665
Michal Vasko2e6defd2016-10-07 15:48:15 +02001666 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1667 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1668 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001669 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001670
1671 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001672#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001673 case NC_TI_LIBSSH:
1674 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1675 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1676 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001677 ret = -1;
1678 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001679 }
1680 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1681 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1682 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1683 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1684 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001685#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001686#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001687 case NC_TI_OPENSSL:
1688 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1689 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1690 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001691 ret = -1;
1692 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001693 }
1694 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001695#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001696 default:
1697 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001698 ret = -1;
1699 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001700 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001701
Michal Vaskoade892d2017-02-22 13:40:35 +01001702cleanup:
1703 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001704 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001705
Michal Vaskoade892d2017-02-22 13:40:35 +01001706 /* BIND UNLOCK */
1707 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001708
Michal Vaskoade892d2017-02-22 13:40:35 +01001709 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001710}
1711
Michal Vasko3031aae2016-01-27 16:07:18 +01001712int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001713nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001714{
1715 struct nc_endpt *endpt;
1716 struct nc_bind *bind = NULL;
1717 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001718 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001719
Michal Vasko45e53ae2016-04-07 11:46:03 +02001720 if (!endpt_name) {
1721 ERRARG("endpt_name");
1722 return -1;
1723 } else if ((!address && !port) || (address && port)) {
1724 ERRARG("address and port");
1725 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001726 }
1727
Michal Vaskoe2713da2016-08-22 16:06:40 +02001728 if (address) {
1729 set_addr = 1;
1730 } else {
1731 set_addr = 0;
1732 }
1733
Michal Vaskoade892d2017-02-22 13:40:35 +01001734 /* BIND LOCK */
1735 pthread_mutex_lock(&server_opts.bind_lock);
1736
1737 /* ENDPT LOCK */
1738 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001739 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001740 /* BIND UNLOCK */
1741 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001742 return -1;
1743 }
1744
Michal Vaskoe2713da2016-08-22 16:06:40 +02001745 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001746
Michal Vaskoe2713da2016-08-22 16:06:40 +02001747 if (set_addr) {
1748 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001749 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001750 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001751 }
1752
Michal Vaskoe2713da2016-08-22 16:06:40 +02001753 /* we have all the information we need to create a listening socket */
1754 if (address && port) {
1755 /* create new socket, close the old one */
1756 sock = nc_sock_listen(address, port);
1757 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001758 ret = -1;
1759 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001760 }
1761
1762 if (bind->sock > -1) {
1763 close(bind->sock);
1764 }
1765 bind->sock = sock;
1766 } /* else we are just setting address or port */
1767
1768 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001769 lydict_remove(server_opts.ctx, bind->address);
1770 bind->address = lydict_insert(server_opts.ctx, address, 0);
1771 } else {
1772 bind->port = port;
1773 }
1774
Michal Vaskoe2713da2016-08-22 16:06:40 +02001775 if (sock > -1) {
1776#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001777 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001778#elif defined(NC_ENABLED_SSH)
1779 VRB("Listening on %s:%u for SSH connections.", address, port);
1780#else
1781 VRB("Listening on %s:%u for TLS connections.", address, port);
1782#endif
1783 }
1784
Michal Vasko4c1fb492017-01-30 14:31:07 +01001785cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001786 /* ENDPT UNLOCK */
1787 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001788
Michal Vaskoade892d2017-02-22 13:40:35 +01001789 /* BIND UNLOCK */
1790 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001791
Michal Vasko4c1fb492017-01-30 14:31:07 +01001792 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001793}
1794
Michal Vaskoe2713da2016-08-22 16:06:40 +02001795API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001796nc_server_endpt_set_address(const char *endpt_name, const char *address)
1797{
1798 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1799}
1800
1801API int
1802nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1803{
1804 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1805}
1806
1807API int
Michal Vasko59050372016-11-22 14:33:55 +01001808nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001809{
1810 uint32_t i;
1811 int ret = -1;
1812
Michal Vaskoade892d2017-02-22 13:40:35 +01001813 /* BIND LOCK */
1814 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001815
Michal Vaskoade892d2017-02-22 13:40:35 +01001816 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001817 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001818
Michal Vasko59050372016-11-22 14:33:55 +01001819 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001820 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001821 for (i = 0; i < server_opts.endpt_count; ++i) {
1822 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001823 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001824#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001825 case NC_TI_LIBSSH:
1826 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1827 free(server_opts.endpts[i].opts.ssh);
1828 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001829#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001830#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001831 case NC_TI_OPENSSL:
1832 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1833 free(server_opts.endpts[i].opts.tls);
1834 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001835#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001836 default:
1837 ERRINT;
1838 /* won't get here ...*/
1839 break;
1840 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001841 ret = 0;
1842 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001843 free(server_opts.endpts);
1844 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001845
1846 /* remove all binds */
1847 for (i = 0; i < server_opts.endpt_count; ++i) {
1848 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1849 if (server_opts.binds[i].sock > -1) {
1850 close(server_opts.binds[i].sock);
1851 }
1852 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001853 free(server_opts.binds);
1854 server_opts.binds = NULL;
1855
Michal Vasko3031aae2016-01-27 16:07:18 +01001856 server_opts.endpt_count = 0;
1857
Michal Vasko1a38c862016-01-15 15:50:07 +01001858 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001859 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001860 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001861 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001862 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001863 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001864 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001865#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001866 case NC_TI_LIBSSH:
1867 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1868 free(server_opts.endpts[i].opts.ssh);
1869 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001870#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001871#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001872 case NC_TI_OPENSSL:
1873 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1874 free(server_opts.endpts[i].opts.tls);
1875 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001876#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001877 default:
1878 ERRINT;
1879 break;
1880 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001881
Michal Vaskoe2713da2016-08-22 16:06:40 +02001882 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001883 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1884 if (server_opts.binds[i].sock > -1) {
1885 close(server_opts.binds[i].sock);
1886 }
1887
1888 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001889 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001890 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001891 free(server_opts.binds);
1892 server_opts.binds = NULL;
1893 free(server_opts.endpts);
1894 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001895 } else if (i < server_opts.endpt_count) {
1896 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1897 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001898 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001899
1900 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001901 if (name) {
1902 break;
1903 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001904 }
1905 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001906 }
1907
Michal Vaskoade892d2017-02-22 13:40:35 +01001908 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001909 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001910
Michal Vaskoade892d2017-02-22 13:40:35 +01001911 /* BIND UNLOCK */
1912 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001913
1914 return ret;
1915}
1916
Michal Vasko71090fc2016-05-24 16:37:28 +02001917API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001918nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001919{
Michal Vasko71090fc2016-05-24 16:37:28 +02001920 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001921 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001922 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001923 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02001924 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01001925
Michal Vasko45e53ae2016-04-07 11:46:03 +02001926 if (!server_opts.ctx) {
1927 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001928 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001929 } else if (!session) {
1930 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001931 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001932 }
1933
Michal Vaskoade892d2017-02-22 13:40:35 +01001934 /* BIND LOCK */
1935 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001936
1937 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001938 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001939 /* BIND UNLOCK */
1940 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001941 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001942 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001943
Michal Vaskoe2713da2016-08-22 16:06:40 +02001944 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001945 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001946 /* BIND UNLOCK */
1947 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001948 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001949 if (!ret) {
1950 return NC_MSG_WOULDBLOCK;
1951 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001952 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001953 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001954
1955 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1956 /* ENDPT READ LOCK */
1957 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1958
1959 /* BIND UNLOCK */
1960 pthread_mutex_unlock(&server_opts.bind_lock);
1961
Michal Vaskob48aa812016-01-18 14:13:09 +01001962 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001963
Michal Vasko131120a2018-05-29 15:44:02 +02001964 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01001965 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001966 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001967 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001968 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001969 msgtype = NC_MSG_ERROR;
1970 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001971 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001972 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01001973 (*session)->ctx = server_opts.ctx;
1974 (*session)->flags = NC_SESSION_SHAREDCTX;
1975 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1976 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001977
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001978 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001979#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001980 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1981 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001982 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001983 if (ret < 0) {
1984 msgtype = NC_MSG_ERROR;
1985 goto cleanup;
1986 } else if (!ret) {
1987 msgtype = NC_MSG_WOULDBLOCK;
1988 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001989 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001990 } else
1991#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001992#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001993 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1994 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001995 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001996 if (ret < 0) {
1997 msgtype = NC_MSG_ERROR;
1998 goto cleanup;
1999 } else if (!ret) {
2000 msgtype = NC_MSG_WOULDBLOCK;
2001 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002002 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002003 } else
2004#endif
2005 {
Michal Vasko9e036d52016-01-08 10:49:26 +01002006 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002007 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002008 msgtype = NC_MSG_ERROR;
2009 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002010 }
2011
Michal Vasko2cc4c682016-03-01 09:16:48 +01002012 (*session)->data = NULL;
2013
Michal Vaskoade892d2017-02-22 13:40:35 +01002014 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002015 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002016
Michal Vaskob48aa812016-01-18 14:13:09 +01002017 /* assign new SID atomically */
2018 /* LOCK */
2019 pthread_spin_lock(&server_opts.sid_lock);
2020 (*session)->id = server_opts.new_session_id++;
2021 /* UNLOCK */
2022 pthread_spin_unlock(&server_opts.sid_lock);
2023
Michal Vasko9e036d52016-01-08 10:49:26 +01002024 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002025 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002026 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002027 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002028 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002029 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002030 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002031
2032 nc_gettimespec_mono(&ts_cur);
2033 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2034 nc_gettimespec_real(&ts_cur);
2035 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002036 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002037
Michal Vasko71090fc2016-05-24 16:37:28 +02002038 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002039
Michal Vasko71090fc2016-05-24 16:37:28 +02002040cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002041 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002042 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002043
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002044 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002045 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002046 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002047}
2048
Michal Vasko2e6defd2016-10-07 15:48:15 +02002049API int
2050nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
2051{
2052 uint16_t i;
2053
2054 if (!name) {
2055 ERRARG("name");
2056 return -1;
2057 } else if (!ti) {
2058 ERRARG("ti");
2059 return -1;
2060 }
2061
2062 /* WRITE LOCK */
2063 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2064
2065 /* check name uniqueness */
2066 for (i = 0; i < server_opts.ch_client_count; ++i) {
2067 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2068 ERR("Call Home client \"%s\" already exists.", name);
2069 /* WRITE UNLOCK */
2070 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2071 return -1;
2072 }
2073 }
2074
2075 ++server_opts.ch_client_count;
2076 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2077 if (!server_opts.ch_clients) {
2078 ERRMEM;
2079 /* WRITE UNLOCK */
2080 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2081 return -1;
2082 }
2083 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
2084 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002085 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2086 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002087
2088 switch (ti) {
2089#ifdef NC_ENABLED_SSH
2090 case NC_TI_LIBSSH:
2091 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2092 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2093 ERRMEM;
2094 /* WRITE UNLOCK */
2095 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2096 return -1;
2097 }
2098 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2099 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2100 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2101 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2102 break;
2103#endif
2104#ifdef NC_ENABLED_TLS
2105 case NC_TI_OPENSSL:
2106 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2107 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2108 ERRMEM;
2109 /* WRITE UNLOCK */
2110 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2111 return -1;
2112 }
2113 break;
2114#endif
2115 default:
2116 ERRINT;
2117 /* WRITE UNLOCK */
2118 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2119 return -1;
2120 }
2121
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002122 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2123
Michal Vasko2e6defd2016-10-07 15:48:15 +02002124 /* set CH default options */
2125 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2126 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2127
2128 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2129
2130 /* WRITE UNLOCK */
2131 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2132
2133 return 0;
2134}
2135
2136API int
Michal Vasko59050372016-11-22 14:33:55 +01002137nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002138{
2139 uint16_t i, j;
2140 int ret = -1;
2141
2142 /* WRITE LOCK */
2143 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2144
Michal Vasko59050372016-11-22 14:33:55 +01002145 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002146 /* remove all CH clients */
2147 for (i = 0; i < server_opts.ch_client_count; ++i) {
2148 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2149
2150 /* remove all endpoints */
2151 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2152 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2153 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2154 }
2155 free(server_opts.ch_clients[i].ch_endpts);
2156
2157 switch (server_opts.ch_clients[i].ti) {
2158#ifdef NC_ENABLED_SSH
2159 case NC_TI_LIBSSH:
2160 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2161 free(server_opts.ch_clients[i].opts.ssh);
2162 break;
2163#endif
2164#ifdef NC_ENABLED_TLS
2165 case NC_TI_OPENSSL:
2166 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2167 free(server_opts.ch_clients[i].opts.tls);
2168 break;
2169#endif
2170 default:
2171 ERRINT;
2172 /* won't get here ...*/
2173 break;
2174 }
2175
2176 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2177
2178 ret = 0;
2179 }
2180 free(server_opts.ch_clients);
2181 server_opts.ch_clients = NULL;
2182
2183 server_opts.ch_client_count = 0;
2184
2185 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002186 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002187 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002188 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 +02002189 /* remove endpt */
2190 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2191
2192 switch (server_opts.ch_clients[i].ti) {
2193#ifdef NC_ENABLED_SSH
2194 case NC_TI_LIBSSH:
2195 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2196 free(server_opts.ch_clients[i].opts.ssh);
2197 break;
2198#endif
2199#ifdef NC_ENABLED_TLS
2200 case NC_TI_OPENSSL:
2201 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2202 free(server_opts.ch_clients[i].opts.tls);
2203 break;
2204#endif
2205 default:
2206 ERRINT;
2207 break;
2208 }
2209
2210 /* remove all endpoints */
2211 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2212 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2213 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2214 }
2215 free(server_opts.ch_clients[i].ch_endpts);
2216
2217 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2218
2219 /* move last client and endpoint(s) to the empty space */
2220 --server_opts.ch_client_count;
2221 if (i < server_opts.ch_client_count) {
2222 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2223 sizeof *server_opts.ch_clients);
2224 } else if (!server_opts.ch_client_count) {
2225 free(server_opts.ch_clients);
2226 server_opts.ch_clients = NULL;
2227 }
2228
2229 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002230 if (name) {
2231 break;
2232 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002233 }
2234 }
2235 }
2236
2237 /* WRITE UNLOCK */
2238 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2239
2240 return ret;
2241}
2242
2243API int
2244nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2245{
2246 uint16_t i;
2247 struct nc_ch_client *client;
2248
2249 if (!client_name) {
2250 ERRARG("client_name");
2251 return -1;
2252 } else if (!endpt_name) {
2253 ERRARG("endpt_name");
2254 return -1;
2255 }
2256
2257 /* LOCK */
2258 client = nc_server_ch_client_lock(client_name, 0, NULL);
2259 if (!client) {
2260 return -1;
2261 }
2262
2263 for (i = 0; i < client->ch_endpt_count; ++i) {
2264 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2265 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2266 /* UNLOCK */
2267 nc_server_ch_client_unlock(client);
2268 return -1;
2269 }
2270 }
2271
2272 ++client->ch_endpt_count;
2273 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2274 if (!client->ch_endpts) {
2275 ERRMEM;
2276 /* UNLOCK */
2277 nc_server_ch_client_unlock(client);
2278 return -1;
2279 }
2280
2281 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2282 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2283 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +00002284 client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002285
2286 /* UNLOCK */
2287 nc_server_ch_client_unlock(client);
2288
2289 return 0;
2290}
2291
2292API int
2293nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2294{
2295 uint16_t i;
2296 int ret = -1;
2297 struct nc_ch_client *client;
2298
2299 if (!client_name) {
2300 ERRARG("client_name");
2301 return -1;
2302 }
2303
2304 /* LOCK */
2305 client = nc_server_ch_client_lock(client_name, 0, NULL);
2306 if (!client) {
2307 return -1;
2308 }
2309
2310 if (!endpt_name) {
2311 /* remove all endpoints */
2312 for (i = 0; i < client->ch_endpt_count; ++i) {
2313 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2314 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Frank Rimpler9f838b02018-07-25 06:44:03 +00002315 if (client->ch_endpts[i].sock_pending != -1) {
2316 close(client->ch_endpts[i].sock_pending);
2317 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002318 }
2319 free(client->ch_endpts);
2320 client->ch_endpts = NULL;
2321 client->ch_endpt_count = 0;
2322
2323 ret = 0;
2324 } else {
2325 for (i = 0; i < client->ch_endpt_count; ++i) {
2326 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2327 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2328 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002329
Michal Vasko4f921012016-10-20 14:07:45 +02002330 /* move last endpoint to the empty space */
2331 --client->ch_endpt_count;
2332 if (i < client->ch_endpt_count) {
2333 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2334 } else if (!server_opts.ch_client_count) {
2335 free(server_opts.ch_clients);
2336 server_opts.ch_clients = NULL;
2337 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002338
Michal Vasko4f921012016-10-20 14:07:45 +02002339 ret = 0;
2340 break;
2341 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002342 }
2343 }
2344
2345 /* UNLOCK */
2346 nc_server_ch_client_unlock(client);
2347
2348 return ret;
2349}
2350
2351API int
2352nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2353{
2354 uint16_t i;
2355 int ret = -1;
2356 struct nc_ch_client *client;
2357
2358 if (!client_name) {
2359 ERRARG("client_name");
2360 return -1;
2361 } else if (!endpt_name) {
2362 ERRARG("endpt_name");
2363 return -1;
2364 } else if (!address) {
2365 ERRARG("address");
2366 return -1;
2367 }
2368
2369 /* LOCK */
2370 client = nc_server_ch_client_lock(client_name, 0, NULL);
2371 if (!client) {
2372 return -1;
2373 }
2374
2375 for (i = 0; i < client->ch_endpt_count; ++i) {
2376 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2377 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2378 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2379
2380 ret = 0;
2381 break;
2382 }
2383 }
2384
2385 /* UNLOCK */
2386 nc_server_ch_client_unlock(client);
2387
2388 if (ret == -1) {
2389 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2390 }
2391
2392 return ret;
2393}
2394
2395API int
2396nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2397{
2398 uint16_t i;
2399 int ret = -1;
2400 struct nc_ch_client *client;
2401
2402 if (!client_name) {
2403 ERRARG("client_name");
2404 return -1;
2405 } else if (!endpt_name) {
2406 ERRARG("endpt_name");
2407 return -1;
2408 } else if (!port) {
2409 ERRARG("port");
2410 return -1;
2411 }
2412
2413 /* LOCK */
2414 client = nc_server_ch_client_lock(client_name, 0, NULL);
2415 if (!client) {
2416 return -1;
2417 }
2418
2419 for (i = 0; i < client->ch_endpt_count; ++i) {
2420 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2421 client->ch_endpts[i].port = port;
2422
2423 ret = 0;
2424 break;
2425 }
2426 }
2427
2428 /* UNLOCK */
2429 nc_server_ch_client_unlock(client);
2430
2431 if (ret == -1) {
2432 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2433 }
2434
2435 return ret;
2436}
2437
2438API int
2439nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2440{
2441 struct nc_ch_client *client;
2442
2443 if (!client_name) {
2444 ERRARG("client_name");
2445 return -1;
2446 } else if (!conn_type) {
2447 ERRARG("conn_type");
2448 return -1;
2449 }
2450
2451 /* LOCK */
2452 client = nc_server_ch_client_lock(client_name, 0, NULL);
2453 if (!client) {
2454 return -1;
2455 }
2456
2457 if (client->conn_type != conn_type) {
2458 client->conn_type = conn_type;
2459
2460 /* set default options */
2461 switch (conn_type) {
2462 case NC_CH_PERSIST:
2463 client->conn.persist.idle_timeout = 86400;
2464 client->conn.persist.ka_max_wait = 30;
2465 client->conn.persist.ka_max_attempts = 3;
2466 break;
2467 case NC_CH_PERIOD:
2468 client->conn.period.idle_timeout = 300;
2469 client->conn.period.reconnect_timeout = 60;
2470 break;
2471 default:
2472 ERRINT;
2473 break;
2474 }
2475 }
2476
2477 /* UNLOCK */
2478 nc_server_ch_client_unlock(client);
2479
2480 return 0;
2481}
2482
2483API int
2484nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2485{
2486 struct nc_ch_client *client;
2487
2488 if (!client_name) {
2489 ERRARG("client_name");
2490 return -1;
2491 }
2492
2493 /* LOCK */
2494 client = nc_server_ch_client_lock(client_name, 0, NULL);
2495 if (!client) {
2496 return -1;
2497 }
2498
2499 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002500 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002501 /* UNLOCK */
2502 nc_server_ch_client_unlock(client);
2503 return -1;
2504 }
2505
2506 client->conn.persist.idle_timeout = idle_timeout;
2507
2508 /* UNLOCK */
2509 nc_server_ch_client_unlock(client);
2510
2511 return 0;
2512}
2513
2514API int
2515nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2516{
2517 struct nc_ch_client *client;
2518
2519 if (!client_name) {
2520 ERRARG("client_name");
2521 return -1;
2522 } else if (!max_wait) {
2523 ERRARG("max_wait");
2524 return -1;
2525 }
2526
2527 /* LOCK */
2528 client = nc_server_ch_client_lock(client_name, 0, NULL);
2529 if (!client) {
2530 return -1;
2531 }
2532
2533 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002534 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002535 /* UNLOCK */
2536 nc_server_ch_client_unlock(client);
2537 return -1;
2538 }
2539
2540 client->conn.persist.ka_max_wait = max_wait;
2541
2542 /* UNLOCK */
2543 nc_server_ch_client_unlock(client);
2544
2545 return 0;
2546}
2547
2548API int
2549nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2550{
2551 struct nc_ch_client *client;
2552
2553 if (!client_name) {
2554 ERRARG("client_name");
2555 return -1;
2556 }
2557
2558 /* LOCK */
2559 client = nc_server_ch_client_lock(client_name, 0, NULL);
2560 if (!client) {
2561 return -1;
2562 }
2563
2564 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002565 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002566 /* UNLOCK */
2567 nc_server_ch_client_unlock(client);
2568 return -1;
2569 }
2570
2571 client->conn.persist.ka_max_attempts = max_attempts;
2572
2573 /* UNLOCK */
2574 nc_server_ch_client_unlock(client);
2575
2576 return 0;
2577}
2578
2579API int
2580nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2581{
2582 struct nc_ch_client *client;
2583
2584 if (!client_name) {
2585 ERRARG("client_name");
2586 return -1;
2587 }
2588
2589 /* LOCK */
2590 client = nc_server_ch_client_lock(client_name, 0, NULL);
2591 if (!client) {
2592 return -1;
2593 }
2594
2595 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002596 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002597 /* UNLOCK */
2598 nc_server_ch_client_unlock(client);
2599 return -1;
2600 }
2601
2602 client->conn.period.idle_timeout = idle_timeout;
2603
2604 /* UNLOCK */
2605 nc_server_ch_client_unlock(client);
2606
2607 return 0;
2608}
2609
2610API int
2611nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2612{
2613 struct nc_ch_client *client;
2614
2615 if (!client_name) {
2616 ERRARG("client_name");
2617 return -1;
2618 } else if (!reconnect_timeout) {
2619 ERRARG("reconnect_timeout");
2620 return -1;
2621 }
2622
2623 /* LOCK */
2624 client = nc_server_ch_client_lock(client_name, 0, NULL);
2625 if (!client) {
2626 return -1;
2627 }
2628
2629 if (client->conn_type != NC_CH_PERIOD) {
2630 ERR("Call Home client \"%s\" is not of periodic connection type.");
2631 /* UNLOCK */
2632 nc_server_ch_client_unlock(client);
2633 return -1;
2634 }
2635
2636 client->conn.period.reconnect_timeout = reconnect_timeout;
2637
2638 /* UNLOCK */
2639 nc_server_ch_client_unlock(client);
2640
2641 return 0;
2642}
2643
2644API int
2645nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2646{
2647 struct nc_ch_client *client;
2648
2649 if (!client_name) {
2650 ERRARG("client_name");
2651 return -1;
2652 }
2653
2654 /* LOCK */
2655 client = nc_server_ch_client_lock(client_name, 0, NULL);
2656 if (!client) {
2657 return -1;
2658 }
2659
2660 client->start_with = start_with;
2661
2662 /* UNLOCK */
2663 nc_server_ch_client_unlock(client);
2664
2665 return 0;
2666}
2667
2668API int
2669nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2670{
2671 struct nc_ch_client *client;
2672
2673 if (!client_name) {
2674 ERRARG("client_name");
2675 return -1;
2676 } else if (!max_attempts) {
2677 ERRARG("max_attempts");
2678 return -1;
2679 }
2680
2681 /* LOCK */
2682 client = nc_server_ch_client_lock(client_name, 0, NULL);
2683 if (!client) {
2684 return -1;
2685 }
2686
2687 client->max_attempts = max_attempts;
2688
2689 /* UNLOCK */
2690 nc_server_ch_client_unlock(client);
2691
2692 return 0;
2693}
2694
2695/* client lock is expected to be held */
2696static NC_MSG_TYPE
2697nc_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 +01002698{
Michal Vasko71090fc2016-05-24 16:37:28 +02002699 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002700 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002701 struct timespec ts_cur;
Michal Vaskob05053d2016-01-22 16:12:06 +01002702
Frank Rimpler9f838b02018-07-25 06:44:03 +00002703 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002704 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002705 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002706 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002707 /* no need to store the socket as pending any longer */
2708 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002709
Michal Vasko131120a2018-05-29 15:44:02 +02002710 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002711 if (!(*session)) {
2712 ERRMEM;
2713 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002714 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002715 }
2716 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002717 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002718 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002719 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2720 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002721
Michal Vaskob05053d2016-01-22 16:12:06 +01002722 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002723#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002724 if (client->ti == NC_TI_LIBSSH) {
2725 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002726 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002727 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002728
Michal Vasko71090fc2016-05-24 16:37:28 +02002729 if (ret < 0) {
2730 msgtype = NC_MSG_ERROR;
2731 goto fail;
2732 } else if (!ret) {
2733 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002734 goto fail;
2735 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002736 } else
2737#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002738#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002739 if (client->ti == NC_TI_OPENSSL) {
2740 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002741 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002742 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002743
Michal Vasko71090fc2016-05-24 16:37:28 +02002744 if (ret < 0) {
2745 msgtype = NC_MSG_ERROR;
2746 goto fail;
2747 } else if (!ret) {
2748 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002749 goto fail;
2750 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002751 } else
2752#endif
2753 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002754 ERRINT;
2755 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002756 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002757 goto fail;
2758 }
2759
2760 /* assign new SID atomically */
2761 /* LOCK */
2762 pthread_spin_lock(&server_opts.sid_lock);
2763 (*session)->id = server_opts.new_session_id++;
2764 /* UNLOCK */
2765 pthread_spin_unlock(&server_opts.sid_lock);
2766
2767 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002768 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002769 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002770 goto fail;
2771 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002772
2773 nc_gettimespec_mono(&ts_cur);
2774 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2775 nc_gettimespec_real(&ts_cur);
2776 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002777 (*session)->status = NC_STATUS_RUNNING;
2778
Michal Vasko71090fc2016-05-24 16:37:28 +02002779 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002780
2781fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002782 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002783 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002784 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002785}
2786
Michal Vasko2e6defd2016-10-07 15:48:15 +02002787struct nc_ch_client_thread_arg {
2788 char *client_name;
2789 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2790};
2791
2792static struct nc_ch_client *
2793nc_server_ch_client_with_endpt_lock(const char *name)
2794{
2795 struct nc_ch_client *client;
2796
2797 while (1) {
2798 /* LOCK */
2799 client = nc_server_ch_client_lock(name, 0, NULL);
2800 if (!client) {
2801 return NULL;
2802 }
2803 if (client->ch_endpt_count) {
2804 return client;
2805 }
2806 /* no endpoints defined yet */
2807
2808 /* UNLOCK */
2809 nc_server_ch_client_unlock(client);
2810
2811 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2812 }
2813
2814 return NULL;
2815}
2816
2817static int
2818nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2819{
Michal Vasko3f05a092018-03-13 10:39:49 +01002820 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002821 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002822 struct timespec ts;
2823 struct nc_ch_client *client;
2824
2825 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01002826 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
2827 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002828 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2829 ERRMEM;
2830 nc_session_free(session, NULL);
2831 return -1;
2832 }
2833 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2834 pthread_cond_init(session->opts.server.ch_cond, NULL);
2835
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002836 session->flags |= NC_SESSION_CALLHOME;
2837
Michal Vasko2e6defd2016-10-07 15:48:15 +02002838 /* CH LOCK */
2839 pthread_mutex_lock(session->opts.server.ch_lock);
2840
2841 /* give the session to the user */
2842 data->session_clb(data->client_name, session);
2843
2844 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02002845 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002846 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002847
Michal Vasko3f05a092018-03-13 10:39:49 +01002848 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2849 if (!r) {
2850 /* we were woken up, something probably happened */
2851 if (session->status != NC_STATUS_RUNNING) {
2852 break;
2853 }
2854 } else if (r != ETIMEDOUT) {
2855 ERR("Pthread condition timedwait failed (%s).", strerror(r));
2856 ret = -1;
2857 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01002858 }
2859
Michal Vasko2e6defd2016-10-07 15:48:15 +02002860 /* check whether the client was not removed */
2861 /* LOCK */
2862 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2863 if (!client) {
2864 /* client was removed, finish thread */
2865 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2866 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01002867 ret = 1;
2868 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002869 }
2870
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002871 if (client->conn_type == NC_CH_PERSIST) {
2872 /* TODO keep-alives */
2873 idle_timeout = client->conn.persist.idle_timeout;
2874 } else {
2875 idle_timeout = client->conn.period.idle_timeout;
2876 }
2877
Michal Vasko9fb42272017-10-05 13:50:05 +02002878 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01002879 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 +02002880 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2881 session->status = NC_STATUS_INVALID;
2882 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2883 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002884
2885 /* UNLOCK */
2886 nc_server_ch_client_unlock(client);
2887
2888 } while (session->status == NC_STATUS_RUNNING);
2889
Michal Vasko27377422018-03-15 08:59:35 +01002890 /* CH UNLOCK */
2891 pthread_mutex_unlock(session->opts.server.ch_lock);
2892
Michal Vasko3f05a092018-03-13 10:39:49 +01002893 if (session->status == NC_STATUS_CLOSING) {
2894 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
2895 session->flags &= ~NC_SESSION_CALLHOME;
2896 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002897
Michal Vasko3f05a092018-03-13 10:39:49 +01002898 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002899}
2900
2901static void *
2902nc_ch_client_thread(void *arg)
2903{
2904 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2905 NC_MSG_TYPE msgtype;
2906 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00002907 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01002908 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002909 struct nc_ch_endpt *cur_endpt;
2910 struct nc_session *session;
2911 struct nc_ch_client *client;
2912
2913 /* LOCK */
2914 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2915 if (!client) {
2916 goto cleanup;
2917 }
2918
2919 cur_endpt = &client->ch_endpts[0];
2920 cur_endpt_name = strdup(cur_endpt->name);
2921
Michal Vasko29af44b2016-10-13 10:59:55 +02002922 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002923 while (1) {
2924 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2925
2926 if (msgtype == NC_MSG_HELLO) {
2927 /* UNLOCK */
2928 nc_server_ch_client_unlock(client);
2929
Michal Vasko29af44b2016-10-13 10:59:55 +02002930 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002931 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2932 goto cleanup;
2933 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002934 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002935
2936 /* LOCK */
2937 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2938 if (!client) {
2939 goto cleanup;
2940 }
2941
2942 /* session changed status -> it was disconnected for whatever reason,
2943 * persistent connection immediately tries to reconnect, periodic waits some first */
2944 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002945 /* UNLOCK */
2946 nc_server_ch_client_unlock(client);
2947
2948 /* TODO wake up sometimes to check for new notifications */
2949 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2950
2951 /* LOCK */
2952 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2953 if (!client) {
2954 goto cleanup;
2955 }
2956 }
2957
2958 /* set next endpoint to try */
2959 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00002960 next_endpt_index = 0;
Michal Vasko2a225342018-09-05 08:38:34 +02002961 } else {
Peter Feiged05f2252018-09-03 08:09:47 +00002962 /* we keep the current one but due to unlock/lock we have to find it again */
2963 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
2964 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
2965 break;
2966 }
2967 }
2968 if (next_endpt_index >= client->ch_endpt_count) {
2969 /* endpoint was removed, start with the first one */
2970 next_endpt_index = 0;
2971 }
2972 }
2973
Michal Vasko2e6defd2016-10-07 15:48:15 +02002974 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002975 /* UNLOCK */
2976 nc_server_ch_client_unlock(client);
2977
Michal Vasko2e6defd2016-10-07 15:48:15 +02002978 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002979 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2980
Michal Vasko6bb116b2016-10-26 13:53:46 +02002981 /* LOCK */
2982 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2983 if (!client) {
2984 goto cleanup;
2985 }
2986
Michal Vasko2e6defd2016-10-07 15:48:15 +02002987 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02002988
2989 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00002990 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
2991 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002992 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002993 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02002994 }
2995
Peter Feiged05f2252018-09-03 08:09:47 +00002996 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002997 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00002998 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02002999 cur_attempts = 0;
3000 } else if (cur_attempts == client->max_attempts) {
3001 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003002 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003003 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003004 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003005 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003006 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003007 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003008 }
3009
3010 cur_attempts = 0;
3011 } /* else we keep the current one */
3012 }
Peter Feiged05f2252018-09-03 08:09:47 +00003013
3014 cur_endpt = &client->ch_endpts[next_endpt_index];
3015 free(cur_endpt_name);
3016 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003017 }
3018
3019cleanup:
3020 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003021 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003022 free(data->client_name);
3023 free(data);
3024 return NULL;
3025}
3026
3027API int
3028nc_connect_ch_client_dispatch(const char *client_name,
Michal Vasko3f05a092018-03-13 10:39:49 +01003029 void (*session_clb)(const char *client_name, struct nc_session *new_session))
3030{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003031 int ret;
3032 pthread_t tid;
3033 struct nc_ch_client_thread_arg *arg;
3034
3035 if (!client_name) {
3036 ERRARG("client_name");
3037 return -1;
3038 } else if (!session_clb) {
3039 ERRARG("session_clb");
3040 return -1;
3041 }
3042
3043 arg = malloc(sizeof *arg);
3044 if (!arg) {
3045 ERRMEM;
3046 return -1;
3047 }
3048 arg->client_name = strdup(client_name);
3049 if (!arg->client_name) {
3050 ERRMEM;
3051 free(arg);
3052 return -1;
3053 }
3054 arg->session_clb = session_clb;
3055
3056 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3057 if (ret) {
3058 ERR("Creating a new thread failed (%s).", strerror(ret));
3059 free(arg->client_name);
3060 free(arg);
3061 return -1;
3062 }
3063 /* the thread now manages arg */
3064
3065 pthread_detach(tid);
3066
3067 return 0;
3068}
3069
Radek Krejci53691be2016-02-22 13:58:37 +01003070#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003071
Michal Vaskoe8e07702017-03-15 10:19:30 +01003072API int
3073nc_server_endpt_count(void)
3074{
3075 return server_opts.endpt_count;
3076}
3077
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003078API time_t
3079nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003080{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003081 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003082 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003083 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003084 }
3085
Michal Vasko2e6defd2016-10-07 15:48:15 +02003086 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003087}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003088
3089API void
3090nc_session_set_notif_status(struct nc_session *session, int notif_status)
3091{
3092 if (!session || (session->side != NC_SERVER)) {
3093 ERRARG("session");
3094 return;
3095 }
3096
3097 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3098}
3099
3100API int
3101nc_session_get_notif_status(const struct nc_session *session)
3102{
3103 if (!session || (session->side != NC_SERVER)) {
3104 ERRARG("session");
3105 return 0;
3106 }
3107
3108 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003109}