Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / assets / linux-centos-7.svg
Author[]Andy Green <andy@warmcat.com> 2019-03-10 06:07 UTC
Committer[]Andy Green <andy@warmcat.com> 2019-03-18 10:59 UTC
Treea7a9772a4e02aea2e65212ec158a5ef675e69a08   Raw Patch
 
initial commit
initial commit
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8ce6a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.cproject +.project +build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b46a3bc --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,141 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SRCS_M + src/master/sai-master.c + src/master/conf.c + src/master/ws.c +) +set(SRCS_B + src/builder/sai-builder.c + src/builder/conf.c + src/builder/protocol.c +) + +set(LIB_DIR lib CACHE PATH "Install dir for libraries") +set(BIN_DIR bin CACHE PATH "Install dir for executables") +set(INCLUDE_DIR include CACHE PATH "Install dir for header files") +set(DATA_DIR share CACHE PATH "Install dir for data files") + +# +# let the code know where the assets were installed +# +set(CMAKE_C_FLAGS "-D LWS_DATA_DIR=\\\"${CMAKE_INSTALL_PREFIX}/${DATA_DIR}\\\" ${CMAKE_C_FLAGS}") + + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_ROLE_WS 1 requirements) +require_lws_config(LWS_WITHOUT_SERVER 0 requirements) +require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) +require_lws_config(LWS_WITH_UNIX_SOCK 1 requirements) + +if (requirements) + add_executable("sai-master" ${SRCS_M}) + add_executable("sai-builder" ${SRCS_B}) + + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror -Wundef ${CMAKE_C_FLAGS}" ) + endif() + + if (websockets_shared) + target_link_libraries("sai-master" websockets_shared) + add_dependencies("sai-master" websockets_shared) + + target_link_libraries("sai-builder" websockets_shared) + add_dependencies("sai-builder" websockets_shared) + else() + target_link_libraries("sai-master" websockets) + target_link_libraries("sai-builder" websockets) + endif() + +# +# libgit2 paths +# +find_path( GIT2_INC_PATH NAMES "git2.h") +find_library(GIT2_LIB_PATH NAMES "git2") + +if (GIT2_INC_PATH AND GIT2_LIB_PATH) + include_directories(BEFORE "${GIT2_INC_PATH}") +else() + message(FATAL_ERROR " Unable to find libgit2") +endif() + +target_link_libraries("sai-master" ${GIT2_LIB_PATH}) +target_link_libraries("sai-builder" ${GIT2_LIB_PATH}) + + install(TARGETS sai-master sai-builder + + LIBRARY DESTINATION "${LIB_DIR}${LIB_SUFFIX}" COMPONENT libraries + ARCHIVE DESTINATION "${LIB_DIR}${LIB_SUFFIX}" COMPONENT libraries + RUNTIME DESTINATION "${BIN_DIR}" COMPONENT libraries + PUBLIC_HEADER DESTINATION "${INCLUDE_DIR}" COMPONENT dev) + + install(FILES assets/index.html + assets/sai.css + assets/sai.js + assets/lws-common.js + assets/sai.svg + assets/favicon.ico + assets/strict-csp.svg + DESTINATION "${DATA_DIR}/sai/assets") + +endif() diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a9b677 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +![Sai](./assets/sai.svg) + +`Sai` (pronouced like 'sigh', "Trial" in Japanese) is a very lightweight +network-aware remote CI runner. It knows how to use `systemd-nspawn` and +`overlayfs` to build remote git repos on a variety of distro versions very +cheaply. + +On small targets, it can also build on the host rootfs directly, and can also +run on an RPi3-class 'buddy' to flash and collect results (eg, over serial, or +gpio) from even smaller targets. + +A self-assembling constellation of Sai Builders make their own connections to a +master, who then received hook notifications, distributes work concurrently over +idle builders that have the required environment, and logs results. + +## General approach + + - Sai builders for various platforms and scenarios are configured and run + independently... they maintain "nailed-up" connections to the master over + wss, and announce their capabilities. The builders do not need any listening + ports or other central management this way. + + - When a git repo being monitored is updated, a hook performs a GET notiftying + the Sai master. + + - The Sai master fetches `.sai.json` from the repo and queues jobs on connected + builders that have the platform and OS versions requested for build in + the `.sai.json`. + + - On the builders, result channels (like stdout, stderr) with logs and + results are first listed and then streamed back to the master and the build + proceeds. Discrete files may also be streamed to the master like this. + + - The master makes human readable current and historical results available + in realtime over https + + - The Sai master node also has a builder integrated. So to get started you + can do it all on one machine. + +## Creating a selfsigned TLS cert + +You can create a P-521 EC key and self-signed certificate for use with Sai +master like this: + +``` +$ openssl ecparam -name secp521r1 -genkey -param_enc explicit -out sai-selfsigned-key.pem +$ echo -e "\n\n\n\n\n\n" | openssl req -new -x509 -key sai-selfsigned-key.pem -out sai-selfsigned-cert.pem +``` + +There's a helper script `sudo ./scripts/gen-selfsigned-certs.sh` to do this and +install the results in `/usr/local/share/sai`, you can override the install +directory by giving it as a commandline argument to the script. + +Or alternatively you can use your own PEM certificate and key from a trusted +certifcate vendor, which matches your domain name. + +Self-signed is secure, but your browser will make you explicitly trust it before +it will let you use it. Afterwards, your browser will notice if the certificate +is different and untrusted for that site and warn you. Because you must +generate it after install, your selfsigned cert will be unique. + diff --git a/assets/404.html b/assets/404.html new file mode 100644 index 0000000..3e5a14b --- /dev/null +++ b/assets/404.html @@ -0,0 +1,9 @@ +<meta charset="UTF-8"> +<html> + <body> + <img src="libwebsockets.org-logo.svg"><br> + <h1>404</h1> + Sorry, that file doesn't exist. + </body> +</html> + diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100644 index 0000000..c0cc2e3 Binary files /dev/null and b/assets/favicon.ico differ diff --git a/assets/index.html b/assets/index.html new file mode 100644 index 0000000..11a3f7b --- /dev/null +++ b/assets/index.html @@ -0,0 +1,17 @@ +<html> + <head> + <meta charset=utf-8 http-equiv="Content-Language" content="en"/> + <link rel="stylesheet" type="text/css" href="sai.css"/> + <script type='text/javascript' src="lws-common.js"></script> + <script type='text/javascript' src='sai.js'></script> + + </head> + <body> + <table class="summary"><tr><td class="logo"> + <img src="sai.svg"><br> + </td><td class="summary" id="summary"> + + </td></tr></table> + </body> +</html> + diff --git a/assets/lws-common.js b/assets/lws-common.js new file mode 100644 index 0000000..5d56ca2 --- /dev/null +++ b/assets/lws-common.js @@ -0,0 +1,128 @@ +/* + * This section around grayOut came from here: + * http://www.codingforums.com/archive/index.php/t-151720.html + * Assumed public domain + * + * Init like this in your main html script, this also reapplies the gray + * + * lws_gray_out(true,{'zindex':'499'}); + * + * To remove the gray + * + * lws_gray_out(false); + * + */ + +function gsize(ptype) +{ + var h = document.compatMode === "CSS1Compat" && + !window.opera ? + document.documentElement.clientHeight : + document.body.clientHeight; + var w = document.compatMode === "CSS1Compat" && + !window.opera ? + document.documentElement.clientWidth : + document.body.clientWidth; + var pageWidth, pageHeight, t; + + if (document.body && + (document.body.scrollWidth || document.body.scrollHeight)) { + t = document.body.scrollWidth; + pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); + t = document.body.scrollHeight; + pageHeight = (h > t) ? ("" + h + "px") : ("" + (t) + "px"); + } else if (document.body.offsetWidth) { + t = document.body.offsetWidth; + pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); + t = document.body.offsetHeight; + pageHeight =(h > t) ? ("" + h + "px") : ("" + (t) + "px"); + } else { + pageWidth = "100%"; + pageHeight = "100%"; + } + return (ptype === 1) ? pageWidth : pageHeight; +} + +function addEvent( obj, type, fn ) { + if ( obj.attachEvent ) { + obj["e" + type + fn] = fn; + obj[type+fn] = function() { obj["e" + type + fn]( window.event );}; + obj.attachEvent("on" + type, obj[type + fn]); + } else + obj.addEventListener(type, fn, false); +} + +function removeEvent( obj, type, fn ) { + if ( obj.detachEvent ) { + obj.detachEvent("on" + type, obj[type + fn]); + obj[type + fn] = null; + } else + obj.removeEventListener(type, fn, false); +} + +function lws_gray_out(vis, _options) { + + var options = _options || {}; + var zindex = options.zindex || 50; + var opacity = options.opacity || 70; + var opaque = (opacity / 100); + var bgcolor = options.bgcolor || "#000000"; + var dark = document.getElementById("darkenScreenObject"); + + if (!dark) { + var tbody = document.getElementsByTagName("body")[0]; + var tnode = document.createElement("div"); + tnode.style.position = "absolute"; + tnode.style.top = "0px"; + tnode.style.left = "0px"; + tnode.style.overflow = "hidden"; + tnode.style.display ="none"; + tnode.id = "darkenScreenObject"; + tbody.appendChild(tnode); + dark = document.getElementById("darkenScreenObject"); + } + if (vis) { + dark.style.opacity = opaque; + dark.style.MozOpacity = opaque; + // dark.style.filter ='alpha(opacity='+opacity+')'; + dark.style.zIndex = zindex; + dark.style.backgroundColor = bgcolor; + dark.style.width = gsize(1); + dark.style.height = gsize(0); + dark.style.display = "block"; + addEvent(window, "resize", + function() { + dark.style.height = gsize(0); + dark.style.width = gsize(1); + } + ); + } else { + dark.style.display = "none"; + removeEvent(window, "resize", + function() { + dark.style.height = gsize(0); + dark.style.width = gsize(1); + } + ); + } +} + +/* + * end of grayOut related stuff + */ + +function new_ws(urlpath, protocol) +{ + if (typeof MozWebSocket != "undefined") + return new MozWebSocket(urlpath, protocol); + + return new WebSocket(urlpath, protocol); +} + +function lws_san(s) +{ + if (s.search("<") !== -1) + return "invalid string"; + + return s; +} diff --git a/assets/sai.css b/assets/sai.css new file mode 100644 index 0000000..c0be30b --- /dev/null +++ b/assets/sai.css @@ -0,0 +1,216 @@ + +span.title { + font-size:18pt; + font-family: Arial; + font-weight:normal; + text-align:center; + color:#000000; +} +span.mount { + font-size:10pt; + font-family: Arial; + font-weight:normal; + text-align:center; + color:#000000; +} +span.mountname { + font-size:14pt; + font-family: Arial; + font-weight:bold; + text-align:center; + color:#404010; +} +span.n { + font-size:12pt; + font-family: Arial; + font-weight:normal; + text-align:center; + color:#808020; +} +span.sn { + font-size:12pt; + font-family: Arial; + font-weight:bold; + text-align:center; + color:#808020; +} +span.v { + font-size:12pt; + font-family: Arial; + font-weight:bold; + text-align:center; + color:#202020; +} +span.m1 { + font-size:12pt; + font-family: Arial; + font-weight:bold; + text-align:center; + color:#202020; +} +span.m2 { + font-size:12pt; + font-family: Arial; + font-weight:normal; + text-align:center; + color:#202020; +} + +.browser { font-size:12pt; font-family: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; border-radius:10px;} +.group2 { vertical-align:middle; + text-align:center; + background:#f0f0e0; + padding:12px; + -webkit-border-radius:10px; + border-radius:10px; } +.explain { vertical-align:middle; + text-align:center; + background:#f0f0c0; padding:12px; + -webkit-border-radius:10px; + border-radius:10px; + color:#404000; + padding:3px; +} +td.wsstatus { vertical-align:middle; width:200px; height:50px; + text-align:center; + background:#f0f0c0; padding:6px; + -webkit-border-radius:8px; + border-radius:8px; + color:#404000; } +.tdform { vertical-align:middle; width:200px; height:50px; + text-align:center; + background:#f0f0d0; padding:6px; + -webkit-border-radius:8px; + margin:10px; + border-radius:8px; + border: 1px solid black; + border-collapse: collapse;font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#000000; + color:#404000; } + +td.l { vertical-align:middle; + text-align:center; + background:#d0d0b0; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; } + +td.bigger { font-size:120%; } + +div.bgw { background:white } +div.conninfo { + border: solid 2px #e0d040; + padding: 4px; + width: 500px; + height:350px; + overflow: auto; +} +span.f12 { font-size:12pt } + +.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; border-radius:10px; } +.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; border-radius:10px; } +.tabs { + position: relative; + min-height: 750px; /* This part sucks */ + clear: both; + margin: 25px 0; +} +.tab { + float: left; +} +.tab label { + background: #eee; + padding: 10px; + border: 1px solid #ccc; + margin-left: -1px; + position: relative; + left: 1px; +} +.tab [type=radio] { + display: none; +} +.content { + position: absolute; + top: 28px; + left: 0; + background: white; + right: 0; + bottom: 0; + padding: 20px; + border: 1px solid #ccc; +} +[type=radio]:checked ~ label { + background: white; + border-bottom: 1px solid white; + z-index: 2; +} +[type=radio]:checked ~ label ~ .content { + z-index: 1; +} + +td.wsstatus { vertical-align:middle; width:200px; height:50px; + text-align:center; + background:#f0f0c0; padding:6px; + -webkit-border-radius:8px; + border-radius:8px; + color:#404000; } +td.l { vertical-align:middle; + text-align:center; + background:#d0d0b0; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; } +td.dl { vertical-align:middle; + text-align:center; + background:#c0c0c0; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; } +td.c { vertical-align:middle; + text-align:center; + background:#c0c0a0; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; } +td.c0 { vertical-align:middle; + text-align:center; + background:#b0b090; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; } +td.dc0 { vertical-align:middle; + text-align:center; + background:#a0a0a0; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; } +td.c1 { vertical-align:middle; + text-align:center; + background:#c0c0c0; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; } +td.t { vertical-align:middle; + text-align:center; + background:#e0e0c0; + padding:3px; + -webkit-border-radius:3px; + border-radius:3px; +} + + +table.summary { + width: 100%; +} +td.logo { + margin: 16px; + margin-right: 32px; + padding: 16px; +} +td.summary { + background: #f8f8f8; + width: 100%; + margin: 16px; + padding: 16px; +} + + \ No newline at end of file diff --git a/assets/sai.js b/assets/sai.js new file mode 100644 index 0000000..59477d8 --- /dev/null +++ b/assets/sai.js @@ -0,0 +1,251 @@ +(function() { + +/* + * We display untrusted stuff in html context... reject anything + * that has HTML stuff in it + */ + +function san(s) +{ + if (s.search("<") !== -1) + return "invalid string"; + + return s; +} + +function humanize(s) +{ + var i = parseInt(s, 10); + + if (i >= (1024 * 1024 * 1024)) + return (i / (1024 * 1024 * 1024)).toFixed(3) + "Gi"; + + if (i >= (1024 * 1024)) + return (i / (1024 * 1024)).toFixed(3) + "Mi"; + + if (i > 1024) + return (i / 1024).toFixed(3) + "Ki"; + + return s; +} + + var pos = 0; + +function get_appropriate_ws_url() +{ + var pcol; + var u = document.URL; + + /* + * We open the websocket encrypted if this page came on an + * https:// url itself, otherwise unencrypted + */ + + if (u.substring(0, 5) === "https") { + pcol = "wss://"; + u = u.substr(8); + } else { + pcol = "ws://"; + if (u.substring(0, 4) === "http") + u = u.substr(7); + } + + u = u.split("/"); + + return pcol + u[0]; +} + + +var sai, jso, s; + +function ws_open_server_status() +{ + sai = new WebSocket(get_appropriate_ws_url() + "/browse", "com-warmcat-sai"); + + console.log(get_appropriate_ws_url() + "/browse"); + + try { + sai.onopen = function() { + // document.getElementById("title").innerHTML = "Server Status (Active)"; + lws_gray_out(false); + }; + + sai.onmessage =function got_packet(msg) { + var u, ci, n; + console.log(msg.data); + if (msg.data.length < 100) + return; + jso = JSON.parse(msg.data); + u = parseInt(san(jso.i.uptime), 10); + + if (parseInt(jso.i.contexts[0].deprecated, 10) === 0) + s = "<table><tr><td></td><td class=\"c0\">"; + else + s = "<table><tr><td></td><td class=\"dc0\">"; + s += + "Server</td><td>" + + "<span class=\"sn\">Server Version:</span> <span class=\"v\">" + + san(jso.i.version) + "</span><br>" + + "<span class=\"sn\">Host Uptime:</span> <span class=\"v\">" + + ((u / (24 * 3600)) | 0) + "d " + + (((u % (24 * 3600)) / 3600) | 0) + "h " + + (((u % 3600) / 60) | 0) + "m</span>"; + if (jso.i.l1) + s = s + ", <span class=\"sn\">Host Load:</span> <span class=\"v\">" + san(jso.i.l1) + " "; + if (jso.i.l2) + s = s + san(jso.i.l2) + " "; + if (jso.i.l3) + s = s + san(jso.i.l3); + if (jso.i.l1) + s =s + "</span>"; + + if (jso.i.statm) { + var sm = jso.i.statm.split(" "); + s += ", <span class=\"sn\">Virt stack + heap Usage:</span> <span class=\"v\">" + + humanize(parseInt(sm[0], 10) * 4096) + "B</span>"; + } + + for (n = 0; n < jso.files.length; n++) { + s += "<br><span class=n>" + san(jso.files[n].path) + ":</span><br> " + san(jso.files[n].val); + } + s += "</td></tr>"; + + for (ci = 0; ci < jso.i.contexts.length; ci++) { + + if (parseInt(jso.i.contexts[ci].deprecated, 10) === 0) + s += "<tr><td></td><td class=\"c\">" + + "Active Context</td><td>"; + else + s += "<tr><td></td><td class=\"c1\">" + + "Deprecated Context " + ci + "</td><td>"; + + u = parseInt(san(jso.i.contexts[ci].context_uptime), 10); + s += "<span class=n>Server Uptime:</span> <span class=v>" + + ((u / (24 * 3600)) | 0) + "d " + + (((u % (24 * 3600)) / 3600) | 0) + "h " + + (((u % 3600) / 60) | 0) + "m</span>"; + + s = s + + "<br>" + + "<span class=n>Listening wsi:</span> <span class=v>" + san(jso.i.contexts[ci].listen_wsi) + "</span>, " + + "<span class=n>Current wsi alive:</span> <span class=v>" + (parseInt(san(jso.i.contexts[ci].wsi_alive), 10) - + parseInt(san(jso.i.contexts[ci].listen_wsi), 10)) + "</span><br>" + + "<span class=n>Total Rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].rx)) +"B</span>, " + + "<span class=n>Total Tx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].tx)) +"B</span><br>" + + + "<span class=n>CONNECTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].h1_conn) +"</span>, " + + "<span class=n>Websocket:</span> <span class=v>" + san(jso.i.contexts[ci].ws_upg) +"</span>, " + + "<span class=n>H2 upgrade:</span> <span class=v>" + san(jso.i.contexts[ci].h2_upg) +"</span>, " + + "<span class=n>H2 ALPN:</span> <span class=v>" + san(jso.i.contexts[ci].h2_alpn) +"</span>, " + + "<span class=n>Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].rejected) +"</span><br>" + + + "<span class=n>TRANSACTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].h1_trans) + "</span>, " + + "<span class=n>H2:</span> <span class=v>" + san(jso.i.contexts[ci].h2_trans) +"</span>, " + + "<span class=n>Total H2 substreams:</span> <span class=v>" + san(jso.i.contexts[ci].h2_subs) +"</span><br>" + + + "<span class=n>CGI: alive:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_alive) + "</span>, " + + "<span class=n>spawned:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_spawned) + + "</span><table>"; + + for (n = 0; n < jso.i.contexts[ci].pt.length; n++) { + + if (parseInt(jso.i.contexts[ci].deprecated, 10) === 0) + s += "<tr><td>&nbsp;&nbsp;</td><td class=\"l\">service thread " + (n + 1); + else + s += "<tr><td>&nbsp;&nbsp;</td><td class=\"dl\">service thread " + (n + 1); + s += "</td><td>" + + "<span class=n>fds:</span> <span class=v>" + san(jso.i.contexts[ci].pt[n].fds_count) + " / " + + san(jso.i.contexts[ci].pt_fd_max) + "</span>, "; + s = s + "<span class=n>ah pool:</span> <span class=v>" + san(jso.i.contexts[ci].pt[n].ah_pool_inuse) + " / " + + san(jso.i.contexts[ci].ah_pool_max) + "</span>, " + + "<span class=n>ah waiting list:</span> <span class=v>" + san(jso.i.contexts[ci].pt[n].ah_wait_list); + + s = s + "</span></td></tr>"; + + } + for (n = 0; n < jso.i.contexts[ci].vhosts.length; n++) { + if (parseInt(jso.i.contexts[ci].deprecated, 10) === 0) + s += "<tr><td>&nbsp;&nbsp;</td><td class=\"l\">vhost " + (n + 1); + else + s += "<tr><td>&nbsp;&nbsp;</td><td class=\"dl\">vhost " + (n + 1); + s += "</td><td><span class=\"mountname\">"; + if (jso.i.contexts[ci].vhosts[n].use_ssl === "1") + s = s + "https://"; + else + s = s + "http://"; + s = s + san(jso.i.contexts[ci].vhosts[n].name) + ":" + + san(jso.i.contexts[ci].vhosts[n].port) + "</span>"; + if (jso.i.contexts[ci].vhosts[n].sts === "1") + s = s + " (STS)"; + s = s +"<br>" + + + "<span class=n>Total Rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].rx)) +"B</span>, " + + "<span class=n>Total Tx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].tx)) +"B</span><br>" + + + "<span class=n>CONNECTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h1_conn) +"</span>, " + + "<span class=n>Websocket:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].ws_upg) +"</span>, " + + "<span class=n>H2 upgrade:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_upg) +"</span>, " + + "<span class=n>H2 ALPN:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_alpn) +"</span>, " + + "<span class=n>Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].rejected) +"</span><br>" + + + "<span class=n>TRANSACTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h1_trans) + "</span>, " + + "<span class=n>H2:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_trans) +"</span>, " + + "<span class=n>Total H2 substreams:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_subs) +"</span><br>"; + + if (jso.i.contexts[ci].vhosts[n].mounts) { + s = s + "<table><tr><td class=t>Mountpoint</td><td class=t>Origin</td><td class=t>Cache Policy</td></tr>"; + + var m; + for (m = 0; m < jso.i.contexts[ci].vhosts[n].mounts.length; m++) { + s = s + "<tr><td>"; + s = s + "<span class=\"m1\">" + san(jso.i.contexts[ci].vhosts[n].mounts[m].mountpoint) + + "</span></td><td><span class=\"m2\">" + + san(jso.i.contexts[ci].vhosts[n].mounts[m].origin) + + "</span></td><td>"; + if (parseInt(san(jso.i.contexts[ci].vhosts[n].mounts[m].cache_max_age), 10)) + s = s + "<span class=n>max-age:</span> <span class=v>" + + san(jso.i.contexts[ci].vhosts[n].mounts[m].cache_max_age) + + "</span>, <span class=n>reuse:</span> <span class=v>" + + san(jso.i.contexts[ci].vhosts[n].mounts[m].cache_reuse) + + "</span>, <span class=n>reval:</span> <span class=v>" + + san(jso.i.contexts[ci].vhosts[n].mounts[m].cache_revalidate) + + "</span>, <span class=n>inter:</span> <span class=v>" + + san(jso.i.contexts[ci].vhosts[n].mounts[m].cache_intermediaries); + s = s + "</span></td></tr>"; + } + s = s + "</table>"; + } + s = s + "</td></tr>"; + } + + s += "</table></td></tr>"; + + } // context + s = s + "</table>"; + + document.getElementById("conninfo").innerHTML = s; + }; + + sai.onclose = function(){ +// document.getElementById("title").innerHTML = "Server Status (Disconnected)"; + lws_gray_out(true,{"zindex":"499"}); + + myVar = setTimeout(ws_open_server_status, 3000); + }; + } catch(exception) { + alert("<p>Error" + exception); + } +} + +/* stuff that has to be delayed until all the page assets are loaded */ + +window.addEventListener("load", function() { + + lws_gray_out(true,{"zindex":"499"}); + + ws_open_server_status(); + +}, false); + +}()); + diff --git a/assets/sai.svg b/assets/sai.svg new file mode 100644 index 0000000..bf913b2 --- /dev/null +++ b/assets/sai.svg @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="78.646mm" height="38.001mm" version="1.1" viewBox="0 0 78.646 38.001" xmlns="http://www.w3.org/2000/svg"> + <g transform="translate(195.5 -47.682)"> + <g> + <g transform="matrix(.70522 0 0 .70522 -57.63 14.056)" dominant-baseline="auto" stroke-width="4.2629" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" aria-label="Sai"> + <path d="m-122.89 79.46q0 5.6894-2.8864 8.7146-2.8586 3.0251-8.2705 3.0251-5.3009 0-9.0199-2.0815v-7.4657q4.5516 2.9696 8.3538 2.9696 2.1648 0 3.2472-1.1934 1.1101-1.1934 1.1101-3.2471 0-2.026-1.3044-3.6912-1.3044-1.693-4.3018-3.7467-2.6088-1.804-3.9687-3.4414-1.3322-1.6652-2.0815-3.719-0.72159-2.0538-0.72159-4.7736 0-5.2176 2.8864-8.2705 2.9141-3.0529 7.7987-3.0529 4.6626 0 8.9644 2.6088l-2.4978 6.0502q-1.3322-0.7771-2.8031-1.3877-1.4432-0.61058-3.1084-0.61058-1.8317 0-2.8308 1.2212-0.97136 1.1934-0.97136 3.2749 0 2.0538 1.1656 3.5802 1.1934 1.5264 4.1353 3.4692 3.9687 2.6643 5.5229 5.4397 1.582 2.7753 1.582 6.3278z"/> + <path d="m-103.13 90.645-1.4709-4.2185h-0.16652q-1.6374 2.7198-3.3859 3.7467-1.7485 1.0269-4.4128 1.0269-3.4137 0-5.3842-2.5533-1.9427-2.5533-1.9427-7.1049 0-4.8846 2.6643-7.2436 2.6921-2.3868 8.0207-2.6366l4.1075-0.16652v-2.2203q0-4.3295-3.7467-4.3295-2.7753 0-6.3833 2.1648l-2.5533-5.1066q4.6071-2.9141 10.019-2.9141 4.9124 0 7.4657 2.8308 2.5811 2.8031 2.5811 7.9652v20.76zm-6.411-5.2176q2.026 0 3.2194-1.804 1.2212-1.8317 1.2212-4.8568v-2.6088l-2.2758 0.11101q-2.5256 0.13877-3.719 1.4432-1.1656 1.3044-1.1656 3.8855 0 3.83 2.7198 3.83z"/> + <path d="m-84.312 90.645h-7.3824v-30.917h7.3824zm-7.6877-38.994q0-1.8317 1.0546-2.8864 1.0824-1.0824 2.9974-1.0824 1.804 0 2.8864 1.0824 1.0824 1.0546 1.0824 2.8864 0 1.8872-1.1101 2.9419-1.1101 1.0269-2.8586 1.0269-1.8317 0-2.9419-1.0269-1.1101-1.0546-1.1101-2.9419z"/> + </g> + <path d="m-165.8 63.781a14.486 14.486 0 0 1-14.486 14.486 14.486 14.486 0 0 1-14.486-14.486 14.486 14.486 0 0 1 14.486-14.486 14.486 14.486 0 0 1 14.486 14.486z" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4552"/> + <g transform="matrix(1.6615 0 0 1.6615 566.58 -162.55)" dominant-baseline="auto" fill="#fff" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" aria-label="裁"> + <path d="m-449.06 137.17 0.72 0.73333q-0.36 0.36-0.62667 0.6-0.26666 0.24-0.52 0.42667 0.2 0.18666 0.41334 0.36 0.22666 0.16 0.46666 0.34666l-1.0933 0.93334q-0.21333-0.2-0.41333-0.4t-0.38667-0.38667l0.34667 1.12q-1.04 0.36-1.9867 0.64t-1.9733 0.58667l-0.42667-1.3733q0.28-0.0667 0.53333-0.12 0.26667-0.0533 0.53334-0.12v-0.49333q0-0.13333-0.0133-0.26667 0-0.13333 0.0133-0.26666-0.18667 0.16-0.38667 0.32t-0.41333 0.30666l-0.8-1.2267q0.73333-0.46667 1.1733-0.86667 0.45333-0.4 0.78667-0.85333h-1.4667v-1.3733h2.1867v-0.73333h-2.2133v-1.4h2.3067v-0.84h-1.92v-1.3467h1.92v-1.3067h1.48v1.3067h1.8v1.3467h-1.8v0.84h2.4533q-0.0267-0.96-0.04-1.84 0-0.89333-0.0133-1.6533h1.3333q0 0.89333 0.0133 1.7733t0.0533 1.72h3.1467v1.4h-3.0533q0.0533 0.64 0.12 1.2267 0.08 0.58666 0.18667 1.1467 0.30667-0.53333 0.56-1.0933 0.25333-0.57333 0.48-1.2l1.0533 0.73333q-0.34666 0.89334-0.76 1.6933-0.4 0.8-0.89333 1.52 0.0533 0.14666 0.0933 0.28 0.0533 0.12 0.12 0.24 0.0533 0.13333 0.13334 0.28 0.08 0.14666 0.16 0.26666 0.0933 0.12 0.17333 0.2 0.0933 0.08 0.17333 0.08 0.0667 0 0.12-0.12t0.08-0.30666q0.04-0.21334 0.0667-0.48 0.04-0.28 0.08-0.61334l1.2133 0.94667q-0.04 0.4-0.16 0.8-0.10666 0.41333-0.28 0.73333-0.17333 0.33334-0.41333 0.54667t-0.54667 0.21333q-0.49333 0-0.89333-0.28-0.38667-0.28-0.70667-0.73333-0.24-0.32-0.45333-0.76-1.0667 1.12-2.4933 1.9067l-0.68-1.16q0.82666-0.46667 1.48-0.98667 0.66666-0.53333 1.2-1.16-0.24-0.88-0.37334-1.88-0.13333-1-0.2-2.04h-2.64v0.73333h2.4667v1.3733zm3.4933-3.6267q-0.14666-0.33333-0.26666-0.6t-0.26667-0.52q-0.13333-0.26667-0.30667-0.53333-0.16-0.26667-0.4-0.61334l1.12-0.62666q0.26667 0.34666 0.44 0.61333 0.18667 0.26667 0.32 0.52 0.14667 0.25333 0.25334 0.50667 0.12 0.25333 0.25333 0.57333zm-5.0133 6.1467q-0.81333-0.85334-1.4533-1.6933-0.08 0.10667-0.12 0.14667v2.0267q0.73333-0.18667 1.5733-0.48zm0.25333-1.6q0.21334-0.17334 0.41334-0.38667t0.46666-0.53333h-1.6133q0.18667 0.25333 0.36 0.48 0.18667 0.21333 0.37333 0.44z" fill="#fff"/> + </g> + </g> + <g transform="matrix(.70522 0 0 .70522 -174.25 113.4)"> + <path d="m-4.5685-49.308 0.56358-0.22543 0.037614-1.5405 1.0144-0.41329-0.52601-1.6156 1.315-3.2312-0.45087-1.6907 0.45087-1.8786 0.71387-0.56358 0.4133-0.07515-0.52601-2.367 0.37572-3.1936 1.0896-1.1272 0.63873-0.90173 0.90173-2.4798 1.5405-1.6532 3.2688-1.578 2.7803-1.9538 1.578-0.78902v-1.2023l0.26301-1.6907 0.33815-0.45086-0.63873-1.3526 0.15028-0.86416-2.1792-2.2168-0.33815-0.71387-0.86417-0.48844-0.22543-0.86416-0.30057-0.33815 0.56358-0.41329 0.63873 0.15029 0.075149-0.6763 0.60116-0.263 0.18786-0.71387 0.56359-0.52601 0.22543-0.60115 0.71387 0.07515 0.26301-0.78902 0.45087-0.26301 0.63872 0.18786 0.48844-0.11272 0.22543-0.63873 0.4133-0.30057 0.37572 0.263 0.48844 0.03753 0.56358-0.22544 0.18786-0.30057 0.90173 0.03754 0.45086 0.22543 0.60115-0.18786 0.78902 0.03754 0.07515 0.263 0.18786 0.22544 0.37572-0.03753 0.63873-0.11272 0.48844 0.71387 0.60116 0.07515 0.37572-0.15029 0.22543 0.48844 0.37572 0.18786 0.4133 0.11272 0.18786 1.0144 0.26301 1.5405-0.11272 2.4046-0.78902 1.0144v0.86416l-0.6763 1.7659 0.56359 1.315 0.22543 1.7659 0.07515 1.6532-0.22543 1.0144 0.9393 1.315 1.315 0.78902 1.9913 1.6907 1.578 2.8179 0.78902 1.9162 0.56359 1.5029 2.1792 6.237 2.7428 2.104 1.315 1.052 1.2399 0.56358 1.7659 1.9913 1.2399 0.90173-0.52601 1.1272-3.1185 1.841 0.07515 0.75144 0.63873 0.03757 7.4393 8.5289-32.876 0.9393-2.0289-1.6907-5.4855-6.3873-6.9884-1.578" fill="#fff"/> + <g> + <g stroke="#000" stroke-width=".2104px"> + <path d="m10.362-76.843c-0.6376 0.593-1.2667 1.2086-2.0611 1.5899-0.85296 0.55731-1.7577 1.026-2.7144 1.3777-1.1515 0.44703-2.249 1.0205-3.3133 1.6421-0.44533 0.53673-0.99887 1.0336-1.2932 1.6752-0.27915 0.68845-0.62957 1.3669-0.64093 2.1259-0.1947 1.4012-0.14246 2.8215-0.082576 4.2303 0.21842 0.95772-0.75295 0.07728-0.58564-0.23317-0.052087 0.6363-0.11151 1.2852 0.11339 1.8983 0.059086 0.7035 0.39732 1.3156 0.76201 1.9026 0.25979 0.48237 0.42621-0.49485 0.049861-0.64401-0.42246-0.60127-0.55769-1.3367-0.75556-2.0298-0.28092-0.84004 0.83193 0.51232 0.83761-0.33999-0.14071-1.1203-0.16543-2.2511-0.21885-3.3779 0.21525-0.83166 0.14769-1.7681 0.62576-2.506 0.50937-0.79208 0.81385-1.7764 1.6779-2.2591 0.6644-0.5814 1.4435-0.98426 2.245-1.3397 1.5925-0.75491 3.1215-1.6375 4.5613-2.6539 0.30997-0.30064 0.52139-0.71619 0.79279-1.0584z"/> + <path d="m0.18632-67.864c-0.81026 0.84595-1.4436 1.8796-1.6735 3.0386-0.51902 1.9602 0.011849 4.0897 1.1234 5.7458 0.31657 0.3763 0.14313 1.6027-0.34404 0.82917-0.2199-0.75017 0.13205-1.7018-0.51972-2.2924-0.62031-0.5222-1.0577 0.40297-1.1589 0.91316-0.19707 0.6876-0.41149 1.4066-0.21395 2.1225 0.20041 0.84804 0.5089 1.6733 0.97565 2.4113 0.26194 0.48086 0.48044 1.0205 0.42332 1.579-0.50432-0.21529-0.23982-1.2098-0.58861-1.3279-0.27578 0.69922-0.29345 1.4961-0.18148 2.2345 0.12681 0.54578 0.38604 1.3016 1.05 1.329 0.76984-0.02128 1.1958-0.78769 1.5335-1.3778 0.72437-1.0482 1.2687-2.2169 1.6088-3.4431 0.10005-0.36142 0.69729-0.98271 0.51575-1.1804-0.7836 0.46497-0.70296 1.4961-1.0702 2.2268-0.42334 1.2014-1.1749 2.2721-2.0357 3.195-0.58201 0.42709-1.3724-0.18352-1.3453-0.83802-0.087069-0.26578-0.22817-1.2234 0.1359-1.1088 0.063778 0.36351 0.36992 1.1657 0.80336 0.65461 0.05495-0.56818-0.36219-1.0758-0.51997-1.6121-0.38228-0.98299-1.0321-1.8824-1.0801-2.9702-0.079364-0.85336 0.080071-1.7484 0.55252-2.4735 0.63943-0.35808 0.95492 0.51504 0.89488 1.0204-0.18909 0.69113 0.085575 1.3851 0.64934 1.8235 0.44087 0.44464 0.37855-0.703 0.34219-1.0142-0.091515-0.8379-0.70685-1.473-0.94405-2.2637-0.43246-1.196-0.64799-2.5185-0.39039-3.7755 0.30779-1.2122 0.74608-2.4085 1.4575-3.4458z"/> + <path d="m-2.311-56.386c-0.60146 1.04-1.1795 2.1313-1.3514 3.3358 0.12534 0.5234 0.20219 1.0726 0.52979 1.5188 0.1333 0.25903 0.53914 0.91641 0.49559 0.91695-0.31121-0.31471-0.5362-1.3919-1.0943-1.0112-0.37426 0.36987-0.53485 0.84441-0.40114 1.356 0.094346 0.37018-0.31806 1.3119 0.38552 1.0114 0.088955-0.48009-0.036024-0.99492 0.05654-1.49-0.050657-0.42712 0.3909-0.80055 0.59223-0.26536 0.23558 0.27655 0.60697 0.891 0.74693 0.9615-0.04167-0.49354-0.23208-0.94639-0.50273-1.3562-0.36782-0.51652-0.4449-1.1581-0.41411-1.7739 0.1508-1.1097 0.55654-2.1644 0.95708-3.2037z"/> + <path d="m1.5678-60.557c1.1022-1.2256 3.0919-1.932 4.6303-1.1525 1.3659 0.85271-0.54293 1.4916-0.93294 2.2761-0.90662 1.1268-0.91908 2.6208-1.1632 3.9833-0.20937 1.3352-0.33617 2.6847-0.10331 4.0274 0.33043 0.925-0.43678 1.8083-1.2909 1.1009 0.42668-0.39292 1.4893-0.25174 1.1496-1.2612-0.38344-2.3073 0.12918-4.6362 0.56823-6.8992 0.26637-0.77922 1.5935-2.0385 1.5693-2.182-0.994 0.53559-1.5711 1.6135-2.0908 2.579-1.0689 2.5699-1.3742 5.3949-1.3554 8.1585 0.48739 0.11492 2.3884-0.06136 1.4636 0.67869-0.62676-0.1629-2.1187 0.24074-2.1601-0.21389 0.59769-1.3403 0.15561-2.8717 0.56796-4.2699 0.26798-1.8623 0.92779-3.6612 1.7769-5.3334 0.31033-0.98244 1.6057-1.3924 1.8238-2.2665-1.0433-0.73893-2.4494-0.27516-3.5043 0.22784-0.3286 0.15951-0.64567 0.34267-0.94855 0.54681z"/> + <path d="m21.228-74.931c1.2521 1.6422 3.8565 1.934 4.3543 4.2342 0.86907 2.0943 2.0102 4.0694 2.5779 6.2768 0.52362 1.4919 0.90865 3.0742 1.6626 4.4573 1.3659 1.0954 2.4067 2.4233 3.8965 3.4146 1.668 0.5471 2.8014 1.833 3.9259 3.1178 1.1368 0.357 1.9138 2.1614 0.11136 1.6355-3.0916-0.11293-6.3276-1.0599-9.3341 0.03857-1.7099 0.46646-1.9685 2.3293-3.0544 3.4025-1.1952-0.66559-2.4806-2.5143-2.0756-4.079-0.15126-1.0739 2.0214-3.3815 2.1548-1.6891-1.8428 0.16934-2.2261 2.6364-1.2496 3.9094 0.58909 1.409 1.5587 0.6725 1.7415-0.45225 2.0893-2.1539 5.3451-2.2401 8.1382-1.8891 1.0773-0.05969 3.4863 0.65921 3.7067-0.08377-1.2467-1.1783-2.1159-2.8625-3.9209-3.3078-0.71054-0.42339-4.7117-1.4212-3.563-1.1937 0.98762 0.21534 1.9752 0.43069 2.9629 0.64604-1.3695-1.3789-3.3164-2.147-4.6951-3.3235-0.34356-0.70868-1.3731-3.5053-0.49569-1.4653 0.07237 0.72896 1.5021 2.7399 1.3868 1.0072-0.80616-2.2107-1.573-4.4398-2.3597-6.6595-0.7712 1.0314 0.15839 2.5745-0.2694 3.6001 0.91192-1.3463 0.31563 1.7258 0.19603 2.3335-0.91726 2.2674-2.9704 3.8162-4.0867 5.9716-1.0233-1.3416 0.25936-4.0824 0.78813-5.8017 0.89423-3.2018 0.51224-6.5781 0.12216-9.8295 0.75147 3.1652 0.99733 6.5285 0.29291 9.7213-0.59913 1.6594-1.6277 3.2972-1.4012 5.1287 1.2469-1.0146 2.4551-3.0605 3.5133-4.5868 1.0181-1.2156 0.55477-2.2629-0.24606-1.296 0.947-2.5886 0.78068-5.4999-0.41629-7.9768-0.55244-1.6308-1.6098-3.0554-3.1387-3.8854-0.45166-0.38892-1.2634-0.65753-1.2256-1.3758z"/> + <path d="m10.601-58.007c-1.0804 1.5941-2.1608 3.1881-3.2412 4.7822 0.82316 0.11042 0.94795-1.3626 1.5673-1.7999 0.44547-0.56234 1.0952-1.8444 1.4136-1.959 0.0528 1.8435-0.0233 3.6987 0.26735 5.5259 0.09811 0.66218-0.38044 2.4467-1.1878 1.7401-0.06839-0.97395 0.65888-1.8342 0.43531-2.8262-0.034911-1.1303-0.069746-2.2605-0.10467-3.3908-0.47609 0.7032-0.14728 1.5981-0.27093 2.3858 0.016143 1.0212 0.022426 2.0563-0.31352 3.034-1.0497-0.2733-0.026958-1.8734-0.31852-2.7034-0.046361-0.48496 0.034911-1.7945-0.35679-0.74935-0.28965 0.54435 0.18643 1.926-0.38783 1.968-0.38554-0.87873-1.094-1.6589-1.3904-2.542 0.22764-1.7503 0.20538-3.5241 0.1787-5.2834 0.043896-0.75864-0.55411-1.0391-0.98571-0.34173-0.56455 0.34821 0.59085 0.81491 0.15105 1.4404-0.074672 0.50785-0.069742 1.7049-0.13389 1.749-0.24849-0.67948 0.50507-2.1155-0.5117-2.0955-0.68319 0.04539-0.9726 1.0898-0.025367 0.76567 0.69374-0.72623-0.10487 0.73149-0.50592 0.02356-0.6542-0.06704-0.25865 1.264-0.52775 1.7579-0.18648 1.4679-0.44832 2.9449-0.46706 4.4217 0.15632 0.89019 0.012247 1.9469 0.52706 2.7057 0.026402-1.3009-0.26216-2.6042-0.19485-3.907 0.14418-1.1775 0.28837-2.355 0.43255-3.5325 0.12729-0.54847 1.2649-1.817 0.87689-0.55609-0.44511 0.91935-0.2545 2.0398-0.81568 2.8725 0.13014 1.7872 0.26027 3.5744 0.39041 5.3616 0.026561-1.718 0.053122-3.4361 0.079705-5.1541 0.20476 1.3182 0.58814 2.609 0.8716 3.897-0.14381 0.41744 0.13809 1.8904 0.2029 0.80265-0.029741-0.95058-0.00795-1.9267-0.42193-2.8082-0.52522-1.1977-0.19336-2.5198-0.23672-3.7794-0.11788-1.0363 0.85498 1.4745 0.6937 0.0344 0.037535-1.171 0.075069-2.3421 0.1126-3.5131 0.68349 0.13125 0.16234 1.4747 0.31586 2.0782-0.043817 1.0619-0.11555 2.1243-0.27293 3.176 0.48068 1.1214 1.3335 2.0487 1.8073 3.1737 0.41138 0.60886-0.18048 2.0516 0.907 1.9433 0.73407 0.18613 1.6904 0.16659 1.5724-0.8561 0.37634-1.2828-0.05892-2.5932-0.06975-3.8933-0.09059-1.3143-0.03443-2.632-0.06227-3.948z"/> + </g> + <path d="m31.855-55.589c0.46866 0.27651 1.405 0.53761 1.4244 1.0557-0.6002 0.21825-1.1888-0.20853-1.7968-0.18897-0.63351-0.23262-1.615 0.40019-0.90335 0.99996 0.65986 0.61161 1.6384 0.42649 2.4601 0.49316 0.86268 0.01401 1.7255-4.21e-4 2.5882 0.0047 0.29536 0.25905 1.1507 0.01039 1.1243-0.07158-1.2914-0.30985-2.6271-0.27913-3.9442-0.25498-0.64188-0.04423-1.2943-0.2016-1.8605-0.50911-0.31977-0.58514 0.77638-0.273 1.0328-0.07476 0.55992 0.15922 1.7815 0.33235 1.8147-0.51458-0.05249-0.53306-0.65843-0.58766-1.0415-0.8013-0.2688-0.08516-0.52712-0.34771-0.81174-0.16477l-0.08634 0.02657z"/> + <g stroke="#000" stroke-width=".2104px"> + <path d="m5.2076-71.61 0.47821 2.0988 0.71733 1.966 1.1158 1.5675 0.053122 0.02657 0.31881-0.21254-1.6206-2.4708-0.63762-1.9926z"/> + <path d="m10.92-62.789 1.2487 0.77046 0.95644 0.39851 0.31881-0.39851-1.2487-0.53135-1.4081-0.87673z"/> + <path d="m22.237-65.472c-0.36018-0.1089-0.74695-0.3089-1.0764-0.03301-0.38657 0.09546-0.43059 0.32102-0.22246 0.66153 0.23539 0.826 0.50666 1.6406 0.86622 2.4212 0.34945 0.93258 0.64118 1.9217 1.2766 2.7066 0.29974 0.1007-0.15872-0.65782-0.20282-0.89787-0.23578-0.64567-0.60087-1.252-0.78697-1.9086-0.10792-0.71082-0.30274-1.4333-0.1154-2.15 0.04716-0.27706 0.11657-0.55597 0.26121-0.79983z"/> + <path d="m2.1523-47.593 7.0935 7.9968 0.47821 0.18597 33.156-1.0627-7.9171-8.6876-1.1424 0.05314 1.0096 0.10627 7.4123 8.2625-32.28 0.85016 0.66419-0.29224 0.10627-0.55792-0.58448-0.66419-0.53135-0.50478-5.6589-6.2434-0.95643-0.23911-0.50479 0.26568-0.18597 0.45165 0.45165-0.45165 0.77046 0.02657 0.34538 0.26567 6.084 6.7216-0.31881-0.05314-0.63762 0.31881 0.053122 0.66419z"/> + <path d="m3.9589-48.15 20.776-0.37195 0.42508 0.45165-20.723 0.39851z"/> + <path d="m7.0939-69.909 0.37195-0.26568 0.42507 0.13284 0.25079 0.74428-0.76265 0.33856 0.19104 0.47651 0.74479-0.3324 0.39963 1.2173-0.10627 0.34538 0.34538 0.58449-0.45165 0.05313-0.42507-0.05313-1.0361-2.9224z"/> + <path d="m9.1927-63.985 1.3815 3.8789 0.31881-0.02657 0.2391-0.18597-1.169-3.6397-0.55792-0.58449 0.10627 0.74389z"/> + <path d="m6.2171-61.753c-0.16891-0.40905-0.47081-0.7817-0.46122-1.2421-0.094576-0.4899 0.47562-0.775 0.44275-1.2485 0.044055-0.43146 0.40462-0.75373 0.78891-0.91004 0.11172-0.54615 0.58351-0.84258 1.0029-1.0926 0.018211-0.47036 0.36852-0.39281 0.72065-0.31217 0.2579 0.06633 0.43404 0.07125 0.23247-0.20756-0.067037-0.17818-0.40219-0.79636-0.10295-0.37361 0.24537 0.2948 0.43435 0.75249 0.87672 0.47822 0.31972-0.04036 0.21434 0.57947 0.45727 0.77789 0.24102 0.28096 0.25767 0.64678 0.30782 0.99516 0.07944 0.48383 0.107 0.97337 0.14484 1.4615-0.2966 0.09917-0.43825-0.10236-0.54889-0.33071-0.1447-0.23514-0.28941-0.47028-0.43411-0.70542-0.5092-0.38986 0.16707 0.84354-0.35204 0.43172-0.15728 0.08386 0.126 0.62972-0.00477 0.90512-0.030616 0.43413-0.106 0.85805-0.39831 1.1988-0.11628 0.05612-0.18203-0.50362-0.19311-0.70058-0.021471-0.14297-0.016938-0.73582-0.069105-0.31507-0.21695 0.41641 0.14658 1.2185-0.39734 1.304-0.43335-0.35406-0.099284-0.94736-0.32176-1.4001-0.020994-0.28655-0.27905-0.34096-0.16567-7e-3 0.081598 0.3718 0.18597 0.84263-0.13001 1.1365-0.12723 0.15268-0.31938 0.46828-0.22612 0.09387-0.03992-0.25422 0.24064-0.90945-0.23058-0.7675-0.18094 0.01085-0.74234-0.16117-0.72832-0.08871 0.25637 0.2076 0.77543 0.24222 0.86713 0.51922-0.022585 0.24836-0.045169 0.49671-0.067754 0.74506 0.29451 0.33343 0.49621-0.70642 0.76138-0.33911 0.13586 0.15731 0.27172 0.31462 0.40759 0.47194 0.097415-0.14169 0.19483-0.28339 0.29225-0.42508 0.62882 0.22426 0.73622-0.70506 0.76187-1.1606 0.030696-0.38077 0.078966-1.0835 0.35728-1.2006 0.2366 0.28522 0.25611 0.87164 0.68743 0.88669 0.44359 0.165 0.14984-0.52652 0.22704-0.78991-0.02465-0.43907 0.0509-0.89745-0.14265-1.3081-0.13901-0.45492-0.25732-0.92128-0.66094-1.2162-0.23804-0.30818-0.55645-0.50839-0.91425-0.64994-0.46057-0.39149-0.10372 0.65395-0.61139 0.4074-0.48459-0.17814-0.5764 0.2572-0.714 0.62101-0.40062 0.01938-0.61193 0.31243-0.74739 0.65741-0.11886 0.412-0.62051 0.51851-0.78162 0.92353-0.13213 0.38896-0.22491 0.80471-0.42049 1.181-0.16681 0.32014-0.13268 0.74092 0.031014 1.0463 0.15245 0.18292 0.30487 0.36588 0.45734 0.54877z"/> + </g> + </g> + <path d="m10.627-60 0.29225 0.82359 0.3985 0.39851 0.13284-0.37195v-0.66419l-0.26568-0.47822-0.31881 0.1594z" fill="#fff" stroke="#000" stroke-width=".2104px"/> + <g> + <g stroke="#000" stroke-width=".2104px"> + <path d="m17.083-49.771c-0.13668-1.7541-0.27353-3.5173-0.63951-5.2407-0.08891-0.46665-0.38607-1.0545-0.33217-1.4494 0.64456-0.22536 1.4739-0.46639 2.0434 0.05621 0.51258-0.38656 1.3693-0.55055 1.8565-0.0417 0.23098 0.43194 0.39869 0.90586 0.60058 1.3561 0.31178 0.71927 0.26766 1.5162 0.35508 2.2799 0.07293 1.0398 0.08926 2.0845 0.02441 3.1252 0.47742 0.28387 1.131-0.02722 0.82712-0.61098-0.04246-1.2555 0.03221-2.5319-0.28326-3.7604-0.07078-0.55154-0.22496-1.097-0.56018-1.5478-0.19038-0.32258-0.38075-0.64516-0.57112-0.96774 0.70847 0.12398 1.4169 0.24797 2.1254 0.37195 0.34623-0.59303-0.57857-0.51371-0.93059-0.5367-0.47236-0.102-0.95558-0.16277-1.4423-0.18131-1.1025-0.05162-2.2089-0.01738-3.3102 0.01378-1.4715 0.30648-2.7975 1.0484-4.1403 1.693-0.6247 0.31286-1.2425 0.64075-1.8397 1.0038-0.06735 0.47169 0.38642 0.55702 0.642 0.22404 0.40962-0.14878 0.7668-0.75219 1.2136-0.61674-0.04286 1.7072-0.08578 3.4145-0.12868 5.1217 0.54945 0.18768 0.77742-0.43499 0.67-0.86616-0.09309-0.60134 0.0089-1.199-0.0064-1.8022 0.0091-0.91715 0.01813-1.8343 0.02727-2.7514 0.26568-0.16826 0.53135-0.33652 0.79702-0.50478-0.09885 0.92828-0.1773 1.8706-0.0055 2.7954 0.10716 1.0423 0.21011 2.085 0.29765 3.1291 0.659 0.07775 0.36318-0.74785 0.28599-1.1155-0.05693-0.40997-0.04493-0.82619-0.11544-1.238-0.11339-0.99939-0.21841-2.0093-0.11994-3.0147-0.15161-0.27514 0.15822-1.2715 0.41897-0.67473 0.31027 1.7573 0.64872 3.5136 0.80291 5.2934 0.05877 0.20557-0.01399 0.92823 0.2642 0.67235 0.15426-0.6802-0.12952-1.3616-0.09606-2.0466-0.07165-1.4451-0.37977-2.8608-0.6436-4.279 0.28153-0.10878 0.81117-0.44765 0.72963 0.12761 0.25229 1.571 0.64894 3.1292 0.67022 4.7283 0.12969 0.52898-0.12806 1.1573 0.21827 1.6224 1.3469-0.0429 2.6938-0.08579 4.0408-0.12869-0.07061-0.50968-0.856-0.10666-1.2224-0.23911-0.8412-2e-6 -1.6824-2e-6 -2.5236 0z"/> + <path d="m10.574-76.87c0.1674 0.45265 0.45143 0.88074 0.38364 1.3829-0.0064 0.39417 0.02712 0.78411 0.12114 1.1676 0.32877-0.0713 0.67904-0.09664 0.91401-0.36847 0.19625-0.18544 0.53339-0.4778 0.58456-0.04097 0.04954 0.43758 0.09907 0.87516 0.14861 1.3127 0.2766 0.18483 0.52538 0.44735 0.81839 0.58633 0.34173 0.01148 0.69876 0.08826 0.97975-0.15906 0.55338-0.25388 1.0235-0.66348 1.5619-0.93184 0.49753-0.11775 0.9802-0.37243 1.5024-0.26252 0.35074-0.15626 0.54747 0.34314 0.15468 0.34167-0.30162 0.04769-0.64015 0.01809-0.82513 0.30998-0.3206 0.31224-0.70817 0.5752-0.84156 1.0252-0.126 0.31014-0.27614 0.60025-0.589 0.75533-0.34229 0.15913-0.42642 0.36327-0.13085 0.6336 0.19865 0.26262 0.61071 0.24378 0.90044 0.3863 0.51512 0.16422 1.0619 0.28367 1.5894 0.35678 0.51846-0.17587 1.079-0.26844 1.5712-0.4963 0.20854-0.19383 0.59505-0.30365 0.47074-0.65458-0.02258-0.60426-0.03825-1.2089-0.09541-1.8112-0.31074-0.18326-0.60068-0.44842-0.92451-0.58014-0.36421 0.21212-0.31508-0.49922 0.02195-0.31863 0.38941 0.0074 0.77883 0.0147 1.1682 0.02204 0.10627-0.27453 0.21254-0.54906 0.31881-0.82359 0.12688 0.74355 0.24096 1.4904 0.53443 2.1907 0.27952 0.9189 0.67532 1.7928 1.0596 2.6711-0.40182 1.1236-0.76147 2.2662-1.1897 3.3779-0.62857 1.1321-1.2972 2.2497-1.9009 3.3909-0.11886 0.75029-0.31732 1.4945-0.38541 2.2487 0.04278 1.4133 0.08565 2.8265 0.12848 4.2398-0.21084 0.1666-0.34087 0.85748-0.6289 0.52471-0.86053-0.43373-0.99358-1.2828-1.0288-2.1565-0.29242-1.7275-0.46769-3.4827-0.97255-5.1658-0.33357-1.3359-0.72305-2.6554-1.1689-3.9582-0.15604-0.39895-0.56315-0.64973-0.82358-0.98707 0.02656-0.54021 0.05312-1.0804 0.0797-1.6206 0.34538-0.16826 0.69075-0.33652 1.0361-0.50478-0.03952-0.38747-0.18982-0.53358-0.52634-0.27394-0.7484 0.34944-1.5034 0.68268-2.2898 0.93812-0.46936-0.21254-0.93872-0.42508-1.4081-0.63762-0.21014-0.68584-0.54604-1.3452-0.53725-2.0771-0.05129-0.60213-0.15768-1.2063 0.0071-1.8005 0.08719-0.74145 0.28574-1.4835 0.21131-2.2329z"/> + <path d="m7.1736-57.662c0.65998 0.03763 1.3861-0.04285 1.9193-0.46141 0.42444-0.4283 0.68989-0.982 1.1299-1.3973 0.24412-0.24896 0.21384-0.39302-0.06393-0.52544-0.09282-0.20005-0.27843-0.7759-0.33014-0.23052-0.14106 0.60903-0.60483 1.0559-0.84222 1.6202-0.22716 0.54596-0.99114 0.48428-1.317 0.07077-0.36365-0.23603-0.44801-0.7621-0.73655-0.99225 0.027993 0.65604 0.034831 1.3127 0.054711 1.969 0.061869-0.01769 0.12434-0.03546 0.18588-0.05312z"/> + <path d="m16.895-72.067c0.28542-0.01096 0.59182 0.02001 0.83628-0.15946 0.16899-0.10844 0.44311-0.24426 0.59557-0.04133 0.14574 0.28507 0.51518 0.09185 0.7273 0.26356 0.19762 0.1496 0.31056 0.28146 0.01153 0.36074-0.23112 0.1282-0.45371 0.27237-0.66178 0.43551-0.09485 0.23912-0.40923 0.29348-0.62596 0.21509-0.09909-0.29206-0.35018-0.41336-0.5926-0.58069-0.10752-0.15645-0.19136-0.33066-0.29033-0.49342z"/> + <path d="m18.05-74.81c-0.08454-0.09393-0.16908-0.18786-0.25361-0.28179-0.10959-0.0407-0.21917-0.0814-0.32876-0.12211-0.09079 0.05949-0.1816 0.11898-0.2724 0.17847-0.026 0.08488-0.09657 0.16368-0.07483 0.25756 0.02736 0.05084 0.0369 0.11289 0.07721 0.15617 0.04254 0.03488 0.08957-0.01633 0.13486-0.0201 0.15839-0.04981 0.31962-0.09021 0.48306-0.11954 0.07818-0.01622 0.15632-0.03243 0.23446-0.04865z"/> + <path d="m19.346-74.679c-0.047-0.07827-0.09393-0.15655-0.1409-0.23483 0.03244-0.0449 0.04406-0.12847 0.08983-0.14853 0.0734 0.0088 0.14676 0.01762 0.22014 0.02643 0.04246 0.03123 0.11128 0.05531 0.13738 0.091-0.03643 0.06673-0.07277 0.13346-0.10919 0.20019-0.06576 0.02191-0.13151 0.04383-0.19726 0.06575z"/> + </g> + <path d="m14.922-77.816c0.33268-0.24547 0.70482-0.38378 1.1181-0.39454 0.31969-0.05739 0.63863 0.03392 0.95772 0.05638 0.08141 0.10645 0.16282 0.21291 0.24422 0.31936 0.37017 0.03992 0.85554-0.1887 1.1427 0.08209 0.0055 0.4091 0.49512 0.55738 0.37213 1.0029 0.02107 0.29898 0.04215 0.59797 0.06314 0.89695 0.13461-0.40975 0.01678-1.0268 0.25268-1.3143 0.36246-0.05011 0.72292-0.11709 1.053-0.28251-0.33489 0.04004-0.69951 0.22555-1.023 0.11399-0.32436-0.11342-0.60294-0.65288-0.28788-0.90861 0.29968-0.19958 0.83088-0.34808 1.1333-0.11061 0.1425 0.12294 0.26097 0.58686 0.23943 0.12396-7.96e-4 -0.2648 0.14685-0.70033-0.27441-0.555-0.42608-0.05763-0.82142-0.11623-1.1701 0.21098-0.2653 0.29258-0.59998 0.55329-1.008 0.57986-0.40832 0.15033-0.60139-0.16184-0.83691-0.41542-0.32238-0.23904-0.757-0.2827-1.1437-0.22147-0.49101 0.10106-0.99275 0.05388-1.49 0.06455 0.37572 0.05636 0.75144 0.11272 1.1272 0.16907-0.31035 0.19139-0.67633 0.1697-1.0263 0.17286-0.26161 0.04994-0.64607-0.19337-0.76593-0.19484 0.17784 0.36702 0.661 0.32278 0.92907 0.39158-0.12843 0.10486-0.43749 0.23752-0.06529 0.22544 0.26286 0.27246 0.53298 0.54894 0.85139 0.7541 0.35748 0.01504 0.70391 0.11556 1.0534 0.17467 0.40494-0.04561 0.80539-0.24986 0.89617-0.67353 0.07054-0.19232-0.30035-0.48775-0.18767-0.10445-0.15901 0.30493-0.55493 0.58344-0.90947 0.45075-0.45921-0.02413-0.86536-0.20277-1.2036-0.51497l-0.02068-0.04961-0.02068-0.04959z"/> + <g stroke="#000" stroke-width=".2104px"> + <path d="m16.162-77.938-0.12211 0.18786 0.047 0.15029 0.18786 0.07515 0.30058-0.02818 0.12211-0.19725-0.14089-0.25361-0.19726-0.0094z"/> + <path d="m19.309-78.182-0.18786 0.18786 0.02815 0.15029 0.17846 0.14089h0.17846c0.03753 0 0.16908-0.10333 0.16908-0.10333l0.03753-0.22543-0.19726-0.18786z"/> + <path d="m14.001-80.343-0.16907 0.45087-0.0094 0.38511 0.56358 0.19725 0.69508-0.06575 0.4133 0.07515 0.57298 0.12211 0.48844 0.03757 0.42269-0.06575 0.24422-0.38511-0.03762-0.21604-0.44147 0.263-0.55419 0.03757-1.146-0.36633-0.6763-0.47904-0.34754-0.18786z"/> + <path d="m19.055-79.432 0.23483 0.39451 0.26301-0.31936 0.5354-0.24422 0.25361 0.23483 0.21605 0.15029 0.46025-0.50722 0.13151-0.29118-0.76084-0.69508-0.49783-0.47904 0.05638 0.58237-0.03762 0.42269-0.2724 0.2724 0.03753 0.21604-0.1409 0.25361-0.20665-0.28179-0.46025-0.44147z"/> + <path d="m22.22-71.194 0.263-2.0571-1.3432-1.4559-0.21604 0.15029 0.13151 0.49783 0.3945 0.263 0.12211 0.43208-0.25361 0.12211 0.14089 0.49783 0.33816 0.04697-0.05638 0.72326 0.15968 0.58237 0.28179 0.5354z"/> + </g> + <path d="m11.174-79.497c-0.2498-0.57825-0.54942-1.1467-0.76862-1.731-0.0092-0.30427 0.06776-0.64357 0.0064-0.92679-0.72293-0.68585-1.4458-1.3717-2.1688-2.0575-0.040875-0.22659 0.12007-0.56225-0.16835-0.65509-0.22347-0.13334-0.44792-0.26576-0.53553-0.52662-0.143-0.27888-0.38734-0.53272-0.3002-0.86846 0.12542-0.3348-0.23409-0.3102-0.45803-0.38609-0.03157-0.19501-0.10062-0.53129 0.17331-0.61763 0.20789-0.24508 0.54732-0.14316 0.80033-0.02702-0.2053-0.22848-0.14709-0.44643 0.047078-0.65896 0.10946-0.20948 0.56422-0.07133 0.472-0.40612 0.052008-0.21744 0.054552-0.56305 0.30588-0.63124 0.16664-0.13145 0.59458-0.09614 0.52124-0.38075-0.10231-0.29427 0.12509-0.43183 0.34188-0.57443 0.17823-0.26176 0.43741 0.03221 0.65974 0.0055 0.3338-0.03618 0.32752-0.26291 0.32522-0.52381 0.16413-0.15782 0.2328-0.49165 0.51482-0.43206 0.2548-0.08762 0.45866 0.06267 0.68972 0.07825 0.19511-0.06553 0.64153 0.08885 0.52762-0.24708-0.06092-0.25234 0.20499-0.45357 0.38679-0.57504 0.2392-0.07579 0.47919-0.20837 0.71508-0.05066 0.16392 0.21473 0.36386 0.22243 0.61785 0.12997 0.35081 0.07913 0.26883-0.30754 0.45386-0.40952 0.28523-0.05766 0.57388-0.17563 0.84669-0.01296 0.2095 0.13334 0.42262 0.1773 0.68183 0.15713 0.25897 0.047 0.35982-0.36286 0.64167-0.25897 0.29289-0.0338 0.64617 0.12933 0.69174 0.4338-0.08836 0.26536 0.40427 0.10645 0.57248 0.18444 0.19454-0.14269 0.40921-0.23428 0.66245-0.19957 0.47391-0.04047 0.24854 0.78199 0.75889 0.7085 0.32373 0.10162 0.65635-0.28953 0.95376 0.07149 0.27377 0.21945-0.03945 0.82256 0.44249 0.68877 0.31118-0.08327 0.42051 0.15559 0.52498 0.40221 0.07978 0.6912 0.09822 1.388 0.26976 2.067 0.22894 0.76173 0.08507 1.5421 0.01789 2.316 0.02736 0.37824-0.35185 0.58876-0.53737 0.87918-0.28566 0.22447 0.11762 0.56197 0.0079 0.85207-0.0097 0.6801-0.23312 1.3043-0.58087 1.8842-0.05837 0.46933-0.40983 0.44132-0.77199 0.39445-0.92666-0.037-1.8574-0.08729-2.7824-0.09585-0.78481 0.21567-1.5883 0.38356-2.3614 0.6292-0.39591 0.21273-0.79183 0.42546-1.1878 0.63819-0.20969 0.59987-0.48374 1.1825-0.54479 1.8222-0.03436-0.26491 0.01567-0.5756-0.07022-0.81254-0.22575-0.14887-0.4211-0.38329-0.71827-0.33118-0.40947 0.03246-0.33854-0.08054-0.03619-0.26359 0.36077-0.29748 0.74349-0.55822 1.1847-0.72105 0.70137-0.30015 1.3662-0.70281 2.1245-0.84307 0.57714-0.13328 1.1475-0.33214 1.7288-0.42484 0.97512 0.04875 1.9502 0.09752 2.9254 0.14627 0.14308-0.47736 0.33132-0.94991 0.4468-1.4302-0.02187-0.36636-0.10634-0.74508-0.08966-1.1038 0.2184-0.39588 0.38558-0.82976 0.75465-1.1139 0.25666-0.13816 0.2046-0.40028 0.19362-0.64592-0.01558-0.7166-0.03118-1.4332-0.04676-2.1498-0.08767 0.37572-0.17534 0.75145-0.26301 1.1272-0.25809-0.13586-0.35586-0.32592-0.21571-0.59641 0.09695-0.40235 0.19391-0.8047 0.29086-1.207-0.17805-0.07889-0.43074-0.46405-0.55932-0.34001-0.13288 0.29214-0.3085 0.575-0.24847 0.90893 0.04318 0.37437-0.06935 0.72848-0.16907 1.0842-0.35634 0.07894-0.19079-0.32666-0.23011-0.54205 0.0024-0.66467-0.03284-1.33 0.0048-1.9941-0.15432-0.11563-0.51065-0.3241-0.54353-0.38392 0.25178 0.10096 0.60621 0.06068 0.71346 0.3605 0.11767 0.22775 0.25707 0.42334 0.54034 0.40593 0.18623 0.06878 0.56602-0.17426 0.48972-0.36499-4e-3 -0.296-0.31346-0.38351-0.50202-0.16966-0.25176 0.01209-0.47261-0.07515-0.6604-0.24238 0.14266-0.16769 0.17152-0.56176-0.0427-0.61421-0.26935-0.05702-0.34488 0.35387-0.62711 0.17725-0.23085 0.0079-0.47687-0.1523-0.53905-0.35283 0.14955-0.27095 0.11957-0.46609-0.20473-0.53695-0.56491-0.08081-0.533 0.63672-0.07046 0.7256-0.28348 0.05304-0.49062-0.02362-0.57045-0.31668-0.06314-0.29734-0.40246-0.24418-0.48366 0.01447-0.1564 0.19959-0.48064 0.11379-0.71031 0.1888-0.16145 0.08648-0.53915-0.35085-0.32143-0.38233 0.2175-0.17098 0.48324 0.12627 0.68375-0.09047 0.27625-0.01121 0.09152-0.54312-0.1274-0.39786-0.2972-0.03085-0.37794 0.181-0.5333 0.36918-0.13602 0.08016-0.25721-0.31314-0.55202-0.19142-0.34746-0.15029-0.1445 0.31009-0.27502 0.45155-0.20287 0.17765-0.44544 0.21421-0.70633 0.18565-0.29205-0.2225-0.08348-0.26214 0.17552-0.29979 0.23829-0.06227 0.45812-0.2423 0.2667-0.47073-0.1671-0.12277-0.55486-0.2111-0.61035 0.05432-0.03204 0.306-0.33606 0.30223-0.57054 0.37764-0.3077-0.0083-0.34122 0.28091-0.44655 0.50414-0.02656 0.29411-0.35492 0.28251-0.56797 0.26204-0.35908-0.15644-0.24056-0.27674 0.07355-0.38224 0.29868-0.01957 0.27087-0.38048 0.09398-0.51349-0.18034-0.13822-0.55805-0.02314-0.52923 0.23371-0.03491 0.24499-0.07989 0.51503-0.36465 0.56703-0.22372 0.11392-0.51726 0.17846-0.45924 0.49173 0.08717 0.5164-1.1853 0.22046-0.83549 0.04294 0.23996-0.07284 0.38705-0.26227 0.53229-0.454-0.12946-0.22902-0.4582-0.25982-0.59001-0.02911-0.13762 0.24324-0.35723 0.48303-0.27417 0.78054-0.27037 0.23671 0.08855 0.89505-0.47971 0.87375-0.20765 0.08828-0.51343-0.05773-0.67726 0.04788-0.10248 0.31148-0.17309 0.62937-0.19561 0.95696-0.37792 0.08427-0.080215 0.33648 0.029344 0.5419 0.351 0.50563 0.69376 1.0288 1.0854 1.4999 0.38952 0.3209 0.77066 0.6549 1.2492 0.83615 0.67259 0.35059 1.3817 0.64068 2.0307 1.0303 0.45753 0.35739 0.97048 0.6561 1.3928 1.0508 0.20669 0.23904 0.34672 0.41419 4e-3 0.12662-0.81796-0.63615-1.7258-1.1448-2.597-1.702-0.72713-0.32628-1.4643-0.63333-2.1296-1.0779-0.22688-0.14829-0.51885-0.23525-0.59353-0.52695-0.22872-0.41569-0.42133-0.8615-0.67226-1.2586-0.25161-0.1747-0.46469-0.41327-0.75719-0.52015-0.18372 0.22646-0.093328 0.44228 0.057893 0.65575 0.23774 0.41244 0.46642 0.84423 0.75475 1.2188 0.25341 0.19071 0.47963 0.45959 0.74922 0.60375 0.24936-0.0088 0.52361-0.08102 0.66624 0.18194 0.01185 0.12356-0.4122 0.14855-0.58223 0.20965-0.4287 0.11072-0.86782 0.15414-1.3078 0.19226-0.041908-0.28015-0.057416-0.53569 0.30314-0.44078 0.35439 0.04755 0.42609-0.04503 0.16167-0.30421-0.34623-0.46097-0.66009-0.95858-1.0261-1.3971-0.24686-0.165-0.46958-0.28934-0.37796 0.11586 0.042226 0.47538-0.09006 0.0106-0.1734-0.19049-0.14499-0.22604-0.51206 0.09176-0.31599 0.21907 0.30075 0.03387 0.34917 0.29362 0.40853 0.54358 0.092446 0.25321 0.081583 0.57791 0.21217 0.79272 0.22901 0.13502 0.58857 0.13748 0.71976 0.38184-0.00556 0.26869 0.11605 0.48319 0.299 0.67147 0.50046 0.65614 1.1143 1.2145 1.6679 1.8248 0.74032-0.28406 1.4508-0.65931 2.2355-0.8078 0.04287-0.29274 0.0905-0.56644 0.30058-0.18786 0.24627 0.0074 0.54525-0.0526 0.53037 0.2891-0.08038 0.16332-0.43481 0.19495-0.61878 0.30848-0.69066 0.28298-1.3813 0.56596-2.072 0.84894 0.05925 0.41544-0.0071 0.86206 0.14054 1.2555 0.11461 0.30256 0.30617 0.60038 0.37375 0.90582-0.03992 0.13119-0.07984 0.26238-0.11976 0.39357z"/> + <g stroke="#000" stroke-width=".2104px"> + <path d="m10.141-88.796 0.63873 0.6763 0.45086 0.82658-0.09393 1.5592-0.3945 0.16908 0.4133 0.41329 0.78902 0.05636 0.15028 0.35694 1.2023 0.82659 0.95809 0.46965 0.54479-0.39451 1.0144 0.11272 0.07515-0.39451-0.50723-1.672v-0.43208l1.5968-0.92052 1.4653 0.75144s0 0.86416-0.03753 0.9393c-0.03762 0.07513-0.43208 1.409-0.43208 1.409l1.409 0.01879 0.35693 0.05636 0.13151-0.60116-1.1647-0.03757 0.20665-0.48844 0.48844 0.05636 0.24422 0.37572 0.16906-0.30058-0.45086-0.24422 0.11272-0.35694h0.8078v-0.35694l-0.88294-0.05636-0.07515-0.24422 0.77023-0.01879-0.1315-0.30058-0.77023-0.11272s0.05638-0.35694-0.07515-0.35694c-0.1315 0-0.67629-0.09393-0.67629-0.09393l-0.33816-0.5448-0.31936 0.07515-0.46965-0.07515-0.263-0.28179-0.45087 0.30057-1.4841 0.77023-0.30058-0.03757-0.37572 0.75144 0.52601 1.841-0.33816 0.33815-0.41329 0.31936s-0.73266-0.56358-0.82659-0.63873c-0.09392-0.07515-0.99566-0.45086-0.99566-0.45086l0.03762-0.35694 0.1315-0.56358-0.92052-0.01879-0.05638-1.8974-0.65751-0.99566z"/> + <path d="m16.885-72.997 0.73265 0.18786 0.69508 0.07514 0.56359 0.07515 0.4133-0.20665 0.15029-0.35693 0.03753-0.03757-0.18786 0.1315-0.24421 0.22543-0.37573 0.05636-0.99566-0.30058-0.60115-0.20665-0.35694 0.30058z"/> + <path d="m14.95-90.149-0.48844 0.35693-0.28179 0.4133-0.78901-0.05638-0.2818 0.45087s-0.263 0.43208-0.263 0.54479c0 0.11272 0 0.58237 0.01876 0.75144 0.01877 0.16907-0.03762 0.45086-0.03762 0.45086l-0.28179 0.43208 0.22543 0.52601v0.1315l-0.24422 0.41329-0.16907 1.0144 1.1459 0.52601 0.63873 0.50722 0.58237-0.39451-0.43208 0.11272s-0.60115-0.18786-0.48844-0.28179c0.11272-0.09393 0.526-0.45086 0.526-0.45086l0.05638-0.30058-0.41329 0.31936-0.39451 0.18786-0.52601-0.52601-0.28179-0.48844-0.07515-0.35694 0.56358-0.31936 0.35694 0.16908 0.45086-0.05636 0.07515-0.39451 0.45087-0.39451 0.16907-0.63873-0.43208-0.33815 0.18786 0.45087-0.11272 0.41329-0.37572 0.37572-0.92052-0.35694 0.263-0.58237 0.24421-0.16908-0.26301-0.22543-0.01876-0.46965 0.28179-0.07515 0.50723 0.13151-0.35694-0.28179-0.22543-0.33816h0.37572c0.11272 0 0.35694-0.01876 0.35694-0.01876l0.46965-0.45086 0.58236-0.20665 0.48844 0.37572 0.43207 0.45087s0.13151 0.18786-0.1315 0.43208c-0.26301 0.24422-0.48844 0.22543-0.48844 0.22543l0.01877-0.43208 0.31936-0.16907-0.95809 0.01877 0.05638 0.31936 0.45087 0.54479 0.24422 0.07515 0.60115-0.33816 0.15029-0.11271 0.97687 0.16907 0.22543-0.37572 0.07515-0.41329-0.5448-0.263-0.46965-0.07515 0.18786 0.45086s0.37572-0.07515 0.37572 0.03753c0 0.11272 0.11272 0.30058 0 0.31936-0.11272 0.01876-0.69508-0.03762-0.69508-0.03762v-0.48844l0.18786-0.3945 0.56359-0.07515 0.84538-0.31936 0.28179-0.07515 0.45086 0.41329 0.48844-0.05638-0.71388-0.5448-0.78902-0.05638-0.8078 0.45087-0.48844 0.1315s-0.48844 0.07515-0.60115-0.1315c-0.11272-0.20665-0.71387-0.50723-0.71387-0.50723z"/> + <path d="m16.007-85.743 0.89001-0.50478 0.96972 0.46493-0.0797 0.54464-0.39851 0.09299-0.27896 0.06641 0.5712 0.06641 0.0797 0.39851-0.62433 0.14612-0.0797-0.62434-0.62433-0.02657 0.05312 0.9033-0.19926 0.10627-0.0797-0.94315-0.14612-0.43836-0.09298 0.33209 0.17269 1.0361-0.29225-0.94315 0.01328-0.86344 0.95643-0.55792 0.34538-0.13284 0.99629 0.47822-0.02656 0.39851-0.17269 0.51807-0.1594 0.11955 0.13284 0.17269 0.03985 0.70404-0.18598 0.25239h-1.1823l0.09298-0.25239 0.22582-0.13284 0.02656-0.46493-0.29224-0.01328v0.62434l-0.05312 0.14612-0.53135 0.0797-0.06973-1.7169z"/> + <path d="m11.08-79.573-0.17846 0.12211-0.17847 0.38512-0.16907 0.5354-0.09392 0.43208-0.047 0.5354-0.0094 0.45087 0.01877 0.33815 0.29119 0.43208-0.12211-0.70448-0.03753-0.89234 0.1315-0.78902 0.18786-0.51662z"/> + <path d="m11.963-77.713c-0.30565 5.68e-4 -0.4811-0.29263-0.48818-0.56892-0.08238-0.22488-0.0198-0.5817-0.26433-0.69053-0.06306 0.3027-0.17982 0.60644-0.13145 0.92272 0.05916 0.33285-0.10759 0.74997 0.16452 1.0071 0.21524 0.17767 0.3168 0.44045 0.22469 0.70778 0.03054 0.19542 0.24179 0.51901 0.4599 0.4392 0.33943-0.05929 0.26544-0.39818 0.28895-0.65428-0.05384-0.23561-0.2368-0.42299-0.34802-0.63701 0.04859-0.11102 0.19567-0.6216 0.1634-0.27226-0.05702 0.22284 0.03976 0.42604 0.16637 0.60356 0.1949 0.22129 0.06521 0.52484 0.03674 0.78881-0.08469 0.1887-0.39908 0.37342-0.57268 0.19454-0.15816-0.1525-0.33996-0.3751-0.23294-0.60943 0.07697-0.28351-0.23924-0.47535-0.3462-0.71493-0.14076-0.19556-0.04183-0.4656-0.07436-0.69476 0.01877-0.2331-0.05082-0.48278 0.04016-0.70201 0.04907-0.16686 0.15421-0.65133 0.32075-0.29918 0.14876 0.28168 0.16499 0.60935 0.2558 0.91137 0.06847 0.11391 0.23394 0.17618 0.33692 0.26828z"/> + <path d="m10.614-54.713 1.8597-0.90329 2.2184-1.0627 2.2715-0.4915-0.10627-0.30552-1.5808 0.39851-1.6339 0.51807 1.4612-0.78374 1.4479-0.39851 0.30553-0.10627-0.1594-0.33209-1.4081 0.4915-1.5011 0.6509-1.5409 0.89001s-1.6339 0.94315-1.6738 1.0096c-0.03985 0.06641 0.0797 0.4915 0.0797 0.4915z"/> + <path d="m18.85-57.755 0.02656 0.34538 2.4708 0.19926 1.3018 0.21254 0.10627-0.4915-1.9926-0.21254-0.03984 0.34538-0.38522-0.03985-0.22582-0.35866z"/> + <path d="m7.3595-74.319 0.26568 1.4878s0.45165 1.4346 0.5712 1.5808c0.11955 0.14612 0.75718 1.8597 0.75718 1.8597l1.4479 2.6568 1.6339 1.9926 1.2221 1.3549 1.0229 0.79702-1.2221 0.99628-0.82359 0.87673-0.82359 1.1823-0.87673 1.5542 3.2545-1.7269 2.5903-0.74389-0.78374-3.9054-0.53135-2.0988-0.54464-0.06641-0.4915 1.6073-0.43836 1.5409-0.35866-0.29224 0.78374-2.9756-1.2885-0.59777-0.87673 1.7003-0.27896-0.22582 0.82359-1.5675-1.7136-1.5542-1.0229-1.3682-1.2752-2.8959-0.079698-1.78z"/> + <path d="m6.7751-64.038c0.14577 0.03466 0.32081 0.01259 0.43104 0.13353 0.10872 0.08833 0.21742 0.17666 0.32614 0.26499v0.1594c0.064811-0.09455 0.20744-0.18639 0.22591-0.28256-0.1063-0.13607-0.2126-0.27213-0.31889-0.4082-0.13563-0.04495-0.27786-0.20596-0.40897-0.1735-0.14196 0.05915-0.24088 0.14451-0.25522 0.30634z"/> + <path d="m7.5588-65.034 0.51807 0.21254 0.31881 0.54463 0.1594 0.74389-0.039841 0.37195-0.25239-0.87673-0.54464-0.4118-0.50479-0.26568-0.14612-0.23911z"/> + <path d="m11.106-70.055 0.51807 0.77046 0.75717 0.75717 1.0361 0.77046 0.79703 0.19926 0.15941-0.22582-0.99628-0.86344-0.95643-1.0494-1.1823-0.43836z"/> + <path d="m25.376-48.031c-0.32563 0.25048-0.65125 0.50096-0.97688 0.75144 0.01249 0.22543 0.02505 0.45086 0.03754 0.6763 0.40077 0.20039 0.80154 0.40077 1.2023 0.60116 0.44361-0.12388 0.8745-0.30463 1.3274-0.38823 0.25458 0.0223 0.58962-0.18248 0.76261 0.07127 0.37505 0.39282 0.76436 0.77214 1.2042 1.0929 0.30848 0.32332 0.75042 0.39709 1.1563 0.53541 0.27039 0.12559 0.5671 0.0587 0.85345 0.06664 0.42373-0.0084 0.84746-0.01694 1.2712-0.02542-0.40077-0.15029-0.80154-0.30058-1.2023-0.45087-0.12389-0.26498-0.03332-0.73814-0.30711-0.85644-0.33589-0.12095-0.21232 0.4519-0.32244 0.66028 6.28e-4 0.10996 0.18241 0.14348 0.26087 0.2206 0.12289 0.07953 0.24579 0.15904 0.36868 0.23856-0.34222 0.23853-0.60806-0.0826-0.9406-0.12303-0.29504 0.0076-0.58638-0.02152-0.794-0.25731-0.30543-0.20227-0.57178-0.45092-0.80065-0.73624-0.27608-0.3203-0.5771-0.62465-0.83044-0.9613-0.14303-0.32182-0.28606-0.64364-0.4291-0.96546 0.08768-0.15029 0.17534-0.30057 0.263-0.45086-0.27552 0.12524-0.55105 0.25048-0.82658 0.37572-0.19008 0.34778-0.41482 0.68331-0.49614 1.0777-0.05066 0.21366-0.08681 0.44913-0.34498 0.27762-0.2317-0.0804-0.50216-0.10054-0.67427-0.29256-0.12107-0.10377-0.24214-0.20755-0.3632-0.31132 0.14753-0.2806 0.64736-0.18047 0.56682-0.58628 0.01145-0.0801 0.0229-0.16021 0.03435-0.24031z"/> + <path d="m30.421-45.889-0.76084-0.22543h-0.57298l0.47904-0.24422 0.65751-0.35694 1.0614-0.08454 0.71387 0.11272 0.85477 0.64812 0.35694 0.32876 0.25362-0.12211 0.07515 0.37572 0.08453 0.23483 0.43209 0.36633 0.45086-0.06575 0.09393-0.16907-0.07515 0.41329-0.30058 0.0094-0.65751-0.45087-0.24422-0.44147-0.25361-0.16907-0.89234-0.57298-1.0144-0.24422-0.89234 0.02818-0.34754 0.21604z"/> + <path d="m31.994-47.631 0.57298 0.04227 0.29588 0.23952 0.88764 0.91582 0.14559 0.2771 0.62464 1.0004 0.33345 0.16907"/> + <path d="m32.008-47.693 0.89703-0.05166s0.52131 0.45556 0.5354 0.47435c0.01408 0.01879 1.0238 1.3949 1.0238 1.3949l0.17846 0.52131 0.14559 0.32406-0.52131-0.57298-0.92052-1.3103-0.71387-0.61994z"/> + <path d="m34.262-48.519 0.68568 0.23483 0.59176 0.93461 0.61994 0.9487 0.32876 0.63403s-0.56828-0.69039-0.56828-0.71857c0-0.02818-0.97687-1.362-0.97687-1.362l-0.56828-0.32406-0.52131-0.19256z"/> + <path d="m33.018-48.092 0.76083 0.22543 0.75614 1.052 0.81719 1.1976 0.0517 0.89234-0.19726-0.92052-1.4794-2.1181z"/> + <path d="m25.658-48.463c0.29722 0.15242 0.55473 0.59925 0.9121 0.30475 0.93392-0.29272 1.7992-0.75484 2.6644-1.1906 0.66209-0.11767 1.3599-0.11978 2.0222-6e-3 0.61402 0.18622 1.2212 0.50604 1.8828 0.3287 0.99568-0.0558 1.8413-0.67506 2.6981-1.129 1.1309-0.68332 2.3931-1.1038 3.5905-1.6519-2.0739-0.11704-4.1513-0.2704-6.223-0.36478-0.56824 0.14664-1.1365 0.29329-1.7047 0.43993 0.76397 0.16908 1.5279 0.33815 2.2919 0.50722-0.3037 0.28805-0.60742 0.57611-0.91112 0.86416-0.57924-0.38664-1.709-0.10546-1.87-0.89947-0.17968-0.77364-1.225 0.08483-0.42835 0.42099 0.37512 0.0058 1.5375 0.46653 0.82318 0.80302-0.13922-0.21113-1.0059-0.68763-0.85606-0.27539 0.87687 0.43401-0.83716 0.19082-1.1436 0.26472-1.0048 0.05136 0.33877-0.96328-0.09098-1.0309-0.34618 0.51538-1.4502 0.25317-1.516 0.75379 0.34917 0.09273 0.85096 0.38505 0.21699 0.51827-0.57491-0.02517-0.9618 0.42786-1.4948 0.42194-0.12369 0.38184-0.60482 0.60835-0.86363 0.92047z"/> + <path d="m18.952-60.918c0.70134-0.03757 1.4027-0.07515 2.104-0.11272 0.5284 0.17486 1.0835 0.30163 1.5477 0.62414 0.16039 0.09253 0.32077 0.18506 0.48116 0.27759-0.8034-0.23062-1.6228-0.39648-2.4509-0.50722-0.51153-0.10593-1.0342-0.15848-1.5543-0.09617-0.16483-0.0077-0.23134-0.01656-0.1277-0.18562z"/> + <path d="m15.645-82.822c0.72634 0.09814 1.4585 0.24257 2.1924 0.13267 0.68988-0.01284 1.4242-0.32374 1.7574-0.95608 0.04684-0.11464 0.42371-0.49459 0.10642-0.3209-0.4272 0.27602-0.74606 0.69806-1.2118 0.92191-0.89929 0.40347-1.9033 0.09445-2.8443 0.2224z"/> + </g> + </g> + <path d="m-4.8315-49.646 28.592-0.45087 0.71388 0.82659-29.344 0.15029z" fill="#1a1a1a" stroke="#000" stroke-width=".2104px"/> + </g> + </g> +</svg> diff --git a/assets/strict-csp.svg b/assets/strict-csp.svg new file mode 100644 index 0000000..cd128f1 --- /dev/null +++ b/assets/strict-csp.svg @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="24.78mm" height="24.78mm" version="1.1" viewBox="0 0 24.780247 24.780247" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <linearGradient id="linearGradient955" x1="66.618" x2="82.588" y1="81.176" y2="64.828" gradientTransform="matrix(.82538 0 0 .82538 -392 -92.399)" gradientUnits="userSpaceOnUse"> + <stop stop-color="#0aa70b" offset="0"/> + <stop stop-color="#3bff39" offset="1"/> + </linearGradient> + <filter id="filter945" x="-.0516" y="-.0516" width="1.1032" height="1.1032" color-interpolation-filters="sRGB"> + <feGaussianBlur stdDeviation="0.58510713"/> + </filter> + </defs> + <g transform="translate(342.15 43.638)"> + <circle transform="matrix(.82538 0 0 .82538 -392 -92.399)" cx="75.406" cy="74.089" r="13.607" filter="url(#filter945)" stroke="#000" stroke-linecap="round" stroke-width="1.565"/> + <circle cx="-330.23" cy="-31.716" r="11.231" fill="url(#linearGradient955)" stroke="#000" stroke-linecap="round" stroke-width="1.2917"/> + <g transform="matrix(.70929 0 0 .70929 -99.465 -12.686)" stroke-width=".51676px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal" aria-label="Strict"> + <path d="m-330.78-33.775q0 0.73996-0.53676 1.154-0.53676 0.41407-1.4569 0.41407-0.99684 0-1.5336-0.25688v-0.62878q0.34506 0.14569 0.75147 0.23004 0.4064 0.08435 0.80514 0.08435 0.65177 0 0.9815-0.24538 0.32972-0.24921 0.32972-0.69012 0-0.29138-0.11885-0.47542-0.11502-0.18787-0.39107-0.34506-0.27221-0.15719-0.83198-0.35656-0.78213-0.27988-1.1195-0.66328-0.33356-0.3834-0.33356-1.0007 0-0.64794 0.48692-1.0313 0.48691-0.3834 1.2882-0.3834 0.83581 0 1.5374 0.30672l-0.2032 0.56743q-0.69395-0.29138-1.3496-0.29138-0.51759 0-0.80897 0.22237t-0.29138 0.61727q0 0.29138 0.10735 0.47925 0.10735 0.18403 0.36039 0.34123 0.25688 0.15336 0.78214 0.34122 0.88182 0.31439 1.2115 0.67478 0.33356 0.3604 0.33356 0.93549z"/> + <path d="m-328.37-32.732q0.16869 0 0.32589-0.023 0.15719-0.02684 0.24921-0.05368v0.48692q-0.10352 0.04984-0.30672 0.08051-0.19937 0.03451-0.3604 0.03451-1.2192 0-1.2192-1.2844v-2.4998h-0.60194v-0.30672l0.60194-0.26455 0.26838-0.89716h0.36807v0.97384h1.2192v0.49458h-1.2192v2.4729q0 0.37957 0.18019 0.58277 0.1802 0.2032 0.49459 0.2032z"/> + <path d="m-325.04-36.562q0.27989 0 0.50226 0.04601l-0.0882 0.59044q-0.26072-0.05751-0.46008-0.05751-0.50993 0-0.87415 0.41407-0.3604 0.41407-0.3604 1.0313v2.2544h-0.63644v-4.2021h0.52525l0.0729 0.7783h0.0307q0.23388-0.41024 0.5636-0.63261 0.32972-0.22237 0.72462-0.22237z"/> + <path d="m-323.11-32.284h-0.63644v-4.2021h0.63644zm-0.69012-5.3408q0-0.21854 0.10735-0.31822 0.10735-0.10352 0.26838-0.10352 0.15336 0 0.26455 0.10352 0.11118 0.10352 0.11118 0.31822 0 0.2147-0.11118 0.32206-0.11119 0.10352-0.26455 0.10352-0.16103 0-0.26838-0.10352-0.10735-0.10735-0.10735-0.32206z"/> + <path d="m-320.07-32.207q-0.91249 0-1.4147-0.55976-0.49842-0.5636-0.49842-1.5911 0-1.0543 0.50609-1.6294 0.50992-0.5751 1.4492-0.5751 0.30288 0 0.60577 0.06518 0.30288 0.06518 0.47541 0.15336l-0.19553 0.54059q-0.21087-0.08435-0.46008-0.13802-0.24921-0.05751-0.44091-0.05751-1.2806 0-1.2806 1.6333 0 0.77447 0.31055 1.1885 0.31439 0.41407 0.92783 0.41407 0.52526 0 1.0774-0.22621v0.5636q-0.42174 0.21854-1.062 0.21854z"/> + <path d="m-316.65-32.732q0.16869 0 0.32589-0.023 0.15719-0.02684 0.24921-0.05368v0.48692q-0.10352 0.04984-0.30672 0.08051-0.19937 0.03451-0.3604 0.03451-1.2192 0-1.2192-1.2844v-2.4998h-0.60194v-0.30672l0.60194-0.26455 0.26838-0.89716h0.36806v0.97384h1.2192v0.49458h-1.2192v2.4729q0 0.37957 0.1802 0.58277 0.1802 0.2032 0.49459 0.2032z"/> + </g> + <g fill="#fff"> + <g transform="matrix(.70929 0 0 .70929 -99.465 -12.686)" stroke-width=".3317px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal" aria-label="Content"> + <path d="m-332.67-30.173q-0.5931 0-0.93764 0.39622-0.34208 0.39376-0.34208 1.0804 0 0.70631 0.32977 1.0927 0.33224 0.38392 0.94503 0.38392 0.37653 0 0.85889-0.13536v0.36669q-0.37407 0.14028-0.92288 0.14028-0.7949 0-1.228-0.48236-0.43067-0.48236-0.43067-1.3708 0-0.55619 0.20672-0.97456 0.20919-0.41837 0.60049-0.64478 0.39376-0.22641 0.92533-0.22641 0.56603 0 0.98933 0.20672l-0.1772 0.35931q-0.40852-0.19196-0.81705-0.19196z"/> + <path d="m-328.77-28.248q0 0.65955-0.33224 1.0312-0.33223 0.36915-0.91795 0.36915-0.36177 0-0.64233-0.16981-0.28055-0.16981-0.43313-0.48728-0.15259-0.31747-0.15259-0.74322 0-0.65955 0.32978-1.0262 0.32977-0.36915 0.91549-0.36915 0.56603 0 0.89827 0.37653 0.3347 0.37653 0.3347 1.0189zm-2.0549 0q0 0.51681 0.20672 0.78752 0.20673 0.27071 0.60787 0.27071t0.60787-0.26825q0.20918-0.27071 0.20918-0.78998 0-0.51435-0.20918-0.78014-0.20673-0.26825-0.61279-0.26825-0.40115 0-0.60541 0.26333-0.20426 0.26333-0.20426 0.78506z"/> + <path d="m-326.21-26.897v-1.7449q0-0.32978-0.15012-0.4922-0.15012-0.16243-0.47005-0.16243-0.42329 0-0.62017 0.22887-0.19688 0.22887-0.19688 0.75553v1.4151h-0.40853v-2.6973h0.33223l0.0664 0.36915h0.0197q0.12551-0.19934 0.35192-0.30762 0.22642-0.11075 0.50451-0.11075 0.48728 0 0.73338 0.23626 0.2461 0.2338 0.2461 0.75061v1.7596z"/> + <path d="m-324.09-27.185q0.10828 0 0.20918-0.01477 0.1009-0.01723 0.15997-0.03445v0.31255q-0.0665 0.03199-0.19688 0.05168-0.12797 0.02215-0.23134 0.02215-0.7826 0-0.7826-0.82444v-1.6046h-0.38637v-0.19688l0.38637-0.16981 0.17227-0.57588h0.23626v0.6251h0.7826v0.31747h-0.7826v1.5873q0 0.24364 0.11567 0.37407 0.11566 0.13043 0.31747 0.13043z"/> + <path d="m-322.04-26.848q-0.59802 0-0.94502-0.36423-0.34454-0.36423-0.34454-1.0115 0-0.65217 0.31993-1.0361 0.32239-0.38392 0.86381-0.38392 0.50697 0 0.80229 0.3347 0.29532 0.33224 0.29532 0.87858v0.25841h-1.8581q0.0123 0.47497 0.23872 0.72107 0.22887 0.2461 0.64232 0.2461 0.4356 0 0.86135-0.18212v0.36423q-0.21657 0.09352-0.41099 0.13289-0.19195 0.04184-0.46513 0.04184zm-0.11074-2.4536q-0.32485 0-0.51927 0.21165-0.19196 0.21165-0.22642 0.58572h1.4102q0-0.38638-0.17227-0.59064-0.17227-0.20672-0.4922-0.20672z"/> + <path d="m-318.51-26.897v-1.7449q0-0.32978-0.15012-0.4922-0.15013-0.16243-0.47006-0.16243-0.42329 0-0.62017 0.22887-0.19688 0.22887-0.19688 0.75553v1.4151h-0.40853v-2.6973h0.33224l0.0664 0.36915h0.0197q0.12552-0.19934 0.35193-0.30762 0.22641-0.11075 0.5045-0.11075 0.48728 0 0.73338 0.23626 0.2461 0.2338 0.2461 0.75061v1.7596z"/> + <path d="m-316.4-27.185q0.10829 0 0.20919-0.01477 0.1009-0.01723 0.15996-0.03445v0.31255q-0.0664 0.03199-0.19688 0.05168-0.12797 0.02215-0.23133 0.02215-0.7826 0-0.7826-0.82444v-1.6046h-0.38638v-0.19688l0.38638-0.16981 0.17227-0.57588h0.23625v0.6251h0.7826v0.31747h-0.7826v1.5873q0 0.24364 0.11567 0.37407 0.11567 0.13043 0.31747 0.13043z"/> + </g> + <g transform="matrix(.70929 0 0 .70929 -99.465 -12.686)" stroke-width=".32428px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal" aria-label="Security"> + <path d="m-332.03-22.859q0 0.46434-0.33683 0.72417-0.33682 0.25984-0.91423 0.25984-0.62553 0-0.96236-0.1612v-0.39456q0.21653 0.09142 0.47155 0.14435 0.25503 0.05293 0.50524 0.05293 0.409 0 0.61591-0.15398 0.20691-0.15638 0.20691-0.43306 0-0.18285-0.0746-0.29833-0.0722-0.11789-0.2454-0.21653-0.17082-0.09864-0.52208-0.22375-0.4908-0.17563-0.70252-0.41622-0.20931-0.24059-0.20931-0.62794 0-0.4066 0.30555-0.64718t0.80838-0.24059q0.52448 0 0.96476 0.19247l-0.12751 0.35607q-0.43547-0.18285-0.84687-0.18285-0.3248 0-0.50765 0.13954-0.18284 0.13954-0.18284 0.38735 0 0.18285 0.0674 0.30074 0.0674 0.11548 0.22615 0.21412 0.1612 0.09624 0.4908 0.21412 0.55336 0.19728 0.76027 0.42344 0.20931 0.22615 0.20931 0.58704z"/> + <path d="m-330.26-21.875q-0.58463 0-0.92386-0.35607-0.33683-0.35607-0.33683-0.98882 0-0.63756 0.31277-1.0129 0.31517-0.37532 0.84446-0.37532 0.49562 0 0.78432 0.3272 0.28871 0.3248 0.28871 0.8589v0.25262h-1.8164q0.012 0.46434 0.23338 0.70492 0.22374 0.24059 0.62793 0.24059 0.42584 0 0.84206-0.17804v0.35607q-0.21171 0.09142-0.40178 0.12992-0.18766 0.0409-0.45471 0.0409zm-0.10827-2.3987q-0.31757 0-0.50764 0.20691-0.18766 0.20691-0.22134 0.5726h1.3786q0-0.37772-0.16841-0.57741-0.16841-0.2021-0.48118-0.2021z"/> + <path d="m-327.56-21.875q-0.5726 0-0.88777-0.35126-0.31277-0.35366-0.31277-0.99844 0-0.66162 0.31758-1.0225 0.31998-0.36088 0.90942-0.36088 0.19007 0 0.38013 0.0409 0.19007 0.0409 0.29833 0.09624l-0.1227 0.33923q-0.13232-0.05293-0.2887-0.08661-0.15639-0.03609-0.27668-0.03609-0.80357 0-0.80357 1.0249 0 0.48599 0.19488 0.74582 0.19728 0.25984 0.58223 0.25984 0.3296 0 0.67605-0.14195v0.35366q-0.26465 0.13714-0.66643 0.13714z"/> + <path d="m-325.89-24.56v1.7106q0 0.32239 0.14676 0.48118 0.14675 0.15879 0.45952 0.15879 0.41381 0 0.60388-0.22615 0.19247-0.22615 0.19247-0.73861v-1.3858h0.39938v2.6369h-0.32961l-0.0577-0.35367h-0.0217q-0.1227 0.19488-0.34163 0.29833-0.21653 0.10345-0.49561 0.10345-0.48118 0-0.72177-0.22856-0.23818-0.22856-0.23818-0.73139v-1.725z"/> + <path d="m-322.04-24.608q0.17563 0 0.31517 0.02887l-0.0553 0.37051q-0.1636-0.03609-0.2887-0.03609-0.31999 0-0.54855 0.25984-0.22615 0.25984-0.22615 0.64718v1.4147h-0.39938v-2.6369h0.32961l0.0457 0.4884h0.0192q0.14676-0.25743 0.35366-0.39697 0.20691-0.13954 0.45472-0.13954z"/> + <path d="m-320.83-21.923h-0.39938v-2.6369h0.39938zm-0.43306-3.3514q0-0.13714 0.0674-0.19969 0.0674-0.06496 0.16841-0.06496 0.0962 0 0.16601 0.06496 0.0698 0.06496 0.0698 0.19969 0 0.13473-0.0698 0.2021-0.0698 0.06496-0.16601 0.06496-0.10105 0-0.16841-0.06496-0.0674-0.06736-0.0674-0.2021z"/> + <path d="m-319.13-22.205q0.10586 0 0.2045-0.01443 0.0986-0.01684 0.15638-0.03368v0.30555q-0.065 0.03128-0.19247 0.05052-0.1251 0.02165-0.22615 0.02165-0.76507 0-0.76507-0.80597v-1.5686h-0.37773v-0.19247l0.37773-0.16601 0.16841-0.56298h0.23096v0.6111h0.76508v0.31036h-0.76508v1.5518q0 0.23818 0.11308 0.3657t0.31036 0.12751z"/> + <path d="m-318.66-24.56h0.42825l0.57742 1.5037q0.19006 0.51486 0.23577 0.74342h0.0192q0.0313-0.1227 0.12992-0.41862 0.10105-0.29833 0.6544-1.8285h0.42825l-1.1332 3.0025q-0.16841 0.44509-0.39456 0.63034-0.22375 0.18766-0.55095 0.18766-0.18285 0-0.36088-0.0409v-0.31998q0.13232 0.02887 0.29592 0.02887 0.41141 0 0.58704-0.46193l0.14676-0.37532z"/> + </g> + <g transform="matrix(.70929 0 0 .70929 -99.465 -12.686)" stroke-width=".32334px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal" aria-label="Policy"> + <path d="m-329.37-19.254q0 0.53256-0.36464 0.82043-0.36224 0.28547-1.0387 0.28547h-0.41261v1.3794h-0.40782v-3.5072h0.90919q1.3146 0 1.3146 1.0219zm-1.816 0.75566h0.36703q0.54215 0 0.78445-0.17512 0.24229-0.17512 0.24229-0.56135 0-0.34784-0.2279-0.51817t-0.71008-0.17032h-0.45579z"/> + <path d="m-326.43-18.086q0 0.64291-0.32386 1.0051-0.32385 0.35984-0.89479 0.35984-0.35264 0-0.62612-0.16552t-0.42221-0.47498q-0.14873-0.30946-0.14873-0.72447 0-0.64291 0.32145-1.0003 0.32146-0.35984 0.8924-0.35984 0.55175 0 0.8756 0.36703 0.32626 0.36704 0.32626 0.99315zm-2.0031 0q0 0.50377 0.20151 0.76765t0.59253 0.26388q0.39103 0 0.59254-0.26148 0.2039-0.26388 0.2039-0.77005 0-0.50137-0.2039-0.76046-0.20151-0.26148-0.59733-0.26148-0.39103 0-0.59014 0.25668-0.19911 0.25668-0.19911 0.76525z"/> + <path d="m-325.33-16.769h-0.39822v-3.7327h0.39822z"/> + <path d="m-324.09-16.769h-0.39822v-2.6292h0.39822zm-0.43181-3.3417q0-0.13674 0.0672-0.19911 0.0672-0.06477 0.16793-0.06477 0.0959 0 0.16552 0.06477 0.0696 0.06477 0.0696 0.19911t-0.0696 0.20151q-0.0696 0.06477-0.16552 0.06477-0.10076 0-0.16793-0.06477-0.0672-0.06717-0.0672-0.20151z"/> + <path d="m-322.19-16.721q-0.57094 0-0.8852-0.35024-0.31186-0.35264-0.31186-0.99555 0-0.6597 0.31666-1.0195 0.31906-0.35984 0.90679-0.35984 0.18951 0 0.37903 0.04078 0.18951 0.04078 0.29746 0.09596l-0.12234 0.33825q-0.13194-0.05278-0.28787-0.08636-0.15593-0.03598-0.27588-0.03598-0.80123 0-0.80123 1.0219 0 0.48458 0.19431 0.74366 0.19671 0.25908 0.58054 0.25908 0.32865 0 0.67409-0.14154v0.35264q-0.26388 0.13674-0.6645 0.13674z"/> + <path d="m-321.31-19.398h0.427l0.57574 1.4993q0.18952 0.51337 0.2351 0.74127h0.0192q0.0312-0.12234 0.12954-0.41741 0.10076-0.29747 0.65251-1.8232h0.427l-1.1299 2.9938q-0.16792 0.4438-0.39342 0.62852-0.2231 0.18712-0.54935 0.18712-0.18232 0-0.35984-0.04078v-0.31906q0.13194 0.02879 0.29507 0.02879 0.41021 0 0.58533-0.46059l0.14634-0.37423z"/> + </g> + </g> + </g> +</svg> diff --git a/etc-sai-EXAMPLE/builder/conf b/etc-sai-EXAMPLE/builder/conf new file mode 100644 index 0000000..83360cd --- /dev/null +++ b/etc-sai-EXAMPLE/builder/conf @@ -0,0 +1,41 @@ +{ + # + # where to find the nspawn contexts (scanned at runtime) + # + "systemd-nspawn": { + "path": "/home/sai/sai-nspawn", + "instances": 3, + "timeout": 1800 + }, + + # + # physically connected targets, like microconstroller boards + # + "target": [ + { + "name": "optee-hikey-arm64", + "timeout": 3600, + "consoles": [ + { + "name": "dbg", + "path": "/dev/by-id/usb-FTDI_FT232R_USB_UART_A50285BI-if00-port0", + "baudrate": 115200, + "mode": "n81" + } + ] + } + ], + + # + # masters we will accept jobs from + # + "masters": [ + { +# "url": "wss://warmcat.com/builder", + "url": "wss://localhost:443/builder", + "selfsigned": 1, + "priority": 1 + } + ] +} + diff --git a/etc-sai-EXAMPLE/master/conf b/etc-sai-EXAMPLE/master/conf new file mode 100644 index 0000000..58d027a --- /dev/null +++ b/etc-sai-EXAMPLE/master/conf @@ -0,0 +1,14 @@ +# these are the sai server global settings +# +# stuff related to each vhosts should go in one +# file per vhost in ./conf.d/ + +{ + "global": { + "uid": "48", # after init, run as apache user + "gid": "48", # after init, run as apache group + "server-string": "sai", + "init-ssl": "yes" + } +} + diff --git a/etc-sai-EXAMPLE/master/conf.d/localhost b/etc-sai-EXAMPLE/master/conf.d/localhost new file mode 100644 index 0000000..7ccda44 --- /dev/null +++ b/etc-sai-EXAMPLE/master/conf.d/localhost @@ -0,0 +1,127 @@ +# you should create one file like this per vhost in /etc/sai/conf.d + +{ + "vhosts": [{ + # this should match the external hostname for the vhost, + # like xyz.com + "name": "localhost", + # multiple vhosts can occupy the same port (SNI is used + # to resolve) + "port": "443", + # required for avatar cache + "enable-client-ssl": "on", + # "ipv6": "on", + "interface": "lo", + +# "ciphers": "DEFAULT", +# "tls13-ciphers": "DEFAULT", + + # You should store your real certificate key somewhere safer, + # eg on Fedora /etc/pki/tls/private, readable only by root. But + # since the right place for this differs by distro and these + # particular certs are not valuable, we get them from + # /usr/local/share where they are installed. + + "host-ssl-key": "/usr/local/share/sai/sai-selfsigned-key.pem", + "host-ssl-cert": "/usr/local/share/sai/sai-selfsigned-cert.pem", + + "mounts": [ + { + # static assets like js, css, fonts + "mountpoint": "/sai", + "default": "index.html", + "origin": "file:///usr/local/share/sai/assets", + "cache-max-age": "7200", + "cache-reuse": "1", + "cache-revalidate": "0", + "cache-intermediaries": "0", + "extra-mimetypes": { + ".zip": "application/zip", + ".map": "application/json", + ".ttf": "application/x-font-ttf" + } + }, + { + # semi-static cached avatar icons + "mountpoint": "/sai/avatar", + "origin": "callback://avatar-proxy", + "cache-max-age": "2400", + "cache-reuse": "1", + "cache-revalidate": "0", + "cache-intermediaries": "0" + } + ], + + # + # these headers, which will be sent on every transaction, make + # recent browsers definitively ban any external script sources, + # no matter what might manage to get injected later in the + # page. It's a last line of defence against any successful XSS. + # + + "headers": [{ + "content-security-policy": "default-src 'none'; img-src 'self' data: https://travis-ci.org https://api.travis-ci.org https://ci.appveyor.com https://scan.coverity.com; script-src 'self'; font-src 'self'; style-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none';", + "x-content-type-options": "nosniff", + "x-xss-protection": "1; mode=block", + "x-frame-options": "deny", + "referrer-policy": "no-referrer" + }], + + "ws-protocols": [ + + {"com-warmcat-sai": { + # template HTML to use for this vhost. You'd normally + # copy this to gitohashi-vhostname.html and modify it + # to show the content, logos, links, fonts, css etc for + # your vhost. It's not served directly but read from + # the filesystem. Although it's cached in memory by + # gitohashi, it checks for changes and reloads if + # changed automatically. + # + # Putting logos etc as svg in css is highly recommended, + # like fonts these can be transferred once with a loose + # caching policy. So in practice they cost very little, + # and allow the browser to compose the page without + # delay. + # + "html-file": "/usr/local/share/gitohashi/templates/gitohashi-example.html", + # + # vpath required at start of links into this vhost's + # gitohashi content for example if an external http + # server is proxying us, and has been told to direct + # URLs starting "/git" to us, this should be set to + # "/git/" so URLs we generate referring to our own pages + # can work. + # + "vpath": "/sai/", + # + # url mountpoint for the avatar cache + # + "avatar-url": "/sai/avatar/", + # + # libjsgit2 JSON cache... this + # should not be directly served + # + "cache-base": "/var/cache/libjsongit2", + # + # restrict the JSON cache size + # to 2GB + # + "cache-size": "2000000000", + # + # optional flags, b0 = 1 = blog mode + # + "flags": 0 + #"blog-repo-name": "myrepo" + }, + "avatar-proxy": { + "remote-base": "https://www.gravatar.com/avatar/", + # + # this dir is served via avatar-proxy + # + "cache-dir": "/var/cache/libjsongit2" + }} + ] + } + ] +} diff --git a/etc-sai-EXAMPLE/master/conf.d/unixskt b/etc-sai-EXAMPLE/master/conf.d/unixskt new file mode 100644 index 0000000..6c5fddc --- /dev/null +++ b/etc-sai-EXAMPLE/master/conf.d/unixskt @@ -0,0 +1,112 @@ +# you should create one file like this per vhost in /etc/sai/conf.d + +{ + "vhosts": [{ + # this has no special meaning but needs to be unique + "name": "unixskt", + "unix-socket": 1, + # multiple vhosts can exist with different unix skt names + "interface": "/var/run/sai-unixskt", + # required for avatar cache + "enable-client-ssl": "on", + + "mounts": [ + { + # static assets like js, css, fonts + "mountpoint": "/sai", + "default": "index.html", + "origin": "file:///usr/local/share/sai/assets", + "cache-max-age": "7200", + "cache-reuse": "1", + "cache-revalidate": "0", + "cache-intermediaries": "0", + "extra-mimetypes": { + ".zip": "application/zip", + ".map": "application/json", + ".ttf": "application/x-font-ttf" + } + }, + { + # semi-static cached avatar icons + "mountpoint": "/sai/avatar", + "origin": "callback://avatar-proxy", + "cache-max-age": "2400", + "cache-reuse": "1", + "cache-revalidate": "0", + "cache-intermediaries": "0" + } + ], + + # + # these headers, which will be sent on every transaction, make + # recent browsers definitively ban any external script sources, + # no matter what might manage to get injected later in the + # page. It's a last line of defence against any successful XSS. + # + + "headers": [{ + "content-security-policy": "default-src 'none'; img-src 'self' data: https://travis-ci.org https://api.travis-ci.org https://ci.appveyor.com https://scan.coverity.com; script-src 'self'; font-src 'self'; style-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none';", + "x-content-type-options": "nosniff", + "x-xss-protection": "1; mode=block", + "x-frame-options": "deny", + "referrer-policy": "no-referrer" + }], + + "ws-protocols": [ + + {"com-warmcat-sai": { + # template HTML to use for this vhost. You'd normally + # copy this to gitohashi-vhostname.html and modify it + # to show the content, logos, links, fonts, css etc for + # your vhost. It's not served directly but read from + # the filesystem. Although it's cached in memory by + # gitohashi, it checks for changes and reloads if + # changed automatically. + # + # Putting logos etc as svg in css is highly recommended, + # like fonts these can be transferred once with a loose + # caching policy. So in practice they cost very little, + # and allow the browser to compose the page without + # delay. + # + "html-file": "/usr/local/share/gitohashi/templates/gitohashi-example.html", + # + # vpath required at start of links into this vhost's + # gitohashi content for example if an external http + # server is proxying us, and has been told to direct + # URLs starting "/git" to us, this should be set to + # "/git/" so URLs we generate referring to our own pages + # can work. + # + "vpath": "/sai/", + # + # url mountpoint for the avatar cache + # + "avatar-url": "/sai/avatar/", + # + # libjsgit2 JSON cache... this + # should not be directly served + # + "cache-base": "/var/cache/libjsongit2", + # + # restrict the JSON cache size + # to 2GB + # + "cache-size": "2000000000", + # + # optional flags, b0 = 1 = blog mode + # + "flags": 0 + #"blog-repo-name": "myrepo" + }, + "avatar-proxy": { + "remote-base": "https://www.gravatar.com/avatar/", + # + # this dir is served via avatar-proxy + # + "cache-dir": "/var/cache/libjsongit2" + }} + ] + } + ] +} diff --git a/sai.json b/sai.json new file mode 100644 index 0000000..7e4290e --- /dev/null +++ b/sai.json @@ -0,0 +1,13 @@ +{ + "tests": [ + { + "maker": "linux-f29-x86_64", + "configs": [ + { "CMAKE_OPTS": "" }, + { "CMAKE_OPTS": "-DLWS_WITH_HTTP2=1" }, + { "CMAKE_OPTS": "-DLWS_WITHOUT_SERVER=1" } + ] + } + ] +} + diff --git a/scripts/gen-selfsigned-certs.sh b/scripts/gen-selfsigned-certs.sh new file mode 100755 index 0000000..261826b --- /dev/null +++ b/scripts/gen-selfsigned-certs.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +DEST=/usr/local/share/sai +if [ ! -z "$1" ] ; then + DEST=$1 +fi + +sudo openssl ecparam -name secp256r1 -genkey -out $DEST/sai-selfsigned-key.pem +echo -e "\n\n\n\n\n\n" | \ +sudo openssl req -new -x509 \ + -sha256 \ + -subj "/CN=localhost" \ + -days 10000 \ + -key $DEST/sai-selfsigned-key.pem \ + -out $DEST/sai-selfsigned-cert.pem +echo + diff --git a/src/builder/conf.c b/src/builder/conf.c new file mode 100644 index 0000000..deb8588 --- /dev/null +++ b/src/builder/conf.c @@ -0,0 +1,340 @@ +/* + * sai-builder conf.c + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <libwebsockets.h> +#include <string.h> +#include <signal.h> +#include <time.h> + +#include "private.h" + +static const char * const paths[] = { + "systemd-nspawn.path", + "systemd-nspawn.instances", + "systemd-nspawn.timeout", + + "target[]", + "target[].name", + "target[].timeout", + "target[].consoles[]", + "target[].consoles[].name", + "target[].consoles[].path", + "target[].consoles[].baudrate", + "target[].consoles[].mode", + + "masters[]", + "masters[].url", + "masters[].interface", + "masters[].selfsigned", + "masters[].priority", /* array of builder names */ +}; + +enum enum_paths { + LEJPM_NSPAWN_PATH, + LEJPM_NSPAWN_INSTANCES, + LEJPM_NSPAWN_TIMEOUT, + + LEJPM_TARGET, + LEJPM_TARGET_NAME, + LEJPM_TARGET_TIMEOUT, + LEJPM_TARGET_CONSOLES, + LEJPM_TARGET_CONSOLES_NAME, + LEJPM_TARGET_CONSOLES_PATH, + LEJPM_TARGET_CONSOLES_BAUDRATE, + LEJPM_TARGET_CONSOLES_MODE, + + LEJPM_MASTERS, + LEJPM_MASTERS_URL, + LEJPM_MASTERS_INTERFACE, + LEJPM_MASTERS_SELFSIGNED, + LEJPM_MASTERS_PRIORITY, +}; + +struct jpargs { + struct sai_builder *builder; + + struct sai_nspawn *nspawn; + struct sai_master *master; + struct sai_target *target; + struct sai_console *console; +}; + +#define SAI_BUILDER_CONF_CHUNK 512 +#define SAI_BUILDER_PATH_MAX 128 + +static int +sai_builder_scan_overlays_cb(const char *dirpath, void *user, + struct lws_dir_entry *lde) +{ + struct sai_nspawn *nspawn = (struct sai_nspawn *)user; + size_t s = strlen(lde->name); + struct sai_overlay *overlay; + + if (lde->type != LDOT_DIR) + return 0; + + if (strncmp(lde->name, "env-", 4)) + return 0; + + overlay = lwsac_use(&nspawn->builder->conf_head, sizeof(*overlay), + SAI_BUILDER_CONF_CHUNK); + if (!overlay) + return 1; + + memset(overlay, 0, sizeof(*overlay)); + overlay->name = lwsac_use(&nspawn->builder->conf_head, s + 1, + SAI_BUILDER_CONF_CHUNK); + if (!overlay->name) + return 1; + memcpy(overlay->name, lde->name, s + 1); + + lws_dll_add_front(&overlay->overlay_list, &nspawn->overlay_head); + + return 0; +} + +static int +sai_builder_scan_nspawn_cb(const char *dirpath, void *user, + struct lws_dir_entry *lde) +{ + struct sai_builder *builder = (struct sai_builder *)user; + size_t s = strlen(lde->name); + struct sai_nspawn *nspawn; + char path[256]; + + if (lde->type != LDOT_DIR) + return 0; + + nspawn = lwsac_use(&builder->conf_head, sizeof(*nspawn), + SAI_BUILDER_CONF_CHUNK); + if (!nspawn) + return 1; + + memset(nspawn, 0, sizeof(*nspawn)); + nspawn->builder = builder; + nspawn->name = lwsac_use(&builder->conf_head, s + 1, + SAI_BUILDER_CONF_CHUNK); + if (!nspawn->name) { + free(nspawn); + return 1; + } + memcpy((void *)nspawn->name, lde->name, s + 1); + + lws_dll_add_front(&nspawn->nspawn_list, &builder->nspawn_head); + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name); + + if (lws_dir(path, nspawn, sai_builder_scan_overlays_cb)) { + lwsl_err("%s: unable to scan nspawn dir %s\n", __func__, + builder->nspawn_path); + return 1; + } + + return 0; +} + +static signed char +sai_builder_conf_cb(struct lejp_ctx *ctx, char reason) +{ + struct jpargs *a = (struct jpargs *)ctx->user; + const char **pp; + int n; + +#if 0 + lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match); + for (n = 0; n < ctx->wildcount; n++) + lwsl_notice(" %d\n", ctx->wild[n]); +#endif + + if (reason == LEJPCB_OBJECT_START) { + switch (ctx->path_match - 1) { + case LEJPM_TARGET: + a->target = lwsac_use_zeroed(&a->builder->conf_head, + sizeof(*a->target), + SAI_BUILDER_CONF_CHUNK); + if (!a->target) + return 1; + lws_dll_add_front(&a->target->target_list, + &a->builder->target_head); + return 0; + + case LEJPM_TARGET_CONSOLES: + a->console = lwsac_use_zeroed(&a->builder->conf_head, + sizeof(*a->console), + SAI_BUILDER_CONF_CHUNK); + if (!a->console) + return 1; + lws_dll_add_front(&a->console->console_list, + &a->target->console_head); + return 0; + + case LEJPM_MASTERS: + a->master = lwsac_use_zeroed(&a->builder->conf_head, + sizeof(*a->master), + SAI_BUILDER_CONF_CHUNK); + if (!a->master) + return 1; + lws_dll_add_front(&a->master->master_list, + &a->builder->master_head); + return 0; + + default: + return 0; + } + } + + /* we only match on the prepared path strings */ + if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) + return 0; + + if (reason == LEJPCB_VAL_NUM_INT) { + n = atoi(ctx->buf); + switch (ctx->path_match - 1) { + case LEJPM_NSPAWN_INSTANCES: + a->builder->nspawn_instances = n; + break; + case LEJPM_NSPAWN_TIMEOUT: + a->builder->nspawn_timeout = n; + break; + case LEJPM_TARGET_TIMEOUT: + a->target->timeout = n; + break; + case LEJPM_TARGET_CONSOLES_BAUDRATE: + a->console->baudrate = n; + break; + case LEJPM_MASTERS_SELFSIGNED: + a->master->accept_selfsigned = n; + break; + case LEJPM_MASTERS_PRIORITY: + a->master->priority = n; + break; + default: + break; + } + + return 0; + } + + if (reason != LEJPCB_VAL_STR_END) + return 0; + + /* only the end part of the string, where we know the length */ + + switch (ctx->path_match - 1) { + case LEJPM_NSPAWN_PATH: + pp = &a->builder->nspawn_path; + break; + + case LEJPM_TARGET_NAME: + pp = &a->target->name; + break; + + case LEJPM_TARGET_CONSOLES_NAME: + pp = &a->console->name; + break; + + case LEJPM_TARGET_CONSOLES_PATH: + pp = &a->console->path; + break; + + case LEJPM_TARGET_CONSOLES_MODE: + pp = &a->console->mode; + break; + + case LEJPM_MASTERS_URL: + pp = &a->master->url; + break; + + case LEJPM_MASTERS_INTERFACE: + pp = &a->master->interface; + break; + + default: + return 0; + } + + *pp = lwsac_use(&a->builder->conf_head, ctx->npos + 1, + SAI_BUILDER_CONF_CHUNK); + if (!*pp) + return 1; + memcpy((char *)(*pp), ctx->buf, ctx->npos); + ((char *)(*pp))[ctx->npos] = '\0'; + + return 0; +} + +int +sai_builder_config(struct sai_builder *builder, const char *d) +{ + unsigned char buf[128]; + struct lejp_ctx ctx; + struct jpargs a; + int n, m, fd; + + memset(&a, 0, sizeof(a)); + a.builder = builder; + + lws_snprintf((char *)buf, sizeof(buf) - 1, "%s/conf", d); + + fd = lws_open((char *)buf, O_RDONLY); + if (fd < 0) { + lwsl_err("Cannot open %s\n", (char *)buf); + return 2; + } + lwsl_info("%s: %s\n", __func__, (char *)buf); + lejp_construct(&ctx, sai_builder_conf_cb, &a, paths, LWS_ARRAY_SIZE(paths)); + + do { + n = read(fd, buf, sizeof(buf)); + if (!n) + break; + + m = (int)(signed char)lejp_parse(&ctx, buf, n); + } while (m == LEJP_CONTINUE); + + close(fd); + n = ctx.line; + lejp_destruct(&ctx); + + if (m < 0) { + lwsl_err("%s/conf(%u): parsing error %d: %s\n", d, n, m, + lejp_error_to_string(m)); + return 2; + } + + /* scan the configured nspawn directory for what we support */ + + if (builder->nspawn_path && + lws_dir(builder->nspawn_path, builder, sai_builder_scan_nspawn_cb)) { + lwsl_err("%s: unable to scan nspawn dir %s\n", __func__, + builder->nspawn_path); + + return 1; + } + + return 0; +} + +void +sai_builder_config_destroy(struct sai_builder *builder) +{ + lwsac_free(&builder->conf_head); +} diff --git a/src/builder/private.h b/src/builder/private.h new file mode 100644 index 0000000..0bc1347 --- /dev/null +++ b/src/builder/private.h @@ -0,0 +1,99 @@ +/* + * Sai builder definitions src/builder/private.h + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +struct sai_master; +struct sai_builder; + +enum sai_lease_states { + SOS_IDLE, + SOS_RESERVED, + SOS_CONFIRMED +}; + +struct sai_lease { + struct sai_master *lessor; + enum sai_lease_states state; +}; + +struct sai_console { + struct lws_dll console_list; + + const char *name; + const char *path; + const char *mode; + int baudrate; +}; + +struct sai_target { + struct lws_dll target_list; + struct lws_dll console_head; + + const char *name; + + struct sai_lease lease; + + int timeout; +}; + +struct sai_master { + struct lws_dll master_list; + + struct lws *cwsi; + + const char *url; + const char *interface; + int priority; + + unsigned int accept_selfsigned:1; +}; + +struct sai_overlay { + struct lws_dll overlay_list; + + char *name; +}; + +struct sai_nspawn { + struct lws_dll nspawn_list; + struct lws_dll overlay_head; + + struct sai_builder *builder; + + const char *name; + + struct sai_lease lease; +}; + +struct sai_builder { + struct lws_dll master_head; + struct lws_dll target_head; + struct lws_dll nspawn_head; + + struct lwsac *conf_head; + + const char *nspawn_path; + int nspawn_instances; + int nspawn_timeout; +}; + +extern const struct lws_protocols protocol_com_warmcat_sai; +extern int sai_builder_config(struct sai_builder *builder, const char *d); +extern void sai_builder_config_destroy(struct sai_builder *builder); diff --git a/src/builder/protocol.c b/src/builder/protocol.c new file mode 100644 index 0000000..736b35a --- /dev/null +++ b/src/builder/protocol.c @@ -0,0 +1,344 @@ +/* + * sai-builder com-warmcat-sai client protocol implementation + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <libwebsockets.h> +#include <string.h> +#include <signal.h> + +#include "private.h" + +typedef enum { + PHASE_IDLE, + + PFL_FIRST = 128, + + PHASE_START_ATTACH = PFL_FIRST | 1, + PHASE_SUMM_TARGETS = 2, + PHASE_SUMM_NSPAWNS = 3, + PHASE_SUMM_NSPAWNS_OVERLAYS = 4, + +} cursor_phase_t; + +struct pss { + struct sai_target *target; + struct sai_console *console; + + struct sai_nspawn *nspawn; + struct sai_overlay *overlay; + + cursor_phase_t phase; +}; + +struct vhd { + struct lws_context *context; + struct lws_vhost *vhost; + + struct sai_builder builder; + + int *interrupted; + const char **config_dir; +}; + +static int +generate(struct vhd *vhd, struct pss *pss, char *buf, int len) +{ + struct sai_builder *b = &vhd->builder; + char *p = buf, *end = buf + len - 10; + + while (pss->phase != PHASE_IDLE && lws_ptr_diff(end, p) > 100) { + + switch (pss->phase) { + case PHASE_IDLE: + break; + + case PHASE_START_ATTACH: + p += lws_snprintf(p, lws_ptr_diff(end, p), + "{\"schema\":\"com-warmcat-sai-ba\",\n" + " \"hostname\":\"%s\",\n" + " \"nspawn-timeout\":%d,\n" + " \"targets\": [", + lws_canonical_hostname(vhd->context), + b->nspawn_timeout); + pss->target = lws_container_of(b->target_head.next, + struct sai_target, + target_list); + pss->phase = PHASE_SUMM_TARGETS; + break; + + case PHASE_SUMM_TARGETS: + if (pss->target) { + p += lws_snprintf(p, lws_ptr_diff(end, p), + "{\"name\":\"%s\"}", + pss->target->name); + if (pss->target->target_list.next) + *p++ = ','; + *p++ = '\n'; + *p = '\0'; + + pss->target = lws_container_of( + pss->target->target_list.next, + struct sai_target, target_list); + } + if (!pss->target) { + p += lws_snprintf(p, lws_ptr_diff(end, p), + "],\n\"nspawns\": ["); + pss->nspawn = lws_container_of( + b->nspawn_head.next, + struct sai_nspawn, nspawn_list); + pss->phase = PHASE_SUMM_NSPAWNS; + } + break; + + case PHASE_SUMM_NSPAWNS: + if (pss->nspawn) { + *p++ = '{'; + if (pss->nspawn->name) + p += lws_snprintf(p, lws_ptr_diff(end, p), + "\"name\":\"%s\",\n", + pss->nspawn->name); + p += lws_snprintf(p, lws_ptr_diff(end, p), + " \"overlays\": ["); + + pss->overlay = lws_container_of( + pss->nspawn->overlay_head.next, + struct sai_overlay, overlay_list); + if (pss->overlay) { + pss->phase = PHASE_SUMM_NSPAWNS_OVERLAYS; + break; + } +next_nspawn: + *p++ = ']'; + *p++ = '}'; + if (pss->nspawn->nspawn_list.next) + *p++ = ','; + *p++ = '\n'; + *p = '\0'; + pss->nspawn = lws_container_of( + pss->nspawn->nspawn_list.next, + struct sai_nspawn, nspawn_list); + } + if (!pss->nspawn) { + p += lws_snprintf(p, lws_ptr_diff(end, p), "]\n"); + p += lws_snprintf(p, lws_ptr_diff(end, p), "}\n"); + pss->phase = PHASE_IDLE; + } + break; + + case PHASE_SUMM_NSPAWNS_OVERLAYS: + if (pss->overlay) { + p += lws_snprintf(p, lws_ptr_diff(end, p), + "{\"name\":\"%s\"}\n", + pss->overlay->name); + pss->overlay = lws_container_of( + pss->overlay->overlay_list.next, + struct sai_overlay, overlay_list); + if (pss->overlay) { + + } + } + if (!pss->overlay) { + pss->phase = PHASE_SUMM_NSPAWNS; + goto next_nspawn; + } + break; + + default: + break; + } + } + + return lws_ptr_diff(p, buf); +} + +static int +connect_client(struct vhd *vhd, struct sai_master *master) +{ + struct lws_client_connect_info i; + const char *prot; + char url[128], host[64]; + + memset(&i, 0, sizeof(i)); + + strncpy(url, master->url, sizeof(url) - 1); + url[sizeof(url) - 1] = '\0'; + + if (lws_parse_uri(url, &prot, &i.address, &i.port, &i.path)) { + lwsl_err("%s: unable to parse url '%s'\n", __func__, url); + + return 1; + } + + lws_snprintf(host, sizeof(host), "%s:%u", i.address, i.port); + + i.context = vhd->context; + i.host = host; + i.origin = host; + + if (!strcmp(prot, "https") || !strcmp(prot, "wss")) + i.ssl_connection = LCCSCF_USE_SSL; + if (master->accept_selfsigned) + i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; + i.vhost = vhd->vhost; + i.iface = master->interface; + i.pwsi = &master->cwsi; + i.opaque_user_data = (void *)master; + + lwsl_user("connecting to %s://%s:%d/%s\n", prot, i.address, i.port, i.path); + + return !lws_client_connect_via_info(&i); +} + +static void +schedule_callback(struct lws *wsi, int reason, int secs) +{ + lws_timed_callback_vh_protocol(lws_get_vhost(wsi), + lws_get_protocol(wsi), reason, secs); +} + +static int +callback_com_warmcat_sai(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct pss *pss = (struct pss *)user; + struct vhd *vhd = (struct vhd *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + struct sai_master *master = + (struct sai_master *)lws_get_opaque_user_data(wsi); + uint8_t buf[1024 + LWS_PRE], *start = buf + LWS_PRE, + *end = buf + sizeof(buf) -1; + int n, m, f; + + switch (reason) { + + case LWS_CALLBACK_PROTOCOL_INIT: + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), + sizeof(struct vhd)); + if (!vhd) + return -1; + + vhd->context = lws_get_context(wsi); + vhd->vhost = lws_get_vhost(wsi); + + /* get the pointer to "interrupted" we were passed in pvo */ + vhd->interrupted = (int *)lws_pvo_search( + (const struct lws_protocol_vhost_options *)in, + "interrupted")->value; + vhd->config_dir = (const char **)lws_pvo_search( + (const struct lws_protocol_vhost_options *)in, + "configdir")->value; + + if (sai_builder_config(&vhd->builder, *vhd->config_dir)) { + lwsl_err("%s: config failed\n", __func__); + + return 1; + } + + schedule_callback(wsi, LWS_CALLBACK_USER, 1); + break; + + case LWS_CALLBACK_PROTOCOL_DESTROY: + sai_builder_config_destroy(&vhd->builder); + break; + + case LWS_CALLBACK_CLIENT_ESTABLISHED: + pss->phase = PHASE_START_ATTACH; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n"); + + if (pss->phase == PHASE_IDLE) + break; + + f = pss->phase & PFL_FIRST; + n = generate(vhd, pss, (char *)start, lws_ptr_diff(end, start)); + + m = lws_write(wsi, start, n, + lws_write_ws_flags(LWS_WRITE_TEXT, f, + pss->phase == PHASE_IDLE)); + if (m < n) { + lwsl_err("ERROR %d writing to ws socket\n", m); + return -1; + } + + if (pss->phase != PHASE_IDLE) + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLIENT_RECEIVE: + + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", + in ? (char *)in : "(null)"); + master->cwsi = NULL; + schedule_callback(wsi, LWS_CALLBACK_USER, 1); + break; + + case LWS_CALLBACK_CLIENT_CLOSED: + lwsl_user("LWS_CALLBACK_CLIENT_CLOSED\n"); + master->cwsi = NULL; + schedule_callback(wsi, LWS_CALLBACK_USER, 1); + break; + + /* rate-limited client connect retries */ + + case LWS_CALLBACK_USER: + /* + * let's retry any master connections that aren't either + * ongoing or happy + */ + + m = 0; + lws_start_foreach_dll(struct lws_dll *, mp, + vhd->builder.master_head.next) { + struct sai_master *ma = lws_container_of(mp, + struct sai_master, master_list); + + if (!ma->cwsi && connect_client(vhd, ma)) + m = 1; + } lws_end_foreach_dll(mp); + + /* + * if somebody's already unhappy, schedule a retry. Otherwise + * wait until a connection close or error to do it then. + */ + if (m) + schedule_callback(wsi, LWS_CALLBACK_USER, 1); + break; + + default: + break; + } + + return 0; +} + +const struct lws_protocols protocol_com_warmcat_sai = { + "com-warmcat-sai", + callback_com_warmcat_sai, + sizeof(struct pss), + 1024, 0, NULL, 0 +}; diff --git a/src/builder/sai-builder.c b/src/builder/sai-builder.c new file mode 100644 index 0000000..1760330 --- /dev/null +++ b/src/builder/sai-builder.c @@ -0,0 +1,114 @@ +/* + * sai-builder + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <libwebsockets.h> +#include <string.h> +#include <signal.h> + +#include "private.h" + +static struct lws_context *context; +static const char *config_dir = "/etc/sai/builder"; +static int interrupted; + +static const struct lws_protocols *pprotocols[] = { + &protocol_com_warmcat_sai, + NULL +}; + +static const struct lws_protocol_vhost_options pvo_configdir = { + NULL, + NULL, + "configdir", /* pvo name */ + (void *)&config_dir /* pvo value */ +}; + +static const struct lws_protocol_vhost_options pvo_interrupted = { + &pvo_configdir, + NULL, + "interrupted", /* pvo name */ + (void *)&interrupted /* pvo value */ +}; + +static const struct lws_protocol_vhost_options pvo = { + NULL, /* "next" pvo linked-list */ + &pvo_interrupted, /* "child" pvo linked-list */ + "com-warmcat-sai", /* protocol name we belong to on this vhost */ + "" /* ignored */ +}; +static const struct lws_extension extensions[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate" + "; client_no_context_takeover" + "; client_max_window_bits" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + const char *p; + int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("Sai Builder - " + "Copyright (C) 2019 Andy Green <andy@warmcat.com>\n"); + lwsl_user(" sai-builder [-c <config-file>]\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = CONTEXT_PORT_NO_LISTEN; + info.pprotocols = pprotocols; + info.pvo = &pvo; + if (!lws_cmdline_option(argc, argv, "-n")) + info.extensions = extensions; + info.pt_serv_buf_size = 32 * 1024; + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_VALIDATE_UTF8; + + if (lws_cmdline_option(argc, argv, "--libuv")) + info.options |= LWS_SERVER_OPTION_LIBUV; + else + signal(SIGINT, sigint_handler); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (!lws_service(context, 1000) && !interrupted) + ; + + lws_context_destroy(context); + + return 0; +} diff --git a/src/master/conf.c b/src/master/conf.c new file mode 100644 index 0000000..6aacd4d --- /dev/null +++ b/src/master/conf.c @@ -0,0 +1,82 @@ +/* + * Sai master src/master/conf.c + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include <libwebsockets.h> +#include <string.h> +#include <signal.h> +#include <time.h> + +#define SAI_CONFIG_STRING_SIZE (16 * 1024) + +struct lws_context * +sai_lws_context_from_json(const char *config_dir, + const struct lws_protocols **pprotocols) +{ + int cs_len = SAI_CONFIG_STRING_SIZE - 1; + struct lws_context_creation_info info; + struct lws_context *context; + char *cs, *config_strings; + + cs = config_strings = malloc(SAI_CONFIG_STRING_SIZE); + if (!config_strings) { + lwsl_err("Unable to allocate config strings heap\n"); + + return NULL; + } + + memset(&info, 0, sizeof(info)); + + info.external_baggage_free_on_destroy = config_strings; + info.pt_serv_buf_size = 8192; + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_EXPLICIT_VHOSTS | + LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | + LWS_SERVER_OPTION_VALIDATE_UTF8; + + lwsl_notice("Using config dir: \"%s\"\n", config_dir); + + /* + * first go through the config for creating the outer context + */ + if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len)) + goto init_failed; + + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("lws init failed\n"); + /* config_strings freed as 'external baggage' */ + return NULL; + } + + info.pprotocols = pprotocols; + + if (lwsws_get_config_vhosts(context, &info, config_dir, &cs, &cs_len)) { + lws_context_destroy(context); + + return NULL; + } + + return context; + +init_failed: + free(config_strings); + + return NULL; +} diff --git a/src/master/private.h b/src/master/private.h new file mode 100644 index 0000000..7ee6749 --- /dev/null +++ b/src/master/private.h @@ -0,0 +1,98 @@ +/* + * Sai master definitions src/master/private.h + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +struct sai_master; + +enum sai_lease_states { + SOS_IDLE, + SOS_RESERVED, + SOS_CONFIRMED, + SOS_OCCUPIED, + SOS_GOING_DOWN +}; + +typedef struct sai_console { + struct lws_dll console_list; + + const char *name; + const char *path; + const char *mode; + int baudrate; +} sai_console_t; + +typedef struct sai_target { + struct lws_dll target_list; + struct lws_dll console_head; + + const char *name; + + enum sai_lease_states state; +} sai_target_t; + +typedef struct sai_nspawn { + struct lws_dll nspawn_list; + + const char *name; + + enum sai_lease_states state; +} sai_nspawn_t; + +typedef struct sai_builder { + struct lws_dll builder_list; + struct lws_dll target_head; + struct lws_dll nspawn_head; + + struct lws *wsi; + + char *hostname; +} sai_builder_t; + +typedef enum { + SJS_CLONING, + SJS_ASSIGNING, + SJS_WAITING, + SJS_DONE +} sai_job_state_t; + +typedef struct sai_job { + struct lws_dll jobs_list; + char reponame[64]; + char ref[64]; + char head[64]; + + time_t requested; + + sai_job_state_t state; + +} sai_job_t; + +typedef struct sai_master { + struct lws_dll builder_head; + + struct lws_dll pending_jobs_head; + struct lws_dll ongoing_jobs_head; +} sai_master_t; + + +extern struct lws_context * +sai_lws_context_from_json(const char *config_dir, + const struct lws_protocols **pprotocols); +extern const struct lws_protocols protocol_ws; diff --git a/src/master/sai-master.c b/src/master/sai-master.c new file mode 100644 index 0000000..138703c --- /dev/null +++ b/src/master/sai-master.c @@ -0,0 +1,68 @@ +/* + * Sai master + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <libwebsockets.h> +#include <string.h> +#include <signal.h> +#include <time.h> + +#include "private.h" + +static int interrupted; + +static const struct lws_protocols + *pprotocols[] = { &protocol_ws, NULL }; + +static void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + struct lws_context *context; + const char *p, *conf = "/etc/sai/master"; + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("Sai Master - Copyright (C) 2019 Andy Green <andy@warmcat.com>\n"); + + if ((p = lws_cmdline_option(argc, argv, "-c"))) + conf = p; + + context = sai_lws_context_from_json(conf, pprotocols); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/src/master/ws.c b/src/master/ws.c new file mode 100644 index 0000000..19aec34 --- /dev/null +++ b/src/master/ws.c @@ -0,0 +1,317 @@ +/* + * Sai master + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * The same ws interface is connected-to by builders (on path /builder), and + * provides the query transport for browsers (on path /browse). + */ + +#include <libwebsockets.h> +#include <string.h> +#include <signal.h> +#include <time.h> + +#include <git2.h> + +#include "private.h" + +typedef enum { + SWT_BUILDER, + SWT_BROWSE +} ws_type; + +struct pss { + ws_type type; +}; + +struct vhd { + struct lws_context *context; + struct lws_vhost *vhost; + + struct sai_master master; +}; + +/* + * {"schema":"com-warmcat-sai-builder-attach", + "hostname":"learn", "targets": [{"name":"optee-hikey-arm64"} +], +{"nspawns": []} + */ + + +static const char * const paths[] = { + "schema", + "hostname", + "nspawn-timeout", + + "targets[]", + "targets[].name", + + "nspawns[]", + "nspawns[].name", + "nspawns[].overlays[]", + "nspawns[].overlays[].name", +}; + +enum enum_paths { + SBAJP_SCHEMA, + SBAJP_HOSTNAME, + SBAJP_NSPAWN_TIMEOUT, + + SBAJP_TARGETS, + SBAJP_TARGETS_NAME, + + SBAJP_NSPAWNS, + SBAJP_NSPAWNS_NAME, + SBAJP_NSPAWNS_OVERLAYS, + SBAJP_NSPAWNS_OVERLAYS_NAME, +}; + +static int +sai_master_nspawn_destroy(struct lws_dll *d) +{ + sai_nspawn_t *n = lws_container_of(d, sai_nspawn_t, nspawn_list); + + lws_dll_remove(&n->nspawn_list); + free(n); + + return 0; +} + +static int +sai_master_console_destroy(struct lws_dll *d) +{ + sai_console_t *c = lws_container_of(d, sai_console_t, console_list); + + lws_dll_remove(&c->console_list); + free(c); + + return 0; +} + +static int +sai_master_target_destroy(struct lws_dll *d) +{ + sai_target_t *t = lws_container_of(d, sai_target_t, target_list); + + lws_dll_foreach_safe(&t->console_head, sai_master_console_destroy); + + free(t); + + return 0; +} + +static int +sai_master_builder_destroy(struct lws_dll *d) +{ + sai_builder_t *b = lws_container_of(d, sai_builder_t, builder_list); + + lws_dll_foreach_safe(&b->nspawn_head, sai_master_nspawn_destroy); + lws_dll_foreach_safe(&b->target_head, sai_master_target_destroy); + + if (b->hostname) + free(b->hostname); + + free(b); + + return 0; +} + +static int +sai_destroy_job(struct lws_dll *d) +{ + sai_job_t *job = lws_container_of(d, sai_job_t, jobs_list); + + lws_dll_remove(d); + free(job); + + return 0; +} + +static void +sai_master_master_destroy(struct sai_master *master) +{ + lws_dll_foreach_safe(&master->builder_head, sai_master_builder_destroy); + + lws_dll_foreach_safe(&master->pending_jobs_head, sai_destroy_job); + lws_dll_foreach_safe(&master->ongoing_jobs_head, sai_destroy_job); +} + +static int +callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + struct vhd *vhd = (struct vhd *)lws_protocol_vh_priv_get( + lws_get_vhost(wsi), lws_get_protocol(wsi)); + uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start, + *end = &buf[sizeof(buf) - LWS_PRE - 1]; + struct pss *pss = (struct pss *)user; + struct sai_job *job; + const char *ccp; + int n; + + (void)end; + (void)p; + (void)paths; + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), + sizeof(struct vhd)); + if (!vhd) + return -1; + + vhd->context = lws_get_context(wsi); + vhd->vhost = lws_get_vhost(wsi); + break; + + case LWS_CALLBACK_PROTOCOL_DESTROY: + sai_master_master_destroy(&vhd->master); + break; + + case LWS_CALLBACK_HTTP: + + n = HTTP_STATUS_FORBIDDEN; + + if (strcmp((const char *)in, "/sai/update-hook")) { + lwsl_notice("%s: unknown path %s\n", __func__, + (const char *)in); + goto http_resp; + } + + job = malloc(sizeof(*job)); + if (!job) { + lwsl_err("%s: OOM\n", __func__); + + return 1; + } + + ccp = lws_get_urlarg_by_name(wsi, "rn=", (char *)start, + sizeof(job->reponame)); + if (!ccp || !*ccp) { + lwsl_notice("%s: missing rn=\n", __func__); + free(job); + goto http_resp; + } + lws_strncpy(job->reponame, ccp, sizeof(job->reponame)); + ccp = lws_get_urlarg_by_name(wsi, "ref=", (char *)start + 128, + sizeof(job->ref)); + if (!ccp || !*ccp) { + lwsl_notice("%s: missing ref=\n", __func__); + free(job); + goto http_resp; + } + lws_strncpy(job->ref, ccp, sizeof(job->ref)); + ccp = lws_get_urlarg_by_name(wsi, "head=", (char *)start + 256, + sizeof(job->head)); + if (!ccp || !*ccp) { + lwsl_notice("%s: missing ref=\n", __func__); + free(job); + goto http_resp; + } + lws_strncpy(job->head, ccp, sizeof(job->head)); + + job->requested = time(NULL); + + ccp = strrchr(job->ref + strlen(job->ref) - 1, '/'); + if (ccp) + ccp++; + + lwsl_user("%s: added job: %s %s %s\n", __func__, job->reponame, + ccp, job->head); + + { + git_clone_options opts; + git_repository *repo; + const char *url = "https://libwebsockets.org/repo/libwebsockets"; + + git_clone_init_options(&opts, GIT_CLONE_OPTIONS_VERSION); + opts.checkout_branch = ccp; + + if (git_clone(&repo, url, "/tmp", &opts)) { + lwsl_err("%s: unable to clone\n", __func__); + free(job); + goto http_resp; + } + + lwsl_user("%s: clone OK\n", __func__); + } + + lws_dll_add_front(&job->jobs_list, &vhd->master.pending_jobs_head); + n = HTTP_STATUS_OK; + + /* faillthru */ + +http_resp: + if (lws_add_http_header_status(wsi, n, &p, end)) + goto bail; + if (lws_add_http_header_content_length(wsi, 0, &p, end)) + goto bail; + if (lws_finalize_write_http_header(wsi, start, &p, end)) + goto bail; + goto try_to_reuse; + + case LWS_CALLBACK_ESTABLISHED: + + if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) { + if (lws_hdr_copy(wsi, (char *)start, 64, WSI_TOKEN_GET_URI) < 0) + return -1; + } else + if (lws_hdr_copy(wsi, (char *)start, 64, WSI_TOKEN_HTTP_COLON_PATH) < 0) + return -1; + + if (!strcmp((char *)start, "/browse")) { + n = SWT_BUILDER; + } else { + if (!strcmp((char *)start, "builder")) { + n = SWT_BROWSE; + } else { + lwsl_err("%s: unknown URL\n", __func__); + + return -1; + } + } + + pss->type = n; + break; + + case LWS_CALLBACK_RECEIVE: + lwsl_user("RECEIVE: %d\n", (int)len); + lwsl_user("%.*s\n", (int)len, (const char *)in); + break; + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); + +bail: + return 1; + +try_to_reuse: + if (lws_http_transaction_completed(wsi)) + return -1; + + return 0; +} + +const struct lws_protocols protocol_ws = + { "com-warmcat-sai", callback_ws, sizeof(struct pss), 0 };
Page fetched 0s ago, creation time: 49ms (vhost etag hits: 0%, cache hits: 0%)