blob: 82a74a3acb04cf4c60036e7a050162f7e753316e [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 Vaskoa304abf2016-09-06 11:25:30 +0200263 sprintf(buf, "./%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 Vasko460dde52016-05-31 09:00:45 +0200412static int
413send_recv_process(struct nc_session *session, const char* UNUSED(operation), struct nc_rpc* rpc)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200414{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200415 struct nc_reply *reply = NULL;
416 char *data = NULL;
417 int ret = EXIT_SUCCESS;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200418
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200419 /* send the request and get the reply */
420 switch (netconf_send_recv_timed(session, rpc, 50000, 0, &reply)) {
421 case NC_MSG_ERROR:
422 if (nc_session_get_status(session) != NC_STATUS_RUNNING) {
423 ERROR("notifications: receiving rpc-reply failed.");
424 //cmd_disconnect(NULL);
425 ret = EXIT_FAILURE;
426 break;
427 }
428 ERROR("notifications: Unknown error occurred.");
429 ret = EXIT_FAILURE;
430 break;
431 case NC_MSG_NONE:
432 /* error occurred, but processed by callback */
433 break;
434 case NC_MSG_REPLY:
435 switch (reply->type) {
436 case NC_RPL_OK:
437 break;
438 case NC_RPL_DATA:
Michal Vaskof35ea502016-02-24 10:44:54 +0100439 lyd_print_mem(&data, ((struct nc_reply_data *)reply)->data, LYD_JSON, 0);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200440 DEBUG("notifications: recv: %s.", data);
Michal Vaskof35ea502016-02-24 10:44:54 +0100441 free(data);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200442 break;
443 case NC_RPL_ERROR:
444 /* wtf, you shouldn't be here !?!? */
445 DEBUG("notifications: operation failed, but rpc-error was not processed.");
446 ret = EXIT_FAILURE;
447 break;
448 default:
449 DEBUG("notifications: unexpected operation result.");
450 ret = EXIT_FAILURE;
451 break;
452 }
453 break;
454 default:
455 DEBUG("notifications: Unknown error occurred.");
456 ret = EXIT_FAILURE;
457 break;
458 }
459 nc_rpc_free(rpc);
460 nc_reply_free(reply);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200461
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200462 return (ret);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200463}
464
465/**
466 * \brief Callback to store incoming notification
467 * \param [in] eventtime - time when notification occured
468 * \param [in] content - content of notification
469 */
Michal Vasko460dde52016-05-31 09:00:45 +0200470static void
471notification_fileprint(struct nc_session *session, const struct nc_notif *notif)
Tomas Cejkaba21b382013-04-13 02:37:32 +0200472{
Michal Vaskof35ea502016-02-24 10:44:54 +0100473 time_t eventtime;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200474 struct session_with_mutex *target_session = NULL;
475 notification_t *ntf = NULL;
476 const char *session_id = NULL;
Michal Vaskof35ea502016-02-24 10:44:54 +0100477 char *content;
478 (void)session;
479
480 eventtime = nc_datetime2time(notif->datetime);
481 lyd_print_mem(&content, notif->tree, LYD_JSON, 0);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200482
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200483 DEBUG("Accepted notif: %lu %s\n", (unsigned long int) eventtime, content);
Tomas Cejka15c56302013-05-30 01:11:30 +0200484
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200485 session_id = pthread_getspecific(thread_key);
486 DEBUG("notification: fileprint getspecific (%s)", session_id);
487 if (pthread_rwlock_wrlock(&session_lock) != 0) {
488 ERROR("notifications: Error while locking rwlock");
Michal Vaskof35ea502016-02-24 10:44:54 +0100489 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200490 return;
491 }
492 DEBUG("Get session with mutex from key %s.", session_id);
493 target_session = get_ncsession_from_sid(session_id);
494 if (target_session == NULL) {
495 ERROR("notifications: no session found last_session_key (%s)", session_id);
Michal Vaskof35ea502016-02-24 10:44:54 +0100496 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200497 goto unlock_glob;
498 }
499 if (pthread_mutex_lock(&target_session->lock) != 0) {
500 ERROR("notifications: Error while locking rwlock");
501 }
Tomas Cejka15c56302013-05-30 01:11:30 +0200502
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200503 DEBUG("notification: ready to push to notifications queue");
Michal Vaskoc3146782015-11-04 14:46:41 +0100504 if (target_session->notif_count < NOTIFICATION_QUEUE_SIZE) {
505 ++target_session->notif_count;
506 target_session->notifications = realloc(target_session->notifications,
507 target_session->notif_count * sizeof *target_session->notifications);
508 if (target_session->notifications) {
509 ntf = target_session->notifications + target_session->notif_count - 1;
510 }
511 }
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200512 if (ntf == NULL) {
513 ERROR("notifications: Failed to allocate element ");
Michal Vaskof35ea502016-02-24 10:44:54 +0100514 free(content);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200515 goto unlock_all;
516 }
517 ntf->eventtime = eventtime;
518 ntf->content = content;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200519
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200520 DEBUG("added notif to queue %u (%s)", (unsigned int) ntf->eventtime, "notification");
Tomas Cejkaba21b382013-04-13 02:37:32 +0200521
Tomas Cejka885ec3e2014-06-22 18:01:34 +0200522unlock_all:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200523 if (pthread_mutex_unlock(&target_session->lock) != 0) {
524 ERROR("notifications: Error while unlocking rwlock");
525 }
Tomas Cejka885ec3e2014-06-22 18:01:34 +0200526unlock_glob:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200527 if (pthread_rwlock_unlock(&session_lock) != 0) {
528 ERROR("notifications: Error while locking rwlock");
529 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200530}
531
Michal Vasko460dde52016-05-31 09:00:45 +0200532int
533notif_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 +0200534{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200535 time_t start = -1;
536 time_t stop = -1;
537 char *stream = NULL;
538 struct nc_rpc *rpc = NULL;
539 struct nc_session *session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200540
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200541 DEBUG("notif_subscribe");
542 if (locked_session == NULL) {
543 DEBUG("notifications: no locked_session was given.");
544 /* Close notification client */
545 return -1;
546 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200547
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200548 pthread_mutex_lock(&locked_session->lock);
549 session = locked_session->session;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200550
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200551 start = time(NULL) + start_time;
552 stop = time(NULL) + stop_time;
553 start = 0;
554 stop = 0;
555 DEBUG("notifications: history: %u %u", (unsigned int) start, (unsigned int) stop);
Tomas Cejkaba21b382013-04-13 02:37:32 +0200556
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200557 if (session == NULL) {
558 ERROR("notifications: NETCONF session not established.");
559 goto operation_failed;
560 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200561
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200562 /* check times */
563 if (start != -1 && stop != -1 && start > stop) {
564 ERROR("notifications: Subscription start time must be lower than the end time.");
565 goto operation_failed;
566 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200567
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200568 DEBUG("Prepare to execute subscription.");
569 /* create requests */
Michal Vasko1cb9ebc2016-05-30 09:39:15 +0200570 rpc = nc_rpc_subscribe(stream, NULL, (start_time == -1) ? NULL : nc_time2datetime(start, NULL, NULL),
571 (stop_time == 0) ? NULL : nc_time2datetime(stop, NULL, NULL), NC_PARAMTYPE_CONST);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200572 if (rpc == NULL) {
573 ERROR("notifications: creating an rpc request failed.");
574 goto operation_failed;
575 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200576
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200577 DEBUG("Send NC subscribe.");
578 create_err_reply_p();
579 if (send_recv_process(session, "subscribe", rpc) != 0) {
580 ERROR("Subscription RPC failed.");
581 goto operation_failed;
582 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200583
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200584 GETSPEC_ERR_REPLY
585 if (err_reply != NULL) {
Michal Vasko460dde52016-05-31 09:00:45 +0200586 free_err_reply();
587 ERROR("RPC-Error received and cleaned, because we can't send it anywhere.");
588 goto operation_failed;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200589 }
Tomas Cejka442258e2014-04-01 18:17:18 +0200590
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200591 rpc = NULL; /* just note that rpc is already freed by send_recv_process() */
Tomas Cejkaba21b382013-04-13 02:37:32 +0200592
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200593 DEBUG("notifications: creating libnetconf notification thread (%s).", session_id);
Tomas Cejka654f84e2013-04-19 11:55:01 +0200594
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200595 pthread_mutex_unlock(&locked_session->lock);
596
597 /* store hash identification of netconf session for notifications printing callback */
598 if (pthread_setspecific(thread_key, session_id) != 0) {
599 ERROR("notifications: cannot set thread-specific hash value.");
600 }
601
602 DEBUG("Create notification_thread.");
603 nc_recv_notif_dispatch(session, notification_fileprint);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200604 return 0;
Tomas Cejka5ce57b62013-08-29 17:47:39 +0200605
606operation_failed:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200607 pthread_mutex_unlock(&locked_session->lock);
608 return -1;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200609}
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100610
Michal Vasko56d70bc2016-05-10 12:06:52 +0200611static int
612callback_notification(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100613{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200614 int n = 0, m = 0, i;
615 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 40960 + LWS_SEND_BUFFER_POST_PADDING];
616 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
617 struct per_session_data__notif_client *pss = (struct per_session_data__notif_client *)user;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200618
Michal Vasko56d70bc2016-05-10 12:06:52 +0200619 debug_print_clb(__func__, reason);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100620
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200621 switch (reason) {
622 case LWS_CALLBACK_ESTABLISHED:
623 DEBUG("notification client connected.");
624 break;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200625
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200626 case LWS_CALLBACK_SERVER_WRITEABLE:
627 if (pss->session_id == NULL) {
628 return 0;
629 }
630 //DEBUG("Callback server writeable.");
631 //DEBUG("lock session lock.");
632 if (pthread_rwlock_wrlock(&session_lock) != 0) {
633 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
634 return -1;
635 }
636 //DEBUG("get session_with_mutex for %s.", pss->session_id);
637 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
638 if (ls == NULL) {
639 DEBUG("notification: session not found");
640 if (pthread_rwlock_unlock (&session_lock) != 0) {
641 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
642 return -1;
643 }
644 return -1;
645 }
646 pthread_mutex_lock(&ls->lock);
647 if (pthread_rwlock_unlock(&session_lock) != 0) {
648 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
649 }
650
651 //DEBUG("check for closed session.");
652 if (ls->closed == 1) {
653 DEBUG("unlock session key.");
654 if (pthread_rwlock_unlock (&session_lock) != 0) {
655 DEBUG("Error while unlocking unlock: %d (%s)", errno, strerror(errno));
656 return -1;
657 }
658 return -1;
659 }
660 //DEBUG("lock private lock.");
661 notification_t *notif = NULL;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200662
Michal Vasko1ac11282015-11-05 10:28:07 +0100663 if (ls->notif_count) {
664 DEBUG("notification: POP notifications for session");
Michal Vasko43d94a62015-11-05 10:59:32 +0100665 for (i = ls->notif_count; i; --i) {
666 notif = ls->notifications + i - 1;
Tomas Cejka73286932013-05-27 22:54:35 +0200667
Michal Vasko1ac11282015-11-05 10:28:07 +0100668 n = 0;
669 pthread_mutex_lock(&json_lock);
670 json_object *notif_json = json_object_new_object();
671 json_object_object_add(notif_json, "eventtime", json_object_new_int64(notif->eventtime));
672 json_object_object_add(notif_json, "content", json_object_new_string(notif->content));
673 pthread_mutex_unlock(&json_lock);
Tomas Cejka15c56302013-05-30 01:11:30 +0200674
Michal Vasko1ac11282015-11-05 10:28:07 +0100675 const char *msgtext = json_object_to_json_string(notif_json);
Tomas Cejka15c56302013-05-30 01:11:30 +0200676
Michal Vasko1ac11282015-11-05 10:28:07 +0100677 //n = sprintf((char *)p, "{\"eventtime\": \"%s\", \"content\": \"notification\"}", t);
678 n = sprintf((char *)p, "%s", msgtext);
679 DEBUG("ws send %dB in %lu", n, sizeof(buf));
Michal Vasko262d34b2016-05-06 13:54:37 +0200680 m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
Michal Vasko1ac11282015-11-05 10:28:07 +0100681 if (lws_send_pipe_choked(wsi)) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200682 lws_callback_on_writable(wsi);
Michal Vasko1ac11282015-11-05 10:28:07 +0100683 break;
684 }
Michal Vaskoc3146782015-11-04 14:46:41 +0100685
Michal Vasko1ac11282015-11-05 10:28:07 +0100686 pthread_mutex_lock(&json_lock);
687 json_object_put(notif_json);
688 pthread_mutex_unlock(&json_lock);
689 free(notif->content);
Michal Vaskoc3146782015-11-04 14:46:41 +0100690 }
Michal Vasko43d94a62015-11-05 10:59:32 +0100691 ls->notif_count = 0;
692 free(ls->notifications);
693 ls->notifications = NULL;
694
Michal Vasko1ac11282015-11-05 10:28:07 +0100695 DEBUG("notification: POP notifications done");
Michal Vaskoc3146782015-11-04 14:46:41 +0100696 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200697
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200698 //DEBUG("unlock private lock");
699 if (pthread_mutex_unlock(&ls->lock) != 0) {
700 DEBUG("notification: cannot unlock session");
701 }
702 //DEBUG("unlock session lock");
Tomas Cejkaba21b382013-04-13 02:37:32 +0200703
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200704 if (m < n) {
705 DEBUG("ERROR %d writing to di socket.", n);
Tomas Cejka15c56302013-05-30 01:11:30 +0200706
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200707 return -1;
708 }
709 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100710
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200711 case LWS_CALLBACK_RECEIVE:
712 DEBUG("Callback receive.");
713 DEBUG("received: (%s)", (char *)in);
714 if (pss->session_id == NULL) {
715 char *sid_end;
716 int start = -1;
717 time_t stop = time(NULL) + 30;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200718
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100719 sid_end = strchr(in, ' ');
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200720 pss->session_id = strndup(in, sid_end - (char *)in);
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100721
722 ++sid_end;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200723 sscanf(sid_end, "%d %d", (int *) &start, (int *) &stop);
724 DEBUG("notification: SID (%s) from (%s) (%i,%i)", pss->session_id, (char *) in, (int) start, (int) stop);
Tomas Cejka15c56302013-05-30 01:11:30 +0200725
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200726 DEBUG("lock session lock");
727 if (pthread_rwlock_rdlock (&session_lock) != 0) {
728 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
729 return -1;
730 }
731 DEBUG("get session with ID (%s)", pss->session_id);
732 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
733 if (ls == NULL) {
734 DEBUG("notification: session_id not found (%s)", pss->session_id);
735 DEBUG("unlock session lock");
736 if (pthread_rwlock_unlock (&session_lock) != 0) {
737 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
738 }
739 DEBUG("Close notification client");
740 return -1;
741 }
742 DEBUG("lock private lock");
743 pthread_mutex_lock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200744
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200745 DEBUG("unlock session lock");
746 if (pthread_rwlock_unlock (&session_lock) != 0) {
747 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
748 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200749
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200750 DEBUG("Found session to subscribe notif.");
751 if (ls->closed == 1) {
752 DEBUG("session already closed - handle no notification");
753 DEBUG("unlock private lock");
754 pthread_mutex_unlock(&ls->lock);
755 DEBUG("Close notification client");
756 return -1;
757 }
Michal Vasko460dde52016-05-31 09:00:45 +0200758 if (nc_session_ntf_thread_running(ls->session)) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200759 DEBUG("notification: already subscribed");
760 DEBUG("unlock private lock");
761 pthread_mutex_unlock(&ls->lock);
762 /* do not close client, only do not subscribe again */
763 return 0;
764 }
765 DEBUG("notification: prepare to subscribe stream");
766 DEBUG("unlock session lock");
767 pthread_mutex_unlock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200768
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200769 /* notif_subscribe locks on its own */
770 return notif_subscribe(ls, pss->session_id, (time_t) start, (time_t) stop);
771 }
772 if (len < 6)
773 break;
774 break;
775 /*
776 * this just demonstrates how to use the protocol filter. If you won't
777 * study and reject connections based on header content, you don't need
778 * to handle this callback
779 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100780
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200781 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
782 //dump_handshake_info(wsi);
783 /* you could return non-zero here and kill the connection */
784 break;
785 //gives segfault :-(
786 //case LWS_CALLBACK_CLOSED:
787 // if (pss->session_key != NULL) {
788 // free(pss->session_key);
789 // }
790 // if (pss != NULL) {
791 // free(pss);
792 // }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100793
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200794 default:
795 break;
796 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100797
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200798 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100799}
800
Michal Vasko262d34b2016-05-06 13:54:37 +0200801static struct lws_protocols protocols[] = {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200802 /* first protocol must always be HTTP handler */
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200803 {
804 "http-only", /* name */
805 callback_http, /* callback */
Michal Vasko262d34b2016-05-06 13:54:37 +0200806 sizeof(struct per_session_data__http), /* per_session_data_size */
807 0, /* max frame size / rx buffer */
808 1,
809 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200810 },
811 {
812 "notification-protocol",
813 callback_notification,
814 sizeof(struct per_session_data__notif_client),
815 4000,
Michal Vasko262d34b2016-05-06 13:54:37 +0200816 2,
817 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200818 },
Michal Vasko262d34b2016-05-06 13:54:37 +0200819 { NULL, NULL, 0, 0, 0, NULL } /* terminator */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100820};
821
Tomas Cejkaba21b382013-04-13 02:37:32 +0200822/**
823 * initialization of notification module
824 */
Michal Vasko56d70bc2016-05-10 12:06:52 +0200825int
826notification_init(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100827{
Michal Vasko262d34b2016-05-06 13:54:37 +0200828 char cert_path[1024], key_path[1024];
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200829 struct lws_context_creation_info info;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200830 int debug_level = 7;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100831
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200832 memset(&info, 0, sizeof info);
833 info.port = NOTIFICATION_SERVER_PORT;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100834
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200835 /* tell the library what debug level to emit and to send it to syslog */
836 lws_set_log_level(debug_level, lwsl_emit_syslog);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100837
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200838 DEBUG("Initialization of libwebsocket");
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200839 max_poll_elements = getdtablesize();
840 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
841 fd_lookup = malloc(max_poll_elements * sizeof (int));
842 if (pollfds == NULL || fd_lookup == NULL) {
843 ERROR("notifications: Out of memory pollfds=%d\n", max_poll_elements);
844 return -1;
845 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100846
Michal Vasko262d34b2016-05-06 13:54:37 +0200847 info.iface = NULL;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200848 info.protocols = protocols;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100849
Michal Vasko262d34b2016-05-06 13:54:37 +0200850 snprintf(cert_path, sizeof(cert_path), SERVER_CERT_DIR "/" SERVER_CERT);
851 snprintf(key_path, sizeof(key_path), SERVER_CERT_DIR "/" SERVER_KEY);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100852
Michal Vasko262d34b2016-05-06 13:54:37 +0200853 info.ssl_cert_filepath = cert_path;
854 info.ssl_private_key_filepath = key_path;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100855
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200856 info.gid = -1;
857 info.uid = -1;
Michal Vaskoa41bb802016-05-30 09:04:55 +0200858 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100859
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200860 /* create server */
Michal Vasko262d34b2016-05-06 13:54:37 +0200861 context = lws_create_context(&info);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200862 if (context == NULL) {
863 DEBUG("libwebsocket init failed.");
864 return -1;
865 }
Tomas Cejka15c56302013-05-30 01:11:30 +0200866
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200867 if (pthread_key_create(&thread_key, NULL) != 0) {
868 ERROR("notifications: pthread_key_create failed");
869 }
870 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100871}
872
Michal Vasko56d70bc2016-05-10 12:06:52 +0200873void
874notification_close(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100875{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200876 if (context) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200877 lws_context_destroy(context);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200878 }
879 free(pollfds);
880 free(fd_lookup);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100881
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200882 DEBUG("libwebsockets-test-server exited cleanly\n");
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100883}
884
Tomas Cejkaba21b382013-04-13 02:37:32 +0200885
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100886/**
887 * \brief send notification if any
888 * \return < 0 on error
889 */
Michal Vasko56d70bc2016-05-10 12:06:52 +0200890int
891notification_handle()
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100892{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200893 static struct timeval tv;
894 static unsigned int olds = 0;
Michal Vasko60920a42016-05-30 10:02:02 +0200895 int n = 0, fd_pos;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100896
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200897 gettimeofday(&tv, NULL);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100898
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200899 /*
900 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
901 * live websocket connection using the DUMB_INCREMENT protocol,
902 * as soon as it can take more packets (usually immediately)
903 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100904
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200905 if (((unsigned int)tv.tv_sec - olds) > 0) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200906 lws_callback_on_writable_all_protocol(context, &protocols[PROTOCOL_NOTIFICATION]);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200907 olds = tv.tv_sec;
908 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100909
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200910 /*
911 * this represents an existing server's single poll action
912 * which also includes libwebsocket sockets
913 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100914
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200915 n = poll(pollfds, count_pollfds, 50);
Michal Vasko262d34b2016-05-06 13:54:37 +0200916 if (n < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200917 return n;
Michal Vasko262d34b2016-05-06 13:54:37 +0200918 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100919
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200920 if (n) {
921 for (n = 0; n < count_pollfds; n++) {
Michal Vasko3b2507e2016-05-30 09:33:04 +0200922 if (pollfds[n].revents & POLLHUP) {
923 ERROR("notifications: poll pipe closed");
Michal Vasko0fdd8212016-05-30 09:55:23 +0200924 if (--count_pollfds) {
Michal Vasko60920a42016-05-30 10:02:02 +0200925 fd_pos = fd_lookup[pollfds[n].fd];
926 pollfds[fd_pos] = pollfds[count_pollfds];
927 fd_lookup[pollfds[count_pollfds].fd] = fd_pos;
Michal Vasko0fdd8212016-05-30 09:55:23 +0200928 }
Michal Vasko3b2507e2016-05-30 09:33:04 +0200929 return -1;
930 } else if (pollfds[n].revents & POLLERR) {
931 ERROR("notifications: poll pipe error");
Michal Vasko0fdd8212016-05-30 09:55:23 +0200932 if (--count_pollfds) {
Michal Vasko60920a42016-05-30 10:02:02 +0200933 fd_pos = fd_lookup[pollfds[n].fd];
934 pollfds[fd_pos] = pollfds[count_pollfds];
935 fd_lookup[pollfds[count_pollfds].fd] = fd_pos;
Michal Vasko0fdd8212016-05-30 09:55:23 +0200936 }
Michal Vasko0fc2d4c2016-05-30 09:03:14 +0200937 return -1;
938 } else if (pollfds[n].revents & (POLLIN | POLLOUT)) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200939 /*
940 * returns immediately if the fd does not
941 * match anything under libwebsockets
942 * control
943 */
Michal Vasko262d34b2016-05-06 13:54:37 +0200944 if (lws_service_fd(context, &pollfds[n]) < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200945 return 1;
946 }
947 }
948 }
949 }
950 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100951}
952
953#endif
954
955
956#ifndef WITH_NOTIFICATIONS
957#ifdef TEST_NOTIFICATION_SERVER
Michal Vasko56d70bc2016-05-10 12:06:52 +0200958int
959main(int argc, char **argv)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100960{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200961 if (notification_init(NULL, NULL) == -1) {
962 fprintf(stderr, "Error during initialization\n");
963 return 1;
964 }
965 while (!force_exit) {
966 notification_handle();
967 }
968 notification_close();
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100969}
970#endif
971#endif