1 /*
2 * libwebsockets web server application
3 *
4 * Written in 2010-2020 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 *
9 * The person who associated a work with this deed has dedicated
10 * the work to the public domain by waiving all of his or her rights
11 * to the work worldwide under copyright law, including all related
12 * and neighboring rights, to the extent allowed by law. You can copy,
13 * modify, distribute and perform the work, even for commercial purposes,
14 * all without asking permission.
15 *
16 * The test apps are intended to be adapted for use in your code, which
17 * may be proprietary. So unlike the library itself, they are licensed
18 * Public Domain.
19 */
20 #include "lws_config.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
25 #include <getopt.h>
26 #endif
27 #include <signal.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <assert.h>
32 #ifndef _WIN32
33 #include <dirent.h>
34 #include <syslog.h>
35 #include <sys/time.h>
36 #include <unistd.h>
37 #include <sys/wait.h>
38 #else
39 #include <io.h>
40 #include "gettimeofday.h"
41 #include <uv.h>
42
fork(void)43 int fork(void)
44 {
45 fprintf(stderr, "Sorry Windows doesn't support fork().\n");
46 return 0;
47 }
48 #endif
49
50 #include <libwebsockets.h>
51
52 #include <uv.h>
53
54 #if defined(LWS_HAVE_MALLOC_TRIM)
55 #include <malloc.h>
56 #endif
57
58 static struct lws_context *context;
59 static lws_sorted_usec_list_t sul_lwsws;
60 static char config_dir[128], default_plugin_path = 1;
61 static int opts = 0, do_reload = 1;
62 static uv_loop_t loop;
63 static uv_signal_t signal_outer[2];
64 static int pids[32];
65 void lwsl_emit_stderr(int level, const char *line);
66
67 #define LWSWS_CONFIG_STRING_SIZE (64 * 1024)
68
69 static const struct lws_extension exts[] = {
70 #if !defined(LWS_WITHOUT_EXTENSIONS)
71 {
72 "permessage-deflate",
73 lws_extension_callback_pm_deflate,
74 "permessage-deflate"
75 },
76 #endif
77 { NULL, NULL, NULL /* terminator */ }
78 };
79
80 #if defined(LWS_WITH_PLUGINS)
81 static const char * const plugin_dirs[] = {
82 INSTALL_DATADIR"/libwebsockets-test-server/plugins/",
83 NULL
84 };
85 #endif
86
87 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
88 static struct option options[] = {
89 { "help", no_argument, NULL, 'h' },
90 { "debug", required_argument, NULL, 'd' },
91 { "configdir", required_argument, NULL, 'c' },
92 { "no-default-plugins", no_argument, NULL, 'n' },
93 { NULL, 0, 0, 0 }
94 };
95 #endif
96
signal_cb(uv_signal_t * watcher,int signum)97 void signal_cb(uv_signal_t *watcher, int signum)
98 {
99 switch (watcher->signum) {
100 case SIGTERM:
101 case SIGINT:
102 break;
103
104 case SIGHUP:
105 if (lws_context_is_deprecated(context))
106 return;
107 lwsl_notice("Dropping listen sockets\n");
108 lws_context_deprecate(context, NULL);
109 return;
110
111 default:
112 signal(SIGABRT, SIG_DFL);
113 abort();
114 break;
115 }
116 lwsl_err("Signal %d caught\n", watcher->signum);
117 uv_signal_stop(watcher);
118 uv_signal_stop(&signal_outer[1]);
119 lws_context_destroy(context);
120 }
121
122 static void
lwsws_min(lws_sorted_usec_list_t * sul)123 lwsws_min(lws_sorted_usec_list_t *sul)
124 {
125 lwsl_debug("%s\n", __func__);
126
127 #if defined(LWS_HAVE_MALLOC_TRIM)
128 malloc_trim(4 * 1024);
129 #endif
130
131 lws_sul_schedule(context, 0, &sul_lwsws, lwsws_min, 60 * LWS_US_PER_SEC);
132 }
133
134 static int
context_creation(void)135 context_creation(void)
136 {
137 int cs_len = LWSWS_CONFIG_STRING_SIZE - 1;
138 struct lws_context_creation_info info;
139 char *cs, *config_strings;
140 void *foreign_loops[1];
141
142 cs = config_strings = malloc(LWSWS_CONFIG_STRING_SIZE);
143 if (!config_strings) {
144 lwsl_err("Unable to allocate config strings heap\n");
145 return -1;
146 }
147
148 memset(&info, 0, sizeof(info));
149
150 info.external_baggage_free_on_destroy = config_strings;
151 info.pt_serv_buf_size = 8192;
152 info.options = (uint64_t)((uint64_t)opts | LWS_SERVER_OPTION_VALIDATE_UTF8 |
153 LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
154 LWS_SERVER_OPTION_LIBUV);
155
156 #if defined(LWS_WITH_PLUGINS)
157 if (default_plugin_path)
158 info.plugin_dirs = plugin_dirs;
159 #endif
160 lwsl_notice("Using config dir: \"%s\"\n", config_dir);
161
162 /*
163 * first go through the config for creating the outer context
164 */
165 if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len))
166 goto init_failed;
167
168 foreign_loops[0] = &loop;
169 info.foreign_loops = foreign_loops;
170 info.pcontext = &context;
171
172 context = lws_create_context(&info);
173 if (context == NULL) {
174 lwsl_err("libwebsocket init failed\n");
175 goto init_failed;
176 }
177
178 /*
179 * then create the vhosts... protocols are entirely coming from
180 * plugins, so we leave it NULL
181 */
182
183 info.extensions = exts;
184
185 if (lwsws_get_config_vhosts(context, &info, config_dir, &cs, &cs_len))
186 return 1;
187
188 lws_sul_schedule(context, 0, &sul_lwsws, lwsws_min, 60 * LWS_US_PER_SEC);
189
190 return 0;
191
192 init_failed:
193 free(config_strings);
194
195 return 1;
196 }
197
198
199 /*
200 * root-level sighup handler
201 */
202
203 static void
reload_handler(int signum)204 reload_handler(int signum)
205 {
206 #ifndef _WIN32
207 int m;
208
209 switch (signum) {
210
211 case SIGHUP: /* reload */
212 fprintf(stderr, "root process receives reload\n");
213 if (!do_reload) {
214 fprintf(stderr, "passing HUP to child processes\n");
215 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
216 if (pids[m])
217 kill(pids[m], SIGHUP);
218 sleep(1);
219 }
220 do_reload = 1;
221 break;
222 case SIGINT:
223 case SIGTERM:
224 case SIGKILL:
225 fprintf(stderr, "parent process waiting 2s...\n");
226 sleep(2); /* give children a chance to deal with the signal */
227 fprintf(stderr, "killing service processes\n");
228 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
229 if (pids[m])
230 kill(pids[m], SIGTERM);
231 exit(0);
232 }
233 #else
234 // kill() implementation needed for WIN32
235 #endif
236 }
237
main(int argc,char ** argv)238 int main(int argc, char **argv)
239 {
240 int n = 0, budget = 100, debug_level = 1024 + 7;
241 #ifndef _WIN32
242 int m;
243 int status;//, syslog_options = LOG_PID | LOG_PERROR;
244 #endif
245
246 strcpy(config_dir, "/etc/lwsws");
247 while (n >= 0) {
248 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
249 n = getopt_long(argc, argv, "hd:c:n", options, NULL);
250 #else
251 n = getopt(argc, argv, "hd:c:n");
252 #endif
253 if (n < 0)
254 continue;
255 switch (n) {
256 case 'd':
257 debug_level = atoi(optarg);
258 break;
259 case 'n':
260 default_plugin_path = 0;
261 break;
262 case 'c':
263 lws_strncpy(config_dir, optarg, sizeof(config_dir));
264 break;
265 case 'h':
266 fprintf(stderr, "Usage: lwsws [-c <config dir>] "
267 "[-d <log bitfield>] [--help] "
268 "[-n]\n");
269 exit(1);
270 }
271 }
272 #ifndef _WIN32
273 /*
274 * We leave our original process up permanently, because that
275 * suits systemd.
276 *
277 * Otherwise we get into problems when reload spawns new processes and
278 * the original one dies randomly.
279 */
280
281 signal(SIGHUP, reload_handler);
282 signal(SIGINT, reload_handler);
283
284 fprintf(stderr, "Root process is %u\n", (unsigned int)getpid());
285
286 while (1) {
287 if (do_reload) {
288 do_reload = 0;
289 n = fork();
290 if (n == 0) /* new */
291 break;
292 /* old */
293 if (n > 0)
294 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
295 if (!pids[m]) {
296 pids[m] = n;
297 break;
298 }
299 }
300 #ifndef _WIN32
301 sleep(2);
302
303 n = waitpid(-1, &status, WNOHANG);
304 if (n > 0)
305 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
306 if (pids[m] == n) {
307 pids[m] = 0;
308 break;
309 }
310 #else
311 // !!! implemenation needed
312 #endif
313 }
314 #endif
315 /* child process */
316
317 lws_set_log_level(debug_level, lwsl_emit_stderr_notimestamp);
318
319 lwsl_notice("lwsws libwebsockets web server - license CC0 + MIT\n");
320 lwsl_notice("(C) Copyright 2010-2020 Andy Green <andy@warmcat.com>\n");
321
322 #if (UV_VERSION_MAJOR > 0) // Travis...
323 uv_loop_init(&loop);
324 #else
325 fprintf(stderr, "Your libuv is too old!\n");
326 return 0;
327 #endif
328 uv_signal_init(&loop, &signal_outer[0]);
329 uv_signal_start(&signal_outer[0], signal_cb, SIGINT);
330 uv_signal_init(&loop, &signal_outer[1]);
331 uv_signal_start(&signal_outer[1], signal_cb, SIGHUP);
332
333 if (context_creation()) {
334 lwsl_err("Context creation failed\n");
335 return 1;
336 }
337
338 lws_service(context, 0);
339
340 lwsl_err("%s: closing\n", __func__);
341
342 for (n = 0; n < 2; n++) {
343 uv_signal_stop(&signal_outer[n]);
344 uv_close((uv_handle_t *)&signal_outer[n], NULL);
345 }
346
347 /* cancel the per-minute sul */
348 lws_sul_cancel(&sul_lwsws);
349
350 lws_context_destroy(context);
351 (void)budget;
352 #if (UV_VERSION_MAJOR > 0) // Travis...
353 while ((n = uv_loop_close(&loop)) && --budget)
354 uv_run(&loop, UV_RUN_ONCE);
355 #endif
356
357 fprintf(stderr, "lwsws exited cleanly: %d\n", n);
358
359 #ifndef _WIN32
360 closelog();
361 #endif
362
363 context = NULL;
364
365 return 0;
366 }
367