Skip to main content

rsfbclient_native/
services.rs

1//! Native service manager: a thin, safe wrapper over the
2//! `isc_service_attach` / `isc_service_start` / `isc_service_query` /
3//! `isc_service_detach` client entry points.
4//!
5//! This is the low-level half of the Services API: it owns the service
6//! handle and moves parameter blocks and reply buffers across the FFI
7//! boundary. The user-facing API (SPB construction, backup/restore
8//! actions, reply parsing) lives in the `rsfbclient` crate's `services`
9//! module, which drives this one.
10
11use crate::{connection::LinkageMarker, ibase, ibase::IBase, status::Status};
12use rsfbclient_core::FbError;
13
14/// A native attachment to a Firebird server's service manager
15/// (`service_mgr`).
16///
17/// Detaches automatically on drop.
18pub struct NativeServiceManager<T: LinkageMarker> {
19    ibase: T::L,
20    status: Status,
21    handle: ibase::isc_svc_handle,
22}
23
24#[cfg(feature = "linking")]
25impl NativeServiceManager<crate::connection::DynLink> {
26    /// Attach to `service_mgr` via the dynamically linked fbclient.
27    pub fn attach_dyn_link(host: &str, port: u16, attach_spb: &[u8]) -> Result<Self, FbError> {
28        Self::attach(ibase::IBaseLinking, host, port, attach_spb)
29    }
30}
31
32#[cfg(feature = "dynamic_loading")]
33impl NativeServiceManager<crate::connection::DynLoad> {
34    /// Attach to `service_mgr`, loading the fbclient library from
35    /// `lib_path` first.
36    pub fn attach_dyn_load(
37        lib_path: &str,
38        host: &str,
39        port: u16,
40        attach_spb: &[u8],
41    ) -> Result<Self, FbError> {
42        let lib = ibase::IBaseDynLoading::with_client(lib_path.as_ref())
43            .map_err(|e| FbError::from(e.to_string()))?;
44        Self::attach(lib, host, port, attach_spb)
45    }
46}
47
48impl<T: LinkageMarker> NativeServiceManager<T> {
49    /// Attach to `service_mgr` on `host:port` with the supplied
50    /// attach-SPB (credentials).
51    ///
52    /// `ibase` is the loaded/linked client library, obtained the same
53    /// way the connection builders obtain theirs.
54    fn attach(ibase: T::L, host: &str, port: u16, attach_spb: &[u8]) -> Result<Self, FbError> {
55        let mut status: Status = Default::default();
56        let mut handle: ibase::isc_svc_handle = 0;
57
58        let conn_string = format!("{}/{}:service_mgr", host, port);
59
60        unsafe {
61            if ibase.isc_service_attach()(
62                &mut status[0],
63                conn_string.len() as u16,
64                conn_string.as_ptr() as *const _,
65                &mut handle,
66                attach_spb.len() as u16,
67                attach_spb.as_ptr() as *const _,
68            ) != 0
69            {
70                return Err(status.as_error(&ibase));
71            }
72        }
73
74        debug_assert_ne!(handle, 0);
75
76        Ok(Self {
77            ibase,
78            status,
79            handle,
80        })
81    }
82
83    /// Start a service action (`isc_action_svc_*` request block).
84    pub fn start(&mut self, request: &[u8]) -> Result<(), FbError> {
85        unsafe {
86            if self.ibase.isc_service_start()(
87                &mut self.status[0],
88                &mut self.handle,
89                std::ptr::null_mut(),
90                request.len() as u16,
91                request.as_ptr() as *const _,
92            ) != 0
93            {
94                return Err(self.status.as_error(&self.ibase));
95            }
96        }
97        Ok(())
98    }
99
100    /// Run one information request against the service, filling `buffer`
101    /// with the reply clumplets.
102    pub fn query(
103        &mut self,
104        send_items: &[u8],
105        receive_items: &[u8],
106        buffer: &mut [u8],
107    ) -> Result<(), FbError> {
108        unsafe {
109            if self.ibase.isc_service_query()(
110                &mut self.status[0],
111                &mut self.handle,
112                std::ptr::null_mut(),
113                send_items.len() as u16,
114                send_items.as_ptr() as *const _,
115                receive_items.len() as u16,
116                receive_items.as_ptr() as *const _,
117                buffer.len() as u16,
118                buffer.as_mut_ptr() as *mut _,
119            ) != 0
120            {
121                return Err(self.status.as_error(&self.ibase));
122            }
123        }
124        Ok(())
125    }
126
127    /// Detach from the service manager. Called automatically on drop.
128    pub fn detach(&mut self) -> Result<(), FbError> {
129        unsafe {
130            if self.handle != 0
131                && self.ibase.isc_service_detach()(&mut self.status[0], &mut self.handle) != 0
132            {
133                return Err(self.status.as_error(&self.ibase));
134            }
135        }
136        self.handle = 0;
137        Ok(())
138    }
139}
140
141impl<T: LinkageMarker> Drop for NativeServiceManager<T> {
142    fn drop(&mut self) {
143        self.detach().ok();
144    }
145}