• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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