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