[RFC] nginx-util: use UCI for server configurationlilik-openwrt-22.03
@ -0,0 +1,475 @@ | |||
#!/bin/sh | |||
# This is a template copy it by: ./README.sh | xclip -selection c | |||
# to https://openwrt.org/docs/guide-user/services/webserver/nginx#configuration | |||
NGINX_UTIL="/usr/bin/nginx-util" | |||
EXAMPLE_COM="example.com" | |||
MSG=" | |||
/* Created by the following bash script that includes the source of some files: | |||
* https://github.com/openwrt/packages/net/nginx-util/files/README.sh | |||
*/" | |||
eval $("${NGINX_UTIL}" get_env) | |||
code() { | |||
local file | |||
[ $# -gt 1 ] && file="$2" || file="$(basename "$1")" | |||
printf "<file nginx %s>\n%s</file>" "$1" "$(cat "${file}")"; | |||
} | |||
ifConfEcho() { | |||
sed -nE "s/^\s*$1=\s*(\S*)\s*\\\\$/\n$2 \"\1\";/p" ../../nginx/Makefile; | |||
} | |||
cat <<EOF | |||
===== Configuration =====${MSG} | |||
The official Documentation contains a | |||
[[https://docs.nginx.com/nginx/admin-guide/|Admin Guide]]. | |||
Here we will look at some often used configuration parts and how we handle them | |||
at OpenWrt. | |||
At different places there are references to the official | |||
[[https://docs.nginx.com/nginx/technical-specs/|Technical Specs]] | |||
for further reading. | |||
**tl;dr:** When starting Nginx by ''/etc/init.d/nginx'', it creates its main | |||
configuration dynamically based on a minimal template and the | |||
[[docs:guide-user:base-system:uci|🡒UCI]] configuration. | |||
The UCI ''/etc/config/nginx'' contains initially: | |||
| ''config server '${LAN_NAME}''' | \ | |||
Default server for the LAN, which includes all ''${CONF_DIR}*.locations''. | | |||
| ''config server '_redirect2ssl''' | \ | |||
Redirects inexistent URLs to HTTPS. | | |||
It enables also the ''${CONF_DIR}'' directory for further configuration: | |||
| ''${CONF_DIR}\$NAME.conf'' | \ | |||
Is included in the main configuration. \ | |||
It is prioritized over a UCI ''config server '\$NAME' ''. | | |||
| ''${CONF_DIR}\$NAME.locations'' | \ | |||
Is include in the ''${LAN_NAME}'' server and can be re-used for others, too. | | |||
| ''$(dirname "${CONF_DIR}")/restrict_locally'' | \ | |||
Is include in the ''${LAN_NAME}'' server and allows only accesses from LAN. | | |||
Setup configuration (for a server ''\$NAME''): | |||
| ''$(basename ${NGINX_UTIL}) [${ADD_SSL_FCT}|del_ssl] \$NAME'' | \ | |||
Add/remove a self-signed certificate and corresponding directives. | | |||
| ''uci set nginx.\$NAME.access_log='logd openwrt''' | \ | |||
Writes accesses to Openwrt’s \ | |||
[[docs:guide-user:base-system:log.essentials|🡒logd]]. | | |||
| ''uci set nginx.\$NAME.error_log='logd' '' | \ | |||
Writes errors to Openwrt’s \ | |||
[[docs:guide-user:base-system:log.essentials|🡒logd]]. | | |||
| ''uci [set|add_list] nginx.\$NAME.key='value' '' | \ | |||
Becomes a ''key value;'' directive if the //key// does not start with //uci_//. | | |||
| ''uci set nginx.\$NAME=[disable|server]'' |\ | |||
Disable/enable inclusion in the dynamic conf.| | |||
| ''uci set nginx.global.uci_enable=false'' | \ | |||
Use a custom ''${NGINX_CONF}'' rather than a dynamic conf. | | |||
==== Basic ====${MSG} | |||
We modify the configuration by changing servers saved in the UCI configuration | |||
at ''/etc/config/nginx'' and/or by creating different configuration files in the | |||
''${CONF_DIR}'' directory. | |||
These files use the file extensions ''.locations'' and ''.conf'' plus ''.crt'' | |||
and ''.key'' for SSL certificates and keys.(( | |||
We can disable a single configuration file in ''${CONF_DIR}'' by giving it | |||
another extension, e.g., by adding ''.disabled''.)) | |||
For the new configuration to take effect, we must reload it by: | |||
<code bash>service nginx reload</code> | |||
For OpenWrt we use a special initial configuration, which is explained in the | |||
section [[#openwrt_s_defaults|🡓OpenWrt’s Defaults]]. | |||
So, we can make a site available at a specific URL in the **LAN** by creating a | |||
''.locations'' file in the directory ''${CONF_DIR}''. | |||
Such a file consists just of some | |||
[[https://nginx.org/en/docs/http/ngx_http_core_module.html#location| | |||
location blocks]]. | |||
Under the latter link, you can find also the official documentation for all | |||
available directives of the HTTP core of Nginx. | |||
Look for //location// in the Context list. | |||
The following example provides a simple template, see at the end for | |||
different [[#locations_for_apps|🡓Locations for Apps]]((look for | |||
[[https://github.com/search?utf8=%E2%9C%93&q=repo%3Aopenwrt%2Fpackages | |||
+extension%3Alocations&type=Code&ref=advsearch&l=&l=| | |||
other packages using a .locations file]], too.)): | |||
<code nginx ${CONF_DIR}example.locations> | |||
location /ex/am/ple { | |||
access_log off; # default: not logging accesses. | |||
# access_log /proc/self/fd/1 openwrt; # use logd (init forwards stdout). | |||
# error_log stderr; # default: logging to logd (init forwards stderr). | |||
error_log /dev/null; # disable error logging after config file is read. | |||
# (state path of a file for access_log/error_log to the file instead.) | |||
index index.html; | |||
} | |||
# location /eg/static { … } | |||
</code> | |||
All location blocks in all ''.locations'' files must use different URLs, | |||
since they are all included in the ''${LAN_NAME}'' server that is part of the | |||
[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]].(( | |||
We reserve the ''location /'' for making LuCI available under the root URL, | |||
e.g. [[https://192.168.1.1/|192.168.1.1/]]. | |||
All other sites shouldn’t use the root ''location /'' without suffix.)) | |||
We should use the root URL for other sites than LuCI only on **other** domain | |||
names, e.g., we could make a site available at https://${EXAMPLE_COM}/. | |||
In order to do that, we create [[#new_server_parts|🡓New Server Parts]] for all | |||
domain names. | |||
We can also activate SSL thereby, see | |||
[[#ssl_server_parts|🡓SSL Server Parts]]. | |||
We use such server parts also for publishing sites to the internet (WAN) | |||
instead of making them available just locally (in the LAN). | |||
Via ''${CONF_DIR}*.conf'' files we can add directives to the //http// part of | |||
the configuration. | |||
If you would change the configuration ''$(basename "${UCI_CONF}").template'' | |||
instead, it is not updated to new package's versions anymore. | |||
Although it is not recommended, you can also disable the whole UCI config and | |||
create your own ''${NGINX_CONF}''; then invoke: | |||
<code bash>uci set nginx.global.uci_enable=false</code> | |||
==== New Server Parts ====${MSG} | |||
For making the router reachable from the WAN at a registered domain name, | |||
it is not enough letting the | |||
[[docs:guide-user:firewall:firewall_configuration|🡒firewall]] accept requests | |||
(typically on ports 80 and 443) and giving the name server the internet IP | |||
address of the router (maybe updated automatically by a | |||
[[docs:guide-user:services:ddns:client|🡒DDNS Client]]). | |||
We also need to set up virtual hosting for this domain name by creating an | |||
appropriate server section in ''/etc/config/nginx'' | |||
(or in a ''${CONF_DIR}*.conf'' file, which cannot be changed using UCI). | |||
All such parts are included in the main configuration of OpenWrt | |||
([[#openwrt_s_defaults|🡓OpenWrt’s Defaults]]). | |||
In the server part, we state the domain as | |||
[[https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name| | |||
server_name]]. | |||
The link points to the same document as for the location blocks in the | |||
[[#basic|🡑Basic Configuration]]: the official documentation for all available | |||
directives of the HTTP core of Nginx. | |||
This time look for //server// in the Context list, too. | |||
The server part should also contain similar location blocks as | |||
++before.| | |||
We can re-include a ''.locations'' file that is included in the server part for | |||
the LAN by default. | |||
Then the site is reachable under the same path at both domains, e.g. by | |||
https://192.168.1.1/ex/am/ple as well as by https://${EXAMPLE_COM}/ex/am/ple. | |||
++ | |||
We can add directives to a server in the UCI configuration by invoking | |||
''uci [set|add_list] nginx.${EXAMPLE_COM//./_}.key=value''. | |||
If the //key// is not starting with //uci_//, it becomes a ''key value;'' | |||
++directive.| | |||
Although the UCI config does not support nesting like Nginx, we can add a whole | |||
block as //value//. | |||
++ | |||
We cannot use dots in a //key// name other than in the //value//. | |||
In the following example we replace the dot in //${EXAMPLE_COM}// by an | |||
underscore for the UCI name of the server, but not for Nginx's //server_name//: | |||
<code bash> | |||
uci add nginx server && | |||
uci rename nginx.@server[-1]=${EXAMPLE_COM//./_} && | |||
uci add_list nginx.${EXAMPLE_COM//./_}.listen='80' && | |||
uci add_list nginx.${EXAMPLE_COM//./_}.listen='[::]:80' && | |||
uci set nginx.${EXAMPLE_COM//./_}.server_name='${EXAMPLE_COM}' && | |||
uci add_list nginx.${EXAMPLE_COM//./_}.include=\ | |||
'$(basename ${CONF_DIR})/${EXAMPLE_COM}.locations' | |||
# uci add_list nginx.${EXAMPLE_COM//./_}.location='/ { … }' \ | |||
# root location for this server. | |||
</code> | |||
We can disable respective re-enable this server again by: | |||
<code bash> | |||
uci set nginx.${EXAMPLE_COM//./_}=disable # respective: \ | |||
uci set nginx.${EXAMPLE_COM//./_}=server | |||
</code> | |||
These changes are made in the RAM (and can be used until a reboot), we can save | |||
them permanently by: | |||
<code bash>uci commit nginx</code> | |||
For creating a similar ''${CONF_DIR}${EXAMPLE_COM}.conf'', we can adopt the | |||
following: | |||
<code nginx ${CONF_DIR}${EXAMPLE_COM}.conf> | |||
server { | |||
listen 80; | |||
listen [::]:80; | |||
server_name ${EXAMPLE_COM}; | |||
include '$(basename ${CONF_DIR})/${EXAMPLE_COM}.locations'; | |||
# location / { … } # root location for this server. | |||
} | |||
</code> | |||
[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]] include the UCI server | |||
''config server '_redirect2ssl' ''. | |||
It acts as //default_server// for HTTP and redirects requests for inexistent | |||
URLs to HTTPS. | |||
For making another domain name accessible to all addresses, the corresponding | |||
server part should listen on port //80// and contain the FQDN as | |||
//server_name//, cf. the official documentation on | |||
[[https://nginx.org/en/docs/http/request_processing.html|request_processing]]. | |||
Furthermore, there is a UCI server named ''${LAN_NAME}''. | |||
It is the //default_server// for HTTPS and allows connections from LAN only. | |||
It includes the file ''$(dirname "${CONF_DIR}")/restrict_locally'' with | |||
appropriate //allow/deny// directives, cf. the official documentation on | |||
[[https://nginx.org/en/docs/http/ngx_http_access_module.html|limiting access]]. | |||
==== SSL Server Parts ====${MSG} | |||
For enabling HTTPS for a domain we need a SSL certificate as well as its key and | |||
add them by the directives //ssl_certificate// respective | |||
//ssl_certificate_key// to the server part of the domain | |||
([[https://nginx.org/en/docs/http/configuring_https_servers.html#sni|TLS SNI]] | |||
is supported by default). | |||
The rest of the configuration is similar as for general | |||
[[#new_server_parts|🡑New Server Parts]]. | |||
We only have to adjust the listen directives by adding the //ssl// parameter and | |||
changing the port from //80// to //443//. | |||
The official documentation of the SSL module contains an | |||
[[https://nginx.org/en/docs/http/ngx_http_ssl_module.html#example| | |||
example]] with some optimizations. | |||
We can extend an existing UCI server section similarly, e.g., for the above | |||
''config server '${EXAMPLE_COM//./_}' '' we invoke: | |||
<code bash> | |||
# Instead of 'del_list' the listen* entries, we could use '443 ssl' beforehand. | |||
uci del_list nginx.${EXAMPLE_COM//./_}.listen='80' && | |||
uci del_list nginx.${EXAMPLE_COM//./_}.listen='[::]:80' && | |||
uci add_list nginx.${EXAMPLE_COM//./_}.listen='443 ssl' && | |||
uci add_list nginx.${EXAMPLE_COM//./_}.listen='[::]:443 ssl' && | |||
uci set nginx.${EXAMPLE_COM//./_}.ssl_certificate=\ | |||
'${CONF_DIR}${EXAMPLE_COM}.crt' && | |||
uci set nginx.${EXAMPLE_COM//./_}.ssl_certificate_key=\ | |||
'${CONF_DIR}${EXAMPLE_COM}.key' && | |||
uci set nginx.${EXAMPLE_COM//./_}.ssl_session_cache=\ | |||
'${SSL_SESSION_CACHE_ARG}' && | |||
uci set nginx.${EXAMPLE_COM//./_}.ssl_session_timeout=\ | |||
'${SSL_SESSION_TIMEOUT_ARG}' && | |||
uci commit nginx | |||
</code> | |||
For making the server in ''${CONF_DIR}${EXAMPLE_COM}.conf'' available | |||
via SSL, we can make similar changes there. | |||
The following command creates a **self-signed** SSL certificate and changes the | |||
corresponding configuration: | |||
<code bash>$(basename "${NGINX_UTIL}") ${ADD_SSL_FCT} ${EXAMPLE_COM}</code> | |||
- If a ''$(basename "${CONF_DIR}")/${EXAMPLE_COM}.conf'' file exists, it\ | |||
adds //ssl_*// directives and changes the //listen// directives there.\ | |||
Else it does that similarly to the example above for a ++selected UCI\ | |||
server.| Hereby it searches the UCI config first for a server with the\ | |||
given name and then for a server whose //server_name// contains the name.\ | |||
For //${EXAMPLE_COM}// it is the latter as a UCI key cannot have dots.++ | |||
- It checks if there is a certificate with key for '${EXAMPLE_COM}' that is\ | |||
valid for at least 13 months or tries to create a self-signed one. | |||
- When cron is activated, it installs a cron job for renewing the self-signed\ | |||
certificate every year if needed, too. We can activate cron by: \ | |||
<code bash>service cron enable && service cron start</code> | |||
This can be undone by invoking: | |||
<code bash>$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM}</code> | |||
For using an SSL certificate and key that are managed otherwise, there is: | |||
<code bash>$(basename "${NGINX_UTIL}") add_ssl ${EXAMPLE_COM} "\$MANAGER" \ | |||
"/absolute/path/to/crt" "/absolute/path/to/key"</code> | |||
It only adds //ssl_*// directives and changes the //listen// directives in | |||
the appropriate configuration, but does not create or change the certificate | |||
or its key. This can be reverted by: | |||
<code bash>$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM} "\$MANAGER"</code> | |||
For example [[https://github.com/ndilieto/uacme|uacme]] or | |||
[[https://github.com/Neilpang/acme.sh|acme.sh]] can be used for creating an SSL | |||
certificate signed by Let’s Encrypt and changing the config | |||
++accordingly.| | |||
They call ''$(basename "${NGINX_UTIL}") add_ssl \$FQDN acme \$CRT \$KEY'' | |||
internally.++ | |||
We can install them by: | |||
<code bash> | |||
opkg update && opkg install uacme #or: acme #and for LuCI: luci-app-acme | |||
</code> | |||
[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]] include a UCI server for the LAN: | |||
''config server '${LAN_NAME}' ''. | |||
It has //ssl_*// directives prepared for a self-signed((Let’s Encrypt (and other | |||
CAs) cannot sign certificates of a **local** server.)) | |||
SSL certificate, which is created on the first start of Nginx. | |||
The server listens on all addresses, is the //default_server// for HTTPS and | |||
allows connections from LAN only (by including the file ''restrict_locally'' | |||
with //allow/deny// directives, cf. the official documentation on | |||
[[https://nginx.org/en/docs/http/ngx_http_access_module.html|limiting access]]). | |||
For making another domain name accessible to all addresses, the corresponding | |||
SSL server part should listen on port //443// and contain the FQDN as | |||
//server_name//, cf. the official documentation on | |||
[[https://nginx.org/en/docs/http/request_processing.html|request_processing]]. | |||
Furthermore, there is also a UCI server named ''_redirect2ssl'', which listens | |||
on all addresses, acts as //default_server// for HTTP and redirects requests for | |||
inexistent URLs to HTTPS. | |||
==== OpenWrt’s Defaults ====${MSG} | |||
Since Nginx is compiled with these presets, we can pretend that the main | |||
configuration will always contain the following directives | |||
(though we can overwrite them): | |||
<code nginx>$(ifConfEcho --pid-path pid)\ | |||
$(ifConfEcho --lock-path lock_file)\ | |||
$(ifConfEcho --error-log-path error_log)\ | |||
$(false && ifConfEcho --http-log-path access_log)\ | |||
$(ifConfEcho --http-proxy-temp-path proxy_temp_path)\ | |||
$(ifConfEcho --http-client-body-temp-path client_body_temp_path)\ | |||
$(ifConfEcho --http-fastcgi-temp-path fastcgi_temp_path)\ | |||
</code> | |||
When starting or reloading the Nginx service, the ''/etc/init.d/nginx'' script | |||
sets also the following directives | |||
(so we cannot change them in the used configuration file): | |||
<code nginx> | |||
daemon off; # procd expects services to run in the foreground | |||
</code> | |||
Then, the init sript creates the main configuration | |||
''$(basename "${UCI_CONF}")'' dynamically from the template: | |||
$(code "${UCI_CONF}.template") | |||
So, the access log is turned off by default and we can look at the error log | |||
by ''logread'', as init.d script forwards stderr and stdout to the | |||
[[docs:guide-user:base-system:log.essentials|🡒runtime log]]. | |||
We can set the //error_log// and //access_log// to files, where the log | |||
messages are forwarded to instead (after the configuration is read). | |||
And for redirecting the access log of a //server// or //location// to the logd, | |||
too, we insert the following directive in the corresponding block: | |||
<code nginx> access_log /proc/self/fd/1 openwrt;</code> | |||
If we setup a server through UCI, we can use the options //error_log// and/or | |||
//access_log// also with the special path | |||
++'logd'.| | |||
When initializing the Nginx service, this special path is replaced by //stderr// | |||
respective ///proc/self/fd/1// (which are forwarded to the runtime log). | |||
++ | |||
For creating the configuration from the template shown above, Nginx’s init | |||
script replaces the comment ''#UCI_HTTP_CONFIG'' by all UCI servers. | |||
For each server section in the the UCI configuration, it basically copies all | |||
options into a Nginx //server { … }// part, in detail: | |||
* Options starting with ''uci_'' are skipped. Currently there is only\ | |||
the ''option ${MANAGE_SSL}=…'' in ++usage.| It is set to\ | |||
//'self-signed'// when invoking\ | |||
''$(basename ${NGINX_UTIL}) ${ADD_SSL_FCT} \$NAME''.\ | |||
Then the corresponding certificate is re-newed if it is about to expire.\ | |||
All those certificates are checked on the initialization of the Nginx service\ | |||
and if Cron is available, it is deployed for checking them annually, too.++ | |||
* All other lists or options of the form ''key='value' '' are written\ | |||
one-to-one as ''key value;'' directives to the configuration file.\ | |||
Just the path //logd// has a special meaning for the logging directives\ | |||
(described in the previous paragraph). | |||
The init.d script of Nginx uses the //$(basename ${NGINX_UTIL})// for creating | |||
the configuration file | |||
++in RAM.| | |||
The main configuration ''${UCI_CONF}'' is a symbolic link to this place | |||
(it is a dead link if the Nginx service is not running). | |||
++ | |||
We could use a custom configuration created at ''${NGINX_CONF}'' instead of the | |||
dynamic configuration, too.(( | |||
For using a custom configuration at ''${NGINX_CONF}'', we execute | |||
<code bash>uci set nginx.global.uci_enable='false' </code> | |||
Then the rest of the UCI config is ignored and //init.d// will not create the | |||
main configuration dynamically from the template anymore. | |||
Invoking ''$(basename ${NGINX_UTIL}) [${ADD_SSL_FCT}|del_ssl] \$FQDN'' | |||
will still try to change a server in ''$(basename "${CONF_DIR}")/\$FQDN.conf'' | |||
(this is less reliable than for a UCI config as it uses regular expressions, not | |||
a complete parser for the Nginx configuration).)) | |||
This is not encouraged since you cannot setup servers using UCI anymore. | |||
Rather, we can put custom configuration parts to ''.conf'' files in the | |||
''${CONF_DIR}'' directory. | |||
The main configuration pulls in all ''$(basename "${CONF_DIR}")/*.conf'' files | |||
into the //http {…}// block behind the created UCI servers. | |||
The initial UCI config is enabled and contains two server section: | |||
$(code "/etc/config/nginx" "nginx.config") | |||
While the LAN server is the //default_server// for HTTPS, the server | |||
redirecting requests for an inexistent ''server_name'' from HTTP to HTTPS acts | |||
as //default_server// if there is ++no other|; | |||
it uses an invalid name for that, more in the official documentation on | |||
[[https://nginx.org/en/docs/http/request_processing.html|request_processing]] | |||
++. | |||
The LAN server pulls in all ''.locations'' files from the directory | |||
''${CONF_DIR}''. | |||
We can install the location parts of different sites there (see | |||
[[#basic|🡑Basic Configuration]]) and re-include them into other servers. | |||
This is needed especially for making them available to the WAN | |||
([[#new_server_parts|🡑New Server Parts]]). | |||
The LAN server listens for all addresses on port //443// and restricts the | |||
access to local addresses by including: | |||
$(code "$(dirname "${CONF_DIR}")/restrict_locally") | |||
When starting or reloading the Nginx service, the init.d looks which UCI servers | |||
have set ''option ${MANAGE_SSL} 'self-signed' '', e.g. the LAN server. | |||
For all those servers it checks if there is a certificate that is still valid | |||
for 13 months or (re-)creates a self-signed one. | |||
If there is any such server, it installs also a cron job that checks the | |||
corresponding certificates once a year. | |||
The option ''${MANAGE_SSL}'' is set to //'self-signed'// respectively removed | |||
from a UCI server named ''${EXAMPLE_COM//./_}'' by the following | |||
(see [[#ssl_server_parts|🡑SSL Server Parts]], too): | |||
<code bash> | |||
$(basename ${NGINX_UTIL}) ${ADD_SSL_FCT} ${EXAMPLE_COM//./_} \ | |||
# respectively: \ | |||
$(basename ${NGINX_UTIL}) del_ssl ${EXAMPLE_COM//./_} | |||
</code> | |||
EOF |
@ -0,0 +1,22 @@ | |||
config main global | |||
option uci_enable 'true' | |||
config server '_lan' | |||
list listen '443 ssl default_server' | |||
list listen '[::]:443 ssl default_server' | |||
option server_name '_lan' | |||
list include 'restrict_locally' | |||
list include 'conf.d/*.locations' | |||
option uci_manage_ssl 'self-signed' | |||
option ssl_certificate '/etc/nginx/conf.d/_lan.crt' | |||
option ssl_certificate_key '/etc/nginx/conf.d/_lan.key' | |||
option ssl_session_cache 'shared:SSL:32k' | |||
option ssl_session_timeout '64m' | |||
option access_log 'off; # logd openwrt' | |||
config server '_redirect2ssl' | |||
list listen '80' | |||
list listen '[::]:80' | |||
option server_name '_redirect2ssl' | |||
option return '302 https://$host$request_uri' |
@ -0,0 +1,10 @@ | |||
allow ::1; | |||
allow fc00::/7; | |||
allow fec0::/10; | |||
allow fe80::/10; | |||
allow 127.0.0.0/8; | |||
allow 10.0.0.0/8; | |||
allow 172.16.0.0/12; | |||
allow 192.168.0.0/16; | |||
allow 169.254.0.0/16; | |||
deny all; |
@ -0,0 +1,32 @@ | |||
# Consider using UCI or creating files in /etc/nginx/conf.d/ for configuration. | |||
# Parsing UCI configuration is skipped if uci set nginx.global.uci_enable=false | |||
# For details see: https://openwrt.org/docs/guide-user/services/webserver/nginx | |||
worker_processes auto; | |||
user root; | |||
events {} | |||
http { | |||
access_log off; | |||
log_format openwrt | |||
'$request_method $scheme://$host$request_uri => $status' | |||
' (${body_bytes_sent}B in ${request_time}s) <- $http_referer'; | |||
include mime.types; | |||
default_type application/octet-stream; | |||
sendfile on; | |||
client_max_body_size 128M; | |||
large_client_header_buffers 2 1k; | |||
gzip on; | |||
gzip_vary on; | |||
gzip_proxied any; | |||
root /www; | |||
#UCI_HTTP_CONFIG | |||
include conf.d/*.conf; | |||
} |
@ -0,0 +1,171 @@ | |||
--- | |||
Language: Cpp | |||
AccessModifierOffset: -2 | |||
AlignAfterOpenBracket: Align | |||
AlignConsecutiveMacros: false | |||
AlignConsecutiveAssignments: false | |||
AlignConsecutiveDeclarations: false | |||
AlignEscapedNewlines: Left | |||
AlignOperands: true | |||
AlignTrailingComments: true | |||
AllowAllArgumentsOnNextLine: true | |||
AllowAllConstructorInitializersOnNextLine: true | |||
AllowAllParametersOfDeclarationOnNextLine: false | |||
AllowShortBlocksOnASingleLine: Always | |||
AllowShortCaseLabelsOnASingleLine: true | |||
# AllowShortEnumsOnASingleLine: true | |||
AllowShortLambdasOnASingleLine: All | |||
AllowShortFunctionsOnASingleLine: Inline | |||
AllowShortIfStatementsOnASingleLine: Always | |||
AllowShortLoopsOnASingleLine: true | |||
AlwaysBreakAfterDefinitionReturnType: None | |||
AlwaysBreakAfterReturnType: None | |||
AlwaysBreakBeforeMultilineStrings: true | |||
AlwaysBreakTemplateDeclarations: Yes | |||
BinPackArguments: true | |||
BinPackParameters: false | |||
# BitFieldColonSpacing: After | |||
BreakBeforeBraces: Custom | |||
BraceWrapping: | |||
AfterCaseLabel: false | |||
AfterClass: false | |||
AfterControlStatement: MultiLine | |||
AfterEnum: false | |||
AfterFunction: true | |||
AfterNamespace: false | |||
AfterObjCDeclaration: false | |||
AfterStruct: false | |||
AfterUnion: false | |||
AfterExternBlock: false | |||
BeforeCatch: true | |||
BeforeElse: true | |||
# BeforeLambdaBody: true | |||
# BeforeWhile: false | |||
IndentBraces: false | |||
SplitEmptyFunction: false | |||
SplitEmptyRecord: false | |||
SplitEmptyNamespace: false | |||
BreakBeforeBinaryOperators: None | |||
BreakBeforeInheritanceComma: false | |||
BreakInheritanceList: BeforeColon | |||
BreakBeforeTernaryOperators: true | |||
BreakConstructorInitializersBeforeComma: false | |||
BreakConstructorInitializers: BeforeColon | |||
BreakAfterJavaFieldAnnotations: false | |||
BreakStringLiterals: true | |||
ColumnLimit: 100 | |||
CommentPragmas: '^ IWYU pragma:' | |||
CompactNamespaces: false | |||
ConstructorInitializerAllOnOneLineOrOnePerLine: true | |||
ConstructorInitializerIndentWidth: 4 | |||
ContinuationIndentWidth: 4 | |||
Cpp11BracedListStyle: true | |||
DeriveLineEnding: true | |||
DerivePointerAlignment: false | |||
DisableFormat: false | |||
ExperimentalAutoDetectBinPacking: false | |||
FixNamespaceComments: true | |||
ForEachMacros: | |||
- foreach | |||
- Q_FOREACH | |||
- BOOST_FOREACH | |||
IncludeBlocks: Preserve | |||
IncludeCategories: | |||
- Regex: '^<ext/.*\.h>' | |||
Priority: 2 | |||
SortPriority: 0 | |||
- Regex: '^<.*\.h>' | |||
Priority: 1 | |||
SortPriority: 0 | |||
- Regex: '^<.*' | |||
Priority: 2 | |||
SortPriority: 0 | |||
- Regex: '.*' | |||
Priority: 3 | |||
SortPriority: 0 | |||
IncludeIsMainRegex: '([-_](test|unittest))?$' | |||
IncludeIsMainSourceRegex: '' | |||
IndentCaseLabels: true | |||
IndentGotoLabels: true | |||
IndentPPDirectives: None | |||
IndentWidth: 4 | |||
IndentWrappedFunctionNames: false | |||
JavaScriptQuotes: Leave | |||
JavaScriptWrapImports: true | |||
KeepEmptyLinesAtTheStartOfBlocks: false | |||
MacroBlockBegin: '' | |||
MacroBlockEnd: '' | |||
MaxEmptyLinesToKeep: 1 | |||
NamespaceIndentation: None | |||
ObjCBinPackProtocolList: Never | |||
ObjCBlockIndentWidth: 2 | |||
ObjCSpaceAfterProperty: false | |||
ObjCSpaceBeforeProtocolList: true | |||
PenaltyBreakAssignment: 2 | |||
PenaltyBreakBeforeFirstCallParameter: 1 | |||
PenaltyBreakComment: 300 | |||
PenaltyBreakFirstLessLess: 120 | |||
PenaltyBreakString: 1000 | |||
PenaltyBreakTemplateDeclaration: 10 | |||
PenaltyExcessCharacter: 1000000 | |||
PenaltyReturnTypeOnItsOwnLine: 200 | |||
PointerAlignment: Left | |||
RawStringFormats: | |||
- Language: Cpp | |||
Delimiters: | |||
- cc | |||
- CC | |||
- cpp | |||
- Cpp | |||
- CPP | |||
- 'c++' | |||
- 'C++' | |||
CanonicalDelimiter: '' | |||
BasedOnStyle: google | |||
- Language: TextProto | |||
Delimiters: | |||
- pb | |||
- PB | |||
- proto | |||
- PROTO | |||
EnclosingFunctions: | |||
- EqualsProto | |||
- EquivToProto | |||
- PARSE_PARTIAL_TEXT_PROTO | |||
- PARSE_TEST_PROTO | |||
- PARSE_TEXT_PROTO | |||
- ParseTextOrDie | |||
- ParseTextProtoOrDie | |||
CanonicalDelimiter: '' | |||
BasedOnStyle: google | |||
ReflowComments: true | |||
SortIncludes: true | |||
SortUsingDeclarations: true | |||
SpaceAfterCStyleCast: false | |||
SpaceAfterLogicalNot: false | |||
SpaceAfterTemplateKeyword: true | |||
SpaceBeforeAssignmentOperators: true | |||
SpaceBeforeCpp11BracedList: false | |||
SpaceBeforeCtorInitializerColon: true | |||
SpaceBeforeInheritanceColon: true | |||
SpaceBeforeParens: ControlStatements | |||
SpaceBeforeRangeBasedForLoopColon: true | |||
SpaceInEmptyBlock: false | |||
SpaceInEmptyParentheses: false | |||
SpacesBeforeTrailingComments: 2 | |||
SpacesInAngles: false | |||
SpacesInConditionalStatement: false | |||
SpacesInContainerLiterals: true | |||
SpacesInCStyleCastParentheses: false | |||
SpacesInParentheses: false | |||
SpacesInSquareBrackets: false | |||
SpaceBeforeSquareBrackets: false | |||
Standard: Auto | |||
StatementMacros: | |||
- Q_UNUSED | |||
- QT_REQUIRE_VERSION | |||
TabWidth: 4 | |||
UseCRLF: false | |||
UseTab: Never | |||
... | |||
@ -0,0 +1,407 @@ | |||
--- | |||
Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-fuchsia-*,-misc-definitions-in-headers,-llvm-header-guard,-*-qualified-auto,-llvm-include-order' | |||
WarningsAsErrors: '' | |||
HeaderFilterRegex: '.*' | |||
AnalyzeTemporaryDtors: false | |||
FormatStyle: file | |||
CheckOptions: | |||
- key: abseil-string-find-startswith.AbseilStringsMatchHeader | |||
value: 'absl/strings/match.h' | |||
- key: abseil-string-find-startswith.IncludeStyle | |||
value: llvm | |||
- key: abseil-string-find-startswith.StringLikeClasses | |||
value: '::std::basic_string' | |||
- key: bugprone-argument-comment.CommentBoolLiterals | |||
value: '0' | |||
- key: bugprone-argument-comment.CommentCharacterLiterals | |||
value: '0' | |||
- key: bugprone-argument-comment.CommentFloatLiterals | |||
value: '0' | |||
- key: bugprone-argument-comment.CommentIntegerLiterals | |||
value: '0' | |||
- key: bugprone-argument-comment.CommentNullPtrs | |||
value: '0' | |||
- key: bugprone-argument-comment.CommentStringLiterals | |||
value: '0' | |||
- key: bugprone-argument-comment.CommentUserDefinedLiterals | |||
value: '0' | |||
- key: bugprone-argument-comment.IgnoreSingleArgument | |||
value: '0' | |||
- key: bugprone-argument-comment.StrictMode | |||
value: '0' | |||
- key: bugprone-assert-side-effect.AssertMacros | |||
value: assert | |||
- key: bugprone-assert-side-effect.CheckFunctionCalls | |||
value: '0' | |||
- key: bugprone-dangling-handle.HandleClasses | |||
value: 'std::basic_string_view;std::experimental::basic_string_view' | |||
- key: bugprone-dynamic-static-initializers.HeaderFileExtensions | |||
value: ',h,hh,hpp,hxx' | |||
- key: bugprone-exception-escape.FunctionsThatShouldNotThrow | |||
value: '' | |||
- key: bugprone-exception-escape.IgnoredExceptions | |||
value: '' | |||
- key: bugprone-misplaced-widening-cast.CheckImplicitCasts | |||
value: '0' | |||
- key: bugprone-not-null-terminated-result.WantToUseSafeFunctions | |||
value: '1' | |||
- key: bugprone-signed-char-misuse.CharTypdefsToIgnore | |||
value: '' | |||
- key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant | |||
value: '1' | |||
- key: bugprone-sizeof-expression.WarnOnSizeOfConstant | |||
value: '1' | |||
- key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression | |||
value: '0' | |||
- key: bugprone-sizeof-expression.WarnOnSizeOfThis | |||
value: '1' | |||
- key: bugprone-string-constructor.LargeLengthThreshold | |||
value: '8388608' | |||
- key: bugprone-string-constructor.WarnOnLargeLength | |||
value: '1' | |||
- key: bugprone-suspicious-enum-usage.StrictMode | |||
value: '0' | |||
- key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens | |||
value: '5' | |||
- key: bugprone-suspicious-missing-comma.RatioThreshold | |||
value: '0.200000' | |||
- key: bugprone-suspicious-missing-comma.SizeThreshold | |||
value: '5' | |||
- key: bugprone-suspicious-string-compare.StringCompareLikeFunctions | |||
value: '' | |||
- key: bugprone-suspicious-string-compare.WarnOnImplicitComparison | |||
value: '1' | |||
- key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison | |||
value: '0' | |||
- key: bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit | |||
value: '16' | |||
- key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField | |||
value: '1' | |||
- key: bugprone-unused-return-value.CheckedFunctions | |||
value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty' | |||
- key: cert-dcl16-c.IgnoreMacros | |||
value: '1' | |||
- key: cert-dcl16-c.NewSuffixes | |||
value: 'L;LL;LU;LLU' | |||
- key: cert-dcl59-cpp.HeaderFileExtensions | |||
value: ',h,hh,hpp,hxx' | |||
- key: cert-err09-cpp.CheckThrowTemporaries | |||
value: '1' | |||
- key: cert-err61-cpp.CheckThrowTemporaries | |||
value: '1' | |||
- key: cert-msc32-c.DisallowedSeedTypes | |||
value: 'time_t,std::time_t' | |||
- key: cert-msc51-cpp.DisallowedSeedTypes | |||
value: 'time_t,std::time_t' | |||
- key: cert-oop11-cpp.IncludeStyle | |||
value: llvm | |||
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField | |||
value: '0' | |||
- key: cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues | |||
value: '1.0;100.0;' | |||
- key: cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues | |||
value: '1;2;3;4;' | |||
- key: cppcoreguidelines-explicit-virtual-functions.AllowOverrideAndFinal | |||
value: '0' | |||
- key: cppcoreguidelines-explicit-virtual-functions.FinalSpelling | |||
value: final | |||
- key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors | |||
value: '1' | |||
- key: cppcoreguidelines-explicit-virtual-functions.OverrideSpelling | |||
value: override | |||
- key: cppcoreguidelines-macro-usage.AllowedRegexp | |||
value: '^DEBUG_*' | |||
- key: cppcoreguidelines-macro-usage.CheckCapsOnly | |||
value: '0' | |||
- key: cppcoreguidelines-macro-usage.IgnoreCommandLineMacros | |||
value: '1' | |||
- key: cppcoreguidelines-no-malloc.Allocations | |||
value: '::malloc;::calloc' | |||
- key: cppcoreguidelines-no-malloc.Deallocations | |||
value: '::free' | |||
- key: cppcoreguidelines-no-malloc.Reallocations | |||
value: '::realloc' | |||
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic | |||
value: '1' | |||
- key: cppcoreguidelines-owning-memory.LegacyResourceConsumers | |||
value: '::free;::realloc;::freopen;::fclose' | |||
- key: cppcoreguidelines-owning-memory.LegacyResourceProducers | |||
value: '::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile' | |||
- key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader | |||
value: '' | |||
- key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle | |||
value: '0' | |||
- key: cppcoreguidelines-pro-type-member-init.IgnoreArrays | |||
value: '0' | |||
- key: cppcoreguidelines-pro-type-member-init.UseAssignment | |||
value: '0' | |||
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions | |||
value: '0' | |||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor | |||
value: '0' | |||
- key: google-build-namespaces.HeaderFileExtensions | |||
value: ',h,hh,hpp,hxx' | |||
- key: google-global-names-in-headers.HeaderFileExtensions | |||
value: ',h,hh,hpp,hxx' | |||
- key: google-readability-braces-around-statements.ShortStatementLines | |||
value: '1' | |||
- key: google-readability-function-size.BranchThreshold | |||
value: '4294967295' | |||
- key: google-readability-function-size.LineThreshold | |||
value: '4294967295' | |||
- key: google-readability-function-size.NestingThreshold | |||
value: '4294967295' | |||
- key: google-readability-function-size.ParameterThreshold | |||
value: '4294967295' | |||
- key: google-readability-function-size.StatementThreshold | |||
value: '800' | |||
- key: google-readability-function-size.VariableThreshold | |||
value: '4294967295' | |||
- key: google-readability-namespace-comments.ShortNamespaceLines | |||
value: '10' | |||
- key: google-readability-namespace-comments.SpacesBeforeComments | |||
value: '2' | |||
- key: google-runtime-int.SignedTypePrefix | |||
value: int | |||
- key: google-runtime-int.TypeSuffix | |||
value: '' | |||
- key: google-runtime-int.UnsignedTypePrefix | |||
value: uint | |||
- key: google-runtime-references.WhiteListTypes | |||
value: '' | |||
- key: hicpp-braces-around-statements.ShortStatementLines | |||
value: '0' | |||
- key: hicpp-function-size.BranchThreshold | |||
value: '4294967295' | |||
- key: hicpp-function-size.LineThreshold | |||
value: '4294967295' | |||
- key: hicpp-function-size.NestingThreshold | |||
value: '4294967295' | |||
- key: hicpp-function-size.ParameterThreshold | |||
value: '4294967295' | |||
- key: hicpp-function-size.StatementThreshold | |||
value: '800' | |||
- key: hicpp-function-size.VariableThreshold | |||
value: '4294967295' | |||
- key: hicpp-member-init.IgnoreArrays | |||
value: '0' | |||
- key: hicpp-member-init.UseAssignment | |||
value: '0' | |||
- key: hicpp-move-const-arg.CheckTriviallyCopyableMove | |||
value: '1' | |||
- key: hicpp-multiway-paths-covered.WarnOnMissingElse | |||
value: '0' | |||
- key: hicpp-named-parameter.IgnoreFailedSplit | |||
value: '0' | |||
- key: hicpp-no-malloc.Allocations | |||
value: '::malloc;::calloc' | |||
- key: hicpp-no-malloc.Deallocations | |||
value: '::free' | |||
- key: hicpp-no-malloc.Reallocations | |||
value: '::realloc' | |||
- key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals | |||
value: '0' | |||
- key: hicpp-special-member-functions.AllowMissingMoveFunctions | |||
value: '0' | |||
- key: hicpp-special-member-functions.AllowSoleDefaultDtor | |||
value: '0' | |||
- key: hicpp-uppercase-literal-suffix.IgnoreMacros | |||
value: '1' | |||
- key: hicpp-uppercase-literal-suffix.NewSuffixes | |||
value: '' | |||
- key: hicpp-use-auto.MinTypeNameLength | |||
value: '5' | |||
- key: hicpp-use-auto.RemoveStars | |||
value: '0' | |||
- key: hicpp-use-emplace.ContainersWithPushBack | |||
value: '::std::vector;::std::list;::std::deque' | |||
- key: hicpp-use-emplace.SmartPointers | |||
value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' | |||
- key: hicpp-use-emplace.TupleMakeFunctions | |||
value: '::std::make_pair;::std::make_tuple' | |||
- key: hicpp-use-emplace.TupleTypes | |||
value: '::std::pair;::std::tuple' | |||
- key: hicpp-use-equals-default.IgnoreMacros | |||
value: '1' | |||
- key: hicpp-use-equals-delete.IgnoreMacros | |||
value: '1' | |||
- key: hicpp-use-noexcept.ReplacementString | |||
value: '' | |||
- key: hicpp-use-noexcept.UseNoexceptFalse | |||
value: '1' | |||
- key: hicpp-use-nullptr.NullMacros | |||
value: '' | |||
- key: hicpp-use-override.AllowOverrideAndFinal | |||
value: '0' | |||
- key: hicpp-use-override.FinalSpelling | |||
value: final | |||
- key: hicpp-use-override.IgnoreDestructors | |||
value: '0' | |||
- key: hicpp-use-override.OverrideSpelling | |||
value: override | |||
- key: llvm-namespace-comment.ShortNamespaceLines | |||
value: '1' | |||
- key: llvm-namespace-comment.SpacesBeforeComments | |||
value: '1' | |||
- key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries | |||
value: '1' | |||
- key: misc-unused-parameters.StrictMode | |||
value: '0' | |||
- key: modernize-loop-convert.MaxCopySize | |||
value: '16' | |||
- key: modernize-loop-convert.MinConfidence | |||
value: reasonable | |||
- key: modernize-loop-convert.NamingStyle | |||
value: CamelCase | |||
- key: modernize-make-shared.IgnoreMacros | |||
value: '1' | |||
- key: modernize-make-shared.IncludeStyle | |||
value: '0' | |||
- key: modernize-make-shared.MakeSmartPtrFunction | |||
value: 'std::make_shared' | |||
- key: modernize-make-shared.MakeSmartPtrFunctionHeader | |||
value: memory | |||
- key: modernize-make-unique.IgnoreMacros | |||
value: '1' | |||
- key: modernize-make-unique.IncludeStyle | |||
value: '0' | |||
- key: modernize-make-unique.MakeSmartPtrFunction | |||
value: 'std::make_unique' | |||
- key: modernize-make-unique.MakeSmartPtrFunctionHeader | |||
value: memory | |||
- key: modernize-pass-by-value.IncludeStyle | |||
value: llvm | |||
- key: modernize-pass-by-value.ValuesOnly | |||
value: '0' | |||
- key: modernize-raw-string-literal.ReplaceShorterLiterals | |||
value: '0' | |||
- key: modernize-replace-auto-ptr.IncludeStyle | |||
value: llvm | |||
- key: modernize-replace-random-shuffle.IncludeStyle | |||
value: llvm | |||
- key: modernize-use-auto.MinTypeNameLength | |||
value: '5' | |||
- key: modernize-use-auto.RemoveStars | |||
value: '0' | |||
- key: modernize-use-default-member-init.IgnoreMacros | |||
value: '1' | |||
- key: modernize-use-default-member-init.UseAssignment | |||
value: '0' | |||
- key: modernize-use-emplace.ContainersWithPushBack | |||
value: '::std::vector;::std::list;::std::deque' | |||
- key: modernize-use-emplace.SmartPointers | |||
value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' | |||
- key: modernize-use-emplace.TupleMakeFunctions | |||
value: '::std::make_pair;::std::make_tuple' | |||
- key: modernize-use-emplace.TupleTypes | |||
value: '::std::pair;::std::tuple' | |||
- key: modernize-use-equals-default.IgnoreMacros | |||
value: '1' | |||
- key: modernize-use-equals-delete.IgnoreMacros | |||
value: '1' | |||
- key: modernize-use-nodiscard.ReplacementString | |||
value: '[[nodiscard]]' | |||
- key: modernize-use-noexcept.ReplacementString | |||
value: '' | |||
- key: modernize-use-noexcept.UseNoexceptFalse | |||
value: '1' | |||
- key: modernize-use-nullptr.NullMacros | |||
value: 'NULL' | |||
- key: modernize-use-override.AllowOverrideAndFinal | |||
value: '0' | |||
- key: modernize-use-override.FinalSpelling | |||
value: final | |||
- key: modernize-use-override.IgnoreDestructors | |||
value: '0' | |||
- key: modernize-use-override.OverrideSpelling | |||
value: override | |||
- key: modernize-use-transparent-functors.SafeMode | |||
value: '0' | |||
- key: modernize-use-using.IgnoreMacros | |||
value: '1' | |||
- key: objc-forbidden-subclassing.ForbiddenSuperClassNames | |||
value: 'ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView' | |||
- key: openmp-exception-escape.IgnoredExceptions | |||
value: '' | |||
- key: performance-faster-string-find.StringLikeClasses | |||
value: 'std::basic_string' | |||
- key: performance-for-range-copy.AllowedTypes | |||
value: '' | |||
- key: performance-for-range-copy.WarnOnAllAutoCopies | |||
value: '0' | |||
- key: performance-inefficient-string-concatenation.StrictMode | |||
value: '0' | |||
- key: performance-inefficient-vector-operation.EnableProto | |||
value: '0' | |||
- key: performance-inefficient-vector-operation.VectorLikeClasses | |||
value: '::std::vector' | |||
- key: performance-move-const-arg.CheckTriviallyCopyableMove | |||
value: '1' | |||
- key: performance-move-constructor-init.IncludeStyle | |||
value: llvm | |||
- key: performance-no-automatic-move.AllowedTypes | |||
value: '' | |||
- key: performance-type-promotion-in-math-fn.IncludeStyle | |||
value: llvm | |||
- key: performance-unnecessary-copy-initialization.AllowedTypes | |||
value: '' | |||
- key: performance-unnecessary-value-param.AllowedTypes | |||
value: '' | |||
- key: performance-unnecessary-value-param.IncludeStyle | |||
value: llvm | |||
- key: portability-simd-intrinsics.Std | |||
value: '' | |||
- key: portability-simd-intrinsics.Suggest | |||
value: '0' | |||
- key: readability-braces-around-statements.ShortStatementLines | |||
value: '0' | |||
- key: readability-else-after-return.WarnOnUnfixable | |||
value: '1' | |||
- key: readability-function-size.BranchThreshold | |||
value: '4294967295' | |||
- key: readability-function-size.LineThreshold | |||
value: '4294967295' | |||
- key: readability-function-size.NestingThreshold | |||
value: '4294967295' | |||
- key: readability-function-size.ParameterThreshold | |||
value: '4294967295' | |||
- key: readability-function-size.StatementThreshold | |||
value: '800' | |||
- key: readability-function-size.VariableThreshold | |||
value: '4294967295' | |||
- key: readability-identifier-naming.IgnoreFailedSplit | |||
value: '0' | |||
- key: readability-implicit-bool-conversion.AllowIntegerConditions | |||
value: '0' | |||
- key: readability-implicit-bool-conversion.AllowPointerConditions | |||
value: '0' | |||
- key: readability-inconsistent-declaration-parameter-name.IgnoreMacros | |||
value: '1' | |||
- key: readability-inconsistent-declaration-parameter-name.Strict | |||
value: '0' | |||
- key: readability-magic-numbers.IgnoredFloatingPointValues | |||
value: '1.0;100.0;' | |||
- key: readability-magic-numbers.IgnoredIntegerValues | |||
value: '1;2;3;4;' | |||
- key: readability-redundant-member-init.IgnoreBaseInCopyConstructors | |||
value: '0' | |||
- key: readability-redundant-smartptr-get.IgnoreMacros | |||
value: '1' | |||
- key: readability-redundant-string-init.StringNames | |||
value: '::std::basic_string' | |||
- key: readability-simplify-boolean-expr.ChainedConditionalAssignment | |||
value: '0' | |||
- key: readability-simplify-boolean-expr.ChainedConditionalReturn | |||
value: '0' | |||
- key: readability-simplify-subscript-expr.Types | |||
value: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array' | |||
- key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold | |||
value: '3' | |||
- key: readability-uppercase-literal-suffix.IgnoreMacros | |||
value: '1' | |||
- key: readability-uppercase-literal-suffix.NewSuffixes | |||
value: '' | |||
- key: zircon-temporary-objects.Names | |||
value: '' | |||
... | |||
@ -0,0 +1,23 @@ | |||
/* Copyright 2020 Peter Stadler | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in the | |||
* documentation and/or other materials provided with the distribution. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
*/ |
@ -1,194 +1,378 @@ | |||
#include <iostream> | |||
#include <numeric> | |||
#include "nginx-util.hpp" | |||
#ifndef NO_SSL | |||
#include "nginx-ssl-util.hpp" | |||
#endif | |||
#include "nginx-util.hpp" | |||
static auto constexpr file_comment_auto_created = | |||
std::string_view{"# This file is re-created when Nginx starts.\n"}; | |||
void create_lan_listen() | |||
// TODO(pst) replace it with blobmsg_get_string if upstream takes const: | |||
#ifndef NO_UBUS | |||
static inline auto _pst_get_string(const blob_attr* attr) -> char* | |||
{ | |||
std::string listen = "# This file is re-created if Nginx starts or" | |||
" a LAN address changes.\n"; | |||
std::string listen_default = listen; | |||
std::string ssl_listen = listen; | |||
std::string ssl_listen_default = listen; | |||
auto add_listen = [&listen, &listen_default | |||
#ifndef NO_SSL | |||
,&ssl_listen, &ssl_listen_default | |||
#endif | |||
] | |||
(const std::string &pre, const std::string &ip, const std::string &suf) | |||
-> void | |||
{ | |||
if (ip.empty()) { return; } | |||
const std::string val = pre + ip + suf; | |||
listen += "\tlisten " + val + ":80;\n"; | |||
listen_default += "\tlisten " + val + ":80 default_server;\n"; | |||
#ifndef NO_SSL | |||
ssl_listen += "\tlisten " + val + ":443 ssl;\n"; | |||
ssl_listen_default += "\tlisten " + val + ":443 ssl default_server;\n"; | |||
return static_cast<char*>(blobmsg_data(attr)); | |||
} | |||
#endif | |||
}; | |||
void create_lan_listen() // create empty files for compatibility: | |||
{ | |||
// TODO(pst): replace by dummies after transitioning nginx config to UCI: | |||
std::vector<std::string> ips; | |||
#ifndef NO_UBUS | |||
try { | |||
auto loopback_status=ubus::call("network.interface.loopback", "status"); | |||
auto loopback_status = ubus::call("network.interface.loopback", "status"); | |||
for (auto ip : loopback_status.filter("ipv4-address", "", "address")) { | |||
add_listen("", static_cast<const char *>(blobmsg_data(ip)), ""); | |||
for (const auto* ip : loopback_status.filter("ipv4-address", "", "address")) { | |||
ips.emplace_back(_pst_get_string(ip)); | |||
} | |||
for (auto ip : loopback_status.filter("ipv6-address", "", "address")) { | |||
add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]"); | |||
for (const auto* ip : loopback_status.filter("ipv6-address", "", "address")) { | |||
ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]"); | |||
} | |||
} catch (const std::runtime_error &) { /* do nothing about it */ } | |||
} | |||
catch (const std::runtime_error&) { /* do nothing about it */ | |||
} | |||
try { | |||
auto lan_status = ubus::call("network.interface.lan", "status"); | |||
for (auto ip : lan_status.filter("ipv4-address", "", "address")) { | |||
add_listen("", static_cast<const char *>(blobmsg_data(ip)), ""); | |||
for (const auto* ip : lan_status.filter("ipv4-address", "", "address")) { | |||
ips.emplace_back(_pst_get_string(ip)); | |||
} | |||
for (auto ip : lan_status.filter("ipv6-address", "", "address")) { | |||
add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]"); | |||
for (const auto* ip : lan_status.filter("ipv6-address", "", "address")) { | |||
ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]"); | |||
} | |||
for (auto ip : lan_status.filter("ipv6-prefix-assignment", "", | |||
"local-address", "address")) | |||
{ | |||
add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]"); | |||
for (const auto* ip : | |||
lan_status.filter("ipv6-prefix-assignment", "", "local-address", "address")) { | |||
ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]"); | |||
} | |||
} catch (const std::runtime_error &) { /* do nothing about it */ } | |||
} | |||
catch (const std::runtime_error&) { /* do nothing about it */ | |||
} | |||
#else | |||
add_listen("", "127.0.0.1", ""); | |||
ips.emplace_back("127.0.0.1"); | |||
#endif | |||
std::string listen = std::string{file_comment_auto_created}; | |||
std::string listen_default = std::string{file_comment_auto_created}; | |||
for (const auto& ip : ips) { | |||
listen += "\tlisten " + ip + ":80;\n"; | |||
listen_default += "\tlisten " + ip + ":80 default_server;\n"; | |||
} | |||
write_file(LAN_LISTEN, listen); | |||
write_file(LAN_LISTEN_DEFAULT, listen_default); | |||
#ifndef NO_SSL | |||
std::string ssl_listen = std::string{file_comment_auto_created}; | |||
std::string ssl_listen_default = std::string{file_comment_auto_created}; | |||
for (const auto& ip : ips) { | |||
ssl_listen += "\tlisten " + ip + ":443 ssl;\n"; | |||
ssl_listen_default += "\tlisten " + ip + ":443 ssl default_server;\n"; | |||
} | |||
write_file(LAN_SSL_LISTEN, ssl_listen); | |||
write_file(LAN_SSL_LISTEN_DEFAULT, ssl_listen_default); | |||
#endif | |||
} | |||
inline auto change_if_starts_with(const std::string_view& subject, | |||
const std::string_view& prefix, | |||
const std::string_view& substitute, | |||
const std::string_view& seperator = " \t\n;") -> std::string | |||
{ | |||
auto view = subject; | |||
view = view.substr(view.find_first_not_of(seperator)); | |||
if (view.rfind(prefix, 0) == 0) { | |||
if (view.size() == prefix.size()) { | |||
return std::string{substitute}; | |||
} | |||
view = view.substr(prefix.size()); | |||
if (seperator.find(view[0]) != std::string::npos) { | |||
auto ret = std::string{substitute}; | |||
ret += view; | |||
return ret; | |||
} | |||
} | |||
return std::string{subject}; | |||
} | |||
inline auto create_server_conf(const uci::section& sec, const std::string& indent = "") | |||
-> std::string | |||
{ | |||
auto secname = sec.name(); | |||
auto legacypath = std::string{CONF_DIR} + secname + ".conf"; | |||
if (access(legacypath.c_str(), R_OK) == 0) { | |||
auto message = std::string{"skipped UCI server 'nginx."} + secname; | |||
message += "' as it could conflict with: " + legacypath + "\n"; | |||
// TODO(pst) std::cerr<<"create_server_conf notice: "<<message; | |||
return indent + "# " + message; | |||
} // else: | |||
auto conf = indent + "server { #see uci show 'nginx." + secname + "'\n"; | |||
for (auto opt : sec) { | |||
for (auto itm : opt) { | |||
if (opt.name().rfind("uci_", 0) == 0) { | |||
continue; | |||
} | |||
// else: standard opt.name() | |||
auto val = itm.name(); | |||
if (opt.name() == "error_log") { | |||
val = change_if_starts_with(val, "logd", "/proc/self/fd/1"); | |||
} | |||
else if (opt.name() == "access_log") { | |||
val = change_if_starts_with(val, "logd", "stderr"); | |||
} | |||
conf += indent + "\t" + opt.name() + " " + itm.name() + ";\n"; | |||
} | |||
} | |||
conf += indent + "}\n"; | |||
return conf; | |||
} | |||
void init_uci(const uci::package& pkg) | |||
{ | |||
auto conf = std::string{file_comment_auto_created}; | |||
static const auto uci_http_config = std::string_view{"#UCI_HTTP_CONFIG\n"}; | |||
const auto tmpl = read_file(std::string{UCI_CONF} + ".template"); | |||
auto pos = tmpl.find(uci_http_config); | |||
if (pos == std::string::npos) { | |||
conf += tmpl; | |||
} | |||
else { | |||
const auto index = tmpl.find_last_not_of(" \t", pos - 1); | |||
const auto before = tmpl.begin() + index + 1; | |||
const auto middle = tmpl.begin() + pos; | |||
const auto after = middle + uci_http_config.length(); | |||
conf.append(tmpl.begin(), before); | |||
const auto indent = std::string{before, middle}; | |||
for (auto sec : pkg) { | |||
if (sec.type() == std::string_view{"server"}) { | |||
conf += create_server_conf(sec, indent) + "\n"; | |||
} | |||
} | |||
conf.append(after, tmpl.end()); | |||
} | |||
write_file(VAR_UCI_CONF, conf); | |||
} | |||
auto is_enabled(const uci::package& pkg) -> bool | |||
{ | |||
for (auto sec : pkg) { | |||
if (sec.type() != std::string_view{"main"}) { | |||
continue; | |||
} | |||
if (sec.name() != std::string_view{"global"}) { | |||
continue; | |||
} | |||
for (auto opt : sec) { | |||
if (opt.name() != "uci_enable") { | |||
continue; | |||
} | |||
for (auto itm : opt) { | |||
if (itm) { | |||
return true; | |||
} | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
/* | |||
* ___________main_thread________________|______________thread_1________________ | |||
* create_lan_listen() or do nothing | config = uci::package("nginx") | |||
* if config_enabled (set in thread_1): | config_enabled = is_enabled(config) | |||
* then init_uci(config) | check_ssl(config, config_enabled) | |||
*/ | |||
void init_lan() | |||
{ | |||
std::exception_ptr ex; | |||
#ifndef NO_SSL | |||
auto thrd = std::thread([]{ //&ex | |||
try { add_ssl_if_needed(std::string{LAN_NAME}); } | |||
std::unique_ptr<uci::package> config; | |||
bool config_enabled = false; | |||
std::mutex configuring; | |||
configuring.lock(); | |||
auto thrd = std::thread([&config, &config_enabled, &configuring, &ex] { | |||
try { | |||
config = std::make_unique<uci::package>("nginx"); | |||
config_enabled = is_enabled(*config); | |||
configuring.unlock(); | |||
check_ssl(*config, config_enabled); | |||
} | |||
catch (...) { | |||
std::cerr<<"init_lan notice: no server named "<<LAN_NAME<<std::endl; | |||
// not: ex = std::current_exception(); | |||
std::cerr << "init_lan error: checking UCI file /etc/config/nginx\n"; | |||
ex = std::current_exception(); | |||
} | |||
}); | |||
#endif | |||
try { create_lan_listen(); } | |||
try { | |||
create_lan_listen(); | |||
} | |||
catch (...) { | |||
std::cerr<<"init_lan error: cannot create LAN listen files"<<std::endl; | |||
std::cerr << "init_lan error: cannot create listen files of local IPs.\n"; | |||
ex = std::current_exception(); | |||
} | |||
#ifndef NO_SSL | |||
thrd.join(); | |||
#endif | |||
configuring.lock(); | |||
if (config_enabled) { | |||
try { | |||
init_uci(*config); | |||
} | |||
catch (...) { | |||
std::cerr << "init_lan error: cannot create " << VAR_UCI_CONF << " from "; | |||
std::cerr << UCI_CONF << ".template using UCI file /etc/config/nginx\n"; | |||
ex = std::current_exception(); | |||
} | |||
} | |||
if (ex) { std::rethrow_exception(ex); } | |||
thrd.join(); | |||
if (ex) { | |||
std::rethrow_exception(ex); | |||
} | |||
} | |||
void get_env() | |||
{ | |||
std::cout<<"NGINX_CONF="<<"'"<<NGINX_CONF<<"'"<<std::endl; | |||
std::cout<<"CONF_DIR="<<"'"<<CONF_DIR<<"'"<<std::endl; | |||
std::cout<<"LAN_NAME="<<"'"<<LAN_NAME<<"'"<<std::endl; | |||
std::cout<<"LAN_LISTEN="<<"'"<<LAN_LISTEN<<"'"<<std::endl; | |||
#ifndef NO_SSL | |||
std::cout<<"LAN_SSL_LISTEN="<<"'"<<LAN_SSL_LISTEN<<"'"<<std::endl; | |||
std::cout<<"SSL_SESSION_CACHE_ARG="<<"'"<<SSL_SESSION_CACHE_ARG(LAN_NAME)<< | |||
"'"<<std::endl; | |||
std::cout<<"SSL_SESSION_TIMEOUT_ARG="<<"'"<<SSL_SESSION_TIMEOUT_ARG<<"'\n"; | |||
std::cout<<"ADD_SSL_FCT="<<"'"<<ADD_SSL_FCT<<"'"<<std::endl; | |||
#endif | |||
std::cout << "UCI_CONF=" | |||
<< "'" << UCI_CONF << "'" << std::endl; | |||
std::cout << "NGINX_CONF=" | |||
<< "'" << NGINX_CONF << "'" << std::endl; | |||
std::cout << "CONF_DIR=" | |||
<< "'" << CONF_DIR << "'" << std::endl; | |||
std::cout << "LAN_NAME=" | |||
<< "'" << LAN_NAME << "'" << std::endl; | |||
std::cout << "LAN_LISTEN=" | |||
<< "'" << LAN_LISTEN << "'" << std::endl; | |||
std::cout << "LAN_SSL_LISTEN=" | |||
<< "'" << LAN_SSL_LISTEN << "'" << std::endl; | |||
std::cout << "SSL_SESSION_CACHE_ARG=" | |||
<< "'" << SSL_SESSION_CACHE_ARG(LAN_NAME) << "'" << std::endl; | |||
std::cout << "SSL_SESSION_TIMEOUT_ARG=" | |||
<< "'" << SSL_SESSION_TIMEOUT_ARG << "'\n"; | |||
std::cout << "ADD_SSL_FCT=" | |||
<< "'" << ADD_SSL_FCT << "'" << std::endl; | |||
std::cout << "MANAGE_SSL=" | |||
<< "'" << MANAGE_SSL << "'" << std::endl; | |||
} | |||
auto main(int argc, char * argv[]) -> int | |||
auto main(int argc, char* argv[]) -> int | |||
{ | |||
// TODO(pst): use std::span when available: | |||
auto args = std::basic_string_view<char *>{argv, static_cast<size_t>(argc)}; | |||
auto args = std::basic_string_view<char*>{argv, static_cast<size_t>(argc)}; | |||
auto cmds = std::array{ | |||
std::array<std::string_view, 2>{"init_lan", ""}, | |||
std::array<std::string_view, 2>{"get_env", ""}, | |||
#ifndef NO_SSL | |||
std::array<std::string_view, 2>{ADD_SSL_FCT, " server_name" }, | |||
std::array<std::string_view, 2>{"del_ssl", " server_name" }, | |||
#endif | |||
std::array<std::string_view, 2>{ | |||
ADD_SSL_FCT, "server_name [manager /path/to/ssl_certificate /path/to/ssl_key]"}, | |||
std::array<std::string_view, 2>{"del_ssl", "server_name [manager]"}, | |||
std::array<std::string_view, 2>{"check_ssl", ""}, | |||
}; | |||
try { | |||
if (argc == 2 && args[1] == cmds[0][0]) { | |||
init_lan(); | |||
} | |||
if (argc==2 && args[1]==cmds[0][0]) { init_lan(); } | |||
else if (argc == 2 && args[1] == cmds[1][0]) { | |||
get_env(); | |||
} | |||
else if (argc==2 && args[1]==cmds[1][0]) { get_env(); } | |||
else if (argc == 3 && args[1] == cmds[2][0]) { | |||
add_ssl_if_needed(std::string{args[2]}); | |||
} | |||
#ifndef NO_SSL | |||
else if (argc==3 && args[1]==cmds[2][0]) | |||
{ add_ssl_if_needed(std::string{args[2]});} | |||
// NOLINTNEXTLINE(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers): 6 | |||
else if (argc == 6 && args[1] == cmds[2][0]) { | |||
// NOLINTNEXTLINE(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers): 5 | |||
add_ssl_if_needed(std::string{args[2]}, args[3], args[4], args[5]); | |||
} | |||
else if (argc==3 && args[1]==cmds[3][0]) | |||
{ del_ssl(std::string{args[2]}); } | |||
else if (argc == 3 && args[1] == cmds[3][0]) { | |||
del_ssl(std::string{args[2]}); | |||
} | |||
else if (argc==2 && args[1]==cmds[3][0]) | |||
{ del_ssl(std::string{LAN_NAME}); } | |||
#endif | |||
else if (argc == 4 && args[1] == cmds[3][0]) { | |||
del_ssl(std::string{args[2]}, args[3]); | |||
} | |||
else if (argc == 2 && args[1] == cmds[3][0]) // TODO(pst) deprecate | |||
{ | |||
try { | |||
auto name = std::string{LAN_NAME}; | |||
if (del_ssl_legacy(name)) { | |||
auto crtpath = std::string{CONF_DIR} + name + ".crt"; | |||
remove(crtpath.c_str()); | |||
auto keypath = std::string{CONF_DIR} + name + ".key"; | |||
remove(keypath.c_str()); | |||
} | |||
} | |||
catch (...) { /* do nothing. */ | |||
} | |||
} | |||
else if (argc == 2 && args[1] == cmds[4][0]) { | |||
check_ssl(uci::package{"nginx"}); | |||
} | |||
else { | |||
std::cerr<<"Tool for creating Nginx configuration files ("; | |||
std::cerr << "Tool for creating Nginx configuration files ("; | |||
#ifdef VERSION | |||
std::cerr<<"version "<<VERSION<<" "; | |||
std::cerr << "version " << VERSION << " "; | |||
#endif | |||
std::cerr<<"with "; | |||
std::cerr << "with libuci, "; | |||
#ifndef NO_UBUS | |||
std::cerr<<"ubus, "; | |||
#endif | |||
#ifndef NO_SSL | |||
std::cerr<<"libopenssl, "; | |||
#ifdef NO_PCRE | |||
std::cerr<<"std::regex, "; | |||
#else | |||
std::cerr<<"PCRE, "; | |||
std::cerr << "libubus, "; | |||
#endif | |||
std::cerr << "libopenssl, "; | |||
#ifndef NO_PCRE | |||
std::cerr << "PCRE, "; | |||
#endif | |||
std::cerr<<"pthread and libstdcpp)."<<std::endl; | |||
std::cerr << "pthread and libstdcpp)." << std::endl; | |||
auto usage = std::string{"usage: "} + *argv + " ["; | |||
for (auto cmd : cmds) { | |||
usage += std::string{cmd[0]}; | |||
usage += std::string{cmd[1]} + "|"; | |||
} | |||
usage[usage.size()-1] = ']'; | |||
std::cerr<<usage<<std::endl; | |||
auto usage = | |||
std::accumulate(cmds.begin(), cmds.end(), std::string{"usage: "} + *argv + " [", | |||
[](const auto& use, const auto& cmd) { | |||
return use + std::string{cmd[0]} + (cmd[1].empty() ? "" : " ") + | |||
std::string{cmd[1]} + "|"; | |||
}); | |||
usage[usage.size() - 1] = ']'; | |||
std::cerr << usage << std::endl; | |||
throw std::runtime_error("main error: argument not recognized"); | |||
} | |||
return 0; | |||
} | |||
catch (const std::exception & e) { std::cerr<<e.what()<<std::endl; } | |||
catch (const std::exception& e) { | |||
std::cerr << " * " << *argv << " " << e.what() << "\n"; | |||
} | |||
catch (...) { perror("main error"); } | |||
catch (...) { | |||
std::cerr << " * * " << *argv; | |||
perror(" main error"); | |||
} | |||
return 1; | |||
} |
@ -0,0 +1,22 @@ | |||
#include <iostream> | |||
#include <mutex> | |||
#include <string> | |||
#include <string_view> | |||
#include <vector> | |||
#include "uci-cxx.hpp" | |||
auto main() -> int | |||
{ | |||
uci::element p = uci::package{"nginx"}; | |||
std::cout << "package " << p.name() << "\n\n"; | |||
for (auto s : p) { | |||
std::cout << "config " << s.type() << " '" << s.name() << "'\n"; | |||
for (auto o : s) { | |||
for (auto i : o) { | |||
std::cout << "\t" << o.type() << " " << o.name() << " '" << i.name() << "'\n"; | |||
} | |||
} | |||
std::cout << "\n"; | |||
} | |||
} |
@ -0,0 +1,474 @@ | |||
#ifndef _UCI_CXX_HPP | |||
#define _UCI_CXX_HPP | |||
#include <uci.h> | |||
#include <memory> | |||
#include <mutex> | |||
#include <stdexcept> | |||
#include <string> | |||
#include <string_view> | |||
namespace uci { | |||
template <class T> | |||
class iterator { // like uci_foreach_element_safe. | |||
private: | |||
const uci_ptr& _ptr; | |||
uci_element* _it = nullptr; | |||
uci_element* _next = nullptr; | |||
// wrapper for clang-tidy | |||
inline auto _list_to_element(const uci_list* cur) -> uci_element* | |||
{ | |||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast) | |||
return list_to_element(cur); // macro casting container=pointer-offset. | |||
} | |||
public: | |||
inline explicit iterator(const uci_ptr& ptr, const uci_list* cur) | |||
: _ptr{ptr}, _it{_list_to_element(cur)} | |||
{ | |||
_next = _list_to_element(_it->list.next); | |||
} | |||
inline iterator(iterator&&) noexcept = default; | |||
inline iterator(const iterator&) = delete; | |||
inline auto operator=(const iterator&) -> iterator& = delete; | |||
inline auto operator=(iterator &&) -> iterator& = delete; | |||
auto operator*() -> T | |||
{ | |||
return T{_ptr, _it}; | |||
} | |||
inline auto operator!=(const iterator& rhs) -> bool | |||
{ | |||
return (&_it->list != &rhs._it->list); | |||
} | |||
inline auto operator++() -> iterator& | |||
{ | |||
_it = _next; | |||
_next = _list_to_element(_next->list.next); | |||
return *this; | |||
} | |||
inline ~iterator() = default; | |||
}; | |||
class locked_context { | |||
private: | |||
static std::mutex inuse; | |||
public: | |||
inline locked_context() | |||
{ | |||
inuse.lock(); | |||
} | |||
inline locked_context(locked_context&&) noexcept = default; | |||
inline locked_context(const locked_context&) = delete; | |||
inline auto operator=(const locked_context&) -> locked_context& = delete; | |||
inline auto operator=(locked_context &&) -> locked_context& = delete; | |||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static) | |||
inline auto get() -> uci_context* // is member to enforce inuse | |||
{ | |||
static auto free_ctx = [](uci_context* ctx) { uci_free_context(ctx); }; | |||
static std::unique_ptr<uci_context, decltype(free_ctx)> lazy_ctx{uci_alloc_context(), | |||
free_ctx}; | |||
if (!lazy_ctx) { // it could be available on a later call: | |||
lazy_ctx.reset(uci_alloc_context()); | |||
if (!lazy_ctx) { | |||
throw std::runtime_error("uci error: cannot allocate context"); | |||
} | |||
} | |||
return lazy_ctx.get(); | |||
} | |||
inline ~locked_context() | |||
{ | |||
inuse.unlock(); | |||
} | |||
}; | |||
template <class T> | |||
class element { | |||
private: | |||
uci_list* _begin = nullptr; | |||
uci_list* _end = nullptr; | |||
uci_ptr _ptr{}; | |||
protected: | |||
[[nodiscard]] inline auto ptr() -> uci_ptr& | |||
{ | |||
return _ptr; | |||
} | |||
[[nodiscard]] inline auto ptr() const -> const uci_ptr& | |||
{ | |||
return _ptr; | |||
} | |||
void init_begin_end(uci_list* begin, uci_list* end) | |||
{ | |||
_begin = begin; | |||
_end = end; | |||
} | |||
inline explicit element(const uci_ptr& pre, uci_element* last) : _ptr{pre} | |||
{ | |||
_ptr.last = last; | |||
} | |||
inline explicit element() = default; | |||
public: | |||
inline element(element&&) noexcept = default; | |||
inline element(const element&) = delete; | |||
inline auto operator=(const element&) -> element& = delete; | |||
inline auto operator=(element &&) -> element& = delete; | |||
auto operator[](std::string_view key) const -> T; | |||
[[nodiscard]] inline auto name() const -> std::string | |||
{ | |||
return _ptr.last->name; | |||
} | |||
void rename(const char* value) const; | |||
void commit() const; | |||
[[nodiscard]] inline auto begin() const -> iterator<T> | |||
{ | |||
return iterator<T>{_ptr, _begin}; | |||
} | |||
[[nodiscard]] inline auto end() const -> iterator<T> | |||
{ | |||
return iterator<T>{_ptr, _end}; | |||
} | |||
inline ~element() = default; | |||
}; | |||
class section; | |||
class option; | |||
class item; | |||
class package : public element<section> { | |||
public: | |||
inline package(const uci_ptr& pre, uci_element* last) : element{pre, last} | |||
{ | |||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast) | |||
ptr().p = uci_to_package(ptr().last); // macro casting pointer-offset. | |||
ptr().package = ptr().last->name; | |||
auto* end = &ptr().p->sections; | |||
auto* begin = end->next; | |||
init_begin_end(begin, end); | |||
} | |||
explicit package(const char* name); | |||
auto set(const char* key, const char* type) const -> section; | |||
}; | |||
class section : public element<option> { | |||
public: | |||
inline section(const uci_ptr& pre, uci_element* last) : element{pre, last} | |||
{ | |||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast) | |||
ptr().s = uci_to_section(ptr().last); // macro casting pointer-offset. | |||
ptr().section = ptr().last->name; | |||
auto* end = &ptr().s->options; | |||
auto* begin = end->next; | |||
init_begin_end(begin, end); | |||
} | |||
auto set(const char* key, const char* value) const -> option; | |||
void del(); | |||
[[nodiscard]] inline auto anonymous() const -> bool | |||
{ | |||
return ptr().s->anonymous; | |||
} | |||
[[nodiscard]] inline auto type() const -> std::string | |||
{ | |||
return ptr().s->type; | |||
} | |||
}; | |||
class option : public element<item> { | |||
public: | |||
inline option(const uci_ptr& pre, uci_element* last) : element{pre, last} | |||
{ | |||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast) | |||
ptr().o = uci_to_option(ptr().last); // macro casting pointer-offset. | |||
ptr().option = ptr().last->name; | |||
if (ptr().o->type == UCI_TYPE_LIST) { // use union ptr().o->v as list: | |||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) | |||
auto* end = &ptr().o->v.list; | |||
auto* begin = end->next; | |||
init_begin_end(begin, end); | |||
} | |||
else { | |||
auto* begin = &ptr().last->list; | |||
auto* end = begin->next; | |||
init_begin_end(begin, end); | |||
} | |||
} | |||
void del(); | |||
[[nodiscard]] inline auto type() const -> std::string | |||
{ | |||
return (ptr().o->type == UCI_TYPE_LIST ? "list" : "option"); | |||
} | |||
}; | |||
class item : public element<item> { | |||
public: | |||
inline item(const uci_ptr& pre, uci_element* last) : element{pre, last} | |||
{ | |||
ptr().value = ptr().last->name; | |||
} | |||
[[nodiscard]] inline auto type() const -> std::string | |||
{ | |||
return (ptr().o->type == UCI_TYPE_LIST ? "list" : "option"); | |||
} | |||
[[nodiscard]] inline auto name() const -> std::string | |||
{ | |||
return (ptr().last->type == UCI_TYPE_ITEM | |||
? ptr().last->name | |||
: | |||
// else: use union ptr().o->v as string: | |||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) | |||
ptr().o->v.string); | |||
} | |||
inline explicit operator bool() const | |||
{ | |||
const auto x = std::string_view{name()}; | |||
if (x == "0" || x == "off" || x == "false" || x == "no" || x == "disabled") { | |||
return false; | |||
} | |||
if (x == "1" || x == "on" || x == "true" || x == "yes" || x == "enabled") { | |||
return true; | |||
} | |||
auto errmsg = std::string{"uci_error: item is not bool "} + name(); | |||
throw std::runtime_error(errmsg); | |||
} | |||
void rename(const char* value) const; | |||
}; | |||
// ------------------------- implementation: ---------------------------------- | |||
std::mutex locked_context::inuse{}; | |||
inline auto uci_error(uci_context* ctx, const char* prefix = nullptr) -> std::runtime_error | |||
{ | |||
char* errmsg = nullptr; | |||
uci_get_errorstr(ctx, &errmsg, prefix); | |||
std::unique_ptr<char, decltype(&std::free)> auto_free{errmsg, std::free}; | |||
return std::runtime_error{errmsg}; | |||
} | |||
template <class T> | |||
auto element<T>::operator[](std::string_view key) const -> T | |||
{ | |||
for (auto elmt : *this) { | |||
if (elmt.name() == key) { | |||
return elmt; | |||
} | |||
} | |||
auto errmsg = std::string{"uci error: cannot find "}.append(key); | |||
throw uci_error(locked_context{}.get(), errmsg.c_str()); | |||
} | |||
template <class T> | |||
void element<T>::rename(const char* value) const | |||
{ | |||
if (value == name()) { | |||
return; | |||
} | |||
auto ctx = locked_context{}; | |||
auto tmp_ptr = uci_ptr{_ptr}; | |||
tmp_ptr.value = value; | |||
if (uci_rename(ctx.get(), &tmp_ptr) != 0) { | |||
auto errmsg = std::string{"uci error: cannot rename "}.append(name()); | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
} | |||
template <class T> | |||
void element<T>::commit() const | |||
{ | |||
auto ctx = locked_context{}; | |||
// TODO(pst) use when possible: | |||
// if (uci_commit(ctx.get(), &_ptr.p, true) != 0) { | |||
// auto errmsg = std::string{"uci error: cannot commit "} + _ptr.package; | |||
// throw uci_error(ctx.get(), errmsg.c_str()); | |||
// } | |||
auto err = uci_save(ctx.get(), _ptr.p); | |||
if (err == 0) { | |||
uci_package* tmp_pkg = nullptr; | |||
uci_context* tmp_ctx = uci_alloc_context(); | |||
err = (tmp_ctx == nullptr ? 1 : 0); | |||
if (err == 0) { | |||
err = uci_load(tmp_ctx, _ptr.package, &tmp_pkg); | |||
} | |||
if (err == 0) { | |||
err = uci_commit(tmp_ctx, &tmp_pkg, false); | |||
} | |||
if (err == 0) { | |||
err = uci_unload(tmp_ctx, tmp_pkg); | |||
} | |||
if (tmp_ctx != nullptr) { | |||
uci_free_context(tmp_ctx); | |||
} | |||
} | |||
if (err != 0) { | |||
auto errmsg = std::string{"uci error: cannot commit "} + _ptr.package; | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
} | |||
package::package(const char* name) | |||
{ | |||
auto ctx = locked_context{}; | |||
auto* pkg = uci_lookup_package(ctx.get(), name); | |||
if (pkg == nullptr) { | |||
if (uci_load(ctx.get(), name, &pkg) != 0) { | |||
auto errmsg = std::string{"uci error: cannot load package "} + name; | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
} | |||
ptr().package = name; | |||
ptr().p = pkg; | |||
ptr().last = &pkg->e; | |||
auto* end = &ptr().p->sections; | |||
auto* begin = end->next; | |||
init_begin_end(begin, end); | |||
} | |||
auto package::set(const char* key, const char* type) const -> section | |||
{ | |||
auto ctx = locked_context{}; | |||
auto tmp_ptr = uci_ptr{ptr()}; | |||
tmp_ptr.section = key; | |||
tmp_ptr.value = type; | |||
if (uci_set(ctx.get(), &tmp_ptr) != 0) { | |||
auto errmsg = std::string{"uci error: cannot set section "} + type + "'" + key + | |||
"' in package " + name(); | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
return section{ptr(), tmp_ptr.last}; | |||
} | |||
auto section::set(const char* key, const char* value) const -> option | |||
{ | |||
auto ctx = locked_context{}; | |||
auto tmp_ptr = uci_ptr{ptr()}; | |||
tmp_ptr.option = key; | |||
tmp_ptr.value = value; | |||
if (uci_set(ctx.get(), &tmp_ptr) != 0) { | |||
auto errmsg = std::string{"uci error: cannot set option "} + key + "'" + value + | |||
"' in package " + name(); | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
return option{ptr(), tmp_ptr.last}; | |||
} | |||
void section::del() | |||
{ | |||
auto ctx = locked_context{}; | |||
if (uci_delete(ctx.get(), &ptr()) != 0) { | |||
auto errmsg = std::string{"uci error: cannot delete section "} + name(); | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
} | |||
void option::del() | |||
{ | |||
auto ctx = locked_context{}; | |||
if (uci_delete(ctx.get(), &ptr()) != 0) { | |||
auto errmsg = std::string{"uci error: cannot delete option "} + name(); | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
} | |||
void item::rename(const char* value) const | |||
{ | |||
if (value == name()) { | |||
return; | |||
} | |||
auto ctx = locked_context{}; | |||
auto tmp_ptr = uci_ptr{ptr()}; | |||
if (tmp_ptr.last->type != UCI_TYPE_ITEM) { | |||
tmp_ptr.value = value; | |||
if (uci_set(ctx.get(), &tmp_ptr) != 0) { | |||
auto errmsg = std::string{"uci error: cannot rename item "} + name(); | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
return; | |||
} // else: | |||
tmp_ptr.value = tmp_ptr.last->name; | |||
if (uci_del_list(ctx.get(), &tmp_ptr) != 0) { | |||
auto errmsg = std::string{"uci error: cannot rename (del) "} + name(); | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
tmp_ptr.value = value; | |||
if (uci_add_list(ctx.get(), &tmp_ptr) != 0) { | |||
auto errmsg = std::string{"uci error: cannot rename (add) "} + value; | |||
throw uci_error(ctx.get(), errmsg.c_str()); | |||
} | |||
} | |||
} // namespace uci | |||
#endif |