blob: 759572bc326cd880f50cabde901bf700515d25b4 [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 */
567 rpc = nc_rpc_subscribe(stream, NULL, (start_time == -1) ? NULL : nc_time2datetime(start, NULL),
Michal Vaskof35ea502016-02-24 10:44:54 +0100568 (stop_time == 0) ? NULL : nc_time2datetime(stop, 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);
602 DEBUG("Subscription finished.");
603 return 0;
Tomas Cejka5ce57b62013-08-29 17:47:39 +0200604
605operation_failed:
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200606 pthread_mutex_unlock(&locked_session->lock);
607 return -1;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200608}
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100609
Michal Vasko56d70bc2016-05-10 12:06:52 +0200610static int
611callback_notification(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100612{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200613 int n = 0, m = 0, i;
614 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 40960 + LWS_SEND_BUFFER_POST_PADDING];
615 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
616 struct per_session_data__notif_client *pss = (struct per_session_data__notif_client *)user;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200617
Michal Vasko56d70bc2016-05-10 12:06:52 +0200618 debug_print_clb(__func__, reason);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100619
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200620 switch (reason) {
621 case LWS_CALLBACK_ESTABLISHED:
622 DEBUG("notification client connected.");
623 break;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200624
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200625 case LWS_CALLBACK_SERVER_WRITEABLE:
626 if (pss->session_id == NULL) {
627 return 0;
628 }
629 //DEBUG("Callback server writeable.");
630 //DEBUG("lock session lock.");
631 if (pthread_rwlock_wrlock(&session_lock) != 0) {
632 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
633 return -1;
634 }
635 //DEBUG("get session_with_mutex for %s.", pss->session_id);
636 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
637 if (ls == NULL) {
638 DEBUG("notification: session not found");
639 if (pthread_rwlock_unlock (&session_lock) != 0) {
640 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
641 return -1;
642 }
643 return -1;
644 }
645 pthread_mutex_lock(&ls->lock);
646 if (pthread_rwlock_unlock(&session_lock) != 0) {
647 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
648 }
649
650 //DEBUG("check for closed session.");
651 if (ls->closed == 1) {
652 DEBUG("unlock session key.");
653 if (pthread_rwlock_unlock (&session_lock) != 0) {
654 DEBUG("Error while unlocking unlock: %d (%s)", errno, strerror(errno));
655 return -1;
656 }
657 return -1;
658 }
659 //DEBUG("lock private lock.");
660 notification_t *notif = NULL;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200661
Michal Vasko1ac11282015-11-05 10:28:07 +0100662 if (ls->notif_count) {
663 DEBUG("notification: POP notifications for session");
Michal Vasko43d94a62015-11-05 10:59:32 +0100664 for (i = ls->notif_count; i; --i) {
665 notif = ls->notifications + i - 1;
Tomas Cejka73286932013-05-27 22:54:35 +0200666
Michal Vasko1ac11282015-11-05 10:28:07 +0100667 n = 0;
668 pthread_mutex_lock(&json_lock);
669 json_object *notif_json = json_object_new_object();
670 json_object_object_add(notif_json, "eventtime", json_object_new_int64(notif->eventtime));
671 json_object_object_add(notif_json, "content", json_object_new_string(notif->content));
672 pthread_mutex_unlock(&json_lock);
Tomas Cejka15c56302013-05-30 01:11:30 +0200673
Michal Vasko1ac11282015-11-05 10:28:07 +0100674 const char *msgtext = json_object_to_json_string(notif_json);
Tomas Cejka15c56302013-05-30 01:11:30 +0200675
Michal Vasko1ac11282015-11-05 10:28:07 +0100676 //n = sprintf((char *)p, "{\"eventtime\": \"%s\", \"content\": \"notification\"}", t);
677 n = sprintf((char *)p, "%s", msgtext);
678 DEBUG("ws send %dB in %lu", n, sizeof(buf));
Michal Vasko262d34b2016-05-06 13:54:37 +0200679 m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
Michal Vasko1ac11282015-11-05 10:28:07 +0100680 if (lws_send_pipe_choked(wsi)) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200681 lws_callback_on_writable(wsi);
Michal Vasko1ac11282015-11-05 10:28:07 +0100682 break;
683 }
Michal Vaskoc3146782015-11-04 14:46:41 +0100684
Michal Vasko1ac11282015-11-05 10:28:07 +0100685 pthread_mutex_lock(&json_lock);
686 json_object_put(notif_json);
687 pthread_mutex_unlock(&json_lock);
688 free(notif->content);
Michal Vaskoc3146782015-11-04 14:46:41 +0100689 }
Michal Vasko43d94a62015-11-05 10:59:32 +0100690 ls->notif_count = 0;
691 free(ls->notifications);
692 ls->notifications = NULL;
693
Michal Vasko1ac11282015-11-05 10:28:07 +0100694 DEBUG("notification: POP notifications done");
Michal Vaskoc3146782015-11-04 14:46:41 +0100695 }
Tomas Cejkaba21b382013-04-13 02:37:32 +0200696
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200697 //DEBUG("unlock private lock");
698 if (pthread_mutex_unlock(&ls->lock) != 0) {
699 DEBUG("notification: cannot unlock session");
700 }
701 //DEBUG("unlock session lock");
Tomas Cejkaba21b382013-04-13 02:37:32 +0200702
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200703 if (m < n) {
704 DEBUG("ERROR %d writing to di socket.", n);
Tomas Cejka15c56302013-05-30 01:11:30 +0200705
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200706 return -1;
707 }
708 break;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100709
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200710 case LWS_CALLBACK_RECEIVE:
711 DEBUG("Callback receive.");
712 DEBUG("received: (%s)", (char *)in);
713 if (pss->session_id == NULL) {
714 char *sid_end;
715 int start = -1;
716 time_t stop = time(NULL) + 30;
Tomas Cejkaba21b382013-04-13 02:37:32 +0200717
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100718 sid_end = strchr(in, ' ');
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200719 pss->session_id = strndup(in, sid_end - (char *)in);
Michal Vasko2bff5fb2015-11-05 10:23:02 +0100720
721 ++sid_end;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200722 sscanf(sid_end, "%d %d", (int *) &start, (int *) &stop);
723 DEBUG("notification: SID (%s) from (%s) (%i,%i)", pss->session_id, (char *) in, (int) start, (int) stop);
Tomas Cejka15c56302013-05-30 01:11:30 +0200724
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200725 DEBUG("lock session lock");
726 if (pthread_rwlock_rdlock (&session_lock) != 0) {
727 DEBUG("Error while locking rwlock: %d (%s)", errno, strerror(errno));
728 return -1;
729 }
730 DEBUG("get session with ID (%s)", pss->session_id);
731 struct session_with_mutex *ls = get_ncsession_from_sid(pss->session_id);
732 if (ls == NULL) {
733 DEBUG("notification: session_id not found (%s)", pss->session_id);
734 DEBUG("unlock session lock");
735 if (pthread_rwlock_unlock (&session_lock) != 0) {
736 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
737 }
738 DEBUG("Close notification client");
739 return -1;
740 }
741 DEBUG("lock private lock");
742 pthread_mutex_lock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200743
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200744 DEBUG("unlock session lock");
745 if (pthread_rwlock_unlock (&session_lock) != 0) {
746 DEBUG("Error while unlocking rwlock: %d (%s)", errno, strerror(errno));
747 }
Tomas Cejka47387fd2013-06-10 20:37:46 +0200748
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200749 DEBUG("Found session to subscribe notif.");
750 if (ls->closed == 1) {
751 DEBUG("session already closed - handle no notification");
752 DEBUG("unlock private lock");
753 pthread_mutex_unlock(&ls->lock);
754 DEBUG("Close notification client");
755 return -1;
756 }
757 if (ls->ntfc_subscribed != 0) {
758 DEBUG("notification: already subscribed");
759 DEBUG("unlock private lock");
760 pthread_mutex_unlock(&ls->lock);
761 /* do not close client, only do not subscribe again */
762 return 0;
763 }
764 DEBUG("notification: prepare to subscribe stream");
765 DEBUG("unlock session lock");
766 pthread_mutex_unlock(&ls->lock);
Tomas Cejka47387fd2013-06-10 20:37:46 +0200767
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200768 /* notif_subscribe locks on its own */
769 return notif_subscribe(ls, pss->session_id, (time_t) start, (time_t) stop);
770 }
771 if (len < 6)
772 break;
773 break;
774 /*
775 * this just demonstrates how to use the protocol filter. If you won't
776 * study and reject connections based on header content, you don't need
777 * to handle this callback
778 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100779
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200780 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
781 //dump_handshake_info(wsi);
782 /* you could return non-zero here and kill the connection */
783 break;
784 //gives segfault :-(
785 //case LWS_CALLBACK_CLOSED:
786 // if (pss->session_key != NULL) {
787 // free(pss->session_key);
788 // }
789 // if (pss != NULL) {
790 // free(pss);
791 // }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100792
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200793 default:
794 break;
795 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100796
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200797 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100798}
799
Michal Vasko262d34b2016-05-06 13:54:37 +0200800static struct lws_protocols protocols[] = {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200801 /* first protocol must always be HTTP handler */
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200802 {
803 "http-only", /* name */
804 callback_http, /* callback */
Michal Vasko262d34b2016-05-06 13:54:37 +0200805 sizeof(struct per_session_data__http), /* per_session_data_size */
806 0, /* max frame size / rx buffer */
807 1,
808 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200809 },
810 {
811 "notification-protocol",
812 callback_notification,
813 sizeof(struct per_session_data__notif_client),
814 4000,
Michal Vasko262d34b2016-05-06 13:54:37 +0200815 2,
816 NULL
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200817 },
Michal Vasko262d34b2016-05-06 13:54:37 +0200818 { NULL, NULL, 0, 0, 0, NULL } /* terminator */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100819};
820
Tomas Cejkaba21b382013-04-13 02:37:32 +0200821/**
822 * initialization of notification module
823 */
Michal Vasko56d70bc2016-05-10 12:06:52 +0200824int
825notification_init(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100826{
Michal Vasko262d34b2016-05-06 13:54:37 +0200827 char cert_path[1024], key_path[1024];
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200828 struct lws_context_creation_info info;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200829 int debug_level = 7;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100830
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200831 memset(&info, 0, sizeof info);
832 info.port = NOTIFICATION_SERVER_PORT;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100833
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200834 /* tell the library what debug level to emit and to send it to syslog */
835 lws_set_log_level(debug_level, lwsl_emit_syslog);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100836
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200837 DEBUG("Initialization of libwebsocket");
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200838 max_poll_elements = getdtablesize();
839 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
840 fd_lookup = malloc(max_poll_elements * sizeof (int));
841 if (pollfds == NULL || fd_lookup == NULL) {
842 ERROR("notifications: Out of memory pollfds=%d\n", max_poll_elements);
843 return -1;
844 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100845
Michal Vasko262d34b2016-05-06 13:54:37 +0200846 info.iface = NULL;
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200847 info.protocols = protocols;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100848
Michal Vasko262d34b2016-05-06 13:54:37 +0200849 snprintf(cert_path, sizeof(cert_path), SERVER_CERT_DIR "/" SERVER_CERT);
850 snprintf(key_path, sizeof(key_path), SERVER_CERT_DIR "/" SERVER_KEY);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100851
Michal Vasko262d34b2016-05-06 13:54:37 +0200852 info.ssl_cert_filepath = cert_path;
853 info.ssl_private_key_filepath = key_path;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100854
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200855 info.gid = -1;
856 info.uid = -1;
Michal Vaskoa41bb802016-05-30 09:04:55 +0200857 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100858
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200859 /* create server */
Michal Vasko262d34b2016-05-06 13:54:37 +0200860 context = lws_create_context(&info);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200861 if (context == NULL) {
862 DEBUG("libwebsocket init failed.");
863 return -1;
864 }
Tomas Cejka15c56302013-05-30 01:11:30 +0200865
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200866 if (pthread_key_create(&thread_key, NULL) != 0) {
867 ERROR("notifications: pthread_key_create failed");
868 }
869 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100870}
871
Michal Vasko56d70bc2016-05-10 12:06:52 +0200872void
873notification_close(void)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100874{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200875 if (context) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200876 lws_context_destroy(context);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200877 }
878 free(pollfds);
879 free(fd_lookup);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100880
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200881 DEBUG("libwebsockets-test-server exited cleanly\n");
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100882}
883
Tomas Cejkaba21b382013-04-13 02:37:32 +0200884
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100885/**
886 * \brief send notification if any
887 * \return < 0 on error
888 */
Michal Vasko56d70bc2016-05-10 12:06:52 +0200889int
890notification_handle()
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100891{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200892 static struct timeval tv;
893 static unsigned int olds = 0;
894 int n = 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100895
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200896 gettimeofday(&tv, NULL);
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100897
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200898 /*
899 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
900 * live websocket connection using the DUMB_INCREMENT protocol,
901 * as soon as it can take more packets (usually immediately)
902 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100903
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200904 if (((unsigned int)tv.tv_sec - olds) > 0) {
Michal Vasko262d34b2016-05-06 13:54:37 +0200905 lws_callback_on_writable_all_protocol(context, &protocols[PROTOCOL_NOTIFICATION]);
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200906 olds = tv.tv_sec;
907 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100908
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200909 /*
910 * this represents an existing server's single poll action
911 * which also includes libwebsocket sockets
912 */
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100913
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200914 n = poll(pollfds, count_pollfds, 50);
Michal Vasko262d34b2016-05-06 13:54:37 +0200915 if (n < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200916 return n;
Michal Vasko262d34b2016-05-06 13:54:37 +0200917 }
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100918
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200919 if (n) {
920 for (n = 0; n < count_pollfds; n++) {
Michal Vasko0fc2d4c2016-05-30 09:03:14 +0200921 if (pollfds[n].revents & (POLLHUP | POLLERR)) {
922 ERROR("notifications: poll pipe closed or error");
923 return -1;
924 } else if (pollfds[n].revents & (POLLIN | POLLOUT)) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200925 /*
926 * returns immediately if the fd does not
927 * match anything under libwebsockets
928 * control
929 */
Michal Vasko262d34b2016-05-06 13:54:37 +0200930 if (lws_service_fd(context, &pollfds[n]) < 0) {
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200931 return 1;
932 }
933 }
934 }
935 }
936 return 0;
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100937}
938
939#endif
940
941
942#ifndef WITH_NOTIFICATIONS
943#ifdef TEST_NOTIFICATION_SERVER
Michal Vasko56d70bc2016-05-10 12:06:52 +0200944int
945main(int argc, char **argv)
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100946{
Michal Vaskofcc9a9c2016-05-06 11:51:35 +0200947 if (notification_init(NULL, NULL) == -1) {
948 fprintf(stderr, "Error during initialization\n");
949 return 1;
950 }
951 while (!force_exit) {
952 notification_handle();
953 }
954 notification_close();
Tomas Cejkad340dbf2013-03-24 20:36:57 +0100955}
956#endif
957#endif