diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 53e5a5e2e9..2046ab5275 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -27,6 +27,7 @@ bind! { _emalloc, _zend_executor_globals, _sapi_globals_struct, + _sapi_module_struct, _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY, _zend_expected_type_Z_EXPECTED_BOOL, @@ -243,6 +244,7 @@ bind! { zend_class_unserialize_deny, zend_executor_globals, sapi_globals_struct, + sapi_module_struct, zend_objects_store_del, zend_hash_move_forward_ex, zend_hash_get_current_key_type_ex, @@ -254,6 +256,7 @@ bind! { ZEND_ACC_NOT_SERIALIZABLE, executor_globals, sapi_globals, + sapi_module, php_printf, __zend_malloc, tsrm_get_ls_cache, diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index dd3978a472..2617c4e6c6 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -201,6 +201,8 @@ pub type __time_t = ::std::os::raw::c_long; pub type __blksize_t = ::std::os::raw::c_long; pub type __blkcnt_t = ::std::os::raw::c_long; pub type __syscall_slong_t = ::std::os::raw::c_long; +pub type gid_t = __gid_t; +pub type uid_t = __uid_t; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __sigset_t { @@ -2265,6 +2267,12 @@ extern "C" { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct sapi_header_struct { + pub header: *mut ::std::os::raw::c_char, + pub header_len: usize, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct sapi_headers_struct { pub headers: zend_llist, pub http_response_code: ::std::os::raw::c_int, @@ -2273,6 +2281,10 @@ pub struct sapi_headers_struct { pub http_status_line: *mut ::std::os::raw::c_char, } pub type sapi_post_entry = _sapi_post_entry; +pub type sapi_module_struct = _sapi_module_struct; +extern "C" { + pub static mut sapi_module: sapi_module_struct; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct sapi_request_info { @@ -2323,6 +2335,111 @@ pub type sapi_globals_struct = _sapi_globals_struct; extern "C" { pub static mut sapi_globals: sapi_globals_struct; } +pub const sapi_header_op_enum_SAPI_HEADER_REPLACE: sapi_header_op_enum = 0; +pub const sapi_header_op_enum_SAPI_HEADER_ADD: sapi_header_op_enum = 1; +pub const sapi_header_op_enum_SAPI_HEADER_DELETE: sapi_header_op_enum = 2; +pub const sapi_header_op_enum_SAPI_HEADER_DELETE_ALL: sapi_header_op_enum = 3; +pub const sapi_header_op_enum_SAPI_HEADER_SET_STATUS: sapi_header_op_enum = 4; +pub type sapi_header_op_enum = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _sapi_module_struct { + pub name: *mut ::std::os::raw::c_char, + pub pretty_name: *mut ::std::os::raw::c_char, + pub startup: ::std::option::Option< + unsafe extern "C" fn(sapi_module: *mut _sapi_module_struct) -> ::std::os::raw::c_int, + >, + pub shutdown: ::std::option::Option< + unsafe extern "C" fn(sapi_module: *mut _sapi_module_struct) -> ::std::os::raw::c_int, + >, + pub activate: ::std::option::Option ::std::os::raw::c_int>, + pub deactivate: ::std::option::Option ::std::os::raw::c_int>, + pub ub_write: ::std::option::Option< + unsafe extern "C" fn(str_: *const ::std::os::raw::c_char, str_length: usize) -> usize, + >, + pub flush: + ::std::option::Option, + pub get_stat: ::std::option::Option *mut zend_stat_t>, + pub getenv: ::std::option::Option< + unsafe extern "C" fn( + name: *const ::std::os::raw::c_char, + name_len: usize, + ) -> *mut ::std::os::raw::c_char, + >, + pub sapi_error: ::std::option::Option< + unsafe extern "C" fn( + type_: ::std::os::raw::c_int, + error_msg: *const ::std::os::raw::c_char, + ... + ), + >, + pub header_handler: ::std::option::Option< + unsafe extern "C" fn( + sapi_header: *mut sapi_header_struct, + op: sapi_header_op_enum, + sapi_headers: *mut sapi_headers_struct, + ) -> ::std::os::raw::c_int, + >, + pub send_headers: ::std::option::Option< + unsafe extern "C" fn(sapi_headers: *mut sapi_headers_struct) -> ::std::os::raw::c_int, + >, + pub send_header: ::std::option::Option< + unsafe extern "C" fn( + sapi_header: *mut sapi_header_struct, + server_context: *mut ::std::os::raw::c_void, + ), + >, + pub read_post: ::std::option::Option< + unsafe extern "C" fn(buffer: *mut ::std::os::raw::c_char, count_bytes: usize) -> usize, + >, + pub read_cookies: ::std::option::Option *mut ::std::os::raw::c_char>, + pub register_server_variables: + ::std::option::Option, + pub log_message: ::std::option::Option< + unsafe extern "C" fn( + message: *const ::std::os::raw::c_char, + syslog_type_int: ::std::os::raw::c_int, + ), + >, + pub get_request_time: + ::std::option::Option zend_result>, + pub terminate_process: ::std::option::Option, + pub php_ini_path_override: *mut ::std::os::raw::c_char, + pub default_post_reader: ::std::option::Option, + pub treat_data: ::std::option::Option< + unsafe extern "C" fn( + arg: ::std::os::raw::c_int, + str_: *mut ::std::os::raw::c_char, + destArray: *mut zval, + ), + >, + pub executable_location: *mut ::std::os::raw::c_char, + pub php_ini_ignore: ::std::os::raw::c_int, + pub php_ini_ignore_cwd: ::std::os::raw::c_int, + pub get_fd: ::std::option::Option< + unsafe extern "C" fn(fd: *mut ::std::os::raw::c_int) -> ::std::os::raw::c_int, + >, + pub force_http_10: ::std::option::Option ::std::os::raw::c_int>, + pub get_target_uid: + ::std::option::Option ::std::os::raw::c_int>, + pub get_target_gid: + ::std::option::Option ::std::os::raw::c_int>, + pub input_filter: ::std::option::Option< + unsafe extern "C" fn( + arg: ::std::os::raw::c_int, + var: *const ::std::os::raw::c_char, + val: *mut *mut ::std::os::raw::c_char, + val_len: usize, + new_val_len: *mut usize, + ) -> ::std::os::raw::c_uint, + >, + pub ini_defaults: + ::std::option::Option, + pub phpinfo_as_text: ::std::os::raw::c_int, + pub ini_entries: *const ::std::os::raw::c_char, + pub additional_functions: *const zend_function_entry, + pub input_filter_init: ::std::option::Option ::std::os::raw::c_uint>, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _sapi_post_entry { diff --git a/src/ffi.rs b/src/ffi.rs index c658239af6..343b1fada2 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -27,6 +27,7 @@ extern "C" { pub fn ext_php_rs_zend_object_release(obj: *mut zend_object); pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; pub fn ext_php_rs_sapi_globals() -> *mut sapi_globals_struct; + pub fn ext_php_rs_sapi_module() -> *mut sapi_module_struct; pub fn ext_php_rs_zend_try_catch( func: unsafe extern "C" fn(*const c_void) -> *const c_void, ctx: *const c_void, diff --git a/src/wrapper.c b/src/wrapper.c index a708e57cd8..109b5aee9b 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -52,6 +52,10 @@ sapi_globals_struct *ext_php_rs_sapi_globals() { #endif } +sapi_module_struct *ext_php_rs_sapi_module() { + return &sapi_module; +} + bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result) { zend_try { *result = callback(ctx); diff --git a/src/wrapper.h b/src/wrapper.h index cb5c6678fb..883f1b812d 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -33,5 +33,6 @@ void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce); void ext_php_rs_zend_object_release(zend_object *obj); zend_executor_globals *ext_php_rs_executor_globals();; sapi_globals_struct *ext_php_rs_sapi_globals(); +sapi_module_struct *ext_php_rs_sapi_module(); bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result); void ext_php_rs_zend_bailout(); diff --git a/src/zend/globals.rs b/src/zend/globals.rs index cbea80a2b4..6a6bf70424 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -9,8 +9,8 @@ use crate::boxed::ZBox; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; use crate::ffi::{ - _sapi_globals_struct, _zend_executor_globals, ext_php_rs_executor_globals, - ext_php_rs_sapi_globals, zend_ini_entry, + _sapi_globals_struct, _sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals, + ext_php_rs_sapi_globals, ext_php_rs_sapi_module, zend_ini_entry, }; use crate::types::{ZendHashTable, ZendObject}; @@ -20,6 +20,9 @@ pub type ExecutorGlobals = _zend_executor_globals; /// Stores global SAPI variables used in the PHP executor. pub type SapiGlobals = _sapi_globals_struct; +/// Stores the SAPI module used in the PHP executor. +pub type SapiModule = _sapi_module_struct; + impl ExecutorGlobals { /// Returns a reference to the PHP executor globals. /// @@ -167,6 +170,40 @@ impl SapiGlobals { } } +impl SapiModule { + /// Returns a reference to the PHP SAPI module. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get() -> GlobalReadGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_sapi_module().as_ref() } + .expect("Static executor globals were invalid"); + let guard = SAPI_MODULE_LOCK.read(); + GlobalReadGuard { globals, guard } + } + + /// Returns a mutable reference to the PHP executor globals. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get_mut() -> GlobalWriteGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_sapi_module().as_mut() } + .expect("Static executor globals were invalid"); + let guard = SAPI_MODULE_LOCK.write(); + GlobalWriteGuard { globals, guard } + } +} + /// Executor globals rwlock. /// /// PHP provides no indication if the executor globals are being accessed so @@ -179,6 +216,12 @@ static GLOBALS_LOCK: RwLock<()> = const_rwlock(()); /// this is only effective on the Rust side. static SAPI_LOCK: RwLock<()> = const_rwlock(()); +/// SAPI globals rwlock. +/// +/// PHP provides no indication if the executor globals are being accessed so +/// this is only effective on the Rust side. +static SAPI_MODULE_LOCK: RwLock<()> = const_rwlock(()); + /// Wrapper guard that contains a reference to a given type `T`. Dropping a /// guard releases the lock on the relevant rwlock. pub struct GlobalReadGuard { diff --git a/src/zend/mod.rs b/src/zend/mod.rs index 67840f908d..5ef716115b 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -21,6 +21,7 @@ pub use function::Function; pub use function::FunctionEntry; pub use globals::ExecutorGlobals; pub use globals::SapiGlobals; +pub use globals::SapiModule; pub use handlers::ZendObjectHandlers; pub use ini_entry_def::IniEntryDef; pub use module::ModuleEntry;