nginx_sys/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![no_std]
4
5pub mod detail;
6mod event;
7#[cfg(ngx_feature = "http")]
8mod http;
9mod queue;
10mod rbtree;
11#[cfg(ngx_feature = "stream")]
12mod stream;
13mod string;
14
15use core::ptr;
16
17#[doc(hidden)]
18mod bindings {
19    #![allow(unknown_lints)] // unnecessary_transmutes
20    #![allow(missing_docs)]
21    #![allow(non_upper_case_globals)]
22    #![allow(non_camel_case_types)]
23    #![allow(non_snake_case)]
24    #![allow(dead_code)]
25    #![allow(clippy::all)]
26    #![allow(improper_ctypes)]
27    #![allow(rustdoc::broken_intra_doc_links)]
28    #![allow(unnecessary_transmutes)]
29    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
30}
31#[doc(no_inline)]
32pub use bindings::*;
33pub use event::*;
34#[cfg(ngx_feature = "http")]
35pub use http::*;
36pub use queue::*;
37pub use rbtree::*;
38#[cfg(ngx_feature = "stream")]
39pub use stream::*;
40
41/// Default alignment for pool allocations.
42pub const NGX_ALIGNMENT: usize = NGX_RS_ALIGNMENT;
43
44// Check if the allocations made with ngx_palloc are properly aligned.
45// If the check fails, objects allocated from `ngx_pool` can violate Rust pointer alignment
46// requirements.
47const _: () = assert!(core::mem::align_of::<ngx_str_t>() <= NGX_ALIGNMENT);
48
49impl ngx_array_t {
50    /// Returns the contents of this array as a slice of `T`.
51    ///
52    /// # Safety
53    ///
54    /// The array must be a valid, initialized array containing elements of type T or compatible in
55    /// layout with T (e.g. `#[repr(transparent)]` wrappers).
56    pub unsafe fn as_slice<T>(&self) -> &[T] {
57        debug_assert_eq!(
58            core::mem::size_of::<T>(),
59            self.size,
60            "ngx_array_t::as_slice(): element size mismatch"
61        );
62        if self.nelts == 0 {
63            &[]
64        } else {
65            // SAFETY: in a valid array, `elts` is a valid well-aligned pointer to at least `nelts`
66            // elements of size `size`
67            core::slice::from_raw_parts(self.elts.cast(), self.nelts)
68        }
69    }
70
71    /// Returns the contents of this array as a mutable slice of `T`.
72    ///
73    /// # Safety
74    ///
75    /// The array must be a valid, initialized array containing elements of type T or compatible in
76    /// layout with T (e.g. `#[repr(transparent)]` wrappers).
77    pub unsafe fn as_slice_mut<T>(&mut self) -> &mut [T] {
78        debug_assert_eq!(
79            core::mem::size_of::<T>(),
80            self.size,
81            "ngx_array_t::as_slice_mut(): element size mismatch"
82        );
83        if self.nelts == 0 {
84            &mut []
85        } else {
86            // SAFETY: in a valid array, `elts` is a valid well-aligned pointer to at least `nelts`
87            // elements of size `size`
88            core::slice::from_raw_parts_mut(self.elts.cast(), self.nelts)
89        }
90    }
91}
92
93impl ngx_command_t {
94    /// Creates a new empty [`ngx_command_t`] instance.
95    ///
96    /// This method replaces the `ngx_null_command` C macro. This is typically used to terminate an
97    /// array of configuration directives.
98    ///
99    /// [`ngx_command_t`]: https://nginx.org/en/docs/dev/development_guide.html#config_directives
100    pub const fn empty() -> Self {
101        Self {
102            name: ngx_str_t::empty(),
103            type_: 0,
104            set: None,
105            conf: 0,
106            offset: 0,
107            post: ptr::null_mut(),
108        }
109    }
110}
111
112impl ngx_module_t {
113    /// Create a new `ngx_module_t` instance with default values.
114    pub const fn default() -> Self {
115        Self {
116            ctx_index: ngx_uint_t::MAX,
117            index: ngx_uint_t::MAX,
118            name: ptr::null_mut(),
119            spare0: 0,
120            spare1: 0,
121            version: nginx_version as ngx_uint_t,
122            signature: NGX_RS_MODULE_SIGNATURE.as_ptr(),
123            ctx: ptr::null_mut(),
124            commands: ptr::null_mut(),
125            type_: 0,
126            init_master: None,
127            init_module: None,
128            init_process: None,
129            init_thread: None,
130            exit_thread: None,
131            exit_process: None,
132            exit_master: None,
133            spare_hook0: 0,
134            spare_hook1: 0,
135            spare_hook2: 0,
136            spare_hook3: 0,
137            spare_hook4: 0,
138            spare_hook5: 0,
139            spare_hook6: 0,
140            spare_hook7: 0,
141        }
142    }
143}
144
145impl ngx_variable_value_t {
146    /// Returns the contents of this variable value as a byte slice.
147    pub fn as_bytes(&self) -> &[u8] {
148        match self.len() {
149            0 => &[],
150            // SAFETY: data for non-empty value must be a valid well-aligned pointer.
151            len => unsafe { core::slice::from_raw_parts(self.data, len as usize) },
152        }
153    }
154}
155
156impl AsRef<[u8]> for ngx_variable_value_t {
157    fn as_ref(&self) -> &[u8] {
158        self.as_bytes()
159    }
160}
161
162/// Returns the error code of the last failed operation (`errno`).
163#[inline]
164pub fn ngx_errno() -> ngx_err_t {
165    // SAFETY: GetLastError takes no arguments and reads a thread-local variable
166    #[cfg(windows)]
167    let err = unsafe { GetLastError() };
168
169    #[cfg(not(windows))]
170    let err = errno::errno().0;
171
172    err as ngx_err_t
173}
174
175/// Sets the error code (`errno`).
176#[inline]
177pub fn ngx_set_errno(err: ngx_err_t) {
178    #[cfg(windows)]
179    // SAFETY: SetLastError takes one argument by value and updates a thread-local variable
180    unsafe {
181        SetLastError(err as _)
182    }
183    #[cfg(not(windows))]
184    errno::set_errno(errno::Errno(err as _))
185}
186
187/// Returns the error code of the last failed sockets operation.
188#[inline]
189pub fn ngx_socket_errno() -> ngx_err_t {
190    // SAFETY: WSAGetLastError takes no arguments and reads a thread-local variable
191    #[cfg(windows)]
192    let err = unsafe { WSAGetLastError() };
193
194    #[cfg(not(windows))]
195    let err = errno::errno().0;
196
197    err as ngx_err_t
198}
199
200/// Sets the error code of the sockets operation.
201#[inline]
202pub fn ngx_set_socket_errno(err: ngx_err_t) {
203    #[cfg(windows)]
204    // SAFETY: WSaSetLastError takes one argument by value and updates a thread-local variable
205    unsafe {
206        WSASetLastError(err as _)
207    }
208    #[cfg(not(windows))]
209    errno::set_errno(errno::Errno(err as _))
210}
211
212/// Returns a non cryptograhpically-secure pseudo-random integer.
213#[inline]
214pub fn ngx_random() -> core::ffi::c_long {
215    #[cfg(windows)]
216    unsafe {
217        // Emulate random() as Microsoft CRT does not provide it.
218        // rand() should be thread-safe in the multi-threaded CRT we link to, but will not be seeded
219        // outside of the main thread.
220        let x: u32 = ((rand() as u32) << 16) ^ ((rand() as u32) << 8) ^ (rand() as u32);
221        (0x7fffffff & x) as _
222    }
223    #[cfg(not(windows))]
224    unsafe {
225        random()
226    }
227}
228
229/// Causes the calling thread to relinquish the CPU.
230#[inline]
231pub fn ngx_sched_yield() {
232    #[cfg(windows)]
233    unsafe {
234        SwitchToThread()
235    };
236    #[cfg(all(not(windows), ngx_feature = "have_sched_yield"))]
237    unsafe {
238        sched_yield()
239    };
240    #[cfg(not(any(windows, ngx_feature = "have_sched_yield")))]
241    unsafe {
242        usleep(1)
243    }
244}
245
246/// Returns cached timestamp in seconds, updated at the start of the event loop iteration.
247///
248/// Can be stale when accessing from threads, see [ngx_time_update].
249#[inline]
250pub fn ngx_time() -> time_t {
251    // SAFETY: ngx_cached_time is initialized before any module code can run
252    unsafe { (*ngx_cached_time).sec }
253}
254
255/// Returns cached time, updated at the start of the event loop iteration.
256///
257/// Can be stale when accessing from threads, see [ngx_time_update].
258/// A cached reference to the ngx_timeofday() result is guaranteed to remain unmodified for the next
259/// NGX_TIME_SLOTS seconds.
260#[inline]
261pub fn ngx_timeofday() -> &'static ngx_time_t {
262    // SAFETY: ngx_cached_time is initialized before any module code can run
263    unsafe { &*ngx_cached_time }
264}
265
266/// Initialize a list, using a pool for the backing memory, with capacity to store the given number
267/// of elements and element size.
268///
269/// # Safety
270/// * `list` must be non-null
271/// * `pool` must be a valid pool
272#[inline]
273pub unsafe fn ngx_list_init(
274    list: *mut ngx_list_t,
275    pool: *mut ngx_pool_t,
276    n: ngx_uint_t,
277    size: usize,
278) -> ngx_int_t {
279    unsafe {
280        (*list).part.elts = ngx_palloc(pool, n * size);
281        if (*list).part.elts.is_null() {
282            return NGX_ERROR as ngx_int_t;
283        }
284        (*list).part.nelts = 0;
285        (*list).part.next = ptr::null_mut();
286        (*list).last = ptr::addr_of_mut!((*list).part);
287        (*list).size = size;
288        (*list).nalloc = n;
289        (*list).pool = pool;
290        NGX_OK as ngx_int_t
291    }
292}
293
294/// Add a key-value pair to an nginx table entry (`ngx_table_elt_t`) in the given nginx memory pool.
295///
296/// # Arguments
297///
298/// * `table` - A pointer to the nginx table entry (`ngx_table_elt_t`) to modify.
299/// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`) for memory allocation.
300/// * `key` - The key string to add to the table entry.
301/// * `value` - The value string to add to the table entry.
302///
303/// # Safety
304/// This function is marked as unsafe because it involves raw pointer manipulation and direct memory
305/// allocation using `str_to_uchar`.
306///
307/// # Returns
308/// An `Option<()>` representing the result of the operation. `Some(())` indicates success, while
309/// `None` indicates a null table pointer.
310///
311/// # Example
312/// ```rust
313/// # use nginx_sys::*;
314/// # unsafe fn example(pool: *mut ngx_pool_t, headers: *mut ngx_list_t) {
315/// // Obtain a pointer to the nginx table entry
316/// let table: *mut ngx_table_elt_t = ngx_list_push(headers).cast();
317/// assert!(!table.is_null());
318/// let key: &str = "key"; // The key to add
319/// let value: &str = "value"; // The value to add
320/// let result = add_to_ngx_table(table, pool, key, value);
321/// # }
322/// ```
323pub unsafe fn add_to_ngx_table(
324    table: *mut ngx_table_elt_t,
325    pool: *mut ngx_pool_t,
326    key: impl AsRef<[u8]>,
327    value: impl AsRef<[u8]>,
328) -> Option<()> {
329    if let Some(table) = table.as_mut() {
330        let key = key.as_ref();
331        table.key = ngx_str_t::from_bytes(pool, key)?;
332        table.value = ngx_str_t::from_bytes(pool, value.as_ref())?;
333        table.lowcase_key = ngx_pnalloc(pool, table.key.len).cast();
334        if table.lowcase_key.is_null() {
335            return None;
336        }
337        table.hash = ngx_hash_strlow(table.lowcase_key, table.key.data, table.key.len);
338        return Some(());
339    }
340    None
341}