Skip to main content

opcua_core/
lib.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5#![warn(missing_docs)]
6
7//! The OPC UA Core module holds functionality that is common to server and clients that make use of OPC UA.
8//! It contains message chunking, cryptography / pki, communications and standard handshake messages.
9
10/// Contains debugging utility helper functions
11pub mod debug {
12    use tracing::{enabled, trace};
13
14    /// Prints out the content of a slice in hex and visible char format to aid debugging. Format
15    /// is similar to corresponding functionality in node-opcua
16    pub fn log_buffer(message: &str, buf: &[u8]) {
17        // No point doing anything unless debug level is on
18        if !enabled!(target: "hex", tracing::Level::TRACE) {
19            return;
20        }
21
22        let line_len = 32;
23        let len = buf.len();
24        let last_line_padding = ((len / line_len) + 1) * line_len - len;
25
26        trace!(target: "hex", "{}", message);
27
28        let mut char_line = String::new();
29        let mut hex_line = format!("{:08x}: ", 0);
30
31        for (i, b) in buf.iter().enumerate() {
32            let value = { *b };
33            if i > 0 && i % line_len == 0 {
34                trace!(target: "hex", "{} {}", hex_line, char_line);
35                hex_line = format!("{i:08}: ");
36                char_line.clear();
37            }
38            hex_line = format!("{hex_line} {value:02x}");
39            char_line.push(if (32..=126).contains(&value) {
40                value as char
41            } else {
42                '.'
43            });
44        }
45        if last_line_padding > 0 {
46            for _ in 0..last_line_padding {
47                hex_line.push_str("   ");
48            }
49            trace!(target: "hex", "{} {}", hex_line, char_line);
50        }
51    }
52}
53
54#[cfg(test)]
55pub(crate) mod tests;
56
57/// Contains common OPC-UA constants.
58pub mod constants {
59    /// Default OPC UA port number. Used by a discovery server. Other servers would normally run
60    /// on a different port. So OPC UA for Rust does not use this nr by default but it is used
61    /// implicitly in opc.tcp:// urls and elsewhere.
62    pub const DEFAULT_OPC_UA_SERVER_PORT: u16 = 4840;
63}
64
65pub mod comms;
66pub mod config;
67pub mod handle;
68
69pub mod messages;
70use std::sync::atomic::AtomicBool;
71
72pub use messages::{Message, MessageType, RequestMessage, ResponseMessage};
73
74/// Check for the environment variable OPCUA_TRACE_LOCKS. If it is set to 1 or true, then
75/// tracing will be enabled for locks. This is useful for debugging deadlocks.
76pub fn trace_locks() -> bool {
77    static ENABLED: AtomicBool = AtomicBool::new(false);
78    if ENABLED.load(std::sync::atomic::Ordering::Relaxed) {
79        return true;
80    }
81    let enabled = match std::env::var("OPCUA_TRACE_LOCKS") {
82        Ok(s) => s != "0",
83        Err(_) => false,
84    };
85
86    ENABLED.store(enabled, std::sync::atomic::Ordering::Relaxed);
87
88    enabled
89}
90/// Re-export the tracing crate. This is used for logging and debugging.
91pub use tracing;
92
93/// Tracing macro for obtaining a lock on a `Mutex`. Sometimes deadlocks can happen in code,
94/// and if they do, this macro is useful for finding out where they happened.
95#[macro_export]
96macro_rules! trace_lock {
97    ( $x:expr ) => {{
98        use std::thread;
99        if $crate::trace_locks() {
100            $crate::tracing::trace!(
101                "Thread {:?}, {} locking at {}, line {}",
102                thread::current().id(),
103                stringify!($x),
104                file!(),
105                line!()
106            );
107        }
108        let v = $x.lock();
109        if $crate::trace_locks() {
110            $crate::tracing::trace!(
111                "Thread {:?}, {} lock completed",
112                thread::current().id(),
113                stringify!($x)
114            );
115        }
116        v
117    }};
118}
119
120/// Tracing macro for obtaining a read lock on a `RwLock`.
121#[macro_export]
122macro_rules! trace_read_lock {
123    ( $x:expr ) => {{
124        use std::thread;
125        if $crate::trace_locks() {
126            $crate::tracing::trace!(
127                "Thread {:?}, {} read locking at {}, line {}",
128                thread::current().id(),
129                stringify!($x),
130                file!(),
131                line!()
132            );
133        }
134        let v = $x.read();
135        if $crate::trace_locks() {
136            $crate::tracing::trace!(
137                "Thread {:?}, {} read lock completed",
138                thread::current().id(),
139                stringify!($x)
140            );
141        }
142        v
143    }};
144}
145
146/// Tracing macro for obtaining a write lock on a `RwLock`.
147#[macro_export]
148macro_rules! trace_write_lock {
149    ( $x:expr ) => {{
150        use std::thread;
151        if $crate::trace_locks() {
152            $crate::tracing::trace!(
153                "Thread {:?}, {} write locking at {}, line {}",
154                thread::current().id(),
155                stringify!($x),
156                file!(),
157                line!()
158            );
159        }
160        let v = $x.write();
161        if $crate::trace_locks() {
162            $crate::tracing::trace!(
163                "Thread {:?}, {} write lock completed",
164                thread::current().id(),
165                stringify!($x)
166            );
167        }
168        v
169    }};
170}
171
172/// Common synchronous locks. Re-exports locks from parking_lot used internally.
173pub mod sync {
174    /// Read-write lock. Use this if you usually only need to read the value.
175    pub type RwLock<T> = parking_lot::RwLock<T>;
176    /// Mutually exclusive lock. Use this if you need both read and write often.
177    pub type Mutex<T> = parking_lot::Mutex<T>;
178}