Compare commits

9 Commits
master ... dev

10 changed files with 2443 additions and 1782 deletions

314
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "ansi_term"
@@ -24,9 +24,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.1.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
@@ -34,6 +34,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "bstr"
version = "0.2.17"
@@ -45,15 +51,19 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.78"
version = "1.2.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clap"
@@ -63,7 +73,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"bitflags 1.3.2",
"strsim",
"textwrap",
"unicode-width",
@@ -71,10 +81,32 @@ dependencies = [
]
[[package]]
name = "getrandom"
version = "0.2.8"
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
@@ -91,25 +123,47 @@ dependencies = [
]
[[package]]
name = "libc"
version = "0.2.139"
name = "home"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.184"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "lua-src"
version = "544.0.1"
version = "546.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "708ba3c844d5e9d38def4a09dd871c17c370f519b3c4b7261fbabe4a613a814c"
checksum = "2da0daa7eee611a4c30c8f5ee31af55266e26e573971ba9336d2993e2da129b2"
dependencies = [
"cc",
]
[[package]]
name = "luad"
version = "0.1.0"
version = "0.1.3"
dependencies = [
"clap",
"lazy_static",
"libc",
"mlua",
"nix",
@@ -122,18 +176,19 @@ dependencies = [
[[package]]
name = "luajit-src"
version = "210.4.5+resty2cf5186"
version = "210.4.8+resty107baaf"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b7992a40e602786272d84c6f2beca44a588ededcfd57b48ec6f82008a7cb97"
checksum = "e05167e8b2a2185758d83ed23541e5bd8bce37072e4204e0ef2c9b322bc87c4e"
dependencies = [
"cc",
"which",
]
[[package]]
name = "memchr"
version = "2.5.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "memoffset"
@@ -146,9 +201,9 @@ dependencies = [
[[package]]
name = "mlua"
version = "0.8.7"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ee2ad7a9aa69056b148d9d590344bc155d3ce0d2200e3b2838f7034f6ba33c1"
checksum = "0bb37b0ba91f017aa7ca2b98ef99496827770cd635b4a932a6047c5b4bbe678e"
dependencies = [
"bstr",
"cc",
@@ -162,32 +217,31 @@ dependencies = [
[[package]]
name = "nix"
version = "0.26.1"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset",
"pin-utils",
"static_assertions",
]
[[package]]
name = "num-traits"
version = "0.2.15"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.0"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-utils"
@@ -197,30 +251,33 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.26"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.49"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -262,19 +319,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "serde"
version = "1.0.152"
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.11.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.152"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -282,10 +362,10 @@ dependencies = [
]
[[package]]
name = "static_assertions"
version = "1.1.0"
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "strsim"
@@ -295,9 +375,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.107"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -315,9 +395,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.10"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
@@ -340,15 +420,15 @@ checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"
[[package]]
name = "unicode-ident"
version = "1.0.6"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-width"
version = "0.1.10"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "vec_map"
@@ -358,9 +438,21 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "winapi"
@@ -383,3 +475,111 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -1,6 +1,6 @@
[package]
name = "luad"
version = "0.1.0"
version = "0.1.3"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -15,6 +15,7 @@ toml = "0.5"
libc = "0.2"
rand = "0.8.5"
twoway = "0.2.2"
lazy_static = "1.5.0"
[profile.release]
opt-level = 3

View File

@@ -5,8 +5,12 @@ socket = "unix:/tmp/lua1.sock"
pidfile = "/tmp/luad.pid"
# user name
user = "dany"
user = "root"
# group name
group = "dany"
debug = false
debug = true
workers = 5
log_backend = "stderr" # or syslog (default)

1316
src/fcgi.rs Normal file

File diff suppressed because it is too large Load Diff

1714
src/lib.rs

File diff suppressed because it is too large Load Diff

289
src/logs.rs Normal file
View File

@@ -0,0 +1,289 @@
use lazy_static::lazy_static;
use crate::{APP_VERSION, DAEMON_NAME};
use std::ffi::CString;
use std::fmt::Arguments;
use std::fmt::Write;
use std::sync::{Arc, Mutex};
lazy_static! {
static ref G_LOG_BACKEND: Arc<Mutex<LogBackend>> = Arc::new(Mutex::new(LogBackend::None));
}
/// LOG_MASK is used to create the priority mask in setlogmask
/// For a mask UPTO specified
/// used with [Priority]
///
#[macro_export]
macro_rules! LOG_UPTO
{
($($arg:tt)*) => (
((1 << (($($arg)*) + 1)) - 1)
)
}
/// Macro for error log helper
///
#[macro_export]
macro_rules! INFO {
($($args:tt)*) => ({
let prefix = format!(":info@[{}:{}]: ",file!(), line!());
let _ = crate::LogManager::log(&prefix[..], crate::LogLevel::INFO, format_args!($($args)*));
})
}
/// Macro for warning log helper
///
#[macro_export]
macro_rules! WARN {
($($args:tt)*) => ({
let prefix = format!(":warning@[{}:{}]: ",file!(), line!());
let _ = crate::LogManager::log(&prefix[..], crate::LogLevel::WARN, format_args!($($args)*));
})
}
/// Macro for info log helper
///
#[macro_export]
macro_rules! ERROR {
($($args:tt)*) => ({
let prefix = format!(":error@[{}:{}]: ",file!(), line!());
let _ = crate::LogManager::log(&prefix[..], crate::LogLevel::ERROR, format_args!($($args)*));
})
}
/// Macro for info log debug
///
#[macro_export]
macro_rules! DEBUG {
($($args:tt)*) => ({
let prefix = format!(":debug@[{}:{}]: ",file!(), line!());
let _ = crate::LogManager::log(&prefix[..], crate::LogLevel::DEBUG, format_args!($($args)*));
})
}
/// Different Logging levels for `LOG`
#[repr(i32)]
#[derive(Copy, Clone)]
pub enum LogLevel {
/// Error conditions
ERROR = libc::LOG_ERR,
/// Normal, but significant, condition
INFO = libc::LOG_NOTICE,
/// Warning conditions
WARN = libc::LOG_WARNING,
/// Debugs message
DEBUG = libc::LOG_INFO,
}
trait Log {
fn set_level(&mut self, level: LogLevel);
fn log(
&self,
prefix: &str,
level: LogLevel,
args: Arguments<'_>,
) -> Result<(), Box<dyn std::error::Error>>;
}
enum LogBackend {
SysLog(SysLog),
Stderr(StderrLog),
None,
}
/// Log struct wrapper
///
struct SysLog {}
impl SysLog {
/// Init the system log
///
/// This should be called only once in the entire lifetime
/// of the program, the returned LOG instance should
/// be keep alive during the lifetime of the program (the main function).
/// When it is dropped, the connection to the system log will be
/// closed automatically
fn new() -> Self {
// connect to the system log
unsafe {
libc::openlog(
std::ptr::null(),
libc::LOG_CONS | libc::LOG_PID | libc::LOG_NDELAY,
libc::LOG_DAEMON,
);
let _ = libc::setlogmask(LOG_UPTO!(libc::LOG_NOTICE));
}
Self {}
}
}
impl Log for SysLog {
/// Set log level
///
fn set_level(&mut self, level: LogLevel) {
unsafe {
let _ = libc::setlogmask(LOG_UPTO!(level as i32));
}
}
/// Wrapper function that log error or info message to the
/// connected syslog server
///
/// # Arguments
///
/// * `prefix` - Prefix of the log message
/// * `level` - Log level
/// * `args` - Arguments object representing a format string and its arguments
///
/// # Errors
///
/// * `std error` - All errors related to formatted and C string manipulation
fn log(
&self,
prefix: &str,
level: LogLevel,
args: Arguments<'_>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut output = String::new();
output.write_fmt(args)?;
let log_fmt = format!("{}(v{}){}%s", DAEMON_NAME, APP_VERSION, prefix);
let fmt = CString::new(log_fmt.as_bytes())?;
let c_msg = CString::new(output.as_bytes())?;
unsafe {
libc::syslog(level as i32, fmt.as_ptr(), c_msg.as_ptr());
}
Ok(())
}
}
impl Drop for SysLog {
/// The connection to the syslog will be closed
/// automatically when the log object is drop
fn drop(&mut self) {
// Close the current connection to the system logger
unsafe {
libc::closelog();
}
}
}
struct StderrLog {
level: LogLevel,
}
impl StderrLog {
pub fn new() -> Self {
Self {
level: LogLevel::INFO,
}
}
}
impl Log for StderrLog {
fn set_level(&mut self, level: LogLevel) {
self.level = level
}
/// Wrapper function that log error or info message to the
/// stderr
///
/// # Arguments
///
/// * `prefix` - Prefix of the log message
/// * `level` - Log level
/// * `args` - Arguments object representing a format string and its arguments
///
/// # Errors
///
/// * `std error` - All errors related to formatted and C string manipulation
fn log(
&self,
prefix: &str,
level: LogLevel,
args: Arguments<'_>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut output = String::new();
if (self.level as i32) < (level as i32) {
return Ok(());
}
output.write_fmt(args)?;
eprintln!("{}(v{}){}: {}", DAEMON_NAME, APP_VERSION, prefix, output);
Ok(())
}
}
impl Log for Arc<Mutex<LogBackend>> {
fn set_level(&mut self, level: LogLevel) {
match self.lock().as_deref_mut().unwrap() {
LogBackend::SysLog(log) => log.set_level(level),
LogBackend::Stderr(log) => log.set_level(level),
LogBackend::None => (),
}
}
fn log(
&self,
prefix: &str,
level: LogLevel,
args: Arguments<'_>,
) -> Result<(), Box<dyn std::error::Error>> {
match self.lock().as_deref().unwrap() {
LogBackend::SysLog(log) => log.log(prefix, level, args),
LogBackend::Stderr(log) => log.log(prefix, level, args),
LogBackend::None => Ok(()),
}
}
}
/// Public log manager structure
pub struct LogManager {}
impl LogManager {
/// Init the logging system
///
/// # Arguments
///
/// - `backend` (`Option<String>`) - backend name
///
pub fn init_log(backend: &Option<String>) {
*LogManager::log_instance().lock().as_deref_mut().unwrap() = match backend {
Some(s) if s == "syslog" => LogBackend::SysLog(SysLog::new()),
Some(s) if s == "stderr" => LogBackend::Stderr(StderrLog::new()),
Some(_) => LogBackend::None,
None => LogBackend::None,
};
}
fn log_instance() -> Arc<Mutex<LogBackend>> {
G_LOG_BACKEND.clone()
}
/// Set the log level
///
/// # Arguments
///
/// - `level` (`LogLevel`) - log level
///
pub fn set_level(level: LogLevel) {
LogManager::log_instance().set_level(level);
}
/// Log a message
///
/// # Arguments
///
/// - `prefix` (`&str`) - Log prefix
/// - `level` (`LogLevel`) - Log level
/// - `args` (`Arguments<'_>`) - Arguments
///
/// # Returns
///
/// - `Result<(), Box<dyn std::error::Error>>`
pub fn log(
prefix: &str,
level: LogLevel,
args: Arguments<'_>,
) -> Result<(), Box<dyn std::error::Error>> {
LogManager::log_instance().log(prefix, level, args)
}
}

240
src/luapi.rs Normal file
View File

@@ -0,0 +1,240 @@
use std::io::Write;
use crate::{ERR, ERROR};
use mlua::{prelude::*, Variadic};
/// Magic number for lua slice structure
const LUA_SLICE_MAGIC: usize = 0x8AD73B9F;
/// Convert a Lua Variadic to vector of bytes
///
/// # Arguments
///
/// - `values` (`mlua::Variadic<LuaValue>`) - input variadic
/// - `bin_only` (`bool`) - if true only convert Binary data
///
/// # Returns
///
/// - `Result<Vec<u8>, std::io::Error>`
///
pub fn vec_from_variadic(
values: Variadic<LuaValue>,
bin_only: bool,
) -> Result<Vec<u8>, std::io::Error> {
let mut output: Vec<u8> = Vec::new();
let error = ERR!("Unsupported data type");
for value in values {
match &value {
LuaNil => {}
LuaValue::Boolean(v) => {
if bin_only {
return Err(error);
}
output.extend(v.to_string().as_bytes());
}
LuaValue::Integer(v) => {
if bin_only {
return Err(error);
}
output.extend(v.to_string().as_bytes());
}
LuaValue::Number(v) => {
if bin_only {
return Err(error);
}
output.extend(v.to_string().as_bytes());
}
LuaValue::String(v) => {
output.extend(v.as_bytes());
}
LuaValue::LightUserData(_)
| LuaValue::Table(_)
| LuaValue::Function(_)
| LuaValue::Thread(_) => {
return Err(error);
}
LuaValue::UserData(v) => {
if v.is::<LuabyteArray>() {
let arr = v
.borrow::<LuabyteArray>()
.map_err(|e| ERR!(e.to_string()))?;
output.extend(&arr.0);
} else {
let st = value.to_pointer() as *const LuaSlice;
if unsafe { (*st).magic } != LUA_SLICE_MAGIC {
return Err(error);
}
let data_slice = unsafe { std::slice::from_raw_parts((*st).data, (*st).len) };
output.extend(data_slice);
}
}
LuaValue::Error(e) => {
return Err(ERR!(e.to_string()));
}
}
}
Ok(output)
}
/// Create a new empty LuaByteArray
///
/// # Arguments
///
/// - `_` (`&mlua::Lua`) - a Lua VM
/// - `size` (`usize`) - data size
///
/// # Returns
///
/// - `LuaResult<LuabyteArray>`
pub fn lua_new_bytes(_: &Lua, size: usize) -> LuaResult<LuabyteArray> {
let arr = LuabyteArray(vec![0; size]);
Ok(arr)
}
/// Create a LuaByteArray from a binary slice
///
/// # Arguments
///
/// - `_` (`&mlua::Lua`) - Lua VM
/// - `value` (`mlua::Value`) - LuaSlice value
///
/// # Returns
///
/// - `LuaResult<LuabyteArray>`
pub fn lua_new_from_slice(_: &Lua, value: mlua::Value) -> LuaResult<LuabyteArray> {
let st = value.to_pointer() as *const LuaSlice;
if unsafe { (*st).magic } != LUA_SLICE_MAGIC {
return Err(mlua::Error::external(ERR!("Unsupported data type")));
}
let data_slice = unsafe { std::slice::from_raw_parts((*st).data, (*st).len) };
Ok(LuabyteArray(data_slice.to_vec()))
}
/// Create a new LuaByteArray from string
///
/// # Arguments
///
/// - `_` (`&mlua::Lua`) - Lua VM
/// - `string` (`String`) - input string
///
/// # Returns
///
/// - `LuaResult<LuabyteArray>`
pub fn lua_new_bytes_from_string(_: &Lua, string: String) -> LuaResult<LuabyteArray> {
let arr = LuabyteArray(string.as_bytes().to_vec());
Ok(arr)
}
/// Custom LuaByteArray
///
/// # Fields
///
/// - `field_0` (`Vec<u8>`) - raw data
///
pub struct LuabyteArray(pub Vec<u8>);
impl mlua::UserData for LuabyteArray {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("size", |_, this: &LuabyteArray, ()| Ok(this.0.len()));
methods.add_method("into", |_, this: &LuabyteArray, value: mlua::Value| {
let st = value.to_pointer() as *mut LuaSlice;
if unsafe { (*st).magic } != LUA_SLICE_MAGIC {
return Err(mlua::Error::external(ERR!("Unsupported data type")));
}
unsafe {
(*st).data = this.0.as_ptr() as *const u8;
(*st).len = this.0.len();
}
Ok(())
});
methods.add_method("fileout", |_, this: &LuabyteArray, path: String| {
match std::fs::File::create(&path) {
Ok(mut file) => {
if let Err(error) = file.write_all(&this.0) {
ERROR!("Unable to write byte array to file {}: {}", &path, error);
return Ok(0);
}
}
Err(error) => {
ERROR!("Unable open file {}: {}", path, error);
return Ok(0);
}
}
Ok(1)
});
methods.add_meta_method(mlua::MetaMethod::Index, |_, this, index: isize| {
if index < 1 || index > this.0.len() as isize {
let error = ERR!(format!(
"Index {} out of bound, array size is {}",
index,
this.0.len()
));
ERROR!("{}", error);
return Ok(None);
}
Ok(Some(this.0[index as usize - 1]))
});
methods.add_meta_method(
mlua::MetaMethod::ToString,
|_, this, ()| match String::from_utf8(this.0.clone()) {
Err(error) => {
let err = format!("Unable to convert byte array to string: {}", error);
ERROR!("{}", err);
return Ok(None);
}
Ok(string) => Ok(Some(string)),
},
);
methods.add_method_mut("extend", |_, this, values: Variadic<mlua::Value>| {
let mut output = vec_from_variadic(values, true)
.map_err(|e| mlua::Error::external(ERR!(e.to_string())))?;
this.0.append(&mut output);
Ok(())
});
methods.add_meta_method_mut(
mlua::MetaMethod::NewIndex,
|_, this, (index, value): (isize, u8)| {
if index < 1 || index > this.0.len() as isize {
let error = ERR!(format!(
"Index {} out of bound, array size is {}",
index,
this.0.len()
));
ERROR!("{}", error);
} else {
this.0[index as usize - 1] = value;
}
Ok(())
},
);
methods.add_meta_method(mlua::MetaMethod::Len, |_, this, ()| Ok(this.0.len()));
}
}
/// LuaSlice
///
/// # Fields
///
/// - `magic` (`usize`) - LuaSlice magic number
/// - `len` (`usize`) - data size
/// - `data` (`*const u8`) - raw data value
///
#[repr(C)]
pub struct LuaSlice {
magic: usize,
len: usize,
data: *const u8,
}
/// public C API that return LuaSlice magic number
///
/// # Returns
///
/// - `usize` - magic number
///
#[no_mangle]
pub extern "C" fn lua_slice_magic() -> usize {
return LUA_SLICE_MAGIC;
}

View File

@@ -25,7 +25,8 @@ use std::os::fd::FromRawFd;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixListener;
use std::path::Path;
use std::thread;
const DEFAULT_WORKER_NUMBER: usize = 4;
/// Callback: clean up function
///
@@ -36,11 +37,17 @@ use std::thread;
///
/// * `n` - system exit code
fn clean_up(n: i32) {
if let Ok(socket_name) = std::env::var("socket") {
if let Ok(socket_name) = std::env::var("LUAD_SOCKET") {
let file = socket_name.replace("unix:", "");
let path = Path::new(&file);
if path.exists() {
std::fs::remove_file(path).unwrap();
let _ = std::fs::remove_file(path);
}
}
if let Ok(pidfile) = std::env::var("LUAD_PID") {
let path = Path::new(&pidfile);
if path.exists() {
let _ = std::fs::remove_file(path);
}
}
if n != 0 {
@@ -52,44 +59,72 @@ fn clean_up(n: i32) {
}
}
fn handle_request<T: Read + Write + AsRawFd>(stream: &mut T) {
if let Err(error) = process_request(stream) {
fn handle_request<T: Read + Write + AsRawFd>(mut stream: T) {
if let Err(error) = process_request(&mut stream) {
ERROR!("Unable to process request: {}", error);
}
DEBUG!("Request on socket {} is processed", stream.as_raw_fd());
}
trait Listener {
type Stream: Read + Write + AsRawFd + Send + 'static;
fn incoming(&self) -> Box<dyn Iterator<Item = std::io::Result<Self::Stream>> + '_>;
fn dispatch(&self, pool: &ThreadPool) {
for client in self.incoming() {
if let Ok(stream) = client {
pool.execute(move || handle_request(stream));
} else {
ERROR!(
"Unable to accept client connection: {}",
client.err().unwrap()
);
}
}
}
}
impl Listener for UnixListener {
type Stream = std::os::unix::net::UnixStream;
fn incoming(&self) -> Box<dyn Iterator<Item = std::io::Result<Self::Stream>> + '_> {
Box::new(self.incoming())
}
}
impl Listener for TcpListener {
type Stream = std::net::TcpStream;
fn incoming(&self) -> Box<dyn Iterator<Item = std::io::Result<Self::Stream>> + '_> {
Box::new(self.incoming())
}
}
/// Start the `fastCGI` server
///
/// # Arguments
///
/// * `socket_opt` - The socket string that the server listens on
fn serve(config: &Config) {
let pool = ThreadPool::new(config.workers.unwrap_or(DEFAULT_WORKER_NUMBER));
// bind to a socket if any
if let Some(socket_name) = config.socket.as_deref() {
// test if the socket name is an unix domain socket
if socket_name.starts_with("unix:") {
// e.g unix:/var/run/lighttpd/maint/efcgi.socket
INFO!("Use unix domain socket: {}", socket_name);
std::env::set_var("socket", socket_name);
std::env::set_var("LUAD_SOCKET", socket_name);
clean_up(0);
let listener = UnixListener::bind(socket_name.replace("unix:", "")).unwrap();
for client in listener.incoming() {
let mut stream = client.unwrap();
let _ = std::thread::spawn(move || {
handle_request(&mut stream);
});
let path = socket_name.replace("unix:", "");
let listener = UnixListener::bind(&path).unwrap();
INFO!("Allow writeable for group on {}", path);
if let Err(error) = chmod_file(&path, 0o660) {
ERROR!("Unable to allow writable for group on {}: {}", path, error);
}
listener.dispatch(&pool);
} else {
// TCP socket eg. 127.0.0.1:9000
INFO!("Use TCP socket: {}", socket_name);
let listener = TcpListener::bind(socket_name).unwrap();
for client in listener.incoming() {
let mut stream = client.unwrap();
let _ = thread::spawn(move || {
handle_request(&mut stream);
});
}
listener.dispatch(&pool);
}
} else {
// if there is no socket configuration, assume that the stdio is already mapped
@@ -100,23 +135,11 @@ fn serve(config: &Config) {
if is_unix_socket(fd).unwrap() {
DEBUG!("Stdin is used as Unix domain socket");
let listener = unsafe { UnixListener::from_raw_fd(stdin.as_raw_fd()) };
for client in listener.incoming() {
let mut stream = client.unwrap();
let _ = thread::spawn(move || {
handle_request(&mut stream);
});
}
listener.dispatch(&pool);
} else {
DEBUG!("Stdin is used as TCP Socket");
let listener = unsafe { TcpListener::from_raw_fd(stdin.as_raw_fd()) };
for client in listener.incoming() {
let mut stream = client.unwrap();
let _ = thread::spawn(move || {
handle_request(&mut stream);
});
}
listener.dispatch(&pool);
}
}
}
@@ -128,6 +151,8 @@ struct Config {
user: Option<String>,
group: Option<String>,
debug: bool,
workers: Option<usize>,
log_backend: Option<String>,
}
/// Main application entry
@@ -135,7 +160,6 @@ struct Config {
/// Run a `fastCGI` server
fn main() {
on_exit(clean_up);
let _log = LOG::init_log();
let matches = clap::App::new(DAEMON_NAME)
.author(APP_AUTHOR)
.about("Lua FastCGI daemon")
@@ -156,6 +180,8 @@ fn main() {
user: None,
group: None,
debug: false,
workers: None,
log_backend: Some("syslog".to_string()),
};
if let Ok(val) = std::env::var("debug") {
if val == "1" || val == "true" {
@@ -177,15 +203,17 @@ fn main() {
let mut f = std::fs::File::create(&pidfile).unwrap();
write!(f, "{}", std::process::id()).unwrap();
DEBUG!("PID file created at {}", pidfile);
std::env::set_var("LUAD_PID", pidfile);
}
None => {}
}
}
None => {}
}
LogManager::init_log(&config.log_backend);
if config.debug {
INFO!("Debug enabled");
LOG::enable_debug();
LogManager::set_level(LogLevel::DEBUG);
DEBUG!("Configuration: {:?}", config);
}
serve(&config);
}

110
src/pool.rs Normal file
View File

@@ -0,0 +1,110 @@
use std::sync::mpsc::{self, Receiver};
use std::sync::{Arc, Mutex};
use std::thread;
use crate::{ERR, ERROR, INFO, WARN};
/// A thread pool of workers
///
/// # Fields
///
/// - `workers` (`Vec<Worker>`) - all workers
/// - `dispatcher` (`Option<mpsc::Sender<Job>>`) - job dispatcher
///
pub struct ThreadPool {
workers: Vec<Worker>,
dispatcher: Option<mpsc::Sender<Job>>,
}
struct Worker {
id: usize,
thread: thread::JoinHandle<()>,
}
type Job = Box<dyn FnOnce() + Send + 'static>;
impl Worker {
pub fn new(id: usize, rx: Arc<Mutex<Receiver<Job>>>) -> Self {
let thread = thread::spawn(move || loop {
let message = rx.lock().unwrap().recv();
match message {
Ok(job) => job(),
Err(error) => {
WARN!(
"Unable to fetch job from dispatcher: {}. Shut down worker {}",
error,
id
);
break;
}
}
});
INFO!("Start new worker: {}", id);
Self { id, thread }
}
}
impl ThreadPool {
/// Create a new threadpool of workers
///
/// # Arguments
///
/// - `size` (`usize`) - number of worker
///
/// # Returns
///
/// - `Self`
///
pub fn new(size: usize) -> Self {
assert!(size > 0);
let (dispatcher, receiver) = mpsc::channel();
let dispatcher = Some(dispatcher);
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, receiver.clone()));
}
ThreadPool {
workers,
dispatcher,
}
}
/// Execute a job
///
/// # Arguments
///
/// - `&self` (`undefined`)
/// - `f` (`F: F: FnOnce() + Send + 'static,`) - Job handle
///
pub fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
if let Err(error) = self
.dispatcher
.as_ref()
.ok_or(ERR!("No sender found in dispatcher"))
.and_then(|sender| {
sender
.send(job)
.map_err(|_| ERR!("Unable to send job to worker"))
})
{
ERROR!("Error to dispatch request: {}", error);
}
}
}
impl Drop for ThreadPool {
fn drop(&mut self) {
drop(self.dispatcher.take());
for worker in self.workers.drain(..) {
INFO!("Dropping worker {}", worker.id);
if let Err(_) = worker.thread.join() {
WARN!("Unable to join worker {}", worker.id);
}
}
}
}

131
src/utils.rs Normal file
View File

@@ -0,0 +1,131 @@
use std::ffi::CString;
use crate::{ERR, INFO};
use nix;
/// Drop user privileges
///
/// This function drop the privileges of the current user
/// to another inferior privileges user.
/// e.g. drop from root->maint
///
/// # Arguments
///
/// * `user` - system user name
/// * `group` - system group name
///
/// # Errors
///
/// * `nix::Error` - The error from the nix package
pub fn privdrop(useropt: Option<&str>, groupopt: Option<&str>) -> Result<(), nix::Error> {
match groupopt {
Some(group) => {
INFO!("Dropping current process group to {}", group);
match nix::unistd::Group::from_name(group)? {
Some(group) => nix::unistd::setgid(group.gid),
None => Err(nix::Error::last()),
}?;
}
None => {}
}
match useropt {
Some(user) => {
INFO!("Dropping current process user to {}", user);
match nix::unistd::User::from_name(user)? {
Some(user) => nix::unistd::setuid(user.uid),
None => Err(nix::Error::last()),
}?
}
None => {}
}
Ok(())
}
/// Check if a file descriptor is an unix socket
///
/// # Arguments
///
/// - `fd` (`libc::c_int`) - input file descriptor
///
/// # Returns
///
/// - `Result<bool, std::io::Error>`
///
pub fn is_unix_socket(fd: libc::c_int) -> Result<bool, std::io::Error> {
unsafe {
let mut addr: libc::sockaddr_storage = std::mem::zeroed();
let mut addr_len: libc::socklen_t = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
let addr_storage: *mut libc::sockaddr_storage = &mut addr;
let ret = libc::getsockname(fd, addr_storage as *mut _, &mut addr_len);
if ret != 0 {
return Err(ERR!(format!("Unable to check socket: {}", fd)));
}
Ok(i32::from(addr.ss_family) == libc::AF_UNIX)
}
}
/// Utility function to catch common signal that
/// cause the program to exit
///
/// Signals catched: SIGABRT, SIGINT, SIGTERM, SIGQUIT
///
/// # Arguments
///
/// * `f` - callback function that will be called when a signal is trapped
pub fn on_exit(f: fn(n: i32) -> ()) {
unsafe {
let _ = libc::signal(libc::SIGPIPE, libc::SIG_IGN);
let _ = libc::signal(libc::SIGABRT, (f as *const std::ffi::c_void) as usize);
let _ = libc::signal(libc::SIGINT, (f as *const std::ffi::c_void) as usize);
let _ = libc::signal(libc::SIGTERM, (f as *const std::ffi::c_void) as usize);
let _ = libc::signal(libc::SIGQUIT, (f as *const std::ffi::c_void) as usize);
};
}
/// Change file mode
///
/// # Arguments
///
/// - `file` (`&str`) - input file
///
/// # Returns
///
/// - `Result<(), std::io::Error>`
///
pub fn chmod_file(file: &str, mode: u32) -> Result<(), std::io::Error> {
let c_path = CString::new(file)?;
if unsafe { libc::chmod(c_path.as_ptr(), mode) } != 0 {
return Err(ERR!(format!("Chmod failed: {}", file)));
}
Ok(())
}
/// Return an Error Result object from error string
///
#[macro_export]
macro_rules! ERR {
($x:expr) => {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("({}:{}): {}", file!(), line!(), $x),
)
};
}
/// Return an Error Result object from error string
///
#[macro_export]
macro_rules! OOB {
() => {
ERR!("Out of bound")
};
}
/// Bit value at address
///
#[macro_export]
macro_rules! BITV {
($v:expr,$i:expr) => {
($v & (1 << $i)) >> $i
};
}