[tor-commits] [tor/master] add minimal rust module for logging to tor's logger

nickm at torproject.org nickm at torproject.org
Mon Mar 19 21:20:46 UTC 2018


commit 3dfe8e6522460817100582a33a382be3c3efd988
Author: Chelsea Holland Komlo <me at chelseakomlo.com>
Date:   Mon Nov 27 22:59:54 2017 -0500

    add minimal rust module for logging to tor's logger
    
    Allows an optional no-op for testing purposes
---
 src/common/log.c                      |   7 +
 src/common/torlog.h                   |  10 ++
 src/or/main.c                         |   8 +-
 src/rust/Cargo.lock                   |  10 ++
 src/rust/Cargo.toml                   |   3 +-
 src/rust/protover/Cargo.toml          |   3 +
 src/rust/protover/lib.rs              |   1 +
 src/rust/protover/protover.rs         |  20 ++-
 src/rust/tor_allocate/tor_allocate.rs |  19 ++-
 src/rust/tor_log/Cargo.toml           |  18 +++
 src/rust/tor_log/lib.rs               |  17 +++
 src/rust/tor_log/tor_log.rs           | 236 ++++++++++++++++++++++++++++++++++
 src/rust/tor_util/Cargo.toml          |   3 +
 src/rust/tor_util/ffi.rs              |  13 +-
 src/rust/tor_util/lib.rs              |   1 +
 src/test/test_rust.sh                 |   2 +-
 16 files changed, 350 insertions(+), 21 deletions(-)

diff --git a/src/common/log.c b/src/common/log.c
index 608ffea95..ebe6c228c 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -52,6 +52,13 @@
 
 #define raw_assert(x) assert(x) // assert OK
 
+/** Defining compile-time constants for Tor log levels (used by the Rust
+ * log wrapper at src/rust/tor_log) */
+const int _LOG_WARN = LOG_WARN;
+const int _LOG_NOTICE = LOG_NOTICE;
+const log_domain_mask_t _LD_GENERAL = LD_GENERAL;
+const log_domain_mask_t _LD_NET = LD_NET;
+
 /** Information for a single logfile; only used in log.c */
 typedef struct logfile_t {
   struct logfile_t *next; /**< Next logfile_t in the linked list. */
diff --git a/src/common/torlog.h b/src/common/torlog.h
index be8a39a1b..0ed990b6e 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -31,6 +31,16 @@
  * "maximum severity" read "most severe" and "numerically *lowest* severity".
  */
 
+/** This defines log levels that are linked in the Rust log module, rather
+ * than re-defining these in both Rust and C.
+ *
+ * C_RUST_COUPLED src/rust/tor_log LogSeverity, LogDomain
+ */
+extern const int _LOG_WARN;
+extern const int _LOG_NOTICE;
+extern const log_domain_mask_t _LD_NET;
+extern const log_domain_mask_t _LD_GENERAL;
+
 /** Debug-level severity: for hyper-verbose messages of no interest to
  * anybody but developers. */
 #define LOG_DEBUG   7
diff --git a/src/or/main.c b/src/or/main.c
index e449b95b9..c19d4f690 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -132,7 +132,7 @@ void evdns_shutdown(int);
 #ifdef HAVE_RUST
 // helper function defined in Rust to output a log message indicating if tor is
 // running with Rust enabled. See src/rust/tor_util
-char *rust_welcome_string(void);
+void rust_welcome_string(void);
 #endif
 
 /********* PROTOTYPES **********/
@@ -3283,11 +3283,7 @@ tor_init(int argc, char *argv[])
   }
 
 #ifdef HAVE_RUST
-  char *rust_str = rust_welcome_string();
-  if (rust_str != NULL && strlen(rust_str) > 0) {
-    log_notice(LD_GENERAL, "%s", rust_str);
-  }
-  tor_free(rust_str);
+  rust_welcome_string();
 #endif /* defined(HAVE_RUST) */
 
   if (network_init()<0) {
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 224d2135b..116ef17f1 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -4,6 +4,7 @@ version = "0.0.1"
 dependencies = [
  "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "tor_allocate 0.0.1",
+ "tor_log 0.1.0",
 ]
 
 [[package]]
@@ -26,6 +27,7 @@ dependencies = [
  "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "smartlist 0.0.1",
  "tor_allocate 0.0.1",
+ "tor_log 0.1.0",
  "tor_util 0.0.1",
 ]
 
@@ -44,6 +46,14 @@ dependencies = [
 ]
 
 [[package]]
+name = "tor_log"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tor_allocate 0.0.1",
+]
+
+[[package]]
 name = "tor_rust"
 version = "0.1.0"
 dependencies = [
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 953c9b96b..4ae8033eb 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -1,5 +1,6 @@
 [workspace]
-members = ["tor_util", "protover", "smartlist", "external", "tor_allocate", "tor_rust"]
+members = ["tor_util", "protover", "smartlist", "external", "tor_allocate",
+"tor_rust", "tor_log"]
 
 [profile.release]
 debug = true
diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml
index 04d2f2ed7..279916bd2 100644
--- a/src/rust/protover/Cargo.toml
+++ b/src/rust/protover/Cargo.toml
@@ -18,6 +18,9 @@ path = "../tor_util"
 [dependencies.tor_allocate]
 path = "../tor_allocate"
 
+[dependencies.tor_log]
+path = "../tor_log"
+
 [lib]
 name = "protover"
 path = "lib.rs"
diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs
index 5a5dea440..a9e8e1ee6 100644
--- a/src/rust/protover/lib.rs
+++ b/src/rust/protover/lib.rs
@@ -26,6 +26,7 @@ extern crate libc;
 extern crate smartlist;
 extern crate external;
 extern crate tor_allocate;
+extern crate tor_log;
 
 mod protover;
 pub mod ffi;
diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs
index 7d5947ca2..1c159afef 100644
--- a/src/rust/protover/protover.rs
+++ b/src/rust/protover/protover.rs
@@ -1,13 +1,14 @@
 // Copyright (c) 2016-2017, The Tor Project, Inc. */
 // See LICENSE for licensing information */
 
-use external::c_tor_version_as_new_as;
-
 use std::str::FromStr;
 use std::fmt;
 use std::collections::{HashMap, HashSet};
 use std::string::String;
 
+use tor_log::*;
+use external::c_tor_version_as_new_as;
+
 /// The first version of Tor that included "proto" entries in its descriptors.
 /// Authorities should use this to decide whether to guess proto lines.
 ///
@@ -186,7 +187,6 @@ fn get_versions(version_string: &str) -> Result<HashSet<u32>, &'static str> {
     Ok(versions)
 }
 
-
 /// Parse the subprotocol type and its version numbers.
 ///
 /// # Inputs
@@ -240,6 +240,20 @@ fn get_proto_and_vers<'a>(
 fn contains_only_supported_protocols(proto_entry: &str) -> bool {
     let (name, mut vers) = match get_proto_and_vers(proto_entry) {
         Ok(n) => n,
+        Err("Too many versions to expand") => {
+            tor_log_msg!(
+                LogSeverity::Warn,
+                LogDomain::LdNet,
+                "get_versions",
+                "When expanding a protocol list from an authority, I
+                got too many protocols. This is possibly an attack or a bug,
+                unless the Tor network truly has expanded to support over {}
+                different subprotocol versions. The offending string was: {}",
+                MAX_PROTOCOLS_TO_EXPAND,
+                proto_entry
+            );
+            return false;
+        }
         Err(_) => return false,
     };
 
diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs
index 359df1cd7..3c0037f13 100644
--- a/src/rust/tor_allocate/tor_allocate.rs
+++ b/src/rust/tor_allocate/tor_allocate.rs
@@ -1,12 +1,17 @@
 // Copyright (c) 2016-2017, The Tor Project, Inc. */
 // See LICENSE for licensing information */
+// No-op defined purely for testing at the module level
+use libc::c_char;
 
-use libc::{c_char, c_void};
+#[cfg(not(feature = "testing"))]
 use std::{ptr, slice, mem};
+use libc::c_void;
 
-#[cfg(not(test))]
-extern "C" {
-    fn tor_malloc_(size: usize) -> *mut c_void;
+// Define a no-op implementation for testing Rust modules without linking to C
+#[cfg(feature = "testing")]
+pub fn allocate_and_copy_string(s: &String) -> *mut c_char {
+    use std::ffi::CString;
+    CString::new(s.as_str()).unwrap().into_raw()
 }
 
 // Defined only for tests, used for testing purposes, so that we don't need
@@ -17,6 +22,11 @@ unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void {
     malloc(size)
 }
 
+#[cfg(all(not(test), not(feature = "testing")))]
+extern "C" {
+    fn tor_malloc_(size: usize) -> *mut c_void;
+}
+
 /// Allocate memory using tor_malloc_ and copy an existing string into the
 /// allocated buffer, returning a pointer that can later be called in C.
 ///
@@ -28,6 +38,7 @@ unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void {
 ///
 /// A `*mut c_char` that should be freed by tor_free in C
 ///
+#[cfg(not(feature = "testing"))]
 pub fn allocate_and_copy_string(src: &String) -> *mut c_char {
     let bytes: &[u8] = src.as_bytes();
 
diff --git a/src/rust/tor_log/Cargo.toml b/src/rust/tor_log/Cargo.toml
new file mode 100644
index 000000000..f31d27b04
--- /dev/null
+++ b/src/rust/tor_log/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "tor_log"
+version = "0.1.0"
+authors = ["The Tor Project"]
+
+[lib]
+name = "tor_log"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
+[features]
+testing = []
+
+[dependencies]
+libc = "0.2.22"
+
+[dependencies.tor_allocate]
+path = "../tor_allocate"
diff --git a/src/rust/tor_log/lib.rs b/src/rust/tor_log/lib.rs
new file mode 100644
index 000000000..915910d00
--- /dev/null
+++ b/src/rust/tor_log/lib.rs
@@ -0,0 +1,17 @@
+//! Copyright (c) 2016-2017, The Tor Project, Inc. */
+//! See LICENSE for licensing information */
+
+//! Logging wrapper for Rust to utilize Tor's logger, found at
+//! src/common/log.c and src/common/torlog.h
+//!
+//! Exposes different interfaces depending on whether we are running in test
+//! or non-test mode. When testing, we use a no-op implementation,
+//! otherwise we link directly to C.
+
+extern crate libc;
+extern crate tor_allocate;
+
+mod tor_log;
+
+pub use tor_log::*;
+pub use tor_log::log::*;
diff --git a/src/rust/tor_log/tor_log.rs b/src/rust/tor_log/tor_log.rs
new file mode 100644
index 000000000..394e23244
--- /dev/null
+++ b/src/rust/tor_log/tor_log.rs
@@ -0,0 +1,236 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+
+/// The related domain which the logging message is relevant. For example,
+/// log messages relevant to networking would use LogDomain::LdNet, whereas
+/// general messages can use LdGeneral.
+#[derive(Eq, PartialEq)]
+pub enum LogDomain {
+    LdNet,
+    LdGeneral,
+}
+
+/// The severity level at which to log messages.
+#[derive(Eq, PartialEq)]
+pub enum LogSeverity {
+    Notice,
+    Warn,
+}
+
+/// Main entry point for Rust modules to log messages.
+///
+/// # Inputs
+///
+/// * A `severity` of type LogSeverity, which defines the level of severity the
+/// message will be logged.
+/// * A `domain` of type LogDomain, which defines the domain the log message
+/// will be associated with.
+/// * A `function` of type &str, which defines the name of the function where
+/// the message is being logged. There is a current RFC for a macro that
+/// defines function names. When it is, we should use it. See
+/// https://github.com/rust-lang/rfcs/pull/1719
+/// * A `message` of type &str, which is the log message itself.
+#[macro_export]
+macro_rules! tor_log_msg {
+    ($severity: path,
+     $domain: path,
+     $function: expr,
+     $($message:tt)*) =>
+    {
+        {
+            use std::ffi::CString;
+
+            /// Default function name to log in case of errors when converting
+            /// a function name to a CString
+            const ERR_LOG_FUNCTION: &'static str = "tor_log_msg";
+
+            /// Default message to log in case of errors when converting a log
+            /// message to a CString
+            const ERR_LOG_MSG: &'static str = "Unable to log message from Rust
+            module due to error when converting to CString";
+
+            let func = match CString::new($function) {
+                Ok(n) => n,
+                Err(_) => CString::new(ERR_LOG_FUNCTION).unwrap(),
+            };
+
+            let msg = match CString::new(format!($($message)*)) {
+                Ok(n) => n,
+                Err(_) => CString::new(ERR_LOG_MSG).unwrap(),
+            };
+
+            let func_ptr = func.as_ptr();
+            let msg_ptr = msg.as_ptr();
+
+            unsafe {
+                tor_log_string(translate_severity($severity),
+                translate_domain($domain),
+                func_ptr, msg_ptr
+                )
+            }
+        }
+    };
+}
+
+/// This module exposes no-op functionality purely for the purpose of testing
+/// Rust at the module level.
+#[cfg(any(test, feature = "testing"))]
+pub mod log {
+    use libc::{c_char, c_int};
+    use super::LogDomain;
+    use super::LogSeverity;
+
+    /// Expose a no-op logging interface purely for testing Rust modules at the
+    /// module level.
+    pub fn tor_log_string<'a>(
+        severity: c_int,
+        domain: u32,
+        function: *const c_char,
+        message: *const c_char,
+    ) -> (c_int, u32, String, String) {
+        use std::ffi::CStr;
+
+        let func = unsafe { CStr::from_ptr(function) }.to_str().unwrap();
+        let func_allocated = String::from(func);
+
+        let msg = unsafe { CStr::from_ptr(message) }.to_str().unwrap();
+        let msg_allocated = String::from(msg);
+        (severity, domain, func_allocated, msg_allocated)
+    }
+
+    pub unsafe fn translate_domain(_domain: LogDomain) -> u32 {
+        1
+    }
+
+    pub unsafe fn translate_severity(_severity: LogSeverity) -> c_int {
+        1
+    }
+}
+
+/// This implementation is used when compiling for actual use, as opposed to
+/// testing.
+#[cfg(all(not(test), not(feature = "testing")))]
+pub mod log {
+    use libc::{c_char, c_int};
+    use super::LogDomain;
+    use super::LogSeverity;
+
+    /// Severity log types. These mirror definitions in /src/common/torlog.h
+    /// C_RUST_COUPLED: src/common/log.c, log domain types
+    extern "C" {
+        #[no_mangle]
+        static _LOG_WARN: c_int;
+        static _LOG_NOTICE: c_int;
+    }
+
+    /// Domain log types. These mirror definitions in /src/common/torlog.h
+    /// C_RUST_COUPLED: src/common/log.c, log severity types
+    extern "C" {
+        #[no_mangle]
+        static _LD_NET: u32;
+        static _LD_GENERAL: u32;
+    }
+
+    /// Translate Rust defintions of log domain levels to C. This exposes a 1:1
+    /// mapping between types.
+    ///
+    /// Allow for default cases in case Rust and C log types get out of sync
+    #[allow(unreachable_patterns)]
+    pub unsafe fn translate_domain(domain: LogDomain) -> u32 {
+        match domain {
+            LogDomain::LdNet => _LD_NET,
+            LogDomain::LdGeneral => _LD_GENERAL,
+            _ => _LD_GENERAL,
+        }
+    }
+
+    /// Translate Rust defintions of log severity levels to C. This exposes a
+    /// 1:1 mapping between types.
+    ///
+    /// Allow for default cases in case Rust and C log types get out of sync
+    #[allow(unreachable_patterns)]
+    pub unsafe fn translate_severity(severity: LogSeverity) -> c_int {
+        match severity {
+            LogSeverity::Warn => _LOG_WARN,
+            LogSeverity::Notice => _LOG_NOTICE,
+            _ => _LOG_NOTICE,
+        }
+    }
+
+    /// The main entry point into Tor's logger. When in non-test mode, this
+    /// will link directly with `tor_log_string` in /src/or/log.c
+    extern "C" {
+        pub fn tor_log_string(
+            severity: c_int,
+            domain: u32,
+            function: *const c_char,
+            string: *const c_char,
+        );
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use tor_log::*;
+    use tor_log::log::*;
+
+    use libc::c_int;
+
+    #[test]
+    fn test_get_log_message() {
+
+        fn test_macro<'a>() -> (c_int, u32, String, String) {
+            let (x, y, z, a) =
+                tor_log_msg!(
+                    LogSeverity::Warn,
+                    LogDomain::LdNet,
+                    "test_macro",
+                    "test log message {}",
+                    "a",
+                    );
+            (x, y, z, a)
+        }
+
+        let (severity, domain, function_name, log_msg) = test_macro();
+
+        let expected_severity =
+            unsafe { translate_severity(LogSeverity::Warn) };
+        assert_eq!(severity, expected_severity);
+
+        let expected_domain = unsafe { translate_domain(LogDomain::LdNet) };
+        assert_eq!(domain, expected_domain);
+
+        assert_eq!("test_macro", function_name);
+        assert_eq!("test log message a", log_msg);
+    }
+
+    #[test]
+    fn test_get_log_message_multiple_values() {
+        fn test_macro<'a>() -> (c_int, u32, String, String) {
+            let (x, y, z, a) = tor_log_msg!(
+                LogSeverity::Warn,
+                LogDomain::LdNet,
+                "test_macro 2",
+                "test log message {} {} {} {}",
+                10,
+                9,
+                8,
+                7
+            );
+            (x, y, z, a)
+        }
+
+        let (severity, domain, function_name, log_msg) = test_macro();
+
+        let expected_severity =
+            unsafe { translate_severity(LogSeverity::Warn) };
+        assert_eq!(severity, expected_severity);
+
+        let expected_domain = unsafe { translate_domain(LogDomain::LdNet) };
+        assert_eq!(domain, expected_domain);
+
+        assert_eq!("test_macro 2", function_name);
+        assert_eq!("test log message 10 9 8 7", log_msg);
+    }
+}
diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml
index d7379a598..adc3390b5 100644
--- a/src/rust/tor_util/Cargo.toml
+++ b/src/rust/tor_util/Cargo.toml
@@ -11,6 +11,9 @@ crate_type = ["rlib", "staticlib"]
 [dependencies.tor_allocate]
 path = "../tor_allocate"
 
+[dependencies.tor_log]
+path = "../tor_log"
+
 [dependencies]
 libc = "0.2.22"
 
diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs
index 5c3cdba4b..866d11974 100644
--- a/src/rust/tor_util/ffi.rs
+++ b/src/rust/tor_util/ffi.rs
@@ -5,8 +5,7 @@
 //! called from C.
 //!
 
-use libc::c_char;
-use tor_allocate::allocate_and_copy_string;
+use tor_log::*;
 
 /// Returns a short string to announce Rust support during startup.
 ///
@@ -17,10 +16,12 @@ use tor_allocate::allocate_and_copy_string;
 /// tor_free(rust_str);
 /// ```
 #[no_mangle]
-pub extern "C" fn rust_welcome_string() -> *mut c_char {
-    let rust_welcome = String::from(
+pub extern "C" fn rust_welcome_string() {
+    tor_log_msg!(
+        LogSeverity::Notice,
+        LogDomain::LdGeneral,
+        "rust_welcome_string",
         "Tor is running with Rust integration. Please report \
-         any bugs you encounter.",
+        any bugs you encounter."
     );
-    allocate_and_copy_string(&rust_welcome)
 }
diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs
index 42fa9d5ad..86785c344 100644
--- a/src/rust/tor_util/lib.rs
+++ b/src/rust/tor_util/lib.rs
@@ -7,5 +7,6 @@
 
 extern crate libc;
 extern crate tor_allocate;
+extern crate tor_log;
 
 pub mod ffi;
diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh
index 133f2bb94..0268668b3 100755
--- a/src/test/test_rust.sh
+++ b/src/test/test_rust.sh
@@ -11,7 +11,7 @@ for crate in $crates; do
     cd "${abs_top_builddir:-../../..}/src/rust"
     CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \
       CARGO_HOME="${abs_top_builddir:-../../..}/src/rust" \
-      "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \
+      "${CARGO:-cargo}" test --all-features ${CARGO_ONLINE-"--frozen"} \
       --manifest-path "${abs_top_srcdir:-.}/src/rust/${crate}/Cargo.toml" \
 	|| exitcode=1
     cd -





More information about the tor-commits mailing list