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];
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 (32 * 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 static const char * const plugin_dirs[] = {
81 INSTALL_DATADIR"/libwebsockets-test-server/plugins/",
82 NULL
83 };
84
85 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
86 static struct option options[] = {
87 { "help", no_argument, NULL, 'h' },
88 { "debug", required_argument, NULL, 'd' },
89 { "configdir", required_argument, NULL, 'c' },
90 { NULL, 0, 0, 0 }
91 };
92 #endif
93
signal_cb(uv_signal_t * watcher,int signum)94 void signal_cb(uv_signal_t *watcher, int signum)
95 {
96 switch (watcher->signum) {
97 case SIGTERM:
98 case SIGINT:
99 break;
100
101 case SIGHUP:
102 if (lws_context_is_deprecated(context))
103 return;
104 lwsl_notice("Dropping listen sockets\n");
105 lws_context_deprecate(context, NULL);
106 return;
107
108 default:
109 signal(SIGABRT, SIG_DFL);
110 abort();
111 break;
112 }
113 lwsl_err("Signal %d caught\n", watcher->signum);
114 uv_signal_stop(watcher);
115 uv_signal_stop(&signal_outer[1]);
116 lws_context_destroy(context);
117 }
118
119 static void
lwsws_min(lws_sorted_usec_list_t * sul)120 lwsws_min(lws_sorted_usec_list_t *sul)
121 {
122 lwsl_debug("%s\n", __func__);
123
124 #if defined(LWS_HAVE_MALLOC_TRIM)
125 malloc_trim(4 * 1024);
126 #endif
127
128 lws_sul_schedule(context, 0, &sul_lwsws, lwsws_min, 60 * LWS_US_PER_SEC);
129 }
130
131 static int
context_creation(void)132 context_creation(void)
133 {
134 int cs_len = LWSWS_CONFIG_STRING_SIZE - 1;
135 struct lws_context_creation_info info;
136 char *cs, *config_strings;
137 void *foreign_loops[1];
138
139 cs = config_strings = malloc(LWSWS_CONFIG_STRING_SIZE);
140 if (!config_strings) {
141 lwsl_err("Unable to allocate config strings heap\n");
142 return -1;
143 }
144
145 memset(&info, 0, sizeof(info));
146
147 info.external_baggage_free_on_destroy = config_strings;
148 info.pt_serv_buf_size = 8192;
149 info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 |
150 LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
151 LWS_SERVER_OPTION_LIBUV;
152
153 info.plugin_dirs = plugin_dirs;
154 lwsl_notice("Using config dir: \"%s\"\n", config_dir);
155
156 /*
157 * first go through the config for creating the outer context
158 */
159 if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len))
160 goto init_failed;
161
162 foreign_loops[0] = &loop;
163 info.foreign_loops = foreign_loops;
164 info.pcontext = &context;
165
166 context = lws_create_context(&info);
167 if (context == NULL) {
168 lwsl_err("libwebsocket init failed\n");
169 goto init_failed;
170 }
171
172 /*
173 * then create the vhosts... protocols are entirely coming from
174 * plugins, so we leave it NULL
175 */
176
177 info.extensions = exts;
178
179 if (lwsws_get_config_vhosts(context, &info, config_dir, &cs, &cs_len))
180 return 1;
181
182 lws_sul_schedule(context, 0, &sul_lwsws, lwsws_min, 60 * LWS_US_PER_SEC);
183
184 return 0;
185
186 init_failed:
187 free(config_strings);
188
189 return 1;
190 }
191
192
193 /*
194 * root-level sighup handler
195 */
196
197 static void
reload_handler(int signum)198 reload_handler(int signum)
199 {
200 #ifndef _WIN32
201 int m;
202
203 switch (signum) {
204
205 case SIGHUP: /* reload */
206 fprintf(stderr, "root process receives reload\n");
207 if (!do_reload) {
208 fprintf(stderr, "passing HUP to child processes\n");
209 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
210 if (pids[m])
211 kill(pids[m], SIGHUP);
212 sleep(1);
213 }
214 do_reload = 1;
215 break;
216 case SIGINT:
217 case SIGTERM:
218 case SIGKILL:
219 fprintf(stderr, "master process waiting 2s...\n");
220 sleep(2); /* give children a chance to deal with the signal */
221 fprintf(stderr, "killing service processes\n");
222 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
223 if (pids[m])
224 kill(pids[m], SIGTERM);
225 exit(0);
226 }
227 #else
228 // kill() implementation needed for WIN32
229 #endif
230 }
231
main(int argc,char ** argv)232 int main(int argc, char **argv)
233 {
234 int n = 0, budget = 100, debug_level = 1024 + 7;
235 #ifndef _WIN32
236 int m;
237 int status;//, syslog_options = LOG_PID | LOG_PERROR;
238 #endif
239
240 strcpy(config_dir, "/etc/lwsws");
241 while (n >= 0) {
242 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
243 n = getopt_long(argc, argv, "hd:c:", options, NULL);
244 #else
245 n = getopt(argc, argv, "hd:c:");
246 #endif
247 if (n < 0)
248 continue;
249 switch (n) {
250 case 'd':
251 debug_level = atoi(optarg);
252 break;
253 case 'c':
254 lws_strncpy(config_dir, optarg, sizeof(config_dir));
255 break;
256 case 'h':
257 fprintf(stderr, "Usage: lwsws [-c <config dir>] "
258 "[-d <log bitfield>] [--help]\n");
259 exit(1);
260 }
261 }
262 #ifndef _WIN32
263 /*
264 * We leave our original process up permanently, because that
265 * suits systemd.
266 *
267 * Otherwise we get into problems when reload spawns new processes and
268 * the original one dies randomly.
269 */
270
271 signal(SIGHUP, reload_handler);
272 signal(SIGINT, reload_handler);
273
274 fprintf(stderr, "Root process is %u\n", (unsigned int)getpid());
275
276 while (1) {
277 if (do_reload) {
278 do_reload = 0;
279 n = fork();
280 if (n == 0) /* new */
281 break;
282 /* old */
283 if (n > 0)
284 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
285 if (!pids[m]) {
286 pids[m] = n;
287 break;
288 }
289 }
290 #ifndef _WIN32
291 sleep(2);
292
293 n = waitpid(-1, &status, WNOHANG);
294 if (n > 0)
295 for (m = 0; m < (int)LWS_ARRAY_SIZE(pids); m++)
296 if (pids[m] == n) {
297 pids[m] = 0;
298 break;
299 }
300 #else
301 // !!! implemenation needed
302 #endif
303 }
304 #endif
305 /* child process */
306
307 lws_set_log_level(debug_level, lwsl_emit_stderr_notimestamp);
308
309 lwsl_notice("lwsws libwebsockets web server - license CC0 + MIT\n");
310 lwsl_notice("(C) Copyright 2010-2020 Andy Green <andy@warmcat.com>\n");
311
312 #if (UV_VERSION_MAJOR > 0) // Travis...
313 uv_loop_init(&loop);
314 #else
315 fprintf(stderr, "Your libuv is too old!\n");
316 return 0;
317 #endif
318 uv_signal_init(&loop, &signal_outer[0]);
319 uv_signal_start(&signal_outer[0], signal_cb, SIGINT);
320 uv_signal_init(&loop, &signal_outer[1]);
321 uv_signal_start(&signal_outer[1], signal_cb, SIGHUP);
322
323 if (context_creation()) {
324 lwsl_err("Context creation failed\n");
325 return 1;
326 }
327
328 lws_service(context, 0);
329
330 lwsl_err("%s: closing\n", __func__);
331
332 for (n = 0; n < 2; n++) {
333 uv_signal_stop(&signal_outer[n]);
334 uv_close((uv_handle_t *)&signal_outer[n], NULL);
335 }
336
337 /* cancel the per-minute sul */
338 lws_sul_schedule(context, 0, &sul_lwsws, NULL, LWS_SET_TIMER_USEC_CANCEL);
339
340 lws_context_destroy(context);
341 (void)budget;
342 #if (UV_VERSION_MAJOR > 0) // Travis...
343 while ((n = uv_loop_close(&loop)) && --budget)
344 uv_run(&loop, UV_RUN_ONCE);
345 #endif
346
347 fprintf(stderr, "lwsws exited cleanly: %d\n", n);
348
349 #ifndef _WIN32
350 closelog();
351 #endif
352
353 context = NULL;
354
355 return 0;
356 }
357