blob: 28e5370cc587c925f961852f3a34db11ed85dd81 [file] [log] [blame]
Tomas Cejkad340dbf2013-03-24 20:36:57 +01001/*
2 * libwebsockets-test-server - libwebsockets test implementation
3 *
4 * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301 USA
20 */
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <getopt.h>
25#include <string.h>
26#include <sys/time.h>
27#include <sys/stat.h>
Tomas Cejkaba21b382013-04-13 02:37:32 +020028#include <sys/queue.h>
Tomas Cejkad340dbf2013-03-24 20:36:57 +010029#include <fcntl.h>
Tomas Cejkaba21b382013-04-13 02:37:32 +020030#include <pthread.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010031#include <errno.h>
Michal Vaskof35ea502016-02-24 10:44:54 +010032#include <nc_client.h>
Tomas Cejkaba21b382013-04-13 02:37:32 +020033#include <libwebsockets.h>
Michal Vaskoc3146782015-11-04 14:46:41 +010034
Michal Vaskoa53ef882015-11-24 11:02:01 +010035#include "notification_server.h"
36#include "netopeerguid.h"
Michal Vaskoc3146782015-11-04 14:46:41 +010037#include "../config.h"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010038
Michal Vaskoc3146782015-11-04 14:46:41 +010039#ifdef TEST_NOTIFICATION_SERVER
Tomas Cejkaba21b382013-04-13 02:37:32 +020040static int force_exit = 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +010041#endif
42
43#if defined(TEST_NOTIFICATION_SERVER) || defined(WITH_NOTIFICATIONS)
Tomas Cejkad340dbf2013-03-24 20:36:57 +010044static int max_poll_elements;
45
46static struct pollfd *pollfds;
47static int *fd_lookup;
48static int count_pollfds;
Michal Vasko262d34b2016-05-06 13:54:37 +020049static struct lws_context *context = NULL;
Tomas Cejkad340dbf2013-03-24 20:36:57 +010050
Michal Vaskoc3146782015-11-04 14:46:41 +010051extern struct session_with_mutex *netconf_sessions_list;
Tomas Cejkaba21b382013-04-13 02:37:32 +020052static pthread_key_t thread_key;
53
Tomas Cejkad340dbf2013-03-24 20:36:57 +010054/*
55 * This demo server shows how to use libwebsockets for one or more
56 * websocket protocols in the same server
57 *
58 * It defines the following websocket protocols:
59 *
60 * dumb-increment-protocol: once the socket is opened, an incrementing
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020061 * ascii string is sent down it every 50ms.
62 * If you send "reset\n" on the websocket, then
63 * the incrementing number is reset to 0.
Tomas Cejkad340dbf2013-03-24 20:36:57 +010064 *
65 * lws-mirror-protocol: copies any received packet to every connection also
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020066 * using this protocol, including the sender
Tomas Cejkad340dbf2013-03-24 20:36:57 +010067 */
68
69enum demo_protocols {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020070 /* always first */
71 PROTOCOL_HTTP = 0,
Tomas Cejkad340dbf2013-03-24 20:36:57 +010072
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020073 PROTOCOL_NOTIFICATION,
Tomas Cejkad340dbf2013-03-24 20:36:57 +010074
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020075 /* always last */
76 DEMO_PROTOCOL_COUNT
Tomas Cejkad340dbf2013-03-24 20:36:57 +010077};
78
Michal Vasko262d34b2016-05-06 13:54:37 +020079#define SERVER_CERT_DIR "."
80#define SERVER_CERT "server.crt"
81#define SERVER_KEY "server.key"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010082
83/*
84 * We take a strict whitelist approach to stop ../ attacks
85 */
86
87struct serveable {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020088 const char *urlpath;
89 const char *mimetype;
Tomas Cejka15c56302013-05-30 01:11:30 +020090};
Tomas Cejkad340dbf2013-03-24 20:36:57 +010091
92static const struct serveable whitelist[] = {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020093 { "/favicon.ico", "image/x-icon" },
94 { "/libwebsockets.org-logo.png", "image/png" },
Tomas Cejkad340dbf2013-03-24 20:36:57 +010095
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020096 /* last one is the default served if no match */
97 { "/test.html", "text/html" },
Tomas Cejkad340dbf2013-03-24 20:36:57 +010098};
99
100struct per_session_data__http {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200101 int fd;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100102};
103
104/* this protocol server (always the first one) just knows how to do HTTP */
105
Michal Vasko262d34b2016-05-06 13:54:37 +0200106static int callback_http(struct lws *wsi,
107 enum lws_callback_reasons reason, void *user,
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200108 void *in, size_t UNUSED(len))
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100109{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200110 char client_name[128];
111 char client_ip[128];
112 char buf[256];
113 int n, m;
114 static unsigned char buffer[4096];
115 struct per_session_data__http *pss = (struct per_session_data__http *)user;
Michal Vasko262d34b2016-05-06 13:54:37 +0200116 struct lws_pollargs *pa = (struct lws_pollargs *)in;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100117
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200118 switch (reason) {
119 case LWS_CALLBACK_HTTP:
120 for (n = 0; n < (signed) (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
121 if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
122 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100123
Michal Vasko262d34b2016-05-06 13:54:37 +0200124 sprintf(buf, SERVER_CERT_DIR "%s", whitelist[n].urlpath);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100125
Michal Vasko262d34b2016-05-06 13:54:37 +0200126 if (lws_serve_http_file(wsi, buf, whitelist[n].mimetype, NULL, 0))
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200127 return -1; /* through completion or error, close the socket */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100128
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200129 /*
130 * notice that the sending of the file completes asynchronously,
131 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
132 * it's done
133 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100134
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200135 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100136
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200137 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
138// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
139 /* kill the connection after we sent one file */
140 return -1;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100141
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200142 case LWS_CALLBACK_HTTP_WRITEABLE:
143 /*
144 * we can send more of whatever it is we were sending
145 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100146
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200147 do {
148 n = read(pss->fd, buffer, sizeof buffer);
149 /* problem reading, close conn */
150 if (n < 0)
151 goto bail;
152 /* sent it all, close conn */
153 if (n == 0)
154 goto bail;
155 /*
156 * because it's HTTP and not websocket, don't need to take
157 * care about pre and postamble
158 */
Michal Vasko262d34b2016-05-06 13:54:37 +0200159 m = lws_write(wsi, buffer, n, LWS_WRITE_HTTP);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200160 if (m < 0)
161 /* write failed, close conn */
162 goto bail;
163 if (m != n)
164 /* partial write, adjust */
165 lseek(pss->fd, m - n, SEEK_CUR);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100166
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200167 } while (!lws_send_pipe_choked(wsi));
Michal Vasko262d34b2016-05-06 13:54:37 +0200168 lws_callback_on_writable(wsi);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200169 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100170
171bail:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200172 close(pss->fd);
173 return -1;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100174
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200175 case LWS_CALLBACK_LOCK_POLL:
176 /*
177 * lock mutex to protect pollfd state
178 * called before any other POLL related callback
179 */
180 break;
Tomas Cejka11d5d062014-07-15 13:13:52 +0200181
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200182 case LWS_CALLBACK_UNLOCK_POLL:
183 /*
184 * unlock mutex to protect pollfd state when
185 * called after any other POLL related callback
186 */
187 break;
Tomas Cejka11d5d062014-07-15 13:13:52 +0200188
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200189 /*
190 * callback for confirming to continue with client IP appear in
191 * protocol 0 callback since no websocket protocol has been agreed
192 * yet. You can just ignore this if you won't filter on client IP
193 * since the default uhandled callback return is 0 meaning let the
194 * connection continue.
195 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100196
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200197 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
Michal Vasko262d34b2016-05-06 13:54:37 +0200198 lws_get_peer_addresses(wsi, (int)(long)in, client_name,
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200199 sizeof(client_name), client_ip, sizeof(client_ip));
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100200
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200201 //fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip);
202 /* if we returned non-zero from here, we kill the connection */
203 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100204
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200205 /*
206 * callbacks for managing the external poll() array appear in
207 * protocol 0 callback
208 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100209
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200210 case LWS_CALLBACK_ADD_POLL_FD:
211 if (count_pollfds >= max_poll_elements) {
212 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
213 return 1;
214 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100215
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200216 fd_lookup[pa->fd] = count_pollfds;
217 pollfds[count_pollfds].fd = pa->fd;
218 pollfds[count_pollfds].events = pa->events;
219 pollfds[count_pollfds++].revents = 0;
220 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100221
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200222 case LWS_CALLBACK_DEL_POLL_FD:
223 if (!--count_pollfds)
224 break;
225 m = fd_lookup[pa->fd];
226 /* have the last guy take up the vacant slot */
227 pollfds[m] = pollfds[count_pollfds];
228 fd_lookup[pollfds[count_pollfds].fd] = m;
229 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100230
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200231 case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
232 pollfds[fd_lookup[pa->fd]].events = pa->events;
233 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100234
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100235
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200236 default:
237 break;
238 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100239
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200240 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100241}
242
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100243/*
244 * one of these is auto-created for each connection and a pointer to the
245 * appropriate instance is passed to the callback in the user parameter
246 *
247 * for this example protocol we use it to individualize the count for each
248 * connection.
249 */
250
Tomas Cejkaba21b382013-04-13 02:37:32 +0200251struct per_session_data__notif_client {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200252 int number;
253 char *session_id;
254 struct nc_session *session;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100255};
256
Michal Vasko3d8681d2015-11-05 10:23:41 +0100257struct session_with_mutex *get_ncsession_from_sid(const char *session_id)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200258{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200259 struct session_with_mutex *locked_session = NULL;
260 if (session_id == NULL) {
261 return (NULL);
262 }
263 for (locked_session = netconf_sessions_list;
Michal Vaskof35ea502016-02-24 10:44:54 +0100264 locked_session && (nc_session_get_id(locked_session->session) == (unsigned)atoi(session_id));
Michal Vaskoc3146782015-11-04 14:46:41 +0100265 locked_session = locked_session->next);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200266 return locked_session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200267}
268
269/* rpc parameter is freed after the function call */
Michal Vaskof35ea502016-02-24 10:44:54 +0100270static int send_recv_process(struct nc_session *session, const char* UNUSED(operation), struct nc_rpc* rpc)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200271{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200272 struct nc_reply *reply = NULL;
273 char *data = NULL;
274 int ret = EXIT_SUCCESS;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200275
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200276 /* send the request and get the reply */
277 switch (netconf_send_recv_timed(session, rpc, 50000, 0, &reply)) {
278 case NC_MSG_ERROR:
279 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
280 ERROR("notifications: receiving rpc-reply failed.");
281 //cmd_disconnect(NULL);
282 ret = EXIT_FAILURE;
283 break;
284 }
285 ERROR("notifications: Unknown error occurred.");
286 ret = EXIT_FAILURE;
287 break;
288 case NC_MSG_NONE:
289 /* error occurred, but processed by callback */
290 break;
291 case NC_MSG_REPLY:
292 switch (reply->type) {
293 case NC_RPL_OK:
294 break;
295 case NC_RPL_DATA:
Michal Vaskof35ea502016-02-24 10:44:54 +0100296 lyd_print_mem(&data, ((struct nc_reply_data *)reply)->data, LYD_JSON, 0);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200297 DEBUG("notifications: recv: %s.", data);
Michal Vaskof35ea502016-02-24 10:44:54 +0100298 free(data);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200299 break;
300 case NC_RPL_ERROR:
301 /* wtf, you shouldn't be here !?!? */
302 DEBUG("notifications: operation failed, but rpc-error was not processed.");
303 ret = EXIT_FAILURE;
304 break;
305 default:
306 DEBUG("notifications: unexpected operation result.");
307 ret = EXIT_FAILURE;
308 break;
309 }
310 break;
311 default:
312 DEBUG("notifications: Unknown error occurred.");
313 ret = EXIT_FAILURE;
314 break;
315 }
316 nc_rpc_free(rpc);
317 nc_reply_free(reply);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200318
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200319 return (ret);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200320}
321
322/**
323 * \brief Callback to store incoming notification
324 * \param [in] eventtime - time when notification occured
325 * \param [in] content - content of notification
326 */
Michal Vaskof35ea502016-02-24 10:44:54 +0100327static void notification_fileprint(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200328{
Michal Vaskof35ea502016-02-24 10:44:54 +0100329 time_t eventtime;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200330 struct session_with_mutex *target_session = NULL;
331 notification_t *ntf = NULL;
332 const char *session_id = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +0100333 char *content;
334 (void)session;
335
336 eventtime = nc_datetime2time(notif->datetime);
337 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200338
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200339 DEBUG("Accepted notif: %lu %s\n", (unsigned long int) eventtime, content);
Tomas Cejka15c56302013-05-30 01:11:30 +0200340
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200341 session_id = pthread_getspecific(thread_key);
342 DEBUG("notification: fileprint getspecific (%s)", session_id);
343 if (pthread_rwlock_wrlock(&session_lock) != 0) {
344 ERROR("notifications: Error while locking rwlock");
Michal Vaskof35ea502016-02-24 10:44:54 +0100345 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200346 return;
347 }
348 DEBUG("Get session with mutex from key %s.", session_id);
349 target_session = get_ncsession_from_sid(session_id);
350 if (target_session == NULL) {
351 ERROR("notifications: no session found last_session_key (%s)", session_id);
Michal Vaskof35ea502016-02-24 10:44:54 +0100352 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200353 goto unlock_glob;
354 }
355 if (pthread_mutex_lock(&target_session->lock) != 0) {
356 ERROR("notifications: Error while locking rwlock");
357 }
Tomas Cejka15c56302013-05-30 01:11:30 +0200358
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200359 DEBUG("notification: ready to push to notifications queue");
Michal Vaskoc3146782015-11-04 14:46:41 +0100360 if (target_session->notif_count < NOTIFICATION_QUEUE_SIZE) {
361 ++target_session->notif_count;
362 target_session->notifications = realloc(target_session->notifications,
363 target_session->notif_count * sizeof *target_session->notifications);
364 if (target_session->notifications) {
365 ntf = target_session->notifications + target_session->notif_count - 1;
366 }
367 }
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200368 if (ntf == NULL) {
369 ERROR("notifications: Failed to allocate element ");
Michal Vaskof35ea502016-02-24 10:44:54 +0100370 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200371 goto unlock_all;
372 }
373 ntf->eventtime = eventtime;
374 ntf->content = content;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200375
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200376 DEBUG("added notif to queue %u (%s)", (unsigned int) ntf->eventtime, "notification");
Tomas Cejkaba21b382013-04-13 02:37:32 +0200377
Tomas Cejka885ec3e2014-06-22 18:01:34 +0200378unlock_all:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200379 if (pthread_mutex_unlock(&target_session->lock) != 0) {
380 ERROR("notifications: Error while unlocking rwlock");
381 }
Tomas Cejka885ec3e2014-06-22 18:01:34 +0200382unlock_glob:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200383 if (pthread_rwlock_unlock(&session_lock) != 0) {
384 ERROR("notifications: Error while locking rwlock");
385 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200386}
387
Michal Vaskoc3146782015-11-04 14:46:41 +0100388int notif_subscribe(struct session_with_mutex *locked_session, const char *session_id, time_t start_time, time_t stop_time)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200389{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200390 time_t start = -1;
391 time_t stop = -1;
392 char *stream = NULL;
393 struct nc_rpc *rpc = NULL;
394 struct nc_session *session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200395
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200396 DEBUG("notif_subscribe");
397 if (locked_session == NULL) {
398 DEBUG("notifications: no locked_session was given.");
399 /* Close notification client */
400 return -1;
401 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200402
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200403 pthread_mutex_lock(&locked_session->lock);
404 session = locked_session->session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200405
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200406 start = time(NULL) + start_time;
407 stop = time(NULL) + stop_time;
408 start = 0;
409 stop = 0;
410 DEBUG("notifications: history: %u %u", (unsigned int) start, (unsigned int) stop);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200411
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200412 if (session == NULL) {
413 ERROR("notifications: NETCONF session not established.");
414 goto operation_failed;
415 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200416
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200417 /* check times */
418 if (start != -1 && stop != -1 && start > stop) {
419 ERROR("notifications: Subscription start time must be lower than the end time.");
420 goto operation_failed;
421 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200422
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200423 DEBUG("Prepare to execute subscription.");
424 /* create requests */
425 rpc = nc_rpc_subscribe(stream, NULL, (start_time == -1) ? NULL : nc_time2datetime(start, NULL),
Michal Vaskof35ea502016-02-24 10:44:54 +0100426 (stop_time == 0) ? NULL : nc_time2datetime(stop, NULL), NC_PARAMTYPE_CONST);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200427 if (rpc == NULL) {
428 ERROR("notifications: creating an rpc request failed.");
429 goto operation_failed;
430 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200431
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200432 DEBUG("Send NC subscribe.");
433 create_err_reply_p();
434 if (send_recv_process(session, "subscribe", rpc) != 0) {
435 ERROR("Subscription RPC failed.");
436 goto operation_failed;
437 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200438
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200439 GETSPEC_ERR_REPLY
440 if (err_reply != NULL) {
Tomas Cejka442258e2014-04-01 18:17:18 +0200441 free_err_reply();
Tomas Cejkacf44e522015-04-24 17:29:21 +0200442 ERROR("RPC-Error received and cleaned, because we can't send it anywhere.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200443 goto operation_failed;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200444 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200445
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200446 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
447 locked_session->ntfc_subscribed = 1;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200448
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200449 DEBUG("notifications: creating libnetconf notification thread (%s).", session_id);
Tomas Cejka654f84e2013-04-19 11:55:01 +0200450
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200451 pthread_mutex_unlock(&locked_session->lock);
452
453 /* store hash identification of netconf session for notifications printing callback */
454 if (pthread_setspecific(thread_key, session_id) != 0) {
455 ERROR("notifications: cannot set thread-specific hash value.");
456 }
457
458 DEBUG("Create notification_thread.");
459 nc_recv_notif_dispatch(session, notification_fileprint);
460 DEBUG("Subscription finished.");
461 return 0;
Tomas Cejka5ce57b62013-08-29 17:47:39 +0200462
463operation_failed:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200464 pthread_mutex_unlock(&locked_session->lock);
465 return -1;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200466}
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100467
Michal Vasko262d34b2016-05-06 13:54:37 +0200468static int callback_notification(struct lws *wsi,
469 enum lws_callback_reasons reason,
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200470 void *user, void *in, size_t len)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100471{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200472 int n = 0, m = 0, i;
473 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 40960 + LWS_SEND_BUFFER_POST_PADDING];
474 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
475 struct per_session_data__notif_client *pss = (struct per_session_data__notif_client *)user;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200476
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200477 switch (reason) {
478 case LWS_CALLBACK_ESTABLISHED:
479 DEBUG("ntf_prot clb LWS_CALLBACK_ESTABLISHED");
480 break;
481 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
482 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_CONNECTION_ERROR");
483 break;
484 case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:
485 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH");
486 break;
487 case LWS_CALLBACK_CLIENT_ESTABLISHED:
488 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_ESTABLISHED");
489 break;
490 case LWS_CALLBACK_CLOSED:
491 DEBUG("ntf_prot clb LWS_CALLBACK_CLOSED");
492 break;
493 case LWS_CALLBACK_RECEIVE:
494 DEBUG("ntf_prot clb LWS_CALLBACK_RECEIVE");
495 break;
496 case LWS_CALLBACK_RECEIVE_PONG:
497 DEBUG("ntf_prot clb LWS_CALLBACK_RECEIVE_PONG");
498 break;
499 case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
500 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_RECEIVE_PONG");
501 break;
502 case LWS_CALLBACK_CLIENT_RECEIVE:
503 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_RECEIVE");
504 break;
505 case LWS_CALLBACK_CLIENT_WRITEABLE:
506 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_WRITEABLE");
507 break;
Michal Vasko262d34b2016-05-06 13:54:37 +0200508 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
509 DEBUG("ntf_prot clb LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP");
510 break;
511 case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
512 DEBUG("ntf_prot clb LWS_CALLBACK_CLOSED_CLIENT_HTTP");
513 break;
514 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
515 DEBUG("ntf_prot clb LWS_CALLBACK_RECEIVE_CLIENT_HTTP");
516 break;
517 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
518 DEBUG("ntf_prot clb LWS_CALLBACK_COMPLETED_CLIENT_HTTP");
519 break;
520 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
521 DEBUG("ntf_prot clb LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ");
522 break;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200523 case LWS_CALLBACK_HTTP:
524 DEBUG("ntf_prot clb LWS_CALLBACK_HTTP");
525 break;
526 case LWS_CALLBACK_HTTP_BODY:
527 DEBUG("ntf_prot clb LWS_CALLBACK_HTTP_BODY");
528 break;
529 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
530 DEBUG("ntf_prot clb LWS_CALLBACK_HTTP_BODY_COMPLETION");
531 break;
532 case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
533 DEBUG("ntf_prot clb LWS_CALLBACK_FILTER_HTTP_CONNECTION");
534 break;
535 case LWS_CALLBACK_CLOSED_HTTP:
536 DEBUG("ntf_prot clb LWS_CALLBACK_CLOSED_HTTP");
537 break;
538 case LWS_CALLBACK_HTTP_WRITEABLE:
539 DEBUG("ntf_prot clb LWS_CALLBACK_HTTP_WRITEABLE");
540 break;
541 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
542 DEBUG("ntf_prot clb LWS_CALLBACK_HTTP_FILE_COMPLETION");
543 break;
544 case LWS_CALLBACK_SERVER_WRITEABLE:
545 DEBUG("ntf_prot clb LWS_CALLBACK_SERVER_WRITEABLE");
546 break;
547 case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
548 DEBUG("ntf_prot clb LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED");
549 break;
550 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
551 DEBUG("ntf_prot clb LWS_CALLBACK_FILTER_NETWORK_CONNECTION");
552 break;
553 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
554 DEBUG("ntf_prot clb LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION");
555 break;
556 case LWS_CALLBACK_WSI_CREATE:
557 DEBUG("ntf_prot clb LWS_CALLBACK_WSI_CREATE");
558 break;
559 case LWS_CALLBACK_WSI_DESTROY:
560 DEBUG("ntf_prot clb LWS_CALLBACK_WSI_DESTROY");
561 break;
562 case LWS_CALLBACK_GET_THREAD_ID:
563 DEBUG("ntf_prot clb LWS_CALLBACK_GET_THREAD_ID");
564 break;
565 case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
566 DEBUG("ntf_prot clb LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS");
567 break;
568 case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
569 DEBUG("ntf_prot clb LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS");
570 break;
571 case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
572 DEBUG("ntf_prot clb LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION");
573 break;
574 case LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY:
575 DEBUG("ntf_prot clb LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY");
576 break;
577 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
578 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER");
579 break;
580 case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
581 DEBUG("ntf_prot clb LWS_CALLBACK_CONFIRM_EXTENSION_OKAY");
582 break;
583 case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
584 DEBUG("ntf_prot clb LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED");
585 break;
586 case LWS_CALLBACK_PROTOCOL_INIT:
587 DEBUG("ntf_prot clb LWS_CALLBACK_PROTOCOL_INIT");
588 break;
589 case LWS_CALLBACK_PROTOCOL_DESTROY:
590 DEBUG("ntf_prot clb LWS_CALLBACK_PROTOCOL_DESTROY");
591 break;
592 case LWS_CALLBACK_ADD_POLL_FD:
593 DEBUG("ntf_prot clb LWS_CALLBACK_ADD_POLL_FD");
594 break;
595 case LWS_CALLBACK_DEL_POLL_FD:
596 DEBUG("ntf_prot clb LWS_CALLBACK_DEL_POLL_FD");
597 break;
598 case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
599 DEBUG("ntf_prot clb LWS_CALLBACK_CHANGE_MODE_POLL_FD");
600 break;
601 case LWS_CALLBACK_LOCK_POLL:
602 DEBUG("ntf_prot clb LWS_CALLBACK_LOCK_POLL");
603 break;
604 case LWS_CALLBACK_UNLOCK_POLL:
605 DEBUG("ntf_prot clb LWS_CALLBACK_UNLOCK_POLL");
606 break;
607 case LWS_CALLBACK_USER:
608 DEBUG("ntf_prot clb LWS_CALLBACK_USER");
609 break;
Michal Vasko262d34b2016-05-06 13:54:37 +0200610 case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
611 DEBUG("ntf_prot clb LWS_CALLBACK_WS_PEER_INITIATED_CLOSE");
612 break;
613 case LWS_CALLBACK_WS_EXT_DEFAULTS:
614 DEBUG("ntf_prot clb LWS_CALLBACK_WS_EXT_DEFAULTS");
615 break;
616 case LWS_CALLBACK_CGI:
617 DEBUG("ntf_prot clb LWS_CALLBACK_CGI");
618 break;
619 case LWS_CALLBACK_CGI_TERMINATED:
620 DEBUG("ntf_prot clb LWS_CALLBACK_CGI_TERMINATED");
621 break;
622 case LWS_CALLBACK_CGI_STDIN_DATA:
623 DEBUG("ntf_prot clb LWS_CALLBACK_CGI_STDIN_DATA");
624 break;
625 case LWS_CALLBACK_CGI_STDIN_COMPLETED:
626 DEBUG("ntf_prot clb LWS_CALLBACK_CGI_STDIN_COMPLETED");
627 break;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200628 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100629
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200630 switch (reason) {
631 case LWS_CALLBACK_ESTABLISHED:
632 DEBUG("notification client connected.");
633 break;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200634
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200635 case LWS_CALLBACK_SERVER_WRITEABLE:
636 if (pss->session_id == NULL) {
637 return 0;
638 }
639 //DEBUG("Callback server writeable.");
640 //DEBUG("lock session lock.");
641 if (pthread_rwlock_wrlock(&session_lock) != 0) {
642 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
643 return -1;
644 }
645 //DEBUG("get session_with_mutex for %s.", pss->session_id);
646 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
647 if (ls == NULL) {
648 DEBUG("notification: session not found");
649 if (pthread_rwlock_unlock (&session_lock) != 0) {
650 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
651 return -1;
652 }
653 return -1;
654 }
655 pthread_mutex_lock(&ls->lock);
656 if (pthread_rwlock_unlock(&session_lock) != 0) {
657 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
658 }
659
660 //DEBUG("check for closed session.");
661 if (ls->closed == 1) {
662 DEBUG("unlock session key.");
663 if (pthread_rwlock_unlock (&session_lock) != 0) {
664 DEBUG("Error while unlocking unlock: %d (%s)", errno, strerror(errno));
665 return -1;
666 }
667 return -1;
668 }
669 //DEBUG("lock private lock.");
670 notification_t *notif = NULL;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200671
Michal Vasko1ac11282015-11-05 10:28:07 +0100672 if (ls->notif_count) {
673 DEBUG("notification: POP notifications for session");
Michal Vasko43d94a62015-11-05 10:59:32 +0100674 for (i = ls->notif_count; i; --i) {
675 notif = ls->notifications + i - 1;
Tomas Cejka73286932013-05-27 22:54:35 +0200676
Michal Vasko1ac11282015-11-05 10:28:07 +0100677 n = 0;
678 pthread_mutex_lock(&json_lock);
679 json_object *notif_json = json_object_new_object();
680 json_object_object_add(notif_json, "eventtime", json_object_new_int64(notif->eventtime));
681 json_object_object_add(notif_json, "content", json_object_new_string(notif->content));
682 pthread_mutex_unlock(&json_lock);
Tomas Cejka15c56302013-05-30 01:11:30 +0200683
Michal Vasko1ac11282015-11-05 10:28:07 +0100684 const char *msgtext = json_object_to_json_string(notif_json);
Tomas Cejka15c56302013-05-30 01:11:30 +0200685
Michal Vasko1ac11282015-11-05 10:28:07 +0100686 //n = sprintf((char *)p, "{\"eventtime\": \"%s\", \"content\": \"notification\"}", t);
687 n = sprintf((char *)p, "%s", msgtext);
688 DEBUG("ws send %dB in %lu", n, sizeof(buf));
Michal Vasko262d34b2016-05-06 13:54:37 +0200689 m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
Michal Vasko1ac11282015-11-05 10:28:07 +0100690 if (lws_send_pipe_choked(wsi)) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200691 lws_callback_on_writable(wsi);
Michal Vasko1ac11282015-11-05 10:28:07 +0100692 break;
693 }
Michal Vaskoc3146782015-11-04 14:46:41 +0100694
Michal Vasko1ac11282015-11-05 10:28:07 +0100695 pthread_mutex_lock(&json_lock);
696 json_object_put(notif_json);
697 pthread_mutex_unlock(&json_lock);
698 free(notif->content);
Michal Vaskoc3146782015-11-04 14:46:41 +0100699 }
Michal Vasko43d94a62015-11-05 10:59:32 +0100700 ls->notif_count = 0;
701 free(ls->notifications);
702 ls->notifications = NULL;
703
Michal Vasko1ac11282015-11-05 10:28:07 +0100704 DEBUG("notification: POP notifications done");
Michal Vaskoc3146782015-11-04 14:46:41 +0100705 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200706
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200707 //DEBUG("unlock private lock");
708 if (pthread_mutex_unlock(&ls->lock) != 0) {
709 DEBUG("notification: cannot unlock session");
710 }
711 //DEBUG("unlock session lock");
Tomas Cejkaba21b382013-04-13 02:37:32 +0200712
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200713 if (m < n) {
714 DEBUG("ERROR %d writing to di socket.", n);
Tomas Cejka15c56302013-05-30 01:11:30 +0200715
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200716 return -1;
717 }
718 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100719
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200720 case LWS_CALLBACK_RECEIVE:
721 DEBUG("Callback receive.");
722 DEBUG("received: (%s)", (char *)in);
723 if (pss->session_id == NULL) {
724 char *sid_end;
725 int start = -1;
726 time_t stop = time(NULL) + 30;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200727
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100728 sid_end = strchr(in, ' ');
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200729 pss->session_id = strndup(in, sid_end - (char *)in);
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100730
731 ++sid_end;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200732 sscanf(sid_end, "%d %d", (int *) &start, (int *) &stop);
733 DEBUG("notification: SID (%s) from (%s) (%i,%i)", pss->session_id, (char *) in, (int) start, (int) stop);
Tomas Cejka15c56302013-05-30 01:11:30 +0200734
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200735 DEBUG("lock session lock");
736 if (pthread_rwlock_rdlock (&session_lock) != 0) {
737 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
738 return -1;
739 }
740 DEBUG("get session with ID (%s)", pss->session_id);
741 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
742 if (ls == NULL) {
743 DEBUG("notification: session_id not found (%s)", pss->session_id);
744 DEBUG("unlock session lock");
745 if (pthread_rwlock_unlock (&session_lock) != 0) {
746 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
747 }
748 DEBUG("Close notification client");
749 return -1;
750 }
751 DEBUG("lock private lock");
752 pthread_mutex_lock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200753
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200754 DEBUG("unlock session lock");
755 if (pthread_rwlock_unlock (&session_lock) != 0) {
756 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
757 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200758
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200759 DEBUG("Found session to subscribe notif.");
760 if (ls->closed == 1) {
761 DEBUG("session already closed - handle no notification");
762 DEBUG("unlock private lock");
763 pthread_mutex_unlock(&ls->lock);
764 DEBUG("Close notification client");
765 return -1;
766 }
767 if (ls->ntfc_subscribed != 0) {
768 DEBUG("notification: already subscribed");
769 DEBUG("unlock private lock");
770 pthread_mutex_unlock(&ls->lock);
771 /* do not close client, only do not subscribe again */
772 return 0;
773 }
774 DEBUG("notification: prepare to subscribe stream");
775 DEBUG("unlock session lock");
776 pthread_mutex_unlock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200777
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200778 /* notif_subscribe locks on its own */
779 return notif_subscribe(ls, pss->session_id, (time_t) start, (time_t) stop);
780 }
781 if (len < 6)
782 break;
783 break;
784 /*
785 * this just demonstrates how to use the protocol filter. If you won't
786 * study and reject connections based on header content, you don't need
787 * to handle this callback
788 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100789
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200790 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
791 //dump_handshake_info(wsi);
792 /* you could return non-zero here and kill the connection */
793 break;
794 //gives segfault :-(
795 //case LWS_CALLBACK_CLOSED:
796 // if (pss->session_key != NULL) {
797 // free(pss->session_key);
798 // }
799 // if (pss != NULL) {
800 // free(pss);
801 // }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100802
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200803 default:
804 break;
805 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100806
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200807 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100808}
809
810/* list of supported protocols and callbacks */
Michal Vasko262d34b2016-05-06 13:54:37 +0200811static struct lws_protocols protocols[] = {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200812 /* first protocol must always be HTTP handler */
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200813 {
814 "http-only", /* name */
815 callback_http, /* callback */
Michal Vasko262d34b2016-05-06 13:54:37 +0200816 sizeof(struct per_session_data__http), /* per_session_data_size */
817 0, /* max frame size / rx buffer */
818 1,
819 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200820 },
821 {
822 "notification-protocol",
823 callback_notification,
824 sizeof(struct per_session_data__notif_client),
825 4000,
Michal Vasko262d34b2016-05-06 13:54:37 +0200826 2,
827 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200828 },
Michal Vasko262d34b2016-05-06 13:54:37 +0200829 { NULL, NULL, 0, 0, 0, NULL } /* terminator */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100830};
831
Tomas Cejkaba21b382013-04-13 02:37:32 +0200832/**
833 * initialization of notification module
834 */
Michal Vaskoc3146782015-11-04 14:46:41 +0100835int notification_init(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100836{
Michal Vasko262d34b2016-05-06 13:54:37 +0200837 char cert_path[1024], key_path[1024];
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200838 struct lws_context_creation_info info;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200839 int debug_level = 7;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100840
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200841 memset(&info, 0, sizeof info);
842 info.port = NOTIFICATION_SERVER_PORT;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100843
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200844 /* tell the library what debug level to emit and to send it to syslog */
845 lws_set_log_level(debug_level, lwsl_emit_syslog);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100846
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200847 DEBUG("Initialization of libwebsocket");
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200848 max_poll_elements = getdtablesize();
849 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
850 fd_lookup = malloc(max_poll_elements * sizeof (int));
851 if (pollfds == NULL || fd_lookup == NULL) {
852 ERROR("notifications: Out of memory pollfds=%d\n", max_poll_elements);
853 return -1;
854 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100855
Michal Vasko262d34b2016-05-06 13:54:37 +0200856 info.iface = NULL;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200857 info.protocols = protocols;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100858
Michal Vasko262d34b2016-05-06 13:54:37 +0200859 snprintf(cert_path, sizeof(cert_path), SERVER_CERT_DIR "/" SERVER_CERT);
860 snprintf(key_path, sizeof(key_path), SERVER_CERT_DIR "/" SERVER_KEY);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100861
Michal Vasko262d34b2016-05-06 13:54:37 +0200862 info.ssl_cert_filepath = cert_path;
863 info.ssl_private_key_filepath = key_path;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100864
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200865 info.gid = -1;
866 info.uid = -1;
Michal Vasko262d34b2016-05-06 13:54:37 +0200867 info.options = LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT | LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100868
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200869 /* create server */
Michal Vasko262d34b2016-05-06 13:54:37 +0200870 context = lws_create_context(&info);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200871 if (context == NULL) {
872 DEBUG("libwebsocket init failed.");
873 return -1;
874 }
Tomas Cejka15c56302013-05-30 01:11:30 +0200875
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200876 if (pthread_key_create(&thread_key, NULL) != 0) {
877 ERROR("notifications: pthread_key_create failed");
878 }
879 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100880}
881
Michal Vaskoc3146782015-11-04 14:46:41 +0100882void notification_close(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100883{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200884 if (context) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200885 lws_context_destroy(context);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200886 }
887 free(pollfds);
888 free(fd_lookup);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100889
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200890 DEBUG("libwebsockets-test-server exited cleanly\n");
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100891}
892
Tomas Cejkaba21b382013-04-13 02:37:32 +0200893
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100894/**
895 * \brief send notification if any
896 * \return < 0 on error
897 */
898int notification_handle()
899{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200900 static struct timeval tv;
901 static unsigned int olds = 0;
902 int n = 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100903
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200904 gettimeofday(&tv, NULL);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100905
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200906 /*
907 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
908 * live websocket connection using the DUMB_INCREMENT protocol,
909 * as soon as it can take more packets (usually immediately)
910 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100911
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200912 if (((unsigned int)tv.tv_sec - olds) > 0) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200913 lws_callback_on_writable_all_protocol(context, &protocols[PROTOCOL_NOTIFICATION]);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200914 olds = tv.tv_sec;
915 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100916
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200917 /*
918 * this represents an existing server's single poll action
919 * which also includes libwebsocket sockets
920 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100921
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200922 n = poll(pollfds, count_pollfds, 50);
Michal Vasko262d34b2016-05-06 13:54:37 +0200923 if (n < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200924 return n;
Michal Vasko262d34b2016-05-06 13:54:37 +0200925 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100926
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200927 if (n) {
928 for (n = 0; n < count_pollfds; n++) {
929 if (pollfds[n].revents) {
930 /*
931 * returns immediately if the fd does not
932 * match anything under libwebsockets
933 * control
934 */
Michal Vasko262d34b2016-05-06 13:54:37 +0200935 if (lws_service_fd(context, &pollfds[n]) < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200936 return 1;
937 }
938 }
939 }
940 }
941 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100942}
943
944#endif
945
946
947#ifndef WITH_NOTIFICATIONS
948#ifdef TEST_NOTIFICATION_SERVER
949int main(int argc, char **argv)
950{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200951 if (notification_init(NULL, NULL) == -1) {
952 fprintf(stderr, "Error during initialization\n");
953 return 1;
954 }
955 while (!force_exit) {
956 notification_handle();
957 }
958 notification_close();
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100959}
960#endif
961#endif