• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Notes about lwsws
2=================
3
4@section lwsws Libwebsockets Web Server
5
6lwsws is an implementation of a very lightweight, ws-capable generic web
7server, which uses libwebsockets to implement everything underneath.
8
9If you are basically implementing a standalone server with lws, you can avoid
10reinventing the wheel and use a debugged server including lws.
11
12
13@section lwswsb Build
14
15Just enable -DLWS_WITH_LWSWS=1 at cmake-time.
16
17It enables libuv and plugin support automatically.
18
19NOTICE on Ubuntu, the default libuv package is called "libuv-0.10".  This is ancient.
20
21You should replace this with libuv1 and libuv1-dev before proceeding.
22
23@section lwswsc Lwsws Configuration
24
25lwsws uses JSON config files, they're pure JSON except:
26
27 - '#' may be used to turn the rest of the line into a comment.
28
29 - There's also a single substitution, if a string contains "_lws_ddir_", then that is
30replaced with the LWS install data directory path, eg, "/usr/share" or whatever was
31set when LWS was built + installed.  That lets you refer to installed paths without
32having to change the config if your install path was different.
33
34There is a single file intended for global settings
35
36/etc/lwsws/conf
37```
38	# these are the server global settings
39	# stuff related to vhosts should go in one
40	# file per vhost in ../conf.d/
41
42	{
43	  "global": {
44	   "username": "apache",
45	   "groupname": "apache",
46	   "count-threads": "1",
47	   "server-string": "myserver v1", # returned in http headers
48	   "ws-pingpong-secs": "200", # confirm idle established ws connections this often
49	   "init-ssl": "yes"
50	 }
51	}
52```
53and a config directory intended to take one file per vhost
54
55/etc/lwsws/conf.d/warmcat.com
56```
57	{
58		"vhosts": [{
59			"name": "warmcat.com",
60			"port": "443",
61			"interface": "eth0",  # optional
62			"host-ssl-key": "/etc/pki/tls/private/warmcat.com.key",  # if given enable ssl
63			"host-ssl-cert": "/etc/pki/tls/certs/warmcat.com.crt",
64			"host-ssl-ca": "/etc/pki/tls/certs/warmcat.com.cer",
65			"mounts": [{  # autoserve
66				"mountpoint": "/",
67				"origin": "file:///var/www/warmcat.com",
68				"default": "index.html"
69			}]
70		}]
71	}
72```
73To get started quickly, an example config reproducing the old test server
74on port 7681, non-SSL is provided.  To set it up
75```
76	# mkdir -p /etc/lwsws/conf.d /var/log/lwsws
77	# cp ./lwsws/etc-lwsws-conf-EXAMPLE /etc/lwsws/conf
78	# cp ./lwsws/etc-lwsws-conf.d-localhost-EXAMPLE /etc/lwsws/conf.d/test-server
79	# sudo lwsws
80```
81
82@section lwswsacme Using Letsencrypt or other ACME providers
83
84Lws supports automatic provisioning and renewal of TLS certificates.
85
86See ./READMEs/README.plugin-acme.md for examples of how to set it up on an lwsws vhost.
87
88@section lwsogo Other Global Options
89
90 - `reject-service-keywords` allows you to return an HTTP error code and message of your choice
91if a keyword is found in the user agent
92
93```
94   "reject-service-keywords": [{
95        "scumbot": "404 Not Found"
96   }]
97```
98
99 - `timeout-secs` lets you set the global timeout for various network-related
100 operations in lws, in seconds.  It defaults to 5.
101
102@section lwswsv Lwsws Vhosts
103
104One server can run many vhosts, where SSL is in use SNI is used to match
105the connection to a vhost and its vhost-specific SSL keys during SSL
106negotiation.
107
108Listing multiple vhosts looks something like this
109```
110	{
111	 "vhosts": [ {
112	     "name": "localhost",
113	     "port": "443",
114	     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
115	     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
116	     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
117	     "mounts": [{
118	       "mountpoint": "/",
119	       "origin": "file:///var/www/libwebsockets.org",
120	       "default": "index.html"
121	       }, {
122	        "mountpoint": "/testserver",
123	        "origin": "file:///usr/local/share/libwebsockets-test-server",
124	        "default": "test.html"
125	       }],
126	     # which protocols are enabled for this vhost, and optional
127	     # vhost-specific config options for the protocol
128	     #
129	     "ws-protocols": [{
130	       "warmcat,timezoom": {
131	         "status": "ok"
132	       }
133	     }]
134	    },
135	    {
136	    "name": "localhost",
137	    "port": "7681",
138	     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
139	     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
140	     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
141	     "mounts": [{
142	       "mountpoint": "/",
143	       "origin": ">https://localhost"
144	     }]
145	   },
146	    {
147	    "name": "localhost",
148	    "port": "80",
149	     "mounts": [{
150	       "mountpoint": "/",
151	       "origin": ">https://localhost"
152	     }]
153	   }
154
155	  ]
156	}
157```
158
159That sets up three vhosts all called "localhost" on ports 443 and 7681 with SSL, and port 80 without SSL but with a forced redirect to https://localhost
160
161
162@section lwswsvn Lwsws Vhost name and port sharing
163
164The vhost name field is used to match on incoming SNI or Host: header, so it
165must always be the host name used to reach the vhost externally.
166
167 - Vhosts may have the same name and different ports, these will each create a
168listening socket on the appropriate port.
169
170 - Vhosts may also have the same port and different name: these will be treated as
171true vhosts on one listening socket and the active vhost decided at SSL
172negotiation time (via SNI) or if no SSL, then after the Host: header from
173the client has been parsed.
174
175
176@section lwswspr Lwsws Protocols
177
178Vhosts by default have available the union of any initial protocols from context creation time, and
179any protocols exposed by plugins.
180
181Vhosts can select which plugins they want to offer and give them per-vhost settings using this syntax
182```
183	     "ws-protocols": [{
184	       "warmcat-timezoom": {
185	         "status": "ok"
186	       }
187	     }]
188```
189
190The "x":"y" parameters like "status":"ok" are made available to the protocol during its per-vhost
191LWS_CALLBACK_PROTOCOL_INIT (in is a pointer to a linked list of struct lws_protocol_vhost_options
192containing the name and value pointers).
193
194To indicate that a protocol should be used when no Protocol: header is sent
195by the client, you can use "default": "1"
196```
197	     "ws-protocols": [{
198	       "warmcat-timezoom": {
199	         "status": "ok",
200	         "default": "1"
201	       }
202	     }]
203```
204
205Similarly, if your vhost is serving a raw protocol, you can mark the protocol
206to be selected using "raw": "1"
207```
208	     "ws-protocols": [{
209	       "warmcat-timezoom": {
210	         "status": "ok",
211	         "raw": "1"
212	       }
213	     }]
214```
215
216See also "apply-listen-accept" below.
217
218@section lwswsovo Lwsws Other vhost options
219
220 - If the three options `host-ssl-cert`, `host-ssl-ca` and `host-ssl-key` are given, then the vhost supports SSL.
221
222 Each vhost may have its own certs, SNI is used during the initial connection negotiation to figure out which certs to use by the server name it's asking for from the request DNS name.
223
224 - `keeplive-timeout` (in secs) defaults to 60 for lwsws, it may be set as a vhost option
225
226 - `interface` lets you specify which network interface to listen on, if not given listens on all.  If the network interface is not usable (eg, ethernet cable out) it will be logged at startup with such vhost not listening, and lws will poll for it and bind a listen socket to the interface if and when it becomes available.
227
228 - "`unix-socket`": "1" causes the unix socket specified in the interface option to be used instead of an INET socket
229
230 - "`unix-socket-perms`": "user:group" allows you to control the unix permissons on the listening unix socket.  It's always get to `0600` mode, but you can control the user and group for the socket fd at creation time.  This allows you to use unix user and groups to control who may open the other end of the unix socket on the local system.
231
232 - "`sts`": "1" causes lwsws to send a Strict Transport Security header with responses that informs the client he should never accept to connect to this address using http.  This is needed to get the A+ security rating from SSL Labs for your server.
233
234 - "`access-log`": "filepath"   sets where apache-compatible access logs will be written
235
236 - `"enable-client-ssl"`: `"1"` enables the vhost's client SSL context, you will need this if you plan to create client conections on the vhost that will use SSL.  You don't need it if you only want http / ws client connections.
237
238 - "`ciphers`": "<cipher list>"  OPENSSL only: sets the allowed list of TLS <= 1.2 ciphers and key exchange protocols for the serving SSL_CTX on the vhost.  The default list is restricted to only those providing PFS (Perfect Forward Secrecy) on the author's Fedora system.
239
240 If you need to allow weaker ciphers, you can provide an alternative list here per-vhost.
241
242 - "`client-ssl-ciphers`": "<cipher list>"  OPENSSL only: sets the allowed list of <= TLS1.2 ciphers and key exchange protocols for the client SSL_CTX on the vhost
243
244 - "`tls13-ciphers`": "<cipher list>"  OPENSSL 1.1.1+ only: sets allowed list of TLS1.3+ ciphers and key exchange protocols for the client SSL_CTX on the vhost.  The default is to allow all.
245
246 - "`client-tls13-ciphers`": "<cipher list>"  OPENSSL 1.1.1+ only: sets the allowed list of TLS1.3+ ciphers and key exchange protocols for the client SSL_CTX on the vhost.  The default is to allow all.
247
248 - "`ecdh-curve`": "<curve name>"   The default ecdh curve is "prime256v1", but you can override it here, per-vhost
249
250 - "`noipv6`": "on"  Disable ipv6 completely for this vhost
251
252 - "`ipv6only`": "on"  Only allow ipv6 on this vhost / "off" only allow ipv4 on this vhost
253
254 - "`ssl-option-set`": "<decimal>"  Sets the SSL option flag value for the vhost.
255 It may be used multiple times and OR's the flags together.
256
257 The values are derived from /usr/include/openssl/ssl.h
258```
259	 # define SSL_OP_NO_TLSv1_1                               0x10000000L
260```
261
262 would equate to
263
264```
265	 "`ssl-option-set`": "268435456"
266 ```
267 - "`ssl-option-clear'": "<decimal>"   Clears the SSL option flag value for the vhost.
268 It may be used multiple times and OR's the flags together.
269
270 - "`ssl-client-option-set`" and "`ssl-client-option-clear`" work the same way for the vhost Client SSL context
271
272 - "`headers':: [{ "header1": "h1value", "header2": "h2value" }]
273
274allows you to set arbitrary headers on every file served by the vhost
275
276recommended vhost headers for good client security are
277
278```
279                   "headers": [{
280                        "Content-Security-Policy": "script-src 'self'",
281                        "X-Content-Type-Options": "nosniff",
282                        "X-XSS-Protection": "1; mode=block",
283                        "X-Frame-Options": "SAMEORIGIN"
284                 }]
285
286```
287
288 - "`apply-listen-accept`": "on"  This vhost only serves a non-http protocol, specified in "listen-accept-role" and "listen-accept-protocol"
289
290@section lwswsm Lwsws Mounts
291
292Where mounts are given in the vhost definition, then directory contents may
293be auto-served if it matches the mountpoint.
294
295Mount protocols are used to control what kind of translation happens
296
297 - file://  serve the uri using the remainder of the url past the mountpoint based on the origin directory.
298
299 Eg, with this mountpoint
300```
301	       {
302	        "mountpoint": "/",
303	        "origin": "file:///var/www/mysite.com",
304	        "default": "/"
305	       }
306```
307 The uri /file.jpg would serve /var/www/mysite.com/file.jpg, since / matched.
308
309 - ^http:// or ^https://  these cause any url matching the mountpoint to issue a redirect to the origin url
310
311 - cgi://   this causes any matching url to be given to the named cgi, eg
312```
313	       {
314	        "mountpoint": "/git",
315	        "origin": "cgi:///var/www/cgi-bin/cgit",
316	        "default": "/"
317	       }, {
318	        "mountpoint": "/cgit-data",
319	        "origin": "file:///usr/share/cgit",
320	        "default": "/"
321	       },
322```
323 would cause the url /git/myrepo to pass "myrepo" to the cgi /var/www/cgi-bin/cgit and send the results to the client.
324
325 - http:// or https://  these perform reverse proxying, serving the remote origin content from the mountpoint.  Eg
326
327```
328		{
329		 "mountpoint": "/proxytest",
330		 "origin": "https://libwebsockets.org"
331		}
332```
333
334This will cause your local url `/proxytest` to serve content fetched from libwebsockets.org over ssl; whether it's served from your server using ssl is unrelated and depends how you configured your local server.  Notice if you will use the proxying feature, `LWS_WITH_HTTP_PROXY` is required to be enabled at cmake, and for `https` proxy origins, your lwsws configuration must include `"init-ssl": "1"` and the vhost with the proxy mount must have `"enable-client-ssl": "1"`, even if you are not using ssl to serve.
335
336`/proxytest/abc`, or `/proxytest/abc?def=ghi` etc map to the origin + the part past `/proxytest`, so links and img src urls etc work as do all urls under the origin path.
337
338In addition link and src urls in the document are rewritten so / or the origin url part are rewritten to the mountpoint part.
339
340
341@section lwswsomo Lwsws Other mount options
342
3431) Some protocols may want "per-mount options" in name:value format.  You can
344provide them using "pmo"
345
346	       {
347	        "mountpoint": "/stuff",
348	        "origin": "callback://myprotocol",
349	        "pmo": [{
350	                "myname": "myvalue"
351	        }]
352	       }
353
3542) When using a cgi:// protocol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
355```
356	       {
357	        "mountpoint": "/git",
358	        "origin": "cgi:///var/www/cgi-bin/cgit",
359	        "default": "/",
360	        "cgi-env": [{
361	                "CGIT_CONFIG": "/etc/cgitrc/libwebsockets.org"
362	        }]
363	       }
364```
365 This allows you to customize one cgi depending on the mountpoint (and / or vhost).
366
3673) It's also possible to set the cgi timeout (in secs) per cgi:// mount, like this
368```
369	"cgi-timeout": "30"
370```
3714) `callback://` protocol may be used when defining a mount to associate a
372named protocol callback with the URL namespace area.  For example
373```
374	       {
375	        "mountpoint": "/formtest",
376	        "origin": "callback://protocol-post-demo"
377	       }
378```
379All handling of client access to /formtest[anything] will be passed to the
380callback registered to the protocol "protocol-post-demo".
381
382This is useful for handling POST http body content or general non-cgi http
383payload generation inside a plugin.
384
385See the related notes in README.coding.md
386
3875) Cache policy of the files in the mount can also be set.  If no
388options are given, the content is marked uncacheable.
389```
390	       {
391	        "mountpoint": "/",
392	        "origin": "file:///var/www/mysite.com",
393	        "cache-max-age": "60",      # seconds
394	        "cache-reuse": "1",         # allow reuse at client at all
395	        "cache-revalidate": "1",    # check it with server each time
396	        "cache-intermediaries": "1" # allow intermediary caches to hold
397	       }
398```
399
4006) You can also define a list of additional mimetypes per-mount
401```
402	        "extra-mimetypes": {
403	                 ".zip": "application/zip",
404	                 ".doc": "text/evil"
405	         }
406```
407
408Normally a file suffix MUST match one of the canned mimetypes or one of the extra
409mimetypes, or the file is not served.  This adds a little bit of security because
410even if there is a bug somewhere and the mount dirs are circumvented, lws will not
411serve, eg, /etc/passwd.
412
413If you provide an extra mimetype entry
414
415			"*": ""
416
417Then any file is served, if the mimetype was not known then it is served without a
418Content-Type: header.
419
4207) A mount can be protected by HTTP Basic Auth.  This only makes sense when using
421https, since otherwise the password can be sniffed.
422
423You can add a `basic-auth` entry on an http mount like this
424
425```
426{
427        "mountpoint": "/basic-auth",
428        "origin": "file://_lws_ddir_/libwebsockets-test-server/private",
429        "basic-auth": "/var/www/balogins-private"
430}
431```
432
433Before serving anything, lws will signal to the browser that a username / password
434combination is required, and it will pop up a dialog.  When the user has filled it
435in, lwsws checks the user:password string against the text file named in the `basic-auth`
436entry.
437
438The file should contain user:pass one per line
439
440```
441testuser:testpass
442myuser:hispass
443```
444
445The file should be readable by lwsws, and for a little bit of extra security not
446have a file suffix, so lws would reject to serve it even if it could find it on
447a mount.
448
449After successful authentication, `WSI_TOKEN_HTTP_AUTHORIZATION` contains the
450authenticated username.
451
452In the case you want to also protect being able to connect to a ws protocol on
453a particular vhost by requiring the http part can authenticate using Basic
454Auth before the ws upgrade, this is also possible.  In this case, the
455"basic-auth": and filepath to the credentials file is passed as a pvo in the
456"ws-protocols" section of the vhost definition.
457
458@section lwswscc Requiring a Client Cert on a vhost
459
460You can make a vhost insist to get a client certificate from the peer before
461allowing the connection with
462
463```
464	"client-cert-required": "1"
465```
466
467the connection will only proceed if the client certificate was signed by the
468same CA as the server has been told to trust.
469
470@section rawconf Configuring Fallback and Raw vhosts
471
472Lws supports some unusual modes for vhost listen sockets, which may be
473configured entirely using the JSON per-vhost config language in the related
474vhost configuration section.
475
476There are three main uses for them
477
4781) A vhost bound to a specific role and protocol, not http.  This binds all
479incoming connections on the vhost listen socket to the "raw-proxy" role and
480protocol "myprotocol".
481
482```
483	"listen-accept-role":		"raw-proxy",
484	"listen-accept-protocol":	"myprotocol",
485	"apply-listen-accept":		"1"
486```
487
4882) A vhost that wants to treat noncompliant connections for http or https as
489   belonging to a secondary fallback role and protocol.  This causes non-https
490   connections to an https listener to stop being treated as https, to lose the
491   tls wrapper, and bind to role "raw-proxy" and protocol "myprotocol".  For
492   example, connect a browser on your external IP :443 as usual and it serves
493   as normal, but if you have configured the raw-proxy to portforward
494   127.0.0.1:22, then connecting your ssh client to your external port 443 will
495   instead proxy your sshd over :443 with no http or tls getting in the way.
496
497```
498	"listen-accept-role":		"raw-proxy",
499	"listen-accept-protocol":	"myprotocol",
500	"fallback-listen-accept":	"1",
501	"allow-non-tls":		"1"
502```
503
5043) A vhost wants to either redirect stray http traffic back to https, or to
505   actually serve http on an https listen socket (this is not recommended
506   since it allows anyone to drop the security assurances of https by
507   accident or design).
508
509```
510	"allow-non-tls":		"1",
511	"redirect-http":		"1",
512```
513
514...or,
515
516```
517	"allow-non-tls":		"1",
518	"allow-http-on-https":		"1",
519```
520
521@section lwswspl Lwsws Plugins
522
523Protcols and extensions may also be provided from "plugins", these are
524lightweight dynamic libraries.  They are scanned for at init time, and
525any protocols and extensions found are added to the list given at context
526creation time.
527
528Protocols receive init (LWS_CALLBACK_PROTOCOL_INIT) and destruction
529(LWS_CALLBACK_PROTOCOL_DESTROY) callbacks per-vhost, and there are arrangements
530they can make per-vhost allocations and get hold of the correct pointer from
531the wsi at the callback.
532
533This allows a protocol to choose to strictly segregate data on a per-vhost
534basis, and also allows the plugin to handle its own initialization and
535context storage.
536
537To help that happen conveniently, there are some new apis
538
539 - lws_vhost_get(wsi)
540 - lws_protocol_get(wsi)
541 - lws_callback_on_writable_all_protocol_vhost(vhost, protocol)
542 - lws_protocol_vh_priv_zalloc(vhost, protocol, size)
543 - lws_protocol_vh_priv_get(vhost, protocol)
544
545dumb increment, mirror and status protocol plugins are provided as examples.
546
547
548@section lwswsplaplp Additional plugin search paths
549
550Packages that have their own lws plugins can install them in their own
551preferred dir and ask lwsws to scan there by using a config fragment
552like this, in its own conf.d/ file managed by the other package
553```
554	{
555	  "global": {
556	   "plugin-dir": "/usr/local/share/coherent-timeline/plugins"
557	  }
558	}
559```
560
561@section lwswsssp lws-server-status plugin
562
563One provided protocol can be used to monitor the server status.
564
565Enable the protocol like this on a vhost's ws-protocols section
566```
567	       "lws-server-status": {
568	         "status": "ok",
569	         "update-ms": "5000"
570	       }
571```
572`"update-ms"` is used to control how often updated JSON is sent on a ws link.
573
574And map the provided HTML into the vhost in the mounts section
575```
576	       {
577	        "mountpoint": "/server-status",
578	        "origin": "file:///usr/local/share/libwebsockets-test-server/server-status",
579	        "default": "server-status.html"
580	       }
581```
582You might choose to put it on its own vhost which has "interface": "lo", so it's not
583externally visible, or use the Basic Auth support to require authentication to
584access it.
585
586`"hide-vhosts": "{0 | 1}"` lets you control if information about your vhosts is included.
587Since this includes mounts, you might not want to leak that information, mount names,
588etc.
589
590`"filespath":"{path}"` lets you give a server filepath which is read and sent to the browser
591on each refresh.  For example, you can provide server temperature information on most
592Linux systems by giving an appropriate path down /sys.
593
594This may be given multiple times.
595
596
597@section lwswsreload Lwsws Configuration Reload
598
599You may send lwsws a `HUP` signal, by, eg
600
601```
602$ sudo killall -HUP lwsws
603```
604
605This causes lwsws to "deprecate" the existing lwsws process, and remove and close all of
606its listen sockets, but otherwise allowing it to continue to run, until all
607of its open connections close.
608
609When a deprecated lwsws process has no open connections left, it is destroyed
610automatically.
611
612After sending the SIGHUP to the main lwsws process, a new lwsws process, which can
613pick up the newly-available listen sockets, and use the current configuration
614files, is automatically started.
615
616The new configuration may differ from the original one in arbitrary ways, the new
617context is created from scratch each time without reference to the original one.
618
619Notes
620
6211) Protocols that provide a "shared world" like mirror will have as many "worlds"
622as there are lwsws processes still active.  People connected to a deprecated lwsws
623process remain connected to the existing peers.
624
625But any new connections will apply to the new lwsws process, which does not share
626per-vhost "shared world" data with the deprecated process.  That means no new
627connections on the deprecated context, ie a "shrinking world" for those guys, and a
628"growing world" for people who connect after the SIGHUP.
629
6302) The new lwsws process owes nothing to the previous one.  It starts with fresh
631plugins, fresh configuration, fresh root privileges if that how you start it.
632
633The plugins may have been updated in arbitrary ways including struct size changes
634etc, and lwsws or lws may also have been updated arbitrarily.
635
6363) A root parent process is left up that is not able to do anything except
637respond to SIGHUP or SIGTERM.  Actual serving and network listening etc happens
638in child processes which use the privileges set in the lwsws config files.
639
640@section lwswssysd Lwsws Integration with Systemd
641
642lwsws needs a service file like this as `/usr/lib/systemd/system/lwsws.service`
643```
644[Unit]
645Description=Libwebsockets Web Server
646After=syslog.target
647
648[Service]
649ExecStart=/usr/local/bin/lwsws
650ExecReload=/usr/bin/killall -s SIGHUP lwsws ; sleep 1 ; /usr/local/bin/lwsws
651StandardError=null
652
653[Install]
654WantedBy=multi-user.target
655```
656
657You can find this prepared in `./lwsws/usr-lib-systemd-system-lwsws.service`
658
659
660@section lwswslr Lwsws Integration with logrotate
661
662For correct operation with logrotate, `/etc/logrotate.d/lwsws` (if that's
663where we're putting the logs) should contain
664```
665	/var/log/lwsws/*log {
666	    copytruncate
667	    missingok
668	    notifempty
669	    delaycompress
670	}
671```
672You can find this prepared in `/lwsws/etc-logrotate.d-lwsws`
673
674Prepare the log directory like this
675
676```
677	sudo mkdir /var/log/lwsws
678	sudo chmod 700 /var/log/lwsws
679```
680
681@section lwswsgdb Debugging lwsws with gdb
682
683Hopefully you won't need to debug lwsws itself, but you may want to debug your plugins.  start lwsws like this to have everything running under gdb
684
685```
686sudo gdb -ex "set follow-fork-mode child" -ex "run" --args /usr/local/bin/lwsws
687
688```
689
690this will give nice backtraces in lwsws itself and in plugins, if they were built with symbols.
691
692@section lwswsvgd Running lwsws under valgrind
693
694You can just run lwsws under valgrind as usual and get valid results.  However the results / analysis part of valgrind runs
695after the plugins have removed themselves, this means valgrind backtraces into plugin code is opaque, without
696source-level info because the dynamic library is gone.
697
698There's a simple workaround, use LD_PRELOAD=<plugin.so> before running lwsws, this has the loader bring the plugin
699in before executing lwsws as if it was a direct dependency.  That means it's still mapped until the whole process
700exits after valgtind has done its thing.
701
702
703