blob: e019b2b6fbc37fa7061b19cc96a2c0405a22bb67 [file] [log] [blame]
Andy Greena0da8a82010-11-08 17:12:19 +00001/*
2 * libwebsockets-test-server - libwebsockets test implementation
Andy Greene77ddd82010-11-13 10:03:47 +00003 *
Andy Green6964bb52011-01-23 16:50:33 +00004 * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
Andy Greena0da8a82010-11-08 17:12:19 +00005 *
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 */
Joakim Soderbergd2edfec2013-02-06 15:27:27 +090021#ifdef CMAKE_BUILD
22#include "lws_config.h"
23#endif
Andy Greena0da8a82010-11-08 17:12:19 +000024
Andy Green775c0dd2010-10-29 14:15:22 +010025#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
Andy Greenea71ed12010-10-31 07:40:33 +000028#include <getopt.h>
Andy Green5fd8a5e2010-10-31 11:57:17 +000029#include <string.h>
Andy Green90c7cbc2011-01-27 06:26:52 +000030#include <sys/time.h>
Andy Greenfbf48222013-02-14 23:06:37 +080031#include <sys/stat.h>
32#include <fcntl.h>
Andy Green5fc460c2013-01-15 19:44:33 +080033#include <assert.h>
Joakim Soderbergf272cb02013-02-13 09:29:26 +080034#ifdef WIN32
35
36#ifdef EXTERNAL_POLL
37 #ifndef WIN32_LEAN_AND_MEAN
38 #define WIN32_LEAN_AND_MEAN
39 #endif
40 #include <winsock2.h>
41 #include <ws2tcpip.h>
42 #include <stddef.h>
43
44 #include "websock-w32.h"
45#endif
46
47#else // NOT WIN32
Andy Green058ba812013-01-19 11:32:18 +080048#include <syslog.h>
Joakim Soderberg4c531232013-02-06 15:26:58 +090049#endif
Joakim Soderbergf272cb02013-02-13 09:29:26 +080050
Andy Green08f2c012013-01-30 08:02:26 +080051#include <signal.h>
Andy Green5fd8a5e2010-10-31 11:57:17 +000052
Andy Green7310e9c2010-11-01 09:12:17 +000053#include "../lib/libwebsockets.h"
Andy Green775c0dd2010-10-29 14:15:22 +010054
Andy Green69758fa2011-03-07 07:08:07 +000055static int close_testing;
Edwin van den Oetelaar596b2202013-01-20 20:51:14 +080056int max_poll_elements;
Andy Green5fc460c2013-01-15 19:44:33 +080057
Edwin van den Oetelaar596b2202013-01-20 20:51:14 +080058struct pollfd *pollfds;
59int *fd_lookup;
Edwin van der Oetelaar6c720c42013-01-17 11:16:15 +080060int count_pollfds;
Andy Green08f2c012013-01-30 08:02:26 +080061int force_exit = 0;
Andy Greenfd6764a2013-01-19 11:11:42 +080062
Andy Greenfe2a0d22010-11-12 13:10:40 +000063/*
64 * This demo server shows how to use libwebsockets for one or more
65 * websocket protocols in the same server
66 *
67 * It defines the following websocket protocols:
68 *
69 * dumb-increment-protocol: once the socket is opened, an incrementing
70 * ascii string is sent down it every 50ms.
Andy Green90c7cbc2011-01-27 06:26:52 +000071 * If you send "reset\n" on the websocket, then
72 * the incrementing number is reset to 0.
Andy Green51959682010-11-12 14:12:13 +000073 *
74 * lws-mirror-protocol: copies any received packet to every connection also
Andy Green90c7cbc2011-01-27 06:26:52 +000075 * using this protocol, including the sender
Andy Greenfe2a0d22010-11-12 13:10:40 +000076 */
77
Andy Greenc87fc2f2011-01-18 15:29:04 +000078enum demo_protocols {
79 /* always first */
80 PROTOCOL_HTTP = 0,
81
82 PROTOCOL_DUMB_INCREMENT,
83 PROTOCOL_LWS_MIRROR,
84
85 /* always last */
86 DEMO_PROTOCOL_COUNT
87};
88
Andy Greenfe2a0d22010-11-12 13:10:40 +000089
Andy Greencce2a812012-04-12 11:06:05 +080090#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
Joakim Soderbergf0ee6692013-03-18 17:08:25 +000091char *resource_path = LOCAL_RESOURCE_PATH;
Andy Green775c0dd2010-10-29 14:15:22 +010092
Andy Greenbb2dc8a2013-01-16 10:06:28 +080093/*
94 * We take a strict whitelist approach to stop ../ attacks
95 */
96
97struct serveable {
98 const char *urlpath;
99 const char *mimetype;
100};
101
102static const struct serveable whitelist[] = {
103 { "/favicon.ico", "image/x-icon" },
104 { "/libwebsockets.org-logo.png", "image/png" },
105
106 /* last one is the default served if no match */
107 { "/test.html", "text/html" },
108};
109
Andy Greenfbf48222013-02-14 23:06:37 +0800110struct per_session_data__http {
111 int fd;
112};
113
Andy Green4f3943a2010-11-12 10:44:16 +0000114/* this protocol server (always the first one) just knows how to do HTTP */
Andy Green251f6fa2010-11-03 11:13:06 +0000115
Andy Green6ee372f2012-04-09 15:09:01 +0800116static int callback_http(struct libwebsocket_context *context,
Andy Green62c54d22011-02-14 09:14:25 +0000117 struct libwebsocket *wsi,
Andy Greene77ddd82010-11-13 10:03:47 +0000118 enum libwebsocket_callback_reasons reason, void *user,
Andy Green251f6fa2010-11-03 11:13:06 +0000119 void *in, size_t len)
Andy Green775c0dd2010-10-29 14:15:22 +0100120{
Andy Green058ba812013-01-19 11:32:18 +0800121#if 0
Andy Green863d4d22011-02-13 08:40:37 +0000122 char client_name[128];
123 char client_ip[128];
Andy Green058ba812013-01-19 11:32:18 +0800124#endif
Andy Greenbb2dc8a2013-01-16 10:06:28 +0800125 char buf[256];
Andy Greenfc7c5e42013-02-23 10:50:10 +0800126 int n, m;
Andy Greenfbf48222013-02-14 23:06:37 +0800127 unsigned char *p;
Andy Green73a820a2013-02-18 11:32:49 +0800128 static unsigned char buffer[4096];
Andy Greenfbf48222013-02-14 23:06:37 +0800129 struct stat stat_buf;
130 struct per_session_data__http *pss = (struct per_session_data__http *)user;
Andy Greena50dd1a2013-01-15 12:39:48 +0800131#ifdef EXTERNAL_POLL
Andy Green50097dd2013-02-15 22:36:30 +0800132 int fd = (int)(long)in;
Andy Greena50dd1a2013-01-15 12:39:48 +0800133#endif
Andy Green863d4d22011-02-13 08:40:37 +0000134
Andy Green775c0dd2010-10-29 14:15:22 +0100135 switch (reason) {
Andy Green5fd8a5e2010-10-31 11:57:17 +0000136 case LWS_CALLBACK_HTTP:
Andy Greene77ddd82010-11-13 10:03:47 +0000137
Andy Greenfbf48222013-02-14 23:06:37 +0800138 /* check for the "send a big file by hand" example case */
139
140 if (!strcmp((const char *)in, "/leaf.jpg")) {
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000141 char leaf_path[1024];
142 snprintf(leaf_path, sizeof(leaf_path), "%s/leaf.jpg", resource_path);
Andy Greenfbf48222013-02-14 23:06:37 +0800143
144 /* well, let's demonstrate how to send the hard way */
145
146 p = buffer;
147
Michel Archambaulte8c00aa2013-03-14 11:45:49 -0400148#ifdef WIN32
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000149 pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
Michel Archambaulte8c00aa2013-03-14 11:45:49 -0400150#else
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000151 pss->fd = open(leaf_path, O_RDONLY);
Michel Archambaulte8c00aa2013-03-14 11:45:49 -0400152#endif
153
Andy Greenfbf48222013-02-14 23:06:37 +0800154 if (pss->fd < 0)
155 return -1;
156
157 fstat(pss->fd, &stat_buf);
158
159 /*
160 * we will send a big jpeg file, but it could be
161 * anything. Set the Content-Type: appropriately
162 * so the browser knows what to do with it.
163 */
164
165 p += sprintf((char *)p,
166 "HTTP/1.0 200 OK\x0d\x0a"
167 "Server: libwebsockets\x0d\x0a"
Luce46043c2013-02-23 11:01:21 +0800168 "Content-Type: image/jpeg\x0d\x0a"
Andy Greenfbf48222013-02-14 23:06:37 +0800169 "Content-Length: %u\x0d\x0a\x0d\x0a",
170 (unsigned int)stat_buf.st_size);
171
172 /*
173 * send the http headers...
174 * this won't block since it's the first payload sent
175 * on the connection since it was established
Andy Greenfc7c5e42013-02-23 10:50:10 +0800176 * (too small for partial)
Andy Greenfbf48222013-02-14 23:06:37 +0800177 */
178
179 n = libwebsocket_write(wsi, buffer,
180 p - buffer, LWS_WRITE_HTTP);
181
182 if (n < 0) {
183 close(pss->fd);
184 return -1;
185 }
186 /*
187 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
188 */
189 libwebsocket_callback_on_writable(context, wsi);
190 break;
191 }
192
193 /* if not, send a file the easy way */
194
Andy Greenbb2dc8a2013-01-16 10:06:28 +0800195 for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
Andy Green36eb70d2013-02-01 08:42:15 +0800196 if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
Andy Greenbb2dc8a2013-01-16 10:06:28 +0800197 break;
Andy Greene77ddd82010-11-13 10:03:47 +0000198
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000199 sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
Andy Green5fd8a5e2010-10-31 11:57:17 +0000200
Andy Greenbb2dc8a2013-01-16 10:06:28 +0800201 if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
Andy Greenfbf48222013-02-14 23:06:37 +0800202 return -1; /* through completion or error, close the socket */
Andy Green24b588b2013-01-13 09:53:18 +0800203
Andy Greend280b6e2013-01-15 13:40:23 +0800204 /*
205 * notice that the sending of the file completes asynchronously,
206 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
207 * it's done
208 */
209
Andy Greenbb2dc8a2013-01-16 10:06:28 +0800210 break;
Andy Greend280b6e2013-01-15 13:40:23 +0800211
212 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
Andy Green058ba812013-01-19 11:32:18 +0800213// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
Andy Greend280b6e2013-01-15 13:40:23 +0800214 /* kill the connection after we sent one file */
Andy Greenfbf48222013-02-14 23:06:37 +0800215 return -1;
216
217 case LWS_CALLBACK_HTTP_WRITEABLE:
218 /*
219 * we can send more of whatever it is we were sending
220 */
221
222 do {
223 n = read(pss->fd, buffer, sizeof buffer);
224 /* problem reading, close conn */
225 if (n < 0)
226 goto bail;
227 /* sent it all, close conn */
228 if (n == 0)
229 goto bail;
230 /*
231 * because it's HTTP and not websocket, don't need to take
232 * care about pre and postamble
233 */
Andy Greenfc7c5e42013-02-23 10:50:10 +0800234 m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
235 if (m < 0)
Andy Greenfbf48222013-02-14 23:06:37 +0800236 /* write failed, close conn */
237 goto bail;
Andy Greenfc7c5e42013-02-23 10:50:10 +0800238 if (m != n)
239 /* partial write, adjust */
240 lseek(pss->fd, m - n, SEEK_CUR);
Andy Greenfbf48222013-02-14 23:06:37 +0800241
242 } while (!lws_send_pipe_choked(wsi));
243 libwebsocket_callback_on_writable(context, wsi);
244 break;
245
246bail:
247 close(pss->fd);
248 return -1;
Andy Greena2b0ab02010-11-11 12:28:29 +0000249
Andy Green863d4d22011-02-13 08:40:37 +0000250 /*
251 * callback for confirming to continue with client IP appear in
252 * protocol 0 callback since no websocket protocol has been agreed
253 * yet. You can just ignore this if you won't filter on client IP
254 * since the default uhandled callback return is 0 meaning let the
255 * connection continue.
256 */
257
258 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
Andy Green058ba812013-01-19 11:32:18 +0800259#if 0
Edwin van den Oetelaar8c8a8e12013-02-20 20:56:59 +0800260 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
Andy Green863d4d22011-02-13 08:40:37 +0000261 sizeof(client_name), client_ip, sizeof(client_ip));
262
Andy Green058ba812013-01-19 11:32:18 +0800263 fprintf(stderr, "Received network connect from %s (%s)\n",
264 client_name, client_ip);
265#endif
Andy Green863d4d22011-02-13 08:40:37 +0000266 /* if we returned non-zero from here, we kill the connection */
267 break;
268
Andy Greena50dd1a2013-01-15 12:39:48 +0800269#ifdef EXTERNAL_POLL
270 /*
271 * callbacks for managing the external poll() array appear in
272 * protocol 0 callback
273 */
274
275 case LWS_CALLBACK_ADD_POLL_FD:
Edwin van der Oetelaar6c720c42013-01-17 11:16:15 +0800276
Edwin van den Oetelaar596b2202013-01-20 20:51:14 +0800277 if (count_pollfds >= max_poll_elements) {
Andy Green058ba812013-01-19 11:32:18 +0800278 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
Andy Greena50dd1a2013-01-15 12:39:48 +0800279 return 1;
Andy Green5fc460c2013-01-15 19:44:33 +0800280 }
Andy Green5fc460c2013-01-15 19:44:33 +0800281
Edwin van der Oetelaar6c720c42013-01-17 11:16:15 +0800282 fd_lookup[fd] = count_pollfds;
283 pollfds[count_pollfds].fd = fd;
284 pollfds[count_pollfds].events = (int)(long)len;
Andy Greena50dd1a2013-01-15 12:39:48 +0800285 pollfds[count_pollfds++].revents = 0;
286 break;
287
288 case LWS_CALLBACK_DEL_POLL_FD:
Edwin van der Oetelaar6c720c42013-01-17 11:16:15 +0800289 if (!--count_pollfds)
290 break;
291 m = fd_lookup[fd];
292 /* have the last guy take up the vacant slot */
293 pollfds[m] = pollfds[count_pollfds];
294 fd_lookup[pollfds[count_pollfds].fd] = m;
Andy Greena50dd1a2013-01-15 12:39:48 +0800295 break;
296
297 case LWS_CALLBACK_SET_MODE_POLL_FD:
Edwin van der Oetelaar6c720c42013-01-17 11:16:15 +0800298 pollfds[fd_lookup[fd]].events |= (int)(long)len;
Andy Greena50dd1a2013-01-15 12:39:48 +0800299 break;
300
301 case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
Edwin van der Oetelaar6c720c42013-01-17 11:16:15 +0800302 pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
Andy Greena50dd1a2013-01-15 12:39:48 +0800303 break;
304#endif
Edwin van der Oetelaar6c720c42013-01-17 11:16:15 +0800305
Andy Green4f3943a2010-11-12 10:44:16 +0000306 default:
307 break;
Andy Green775c0dd2010-10-29 14:15:22 +0100308 }
Andy Greenab990e42010-10-31 12:42:52 +0000309
Andy Green775c0dd2010-10-29 14:15:22 +0100310 return 0;
311}
312
Andy Green38c4f0c2011-02-13 08:54:05 +0000313/*
314 * this is just an example of parsing handshake headers, you don't need this
315 * in your code unless you will filter allowing connections by the header
316 * content
317 */
318
319static void
Andy Green16ab3182013-02-10 18:02:31 +0800320dump_handshake_info(struct libwebsocket *wsi)
Andy Green38c4f0c2011-02-13 08:54:05 +0000321{
322 int n;
Andy Greena41314f2011-05-23 10:00:03 +0100323 static const char *token_names[WSI_TOKEN_COUNT] = {
Peter Hinz56885f32011-03-02 22:03:47 +0000324 /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
325 /*[WSI_TOKEN_HOST] =*/ "Host",
326 /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
327 /*[WSI_TOKEN_KEY1] =*/ "key 1",
328 /*[WSI_TOKEN_KEY2] =*/ "key 2",
329 /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
330 /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
331 /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
332 /*[WSI_TOKEN_DRAFT] =*/ "Draft",
333 /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
Andy Green38c4f0c2011-02-13 08:54:05 +0000334
335 /* new for 04 */
Peter Hinz56885f32011-03-02 22:03:47 +0000336 /*[WSI_TOKEN_KEY] =*/ "Key",
337 /*[WSI_TOKEN_VERSION] =*/ "Version",
338 /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
Andy Green38c4f0c2011-02-13 08:54:05 +0000339
340 /* new for 05 */
Peter Hinz56885f32011-03-02 22:03:47 +0000341 /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
Andy Green38c4f0c2011-02-13 08:54:05 +0000342
343 /* client receives these */
Peter Hinz56885f32011-03-02 22:03:47 +0000344 /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
345 /*[WSI_TOKEN_NONCE] =*/ "Nonce",
346 /*[WSI_TOKEN_HTTP] =*/ "Http",
Andy Greena41314f2011-05-23 10:00:03 +0100347 /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
Andy Green38c4f0c2011-02-13 08:54:05 +0000348 };
Andy Green16ab3182013-02-10 18:02:31 +0800349 char buf[256];
Andy Green6ee372f2012-04-09 15:09:01 +0800350
Andy Green38c4f0c2011-02-13 08:54:05 +0000351 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
Andy Green16ab3182013-02-10 18:02:31 +0800352 if (!lws_hdr_total_length(wsi, n))
Andy Green38c4f0c2011-02-13 08:54:05 +0000353 continue;
354
Andy Green16ab3182013-02-10 18:02:31 +0800355 lws_hdr_copy(wsi, buf, sizeof buf, n);
356
357 fprintf(stderr, " %s = %s\n", token_names[n], buf);
Andy Green38c4f0c2011-02-13 08:54:05 +0000358 }
359}
360
Andy Green4f3943a2010-11-12 10:44:16 +0000361/* dumb_increment protocol */
362
Andy Green0ca6a172010-12-19 20:50:01 +0000363/*
364 * one of these is auto-created for each connection and a pointer to the
365 * appropriate instance is passed to the callback in the user parameter
366 *
367 * for this example protocol we use it to individualize the count for each
368 * connection.
369 */
370
Andy Green4f3943a2010-11-12 10:44:16 +0000371struct per_session_data__dumb_increment {
372 int number;
373};
374
375static int
Andy Green6ee372f2012-04-09 15:09:01 +0800376callback_dumb_increment(struct libwebsocket_context *context,
Andy Green62c54d22011-02-14 09:14:25 +0000377 struct libwebsocket *wsi,
Andy Green4f3943a2010-11-12 10:44:16 +0000378 enum libwebsocket_callback_reasons reason,
Andy Greene77ddd82010-11-13 10:03:47 +0000379 void *user, void *in, size_t len)
Andy Green4f3943a2010-11-12 10:44:16 +0000380{
381 int n;
Andy Green90c7cbc2011-01-27 06:26:52 +0000382 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
Andy Green4f3943a2010-11-12 10:44:16 +0000383 LWS_SEND_BUFFER_POST_PADDING];
Andy Green90c7cbc2011-01-27 06:26:52 +0000384 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
Andy Green36eb70d2013-02-01 08:42:15 +0800385 struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
Andy Greene77ddd82010-11-13 10:03:47 +0000386
Andy Green4f3943a2010-11-12 10:44:16 +0000387 switch (reason) {
388
389 case LWS_CALLBACK_ESTABLISHED:
Andy Green058ba812013-01-19 11:32:18 +0800390 lwsl_info("callback_dumb_increment: "
Andy Green6ee372f2012-04-09 15:09:01 +0800391 "LWS_CALLBACK_ESTABLISHED\n");
Andy Green4f3943a2010-11-12 10:44:16 +0000392 pss->number = 0;
393 break;
394
Andy Green6f520a52013-01-29 17:57:39 +0800395 case LWS_CALLBACK_SERVER_WRITEABLE:
Andy Green90c7cbc2011-01-27 06:26:52 +0000396 n = sprintf((char *)p, "%d", pss->number++);
Andy Greenfc7c5e42013-02-23 10:50:10 +0800397 /* too small for partial */
Andy Green4f3943a2010-11-12 10:44:16 +0000398 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
399 if (n < 0) {
Andy Green51b20ee2013-02-17 09:14:08 +0800400 lwsl_err("ERROR %d writing to di socket\n", n);
401 return -1;
Andy Green4f3943a2010-11-12 10:44:16 +0000402 }
Andy Green69758fa2011-03-07 07:08:07 +0000403 if (close_testing && pss->number == 50) {
Andy Green058ba812013-01-19 11:32:18 +0800404 lwsl_info("close tesing limit, closing\n");
Andy Green508946c2013-02-12 10:19:08 +0800405 return -1;
Andy Green69758fa2011-03-07 07:08:07 +0000406 }
Andy Green4f3943a2010-11-12 10:44:16 +0000407 break;
408
409 case LWS_CALLBACK_RECEIVE:
Andy Green058ba812013-01-19 11:32:18 +0800410// fprintf(stderr, "rx %d\n", (int)len);
Andy Green4f3943a2010-11-12 10:44:16 +0000411 if (len < 6)
412 break;
Andy Green36eb70d2013-02-01 08:42:15 +0800413 if (strcmp((const char *)in, "reset\n") == 0)
Andy Green4f3943a2010-11-12 10:44:16 +0000414 pss->number = 0;
415 break;
Andy Green38c4f0c2011-02-13 08:54:05 +0000416 /*
417 * this just demonstrates how to use the protocol filter. If you won't
418 * study and reject connections based on header content, you don't need
419 * to handle this callback
420 */
421
422 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
Andy Green16ab3182013-02-10 18:02:31 +0800423 dump_handshake_info(wsi);
Andy Green38c4f0c2011-02-13 08:54:05 +0000424 /* you could return non-zero here and kill the connection */
425 break;
Andy Green4f3943a2010-11-12 10:44:16 +0000426
427 default:
428 break;
429 }
430
431 return 0;
432}
433
434
Andy Greenfe2a0d22010-11-12 13:10:40 +0000435/* lws-mirror_protocol */
436
Andy Greenca0a1292013-03-16 11:24:23 +0800437#define MAX_MESSAGE_QUEUE 32
Andy Green90c7cbc2011-01-27 06:26:52 +0000438
439struct per_session_data__lws_mirror {
440 struct libwebsocket *wsi;
441 int ringbuffer_tail;
442};
443
444struct a_message {
445 void *payload;
446 size_t len;
447};
448
449static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
450static int ringbuffer_head;
451
Andy Greenfe2a0d22010-11-12 13:10:40 +0000452static int
Andy Green6ee372f2012-04-09 15:09:01 +0800453callback_lws_mirror(struct libwebsocket_context *context,
Andy Green62c54d22011-02-14 09:14:25 +0000454 struct libwebsocket *wsi,
Andy Greenfe2a0d22010-11-12 13:10:40 +0000455 enum libwebsocket_callback_reasons reason,
Andy Greene77ddd82010-11-13 10:03:47 +0000456 void *user, void *in, size_t len)
Andy Greenfe2a0d22010-11-12 13:10:40 +0000457{
458 int n;
Andy Green36eb70d2013-02-01 08:42:15 +0800459 struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
Andy Greene77ddd82010-11-13 10:03:47 +0000460
Andy Greenfe2a0d22010-11-12 13:10:40 +0000461 switch (reason) {
462
Andy Green90c7cbc2011-01-27 06:26:52 +0000463 case LWS_CALLBACK_ESTABLISHED:
Andy Greenca0a1292013-03-16 11:24:23 +0800464 lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
Andy Green90c7cbc2011-01-27 06:26:52 +0000465 pss->ringbuffer_tail = ringbuffer_head;
466 pss->wsi = wsi;
467 break;
468
Andy Greena7109e62013-02-11 12:05:54 +0800469 case LWS_CALLBACK_PROTOCOL_DESTROY:
470 lwsl_notice("mirror protocol cleaning up\n");
471 for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
472 if (ringbuffer[n].payload)
473 free(ringbuffer[n].payload);
474 break;
475
Andy Green9e4c2b62011-03-07 20:47:39 +0000476 case LWS_CALLBACK_SERVER_WRITEABLE:
Andy Green69758fa2011-03-07 07:08:07 +0000477 if (close_testing)
478 break;
Andy Green3182ece2013-01-20 17:08:31 +0800479 while (pss->ringbuffer_tail != ringbuffer_head) {
Andy Green90c7cbc2011-01-27 06:26:52 +0000480
481 n = libwebsocket_write(wsi, (unsigned char *)
482 ringbuffer[pss->ringbuffer_tail].payload +
483 LWS_SEND_BUFFER_PRE_PADDING,
484 ringbuffer[pss->ringbuffer_tail].len,
485 LWS_WRITE_TEXT);
486 if (n < 0) {
Andy Green51b20ee2013-02-17 09:14:08 +0800487 lwsl_err("ERROR %d writing to mirror socket\n", n);
488 return -1;
Andy Green90c7cbc2011-01-27 06:26:52 +0000489 }
Andy Greenca0a1292013-03-16 11:24:23 +0800490 if (n < ringbuffer[pss->ringbuffer_tail].len)
491 lwsl_err("mirror partial write %d vs %d\n",
492 n, ringbuffer[pss->ringbuffer_tail].len);
Andy Green90c7cbc2011-01-27 06:26:52 +0000493
494 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
495 pss->ringbuffer_tail = 0;
496 else
497 pss->ringbuffer_tail++;
498
Andy Green706961d2013-01-17 16:50:35 +0800499 if (((ringbuffer_head - pss->ringbuffer_tail) &
Andy Greenb55451c2013-03-16 12:32:27 +0800500 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
501 libwebsocket_rx_flow_allow_all_protocol(
502 libwebsockets_get_protocol(wsi));
503
Andy Green3182ece2013-01-20 17:08:31 +0800504 // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
Andy Green706961d2013-01-17 16:50:35 +0800505
Andy Green3182ece2013-01-20 17:08:31 +0800506 if (lws_send_pipe_choked(wsi)) {
507 libwebsocket_callback_on_writable(context, wsi);
Andy Greenca0a1292013-03-16 11:24:23 +0800508 break;
Andy Green3182ece2013-01-20 17:08:31 +0800509 }
Andy Greenca0a1292013-03-16 11:24:23 +0800510 /*
511 * for tests with chrome on same machine as client and
512 * server, this is needed to stop chrome choking
513 */
514 usleep(1);
Andy Green90c7cbc2011-01-27 06:26:52 +0000515 }
516 break;
517
Andy Greenfe2a0d22010-11-12 13:10:40 +0000518 case LWS_CALLBACK_RECEIVE:
Andy Green90c7cbc2011-01-27 06:26:52 +0000519
Andy Green706961d2013-01-17 16:50:35 +0800520 if (((ringbuffer_head - pss->ringbuffer_tail) &
521 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
Andy Green058ba812013-01-19 11:32:18 +0800522 lwsl_err("dropping!\n");
Andy Green706961d2013-01-17 16:50:35 +0800523 goto choke;
524 }
525
Andy Green90c7cbc2011-01-27 06:26:52 +0000526 if (ringbuffer[ringbuffer_head].payload)
527 free(ringbuffer[ringbuffer_head].payload);
528
529 ringbuffer[ringbuffer_head].payload =
530 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
531 LWS_SEND_BUFFER_POST_PADDING);
532 ringbuffer[ringbuffer_head].len = len;
533 memcpy((char *)ringbuffer[ringbuffer_head].payload +
534 LWS_SEND_BUFFER_PRE_PADDING, in, len);
535 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
536 ringbuffer_head = 0;
537 else
538 ringbuffer_head++;
539
Andy Green706961d2013-01-17 16:50:35 +0800540 if (((ringbuffer_head - pss->ringbuffer_tail) &
Andy Green3182ece2013-01-20 17:08:31 +0800541 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
Andy Green706961d2013-01-17 16:50:35 +0800542 goto done;
Andy Green90c7cbc2011-01-27 06:26:52 +0000543
Andy Green706961d2013-01-17 16:50:35 +0800544choke:
Andy Greenb55451c2013-03-16 12:32:27 +0800545 lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
546 libwebsocket_rx_flow_control(wsi, 0);
Andy Green706961d2013-01-17 16:50:35 +0800547
Andy Green058ba812013-01-19 11:32:18 +0800548// lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
Andy Green706961d2013-01-17 16:50:35 +0800549done:
Andy Green90c7cbc2011-01-27 06:26:52 +0000550 libwebsocket_callback_on_writable_all_protocol(
551 libwebsockets_get_protocol(wsi));
Andy Greenfe2a0d22010-11-12 13:10:40 +0000552 break;
Andy Green706961d2013-01-17 16:50:35 +0800553
Andy Green38c4f0c2011-02-13 08:54:05 +0000554 /*
555 * this just demonstrates how to use the protocol filter. If you won't
556 * study and reject connections based on header content, you don't need
557 * to handle this callback
558 */
559
560 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
Andy Green16ab3182013-02-10 18:02:31 +0800561 dump_handshake_info(wsi);
Andy Green38c4f0c2011-02-13 08:54:05 +0000562 /* you could return non-zero here and kill the connection */
563 break;
Andy Greenfe2a0d22010-11-12 13:10:40 +0000564
565 default:
566 break;
567 }
568
569 return 0;
570}
571
572
Andy Green4f3943a2010-11-12 10:44:16 +0000573/* list of supported protocols and callbacks */
574
Andy Greenb45993c2010-12-18 15:13:50 +0000575static struct libwebsocket_protocols protocols[] = {
Andy Green0ca6a172010-12-19 20:50:01 +0000576 /* first protocol must always be HTTP handler */
Peter Hinz56885f32011-03-02 22:03:47 +0000577
578 {
579 "http-only", /* name */
580 callback_http, /* callback */
Andy Greenfbf48222013-02-14 23:06:37 +0800581 sizeof (struct per_session_data__http), /* per_session_data_size */
Andy Green54495112013-02-06 21:10:16 +0900582 0, /* max frame size / rx buffer */
Andy Green4f3943a2010-11-12 10:44:16 +0000583 },
Peter Hinz56885f32011-03-02 22:03:47 +0000584 {
585 "dumb-increment-protocol",
586 callback_dumb_increment,
587 sizeof(struct per_session_data__dumb_increment),
Andy Green54495112013-02-06 21:10:16 +0900588 10,
Andy Green4f3943a2010-11-12 10:44:16 +0000589 },
Peter Hinz56885f32011-03-02 22:03:47 +0000590 {
591 "lws-mirror-protocol",
592 callback_lws_mirror,
Andy Green54495112013-02-06 21:10:16 +0900593 sizeof(struct per_session_data__lws_mirror),
Andy Greene7c97e82013-02-09 14:07:32 +0800594 128,
Andy Greenfe2a0d22010-11-12 13:10:40 +0000595 },
Andy Green54495112013-02-06 21:10:16 +0900596 { NULL, NULL, 0, 0 } /* terminator */
Andy Green4f3943a2010-11-12 10:44:16 +0000597};
598
Andy Green08f2c012013-01-30 08:02:26 +0800599void sighandler(int sig)
600{
601 force_exit = 1;
602}
603
Andy Greenea71ed12010-10-31 07:40:33 +0000604static struct option options[] = {
Andy Green90c7cbc2011-01-27 06:26:52 +0000605 { "help", no_argument, NULL, 'h' },
Andy Green46ef0cf2013-01-10 22:28:59 +0800606 { "debug", required_argument, NULL, 'd' },
Andy Green90c7cbc2011-01-27 06:26:52 +0000607 { "port", required_argument, NULL, 'p' },
608 { "ssl", no_argument, NULL, 's' },
Andy Green6ee372f2012-04-09 15:09:01 +0800609 { "interface", required_argument, NULL, 'i' },
Andy Green69758fa2011-03-07 07:08:07 +0000610 { "closetest", no_argument, NULL, 'c' },
Andy Green5c54d622013-01-21 12:58:04 +0800611#ifndef LWS_NO_DAEMONIZE
Andy Greenfd6764a2013-01-19 11:11:42 +0800612 { "daemonize", no_argument, NULL, 'D' },
613#endif
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000614 { "resource_path", required_argument, NULL, 'r' },
Andy Greenea71ed12010-10-31 07:40:33 +0000615 { NULL, 0, 0, 0 }
616};
Andy Green775c0dd2010-10-29 14:15:22 +0100617
Andy Greenea71ed12010-10-31 07:40:33 +0000618int main(int argc, char **argv)
Andy Green775c0dd2010-10-29 14:15:22 +0100619{
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000620 char cert_path[1024];
621 char key_path[1024];
Andy Greenea71ed12010-10-31 07:40:33 +0000622 int n = 0;
Andy Green0ca6a172010-12-19 20:50:01 +0000623 int use_ssl = 0;
Andy Green4739e5c2011-01-22 12:51:57 +0000624 struct libwebsocket_context *context;
Andy Green8014b292011-01-30 20:57:25 +0000625 int opts = 0;
Andy Green32375b72011-02-19 08:32:53 +0000626 char interface_name[128] = "";
Joakim Soderberg63ff1202013-02-11 17:52:23 +0100627 const char *iface = NULL;
Joakim Soderberg4c531232013-02-06 15:26:58 +0900628#ifndef WIN32
Andy Greenfd6764a2013-01-19 11:11:42 +0800629 int syslog_options = LOG_PID | LOG_PERROR;
Joakim Soderberg4c531232013-02-06 15:26:58 +0900630#endif
Andy Green90c7cbc2011-01-27 06:26:52 +0000631 unsigned int oldus = 0;
Andy Green1b265272013-02-09 14:01:09 +0800632 struct lws_context_creation_info info;
Andy Green6f520a52013-01-29 17:57:39 +0800633
Andy Green058ba812013-01-19 11:32:18 +0800634 int debug_level = 7;
Andy Green5c54d622013-01-21 12:58:04 +0800635#ifndef LWS_NO_DAEMONIZE
Andy Greenfd6764a2013-01-19 11:11:42 +0800636 int daemonize = 0;
637#endif
Andy Greene77ddd82010-11-13 10:03:47 +0000638
Andy Green1b265272013-02-09 14:01:09 +0800639 memset(&info, 0, sizeof info);
640 info.port = 7681;
641
Andy Greenea71ed12010-10-31 07:40:33 +0000642 while (n >= 0) {
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000643 n = getopt_long(argc, argv, "ci:hsp:d:Dr:", options, NULL);
Andy Greenea71ed12010-10-31 07:40:33 +0000644 if (n < 0)
645 continue;
646 switch (n) {
Andy Green5c54d622013-01-21 12:58:04 +0800647#ifndef LWS_NO_DAEMONIZE
Andy Greenfd6764a2013-01-19 11:11:42 +0800648 case 'D':
649 daemonize = 1;
Joakim Soderberg4c531232013-02-06 15:26:58 +0900650 #ifndef WIN32
Andy Greenfd6764a2013-01-19 11:11:42 +0800651 syslog_options &= ~LOG_PERROR;
Joakim Soderberg4c531232013-02-06 15:26:58 +0900652 #endif
Andy Greenfd6764a2013-01-19 11:11:42 +0800653 break;
654#endif
Andy Green46ef0cf2013-01-10 22:28:59 +0800655 case 'd':
Andy Green058ba812013-01-19 11:32:18 +0800656 debug_level = atoi(optarg);
Andy Green46ef0cf2013-01-10 22:28:59 +0800657 break;
Andy Green3faa9c72010-11-08 17:03:03 +0000658 case 's':
659 use_ssl = 1;
660 break;
Andy Greenea71ed12010-10-31 07:40:33 +0000661 case 'p':
Andy Green1b265272013-02-09 14:01:09 +0800662 info.port = atoi(optarg);
Andy Greenea71ed12010-10-31 07:40:33 +0000663 break;
Andy Green32375b72011-02-19 08:32:53 +0000664 case 'i':
665 strncpy(interface_name, optarg, sizeof interface_name);
666 interface_name[(sizeof interface_name) - 1] = '\0';
Joakim Soderberg63ff1202013-02-11 17:52:23 +0100667 iface = interface_name;
Andy Green32375b72011-02-19 08:32:53 +0000668 break;
Andy Green69758fa2011-03-07 07:08:07 +0000669 case 'c':
670 close_testing = 1;
671 fprintf(stderr, " Close testing mode -- closes on "
672 "client after 50 dumb increments"
673 "and suppresses lws_mirror spam\n");
674 break;
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000675 case 'r':
676 resource_path = optarg;
677 printf("Setting resource path to \"%s\"\n", resource_path);
678 break;
Andy Greenea71ed12010-10-31 07:40:33 +0000679 case 'h':
Andy Greenab990e42010-10-31 12:42:52 +0000680 fprintf(stderr, "Usage: test-server "
Andy Green46ef0cf2013-01-10 22:28:59 +0800681 "[--port=<p>] [--ssl] "
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000682 "[-d <log bitfield>] "
683 "[--resource_path <path>]\n");
Andy Greenea71ed12010-10-31 07:40:33 +0000684 exit(1);
685 }
Andy Greenea71ed12010-10-31 07:40:33 +0000686 }
Andy Green3faa9c72010-11-08 17:03:03 +0000687
Joakim Soderberg4c531232013-02-06 15:26:58 +0900688#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
Andy Greenfd6764a2013-01-19 11:11:42 +0800689 /*
690 * normally lock path would be /var/lock/lwsts or similar, to
691 * simplify getting started without having to take care about
692 * permissions or running as root, set to /tmp/.lwsts-lock
693 */
694 if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
695 fprintf(stderr, "Failed to daemonize\n");
696 return 1;
697 }
Andy Green24cba922013-01-19 13:56:10 +0800698#endif
Andy Green08f2c012013-01-30 08:02:26 +0800699
700 signal(SIGINT, sighandler);
701
Joakim Soderberg4c531232013-02-06 15:26:58 +0900702#ifndef WIN32
Andy Greenfd6764a2013-01-19 11:11:42 +0800703 /* we will only try to log things according to our debug_level */
704 setlogmask(LOG_UPTO (LOG_DEBUG));
705 openlog("lwsts", syslog_options, LOG_DAEMON);
Joakim Soderberg4c531232013-02-06 15:26:58 +0900706#endif
Andy Green058ba812013-01-19 11:32:18 +0800707
708 /* tell the library what debug level to emit and to send it to syslog */
709 lws_set_log_level(debug_level, lwsl_emit_syslog);
710
Andy Greenfd6764a2013-01-19 11:11:42 +0800711 lwsl_notice("libwebsockets test server - "
712 "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
Andy Green058ba812013-01-19 11:32:18 +0800713 "licensed under LGPL2.1\n");
Edwin van den Oetelaar596b2202013-01-20 20:51:14 +0800714#ifdef EXTERNAL_POLL
715 max_poll_elements = getdtablesize();
716 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
717 fd_lookup = malloc(max_poll_elements * sizeof (int));
718 if (pollfds == NULL || fd_lookup == NULL) {
719 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
720 return -1;
721 }
722#endif
Andy Greene77ddd82010-11-13 10:03:47 +0000723
Joakim Soderberg63ff1202013-02-11 17:52:23 +0100724 info.iface = iface;
Andy Green1b265272013-02-09 14:01:09 +0800725 info.protocols = protocols;
Andy Green3182ece2013-01-20 17:08:31 +0800726#ifndef LWS_NO_EXTENSIONS
Joakim Soderbergf272cb02013-02-13 09:29:26 +0800727 info.extensions = libwebsocket_get_internal_extensions();
Andy Green3182ece2013-01-20 17:08:31 +0800728#endif
Andy Green1b265272013-02-09 14:01:09 +0800729 if (!use_ssl) {
730 info.ssl_cert_filepath = NULL;
731 info.ssl_private_key_filepath = NULL;
732 } else {
Joakim Soderbergf0ee6692013-03-18 17:08:25 +0000733 snprintf(cert_path, sizeof(cert_path), "%s/libwebsockets-test-server.pem", resource_path);
734 snprintf(key_path, sizeof(cert_path), "%s/libwebsockets-test-server.key.pem", resource_path);
735
736 info.ssl_cert_filepath = cert_path;
737 info.ssl_private_key_filepath = key_path;
Andy Green1b265272013-02-09 14:01:09 +0800738 }
739 info.gid = -1;
740 info.uid = -1;
741 info.options = opts;
742
743 context = libwebsocket_create_context(&info);
Andy Green4739e5c2011-01-22 12:51:57 +0000744 if (context == NULL) {
Andy Green058ba812013-01-19 11:32:18 +0800745 lwsl_err("libwebsocket init failed\n");
Andy Green775c0dd2010-10-29 14:15:22 +0100746 return -1;
747 }
Andy Green775c0dd2010-10-29 14:15:22 +0100748
Andy Green3928f612012-07-20 12:58:38 +0800749 n = 0;
Andy Green08f2c012013-01-30 08:02:26 +0800750 while (n >= 0 && !force_exit) {
Andy Green90c7cbc2011-01-27 06:26:52 +0000751 struct timeval tv;
752
753 gettimeofday(&tv, NULL);
Andy Greenb45993c2010-12-18 15:13:50 +0000754
755 /*
Andy Green6f520a52013-01-29 17:57:39 +0800756 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
757 * live websocket connection using the DUMB_INCREMENT protocol,
758 * as soon as it can take more packets (usually immediately)
Andy Greenb45993c2010-12-18 15:13:50 +0000759 */
760
Andy Green90c7cbc2011-01-27 06:26:52 +0000761 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
Andy Green6f520a52013-01-29 17:57:39 +0800762 libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
Andy Green90c7cbc2011-01-27 06:26:52 +0000763 oldus = tv.tv_usec;
764 }
Andy Greene92cd172011-01-19 13:11:55 +0000765
Andy Greena50dd1a2013-01-15 12:39:48 +0800766#ifdef EXTERNAL_POLL
Andy Greene92cd172011-01-19 13:11:55 +0000767
Andy Greena50dd1a2013-01-15 12:39:48 +0800768 /*
769 * this represents an existing server's single poll action
770 * which also includes libwebsocket sockets
771 */
772
773 n = poll(pollfds, count_pollfds, 50);
774 if (n < 0)
775 continue;
776
Andy Greend280b6e2013-01-15 13:40:23 +0800777
Andy Greena50dd1a2013-01-15 12:39:48 +0800778 if (n)
779 for (n = 0; n < count_pollfds; n++)
780 if (pollfds[n].revents)
781 /*
782 * returns immediately if the fd does not
783 * match anything under libwebsockets
784 * control
785 */
786 if (libwebsocket_service_fd(context,
787 &pollfds[n]) < 0)
788 goto done;
Andy Greena50dd1a2013-01-15 12:39:48 +0800789#else
Andy Green0d49c8d2013-02-02 23:02:56 +0800790 /*
791 * If libwebsockets sockets are all we care about,
792 * you can use this api which takes care of the poll()
793 * and looping through finding who needed service.
794 *
795 * If no socket needs service, it'll return anyway after
796 * the number of ms in the second argument.
797 */
798
Andy Green3928f612012-07-20 12:58:38 +0800799 n = libwebsocket_service(context, 50);
Andy Greena50dd1a2013-01-15 12:39:48 +0800800#endif
Andy Greenb45993c2010-12-18 15:13:50 +0000801 }
802
Andy Greena50dd1a2013-01-15 12:39:48 +0800803#ifdef EXTERNAL_POLL
804done:
805#endif
Andy Greened11a022011-01-20 10:23:50 +0000806
Andy Green6964bb52011-01-23 16:50:33 +0000807 libwebsocket_context_destroy(context);
808
Andy Green058ba812013-01-19 11:32:18 +0800809 lwsl_notice("libwebsockets-test-server exited cleanly\n");
810
Joakim Soderberg4c531232013-02-06 15:26:58 +0900811#ifndef WIN32
Andy Green058ba812013-01-19 11:32:18 +0800812 closelog();
Joakim Soderberg4c531232013-02-06 15:26:58 +0900813#endif
Andy Green058ba812013-01-19 11:32:18 +0800814
Andy Green775c0dd2010-10-29 14:15:22 +0100815 return 0;
816}