blob: 633936b1643cd56af0e46c5be73f312f8409a9a3 [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 +010054enum demo_protocols {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020055 /* always first */
56 PROTOCOL_HTTP = 0,
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020057 PROTOCOL_NOTIFICATION,
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020058 /* always last */
59 DEMO_PROTOCOL_COUNT
Tomas Cejkad340dbf2013-03-24 20:36:57 +010060};
61
Michal Vasko262d34b2016-05-06 13:54:37 +020062#define SERVER_CERT_DIR "."
63#define SERVER_CERT "server.crt"
64#define SERVER_KEY "server.key"
Tomas Cejkad340dbf2013-03-24 20:36:57 +010065
66/*
67 * We take a strict whitelist approach to stop ../ attacks
68 */
69
70struct serveable {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020071 const char *urlpath;
72 const char *mimetype;
Tomas Cejka15c56302013-05-30 01:11:30 +020073};
Tomas Cejkad340dbf2013-03-24 20:36:57 +010074
75static const struct serveable whitelist[] = {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020076 { "/favicon.ico", "image/x-icon" },
77 { "/libwebsockets.org-logo.png", "image/png" },
Tomas Cejkad340dbf2013-03-24 20:36:57 +010078
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020079 /* last one is the default served if no match */
80 { "/test.html", "text/html" },
Tomas Cejkad340dbf2013-03-24 20:36:57 +010081};
82
83struct per_session_data__http {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +020084 int fd;
Tomas Cejkad340dbf2013-03-24 20:36:57 +010085};
86
Michal Vasko56d70bc2016-05-10 12:06:52 +020087static void
88debug_print_clb(const char *func, enum lws_callback_reasons reason)
89{
90 switch (reason) {
91 case LWS_CALLBACK_ESTABLISHED:
92 DEBUG("%s: LWS_CALLBACK_ESTABLISHED", func);
93 break;
94 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
95 DEBUG("%s: LWS_CALLBACK_CLIENT_CONNECTION_ERROR", func);
96 break;
97 case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:
98 DEBUG("%s: LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH", func);
99 break;
100 case LWS_CALLBACK_CLIENT_ESTABLISHED:
101 DEBUG("%s: LWS_CALLBACK_CLIENT_ESTABLISHED", func);
102 break;
103 case LWS_CALLBACK_CLOSED:
104 DEBUG("%s: LWS_CALLBACK_CLOSED", func);
105 break;
106 case LWS_CALLBACK_RECEIVE:
107 DEBUG("%s: LWS_CALLBACK_RECEIVE", func);
108 break;
109 case LWS_CALLBACK_RECEIVE_PONG:
110 DEBUG("%s: LWS_CALLBACK_RECEIVE_PONG", func);
111 break;
112 case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
113 DEBUG("%s: LWS_CALLBACK_CLIENT_RECEIVE_PONG", func);
114 break;
115 case LWS_CALLBACK_CLIENT_RECEIVE:
116 DEBUG("%s: LWS_CALLBACK_CLIENT_RECEIVE", func);
117 break;
118 case LWS_CALLBACK_CLIENT_WRITEABLE:
119 DEBUG("%s: LWS_CALLBACK_CLIENT_WRITEABLE", func);
120 break;
121 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
122 DEBUG("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP", func);
123 break;
124 case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
125 DEBUG("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP", func);
126 break;
127 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
128 DEBUG("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP", func);
129 break;
130 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
131 DEBUG("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP", func);
132 break;
133 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
134 DEBUG("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ", func);
135 break;
136 case LWS_CALLBACK_HTTP:
137 DEBUG("%s: LWS_CALLBACK_HTTP", func);
138 break;
139 case LWS_CALLBACK_HTTP_BODY:
140 DEBUG("%s: LWS_CALLBACK_HTTP_BODY", func);
141 break;
142 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
143 DEBUG("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION", func);
144 break;
145 case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
146 DEBUG("%s: LWS_CALLBACK_FILTER_HTTP_CONNECTION", func);
147 break;
148 case LWS_CALLBACK_CLOSED_HTTP:
149 DEBUG("%s: LWS_CALLBACK_CLOSED_HTTP", func);
150 break;
151 case LWS_CALLBACK_HTTP_WRITEABLE:
152 DEBUG("%s: LWS_CALLBACK_HTTP_WRITEABLE", func);
153 break;
154 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
155 DEBUG("%s: LWS_CALLBACK_HTTP_FILE_COMPLETION", func);
156 break;
157 case LWS_CALLBACK_SERVER_WRITEABLE:
158 DEBUG("%s: LWS_CALLBACK_SERVER_WRITEABLE", func);
159 break;
160 case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
161 DEBUG("%s: LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED", func);
162 break;
163 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
164 DEBUG("%s: LWS_CALLBACK_FILTER_NETWORK_CONNECTION", func);
165 break;
166 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
167 DEBUG("%s: LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION", func);
168 break;
169 case LWS_CALLBACK_WSI_CREATE:
170 DEBUG("%s: LWS_CALLBACK_WSI_CREATE", func);
171 break;
172 case LWS_CALLBACK_WSI_DESTROY:
173 DEBUG("%s: LWS_CALLBACK_WSI_DESTROY", func);
174 break;
175 case LWS_CALLBACK_GET_THREAD_ID:
176 DEBUG("%s: LWS_CALLBACK_GET_THREAD_ID", func);
177 break;
178 case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
179 DEBUG("%s: LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS", func);
180 break;
181 case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
182 DEBUG("%s: LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS", func);
183 break;
184 case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
185 DEBUG("%s: LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION", func);
186 break;
187 case LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY:
188 DEBUG("%s: LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY", func);
189 break;
190 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
191 DEBUG("%s: LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER", func);
192 break;
193 case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
194 DEBUG("%s: LWS_CALLBACK_CONFIRM_EXTENSION_OKAY", func);
195 break;
196 case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
197 DEBUG("%s: LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED", func);
198 break;
199 case LWS_CALLBACK_PROTOCOL_INIT:
200 DEBUG("%s: LWS_CALLBACK_PROTOCOL_INIT", func);
201 break;
202 case LWS_CALLBACK_PROTOCOL_DESTROY:
203 DEBUG("%s: LWS_CALLBACK_PROTOCOL_DESTROY", func);
204 break;
205 case LWS_CALLBACK_ADD_POLL_FD:
206 DEBUG("%s: LWS_CALLBACK_ADD_POLL_FD", func);
207 break;
208 case LWS_CALLBACK_DEL_POLL_FD:
209 DEBUG("%s: LWS_CALLBACK_DEL_POLL_FD", func);
210 break;
211 case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
212 DEBUG("%s: LWS_CALLBACK_CHANGE_MODE_POLL_FD", func);
213 break;
214 case LWS_CALLBACK_LOCK_POLL:
215 DEBUG("%s: LWS_CALLBACK_LOCK_POLL", func);
216 break;
217 case LWS_CALLBACK_UNLOCK_POLL:
218 DEBUG("%s: LWS_CALLBACK_UNLOCK_POLL", func);
219 break;
220 case LWS_CALLBACK_USER:
221 DEBUG("%s: LWS_CALLBACK_USER", func);
222 break;
223 case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
224 DEBUG("%s: LWS_CALLBACK_WS_PEER_INITIATED_CLOSE", func);
225 break;
226 case LWS_CALLBACK_WS_EXT_DEFAULTS:
227 DEBUG("%s: LWS_CALLBACK_WS_EXT_DEFAULTS", func);
228 break;
229 case LWS_CALLBACK_CGI:
230 DEBUG("%s: LWS_CALLBACK_CGI", func);
231 break;
232 case LWS_CALLBACK_CGI_TERMINATED:
233 DEBUG("%s: LWS_CALLBACK_CGI_TERMINATED", func);
234 break;
235 case LWS_CALLBACK_CGI_STDIN_DATA:
236 DEBUG("%s: LWS_CALLBACK_CGI_STDIN_DATA", func);
237 break;
238 case LWS_CALLBACK_CGI_STDIN_COMPLETED:
239 DEBUG("%s: LWS_CALLBACK_CGI_STDIN_COMPLETED", func);
240 break;
241 }
242}
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100243
Michal Vasko56d70bc2016-05-10 12:06:52 +0200244static int
245callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t UNUSED(len))
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100246{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200247 char client_name[128];
248 char client_ip[128];
249 char buf[256];
250 int n, m;
251 static unsigned char buffer[4096];
252 struct per_session_data__http *pss = (struct per_session_data__http *)user;
Michal Vasko262d34b2016-05-06 13:54:37 +0200253 struct lws_pollargs *pa = (struct lws_pollargs *)in;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100254
Michal Vasko56d70bc2016-05-10 12:06:52 +0200255 debug_print_clb(__func__, reason);
256
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200257 switch (reason) {
258 case LWS_CALLBACK_HTTP:
259 for (n = 0; n < (signed) (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
260 if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
261 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100262
Michal Vasko262d34b2016-05-06 13:54:37 +0200263 sprintf(buf, SERVER_CERT_DIR "%s", whitelist[n].urlpath);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100264
Michal Vasko262d34b2016-05-06 13:54:37 +0200265 if (lws_serve_http_file(wsi, buf, whitelist[n].mimetype, NULL, 0))
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200266 return -1; /* through completion or error, close the socket */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100267
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200268 /*
269 * notice that the sending of the file completes asynchronously,
270 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
271 * it's done
272 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100273
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200274 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100275
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200276 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
277// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
278 /* kill the connection after we sent one file */
279 return -1;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100280
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200281 case LWS_CALLBACK_HTTP_WRITEABLE:
282 /*
283 * we can send more of whatever it is we were sending
284 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100285
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200286 do {
287 n = read(pss->fd, buffer, sizeof buffer);
288 /* problem reading, close conn */
289 if (n < 0)
290 goto bail;
291 /* sent it all, close conn */
292 if (n == 0)
293 goto bail;
294 /*
295 * because it's HTTP and not websocket, don't need to take
296 * care about pre and postamble
297 */
Michal Vasko262d34b2016-05-06 13:54:37 +0200298 m = lws_write(wsi, buffer, n, LWS_WRITE_HTTP);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200299 if (m < 0)
300 /* write failed, close conn */
301 goto bail;
302 if (m != n)
303 /* partial write, adjust */
304 lseek(pss->fd, m - n, SEEK_CUR);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100305
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200306 } while (!lws_send_pipe_choked(wsi));
Michal Vasko262d34b2016-05-06 13:54:37 +0200307 lws_callback_on_writable(wsi);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200308 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100309
310bail:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200311 close(pss->fd);
312 return -1;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100313
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200314 case LWS_CALLBACK_LOCK_POLL:
315 /*
316 * lock mutex to protect pollfd state
317 * called before any other POLL related callback
318 */
319 break;
Tomas Cejka11d5d062014-07-15 13:13:52 +0200320
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200321 case LWS_CALLBACK_UNLOCK_POLL:
322 /*
323 * unlock mutex to protect pollfd state when
324 * called after any other POLL related callback
325 */
326 break;
Tomas Cejka11d5d062014-07-15 13:13:52 +0200327
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200328 /*
329 * callback for confirming to continue with client IP appear in
330 * protocol 0 callback since no websocket protocol has been agreed
331 * yet. You can just ignore this if you won't filter on client IP
332 * since the default uhandled callback return is 0 meaning let the
333 * connection continue.
334 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100335
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200336 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
Michal Vasko262d34b2016-05-06 13:54:37 +0200337 lws_get_peer_addresses(wsi, (int)(long)in, client_name,
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200338 sizeof(client_name), client_ip, sizeof(client_ip));
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100339
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200340 //fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip);
341 /* if we returned non-zero from here, we kill the connection */
342 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100343
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200344 /*
345 * callbacks for managing the external poll() array appear in
346 * protocol 0 callback
347 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100348
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200349 case LWS_CALLBACK_ADD_POLL_FD:
350 if (count_pollfds >= max_poll_elements) {
351 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
352 return 1;
353 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100354
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200355 fd_lookup[pa->fd] = count_pollfds;
356 pollfds[count_pollfds].fd = pa->fd;
357 pollfds[count_pollfds].events = pa->events;
358 pollfds[count_pollfds++].revents = 0;
359 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100360
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200361 case LWS_CALLBACK_DEL_POLL_FD:
362 if (!--count_pollfds)
363 break;
364 m = fd_lookup[pa->fd];
365 /* have the last guy take up the vacant slot */
366 pollfds[m] = pollfds[count_pollfds];
367 fd_lookup[pollfds[count_pollfds].fd] = m;
368 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100369
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200370 case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
371 pollfds[fd_lookup[pa->fd]].events = pa->events;
372 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100373
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100374
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200375 default:
376 break;
377 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100378
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200379 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100380}
381
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100382/*
383 * one of these is auto-created for each connection and a pointer to the
384 * appropriate instance is passed to the callback in the user parameter
385 *
386 * for this example protocol we use it to individualize the count for each
387 * connection.
388 */
389
Tomas Cejkaba21b382013-04-13 02:37:32 +0200390struct per_session_data__notif_client {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200391 int number;
392 char *session_id;
393 struct nc_session *session;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100394};
395
Michal Vasko56d70bc2016-05-10 12:06:52 +0200396static struct session_with_mutex *
397get_ncsession_from_sid(const char *session_id)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200398{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200399 struct session_with_mutex *locked_session = NULL;
Michal Vasko56d70bc2016-05-10 12:06:52 +0200400
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200401 if (session_id == NULL) {
402 return (NULL);
403 }
Michal Vasko56d70bc2016-05-10 12:06:52 +0200404
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200405 for (locked_session = netconf_sessions_list;
Michal Vaskof35ea502016-02-24 10:44:54 +0100406 locked_session && (nc_session_get_id(locked_session->session) == (unsigned)atoi(session_id));
Michal Vaskoc3146782015-11-04 14:46:41 +0100407 locked_session = locked_session->next);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200408 return locked_session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200409}
410
411/* rpc parameter is freed after the function call */
Michal Vaskof35ea502016-02-24 10:44:54 +0100412static int send_recv_process(struct nc_session *session, const char* UNUSED(operation), struct nc_rpc* rpc)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200413{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200414 struct nc_reply *reply = NULL;
415 char *data = NULL;
416 int ret = EXIT_SUCCESS;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200417
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200418 /* send the request and get the reply */
419 switch (netconf_send_recv_timed(session, rpc, 50000, 0, &reply)) {
420 case NC_MSG_ERROR:
421 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
422 ERROR("notifications: receiving rpc-reply failed.");
423 //cmd_disconnect(NULL);
424 ret = EXIT_FAILURE;
425 break;
426 }
427 ERROR("notifications: Unknown error occurred.");
428 ret = EXIT_FAILURE;
429 break;
430 case NC_MSG_NONE:
431 /* error occurred, but processed by callback */
432 break;
433 case NC_MSG_REPLY:
434 switch (reply->type) {
435 case NC_RPL_OK:
436 break;
437 case NC_RPL_DATA:
Michal Vaskof35ea502016-02-24 10:44:54 +0100438 lyd_print_mem(&data, ((struct nc_reply_data *)reply)->data, LYD_JSON, 0);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200439 DEBUG("notifications: recv: %s.", data);
Michal Vaskof35ea502016-02-24 10:44:54 +0100440 free(data);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200441 break;
442 case NC_RPL_ERROR:
443 /* wtf, you shouldn't be here !?!? */
444 DEBUG("notifications: operation failed, but rpc-error was not processed.");
445 ret = EXIT_FAILURE;
446 break;
447 default:
448 DEBUG("notifications: unexpected operation result.");
449 ret = EXIT_FAILURE;
450 break;
451 }
452 break;
453 default:
454 DEBUG("notifications: Unknown error occurred.");
455 ret = EXIT_FAILURE;
456 break;
457 }
458 nc_rpc_free(rpc);
459 nc_reply_free(reply);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200460
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200461 return (ret);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200462}
463
464/**
465 * \brief Callback to store incoming notification
466 * \param [in] eventtime - time when notification occured
467 * \param [in] content - content of notification
468 */
Michal Vaskof35ea502016-02-24 10:44:54 +0100469static void notification_fileprint(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200470{
Michal Vaskof35ea502016-02-24 10:44:54 +0100471 time_t eventtime;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200472 struct session_with_mutex *target_session = NULL;
473 notification_t *ntf = NULL;
474 const char *session_id = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +0100475 char *content;
476 (void)session;
477
478 eventtime = nc_datetime2time(notif->datetime);
479 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200480
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200481 DEBUG("Accepted notif: %lu %s\n", (unsigned long int) eventtime, content);
Tomas Cejka15c56302013-05-30 01:11:30 +0200482
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200483 session_id = pthread_getspecific(thread_key);
484 DEBUG("notification: fileprint getspecific (%s)", session_id);
485 if (pthread_rwlock_wrlock(&session_lock) != 0) {
486 ERROR("notifications: Error while locking rwlock");
Michal Vaskof35ea502016-02-24 10:44:54 +0100487 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200488 return;
489 }
490 DEBUG("Get session with mutex from key %s.", session_id);
491 target_session = get_ncsession_from_sid(session_id);
492 if (target_session == NULL) {
493 ERROR("notifications: no session found last_session_key (%s)", session_id);
Michal Vaskof35ea502016-02-24 10:44:54 +0100494 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200495 goto unlock_glob;
496 }
497 if (pthread_mutex_lock(&target_session->lock) != 0) {
498 ERROR("notifications: Error while locking rwlock");
499 }
Tomas Cejka15c56302013-05-30 01:11:30 +0200500
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200501 DEBUG("notification: ready to push to notifications queue");
Michal Vaskoc3146782015-11-04 14:46:41 +0100502 if (target_session->notif_count < NOTIFICATION_QUEUE_SIZE) {
503 ++target_session->notif_count;
504 target_session->notifications = realloc(target_session->notifications,
505 target_session->notif_count * sizeof *target_session->notifications);
506 if (target_session->notifications) {
507 ntf = target_session->notifications + target_session->notif_count - 1;
508 }
509 }
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200510 if (ntf == NULL) {
511 ERROR("notifications: Failed to allocate element ");
Michal Vaskof35ea502016-02-24 10:44:54 +0100512 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200513 goto unlock_all;
514 }
515 ntf->eventtime = eventtime;
516 ntf->content = content;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200517
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200518 DEBUG("added notif to queue %u (%s)", (unsigned int) ntf->eventtime, "notification");
Tomas Cejkaba21b382013-04-13 02:37:32 +0200519
Tomas Cejka885ec3e2014-06-22 18:01:34 +0200520unlock_all:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200521 if (pthread_mutex_unlock(&target_session->lock) != 0) {
522 ERROR("notifications: Error while unlocking rwlock");
523 }
Tomas Cejka885ec3e2014-06-22 18:01:34 +0200524unlock_glob:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200525 if (pthread_rwlock_unlock(&session_lock) != 0) {
526 ERROR("notifications: Error while locking rwlock");
527 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200528}
529
Michal Vaskoc3146782015-11-04 14:46:41 +0100530int 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 +0200531{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200532 time_t start = -1;
533 time_t stop = -1;
534 char *stream = NULL;
535 struct nc_rpc *rpc = NULL;
536 struct nc_session *session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200537
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200538 DEBUG("notif_subscribe");
539 if (locked_session == NULL) {
540 DEBUG("notifications: no locked_session was given.");
541 /* Close notification client */
542 return -1;
543 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200544
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200545 pthread_mutex_lock(&locked_session->lock);
546 session = locked_session->session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200547
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200548 start = time(NULL) + start_time;
549 stop = time(NULL) + stop_time;
550 start = 0;
551 stop = 0;
552 DEBUG("notifications: history: %u %u", (unsigned int) start, (unsigned int) stop);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200553
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200554 if (session == NULL) {
555 ERROR("notifications: NETCONF session not established.");
556 goto operation_failed;
557 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200558
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200559 /* check times */
560 if (start != -1 && stop != -1 && start > stop) {
561 ERROR("notifications: Subscription start time must be lower than the end time.");
562 goto operation_failed;
563 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200564
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200565 DEBUG("Prepare to execute subscription.");
566 /* create requests */
Michal Vasko1cb9ebc2016-05-30 09:39:15 +0200567 rpc = nc_rpc_subscribe(stream, NULL, (start_time == -1) ? NULL : nc_time2datetime(start, NULL, NULL),
568 (stop_time == 0) ? NULL : nc_time2datetime(stop, NULL, NULL), NC_PARAMTYPE_CONST);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200569 if (rpc == NULL) {
570 ERROR("notifications: creating an rpc request failed.");
571 goto operation_failed;
572 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200573
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200574 DEBUG("Send NC subscribe.");
575 create_err_reply_p();
576 if (send_recv_process(session, "subscribe", rpc) != 0) {
577 ERROR("Subscription RPC failed.");
578 goto operation_failed;
579 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200580
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200581 GETSPEC_ERR_REPLY
582 if (err_reply != NULL) {
Tomas Cejka442258e2014-04-01 18:17:18 +0200583 free_err_reply();
Tomas Cejkacf44e522015-04-24 17:29:21 +0200584 ERROR("RPC-Error received and cleaned, because we can't send it anywhere.");
Tomas Cejka442258e2014-04-01 18:17:18 +0200585 goto operation_failed;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200586 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200587
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200588 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
589 locked_session->ntfc_subscribed = 1;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200590
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200591 DEBUG("notifications: creating libnetconf notification thread (%s).", session_id);
Tomas Cejka654f84e2013-04-19 11:55:01 +0200592
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200593 pthread_mutex_unlock(&locked_session->lock);
594
595 /* store hash identification of netconf session for notifications printing callback */
596 if (pthread_setspecific(thread_key, session_id) != 0) {
597 ERROR("notifications: cannot set thread-specific hash value.");
598 }
599
600 DEBUG("Create notification_thread.");
601 nc_recv_notif_dispatch(session, notification_fileprint);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200602 return 0;
Tomas Cejka5ce57b62013-08-29 17:47:39 +0200603
604operation_failed:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200605 pthread_mutex_unlock(&locked_session->lock);
606 return -1;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200607}
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100608
Michal Vasko56d70bc2016-05-10 12:06:52 +0200609static int
610callback_notification(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100611{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200612 int n = 0, m = 0, i;
613 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 40960 + LWS_SEND_BUFFER_POST_PADDING];
614 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
615 struct per_session_data__notif_client *pss = (struct per_session_data__notif_client *)user;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200616
Michal Vasko56d70bc2016-05-10 12:06:52 +0200617 debug_print_clb(__func__, reason);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100618
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200619 switch (reason) {
620 case LWS_CALLBACK_ESTABLISHED:
621 DEBUG("notification client connected.");
622 break;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200623
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200624 case LWS_CALLBACK_SERVER_WRITEABLE:
625 if (pss->session_id == NULL) {
626 return 0;
627 }
628 //DEBUG("Callback server writeable.");
629 //DEBUG("lock session lock.");
630 if (pthread_rwlock_wrlock(&session_lock) != 0) {
631 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
632 return -1;
633 }
634 //DEBUG("get session_with_mutex for %s.", pss->session_id);
635 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
636 if (ls == NULL) {
637 DEBUG("notification: session not found");
638 if (pthread_rwlock_unlock (&session_lock) != 0) {
639 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
640 return -1;
641 }
642 return -1;
643 }
644 pthread_mutex_lock(&ls->lock);
645 if (pthread_rwlock_unlock(&session_lock) != 0) {
646 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
647 }
648
649 //DEBUG("check for closed session.");
650 if (ls->closed == 1) {
651 DEBUG("unlock session key.");
652 if (pthread_rwlock_unlock (&session_lock) != 0) {
653 DEBUG("Error while unlocking unlock: %d (%s)", errno, strerror(errno));
654 return -1;
655 }
656 return -1;
657 }
658 //DEBUG("lock private lock.");
659 notification_t *notif = NULL;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200660
Michal Vasko1ac11282015-11-05 10:28:07 +0100661 if (ls->notif_count) {
662 DEBUG("notification: POP notifications for session");
Michal Vasko43d94a62015-11-05 10:59:32 +0100663 for (i = ls->notif_count; i; --i) {
664 notif = ls->notifications + i - 1;
Tomas Cejka73286932013-05-27 22:54:35 +0200665
Michal Vasko1ac11282015-11-05 10:28:07 +0100666 n = 0;
667 pthread_mutex_lock(&json_lock);
668 json_object *notif_json = json_object_new_object();
669 json_object_object_add(notif_json, "eventtime", json_object_new_int64(notif->eventtime));
670 json_object_object_add(notif_json, "content", json_object_new_string(notif->content));
671 pthread_mutex_unlock(&json_lock);
Tomas Cejka15c56302013-05-30 01:11:30 +0200672
Michal Vasko1ac11282015-11-05 10:28:07 +0100673 const char *msgtext = json_object_to_json_string(notif_json);
Tomas Cejka15c56302013-05-30 01:11:30 +0200674
Michal Vasko1ac11282015-11-05 10:28:07 +0100675 //n = sprintf((char *)p, "{\"eventtime\": \"%s\", \"content\": \"notification\"}", t);
676 n = sprintf((char *)p, "%s", msgtext);
677 DEBUG("ws send %dB in %lu", n, sizeof(buf));
Michal Vasko262d34b2016-05-06 13:54:37 +0200678 m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
Michal Vasko1ac11282015-11-05 10:28:07 +0100679 if (lws_send_pipe_choked(wsi)) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200680 lws_callback_on_writable(wsi);
Michal Vasko1ac11282015-11-05 10:28:07 +0100681 break;
682 }
Michal Vaskoc3146782015-11-04 14:46:41 +0100683
Michal Vasko1ac11282015-11-05 10:28:07 +0100684 pthread_mutex_lock(&json_lock);
685 json_object_put(notif_json);
686 pthread_mutex_unlock(&json_lock);
687 free(notif->content);
Michal Vaskoc3146782015-11-04 14:46:41 +0100688 }
Michal Vasko43d94a62015-11-05 10:59:32 +0100689 ls->notif_count = 0;
690 free(ls->notifications);
691 ls->notifications = NULL;
692
Michal Vasko1ac11282015-11-05 10:28:07 +0100693 DEBUG("notification: POP notifications done");
Michal Vaskoc3146782015-11-04 14:46:41 +0100694 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200695
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200696 //DEBUG("unlock private lock");
697 if (pthread_mutex_unlock(&ls->lock) != 0) {
698 DEBUG("notification: cannot unlock session");
699 }
700 //DEBUG("unlock session lock");
Tomas Cejkaba21b382013-04-13 02:37:32 +0200701
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200702 if (m < n) {
703 DEBUG("ERROR %d writing to di socket.", n);
Tomas Cejka15c56302013-05-30 01:11:30 +0200704
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200705 return -1;
706 }
707 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100708
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200709 case LWS_CALLBACK_RECEIVE:
710 DEBUG("Callback receive.");
711 DEBUG("received: (%s)", (char *)in);
712 if (pss->session_id == NULL) {
713 char *sid_end;
714 int start = -1;
715 time_t stop = time(NULL) + 30;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200716
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100717 sid_end = strchr(in, ' ');
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200718 pss->session_id = strndup(in, sid_end - (char *)in);
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100719
720 ++sid_end;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200721 sscanf(sid_end, "%d %d", (int *) &start, (int *) &stop);
722 DEBUG("notification: SID (%s) from (%s) (%i,%i)", pss->session_id, (char *) in, (int) start, (int) stop);
Tomas Cejka15c56302013-05-30 01:11:30 +0200723
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200724 DEBUG("lock session lock");
725 if (pthread_rwlock_rdlock (&session_lock) != 0) {
726 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
727 return -1;
728 }
729 DEBUG("get session with ID (%s)", pss->session_id);
730 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
731 if (ls == NULL) {
732 DEBUG("notification: session_id not found (%s)", pss->session_id);
733 DEBUG("unlock session lock");
734 if (pthread_rwlock_unlock (&session_lock) != 0) {
735 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
736 }
737 DEBUG("Close notification client");
738 return -1;
739 }
740 DEBUG("lock private lock");
741 pthread_mutex_lock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200742
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200743 DEBUG("unlock session lock");
744 if (pthread_rwlock_unlock (&session_lock) != 0) {
745 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
746 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200747
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200748 DEBUG("Found session to subscribe notif.");
749 if (ls->closed == 1) {
750 DEBUG("session already closed - handle no notification");
751 DEBUG("unlock private lock");
752 pthread_mutex_unlock(&ls->lock);
753 DEBUG("Close notification client");
754 return -1;
755 }
756 if (ls->ntfc_subscribed != 0) {
757 DEBUG("notification: already subscribed");
758 DEBUG("unlock private lock");
759 pthread_mutex_unlock(&ls->lock);
760 /* do not close client, only do not subscribe again */
761 return 0;
762 }
763 DEBUG("notification: prepare to subscribe stream");
764 DEBUG("unlock session lock");
765 pthread_mutex_unlock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200766
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200767 /* notif_subscribe locks on its own */
768 return notif_subscribe(ls, pss->session_id, (time_t) start, (time_t) stop);
769 }
770 if (len < 6)
771 break;
772 break;
773 /*
774 * this just demonstrates how to use the protocol filter. If you won't
775 * study and reject connections based on header content, you don't need
776 * to handle this callback
777 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100778
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200779 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
780 //dump_handshake_info(wsi);
781 /* you could return non-zero here and kill the connection */
782 break;
783 //gives segfault :-(
784 //case LWS_CALLBACK_CLOSED:
785 // if (pss->session_key != NULL) {
786 // free(pss->session_key);
787 // }
788 // if (pss != NULL) {
789 // free(pss);
790 // }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100791
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200792 default:
793 break;
794 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100795
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200796 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100797}
798
Michal Vasko262d34b2016-05-06 13:54:37 +0200799static struct lws_protocols protocols[] = {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200800 /* first protocol must always be HTTP handler */
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200801 {
802 "http-only", /* name */
803 callback_http, /* callback */
Michal Vasko262d34b2016-05-06 13:54:37 +0200804 sizeof(struct per_session_data__http), /* per_session_data_size */
805 0, /* max frame size / rx buffer */
806 1,
807 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200808 },
809 {
810 "notification-protocol",
811 callback_notification,
812 sizeof(struct per_session_data__notif_client),
813 4000,
Michal Vasko262d34b2016-05-06 13:54:37 +0200814 2,
815 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200816 },
Michal Vasko262d34b2016-05-06 13:54:37 +0200817 { NULL, NULL, 0, 0, 0, NULL } /* terminator */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100818};
819
Tomas Cejkaba21b382013-04-13 02:37:32 +0200820/**
821 * initialization of notification module
822 */
Michal Vasko56d70bc2016-05-10 12:06:52 +0200823int
824notification_init(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100825{
Michal Vasko262d34b2016-05-06 13:54:37 +0200826 char cert_path[1024], key_path[1024];
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200827 struct lws_context_creation_info info;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200828 int debug_level = 7;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100829
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200830 memset(&info, 0, sizeof info);
831 info.port = NOTIFICATION_SERVER_PORT;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100832
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200833 /* tell the library what debug level to emit and to send it to syslog */
834 lws_set_log_level(debug_level, lwsl_emit_syslog);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100835
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200836 DEBUG("Initialization of libwebsocket");
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200837 max_poll_elements = getdtablesize();
838 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
839 fd_lookup = malloc(max_poll_elements * sizeof (int));
840 if (pollfds == NULL || fd_lookup == NULL) {
841 ERROR("notifications: Out of memory pollfds=%d\n", max_poll_elements);
842 return -1;
843 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100844
Michal Vasko262d34b2016-05-06 13:54:37 +0200845 info.iface = NULL;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200846 info.protocols = protocols;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100847
Michal Vasko262d34b2016-05-06 13:54:37 +0200848 snprintf(cert_path, sizeof(cert_path), SERVER_CERT_DIR "/" SERVER_CERT);
849 snprintf(key_path, sizeof(key_path), SERVER_CERT_DIR "/" SERVER_KEY);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100850
Michal Vasko262d34b2016-05-06 13:54:37 +0200851 info.ssl_cert_filepath = cert_path;
852 info.ssl_private_key_filepath = key_path;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100853
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200854 info.gid = -1;
855 info.uid = -1;
Michal Vaskoa41bb802016-05-30 09:04:55 +0200856 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100857
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200858 /* create server */
Michal Vasko262d34b2016-05-06 13:54:37 +0200859 context = lws_create_context(&info);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200860 if (context == NULL) {
861 DEBUG("libwebsocket init failed.");
862 return -1;
863 }
Tomas Cejka15c56302013-05-30 01:11:30 +0200864
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200865 if (pthread_key_create(&thread_key, NULL) != 0) {
866 ERROR("notifications: pthread_key_create failed");
867 }
868 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100869}
870
Michal Vasko56d70bc2016-05-10 12:06:52 +0200871void
872notification_close(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100873{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200874 if (context) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200875 lws_context_destroy(context);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200876 }
877 free(pollfds);
878 free(fd_lookup);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100879
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200880 DEBUG("libwebsockets-test-server exited cleanly\n");
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100881}
882
Tomas Cejkaba21b382013-04-13 02:37:32 +0200883
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100884/**
885 * \brief send notification if any
886 * \return < 0 on error
887 */
Michal Vasko56d70bc2016-05-10 12:06:52 +0200888int
889notification_handle()
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100890{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200891 static struct timeval tv;
892 static unsigned int olds = 0;
893 int n = 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100894
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200895 gettimeofday(&tv, NULL);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100896
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200897 /*
898 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
899 * live websocket connection using the DUMB_INCREMENT protocol,
900 * as soon as it can take more packets (usually immediately)
901 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100902
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200903 if (((unsigned int)tv.tv_sec - olds) > 0) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200904 lws_callback_on_writable_all_protocol(context, &protocols[PROTOCOL_NOTIFICATION]);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200905 olds = tv.tv_sec;
906 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100907
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200908 /*
909 * this represents an existing server's single poll action
910 * which also includes libwebsocket sockets
911 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100912
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200913 n = poll(pollfds, count_pollfds, 50);
Michal Vasko262d34b2016-05-06 13:54:37 +0200914 if (n < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200915 return n;
Michal Vasko262d34b2016-05-06 13:54:37 +0200916 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100917
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200918 if (n) {
919 for (n = 0; n < count_pollfds; n++) {
Michal Vasko3b2507e2016-05-30 09:33:04 +0200920 if (pollfds[n].revents & POLLHUP) {
921 ERROR("notifications: poll pipe closed");
922 return -1;
923 } else if (pollfds[n].revents & POLLERR) {
924 ERROR("notifications: poll pipe error");
Michal Vasko0fc2d4c2016-05-30 09:03:14 +0200925 return -1;
926 } else if (pollfds[n].revents & (POLLIN | POLLOUT)) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200927 /*
928 * returns immediately if the fd does not
929 * match anything under libwebsockets
930 * control
931 */
Michal Vasko262d34b2016-05-06 13:54:37 +0200932 if (lws_service_fd(context, &pollfds[n]) < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200933 return 1;
934 }
935 }
936 }
937 }
938 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100939}
940
941#endif
942
943
944#ifndef WITH_NOTIFICATIONS
945#ifdef TEST_NOTIFICATION_SERVER
Michal Vasko56d70bc2016-05-10 12:06:52 +0200946int
947main(int argc, char **argv)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100948{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200949 if (notification_init(NULL, NULL) == -1) {
950 fprintf(stderr, "Error during initialization\n");
951 return 1;
952 }
953 while (!force_exit) {
954 notification_handle();
955 }
956 notification_close();
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100957}
958#endif
959#endif