@@ -18,6 +18,8 @@ use crate::{
1818} ;
1919use std:: io;
2020use std:: path:: { Path , PathBuf } ;
21+ use std:: sync:: atomic:: AtomicUsize ;
22+ use std:: sync:: atomic:: Ordering :: SeqCst ;
2123use thiserror:: Error ;
2224
2325use crate :: enterprise:: release_license;
@@ -32,6 +34,9 @@ use std::time::Duration;
3234
3335static MAIN_THREAD_HANDLE : Mutex < Option < JoinHandle < ( ) > > > = Mutex :: new ( None ) ;
3436
37+ /// Used to prevent shutting down Binary Ninja if there are other [`Session`]'s.
38+ static SESSION_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
39+
3540#[ derive( Error , Debug ) ]
3641pub enum InitializationError {
3742 #[ error( "main thread could not be started: {0}" ) ]
@@ -91,13 +96,29 @@ pub struct InitializationOptions {
9196 pub floating_license_duration : Duration ,
9297 /// The bundled plugin directory to use.
9398 pub bundled_plugin_directory : PathBuf ,
99+ /// Whether to initialize user plugins.
100+ ///
101+ /// Set this to false if your use might be impacted by a user installed plugin.
102+ pub user_plugins : bool ,
103+ /// Whether to initialize repo plugins.
104+ ///
105+ /// Set this to false if your use might be impacted by a repo installed plugin.
106+ pub repo_plugins : bool ,
94107}
95108
96109impl InitializationOptions {
97110 pub fn new ( ) -> Self {
98111 Self :: default ( )
99112 }
100113
114+ pub fn minimal ( ) -> Self {
115+ Self {
116+ user_plugins : false ,
117+ repo_plugins : false ,
118+ ..Self :: default ( )
119+ }
120+ }
121+
101122 /// A license to override with, you can use this to make sure you initialize with a specific license.
102123 ///
103124 /// This takes the form of a JSON array. The string should be formed like:
@@ -112,7 +133,7 @@ impl InitializationOptions {
112133 /// If you need to make sure that you do not check out a license set this to false.
113134 ///
114135 /// This is really only useful if you have a headless license but are using an enterprise enabled core.
115- pub fn with_checkout_license ( mut self , should_checkout : bool ) -> Self {
136+ pub fn with_license_checkout ( mut self , should_checkout : bool ) -> Self {
116137 self . checkout_license = should_checkout;
117138 self
118139 }
@@ -130,6 +151,18 @@ impl InitializationOptions {
130151 self . floating_license_duration = duration;
131152 self
132153 }
154+
155+ /// Set this to false if your use might be impacted by a user installed plugin.
156+ pub fn with_user_plugins ( mut self , should_initialize : bool ) -> Self {
157+ self . user_plugins = should_initialize;
158+ self
159+ }
160+
161+ /// Set this to false if your use might be impacted by a repo installed plugin.
162+ pub fn with_repo_plugins ( mut self , should_initialize : bool ) -> Self {
163+ self . repo_plugins = should_initialize;
164+ self
165+ }
133166}
134167
135168impl Default for InitializationOptions {
@@ -141,6 +174,8 @@ impl Default for InitializationOptions {
141174 floating_license_duration : Duration :: from_secs ( 900 ) ,
142175 bundled_plugin_directory : bundled_plugin_directory ( )
143176 . expect ( "Failed to get bundled plugin directory" ) ,
177+ user_plugins : true ,
178+ repo_plugins : true ,
144179 }
145180 }
146181}
@@ -188,8 +223,11 @@ pub fn init_with_opts(options: InitializationOptions) -> Result<(), Initializati
188223 set_bundled_plugin_directory ( options. bundled_plugin_directory ) ;
189224
190225 unsafe {
191- BNInitPlugins ( true ) ;
192- BNInitRepoPlugins ( ) ;
226+ BNInitPlugins ( options. user_plugins ) ;
227+ if options. repo_plugins {
228+ // We are allowed to initialize repo plugins, so do it!
229+ BNInitRepoPlugins ( ) ;
230+ }
193231 }
194232
195233 if !is_license_validated ( ) {
@@ -249,6 +287,14 @@ pub fn license_location() -> Option<LicenseLocation> {
249287pub struct Session { }
250288
251289impl Session {
290+ /// Get a registered [`Session`] for use.
291+ ///
292+ /// This is required so that we can keep track of the [`SESSION_COUNT`].
293+ fn registered_session ( ) -> Self {
294+ let _previous_count = SESSION_COUNT . fetch_add ( 1 , SeqCst ) ;
295+ Self { }
296+ }
297+
252298 /// Before calling new you must make sure that the license is retrievable, otherwise the core won't be able to initialize.
253299 ///
254300 /// If you cannot otherwise provide a license via `BN_LICENSE_FILE` environment variable or the Binary Ninja user directory
@@ -257,7 +303,7 @@ impl Session {
257303 if license_location ( ) . is_some ( ) {
258304 // We were able to locate a license, continue with initialization.
259305 init ( ) ?;
260- Ok ( Self { } )
306+ Ok ( Self :: registered_session ( ) )
261307 } else {
262308 // There was no license that could be automatically retrieved, you must call [Self::new_with_license].
263309 Err ( InitializationError :: NoLicenseFound )
@@ -270,7 +316,7 @@ impl Session {
270316 /// can discover by itself, therefor it is expected that you know where your license is when calling this directly.
271317 pub fn new_with_opts ( options : InitializationOptions ) -> Result < Self , InitializationError > {
272318 init_with_opts ( options) ?;
273- Ok ( Self { } )
319+ Ok ( Self :: registered_session ( ) )
274320 }
275321
276322 /// ```no_run
@@ -364,6 +410,10 @@ impl Session {
364410
365411impl Drop for Session {
366412 fn drop ( & mut self ) {
367- shutdown ( )
413+ let previous_count = SESSION_COUNT . fetch_sub ( 1 , SeqCst ) ;
414+ if previous_count == 1 {
415+ // We were the last session, therefor we can safely shut down.
416+ shutdown ( ) ;
417+ }
368418 }
369419}
0 commit comments