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}