@@ -18,6 +18,8 @@ use crate::{
18
18
} ;
19
19
use std:: io;
20
20
use std:: path:: { Path , PathBuf } ;
21
+ use std:: sync:: atomic:: AtomicUsize ;
22
+ use std:: sync:: atomic:: Ordering :: SeqCst ;
21
23
use thiserror:: Error ;
22
24
23
25
use crate :: enterprise:: release_license;
@@ -32,6 +34,9 @@ use std::time::Duration;
32
34
33
35
static MAIN_THREAD_HANDLE : Mutex < Option < JoinHandle < ( ) > > > = Mutex :: new ( None ) ;
34
36
37
+ /// Used to prevent shutting down Binary Ninja if there are other [`Session`]'s.
38
+ static SESSION_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
39
+
35
40
#[ derive( Error , Debug ) ]
36
41
pub enum InitializationError {
37
42
#[ error( "main thread could not be started: {0}" ) ]
@@ -91,13 +96,29 @@ pub struct InitializationOptions {
91
96
pub floating_license_duration : Duration ,
92
97
/// The bundled plugin directory to use.
93
98
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 ,
94
107
}
95
108
96
109
impl InitializationOptions {
97
110
pub fn new ( ) -> Self {
98
111
Self :: default ( )
99
112
}
100
113
114
+ pub fn minimal ( ) -> Self {
115
+ Self {
116
+ user_plugins : false ,
117
+ repo_plugins : false ,
118
+ ..Self :: default ( )
119
+ }
120
+ }
121
+
101
122
/// A license to override with, you can use this to make sure you initialize with a specific license.
102
123
///
103
124
/// This takes the form of a JSON array. The string should be formed like:
@@ -112,7 +133,7 @@ impl InitializationOptions {
112
133
/// If you need to make sure that you do not check out a license set this to false.
113
134
///
114
135
/// 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 {
116
137
self . checkout_license = should_checkout;
117
138
self
118
139
}
@@ -130,6 +151,18 @@ impl InitializationOptions {
130
151
self . floating_license_duration = duration;
131
152
self
132
153
}
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
+ }
133
166
}
134
167
135
168
impl Default for InitializationOptions {
@@ -141,6 +174,8 @@ impl Default for InitializationOptions {
141
174
floating_license_duration : Duration :: from_secs ( 900 ) ,
142
175
bundled_plugin_directory : bundled_plugin_directory ( )
143
176
. expect ( "Failed to get bundled plugin directory" ) ,
177
+ user_plugins : true ,
178
+ repo_plugins : true ,
144
179
}
145
180
}
146
181
}
@@ -188,8 +223,11 @@ pub fn init_with_opts(options: InitializationOptions) -> Result<(), Initializati
188
223
set_bundled_plugin_directory ( options. bundled_plugin_directory ) ;
189
224
190
225
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
+ }
193
231
}
194
232
195
233
if !is_license_validated ( ) {
@@ -249,6 +287,14 @@ pub fn license_location() -> Option<LicenseLocation> {
249
287
pub struct Session { }
250
288
251
289
impl 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
+
252
298
/// Before calling new you must make sure that the license is retrievable, otherwise the core won't be able to initialize.
253
299
///
254
300
/// 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 {
257
303
if license_location ( ) . is_some ( ) {
258
304
// We were able to locate a license, continue with initialization.
259
305
init ( ) ?;
260
- Ok ( Self { } )
306
+ Ok ( Self :: registered_session ( ) )
261
307
} else {
262
308
// There was no license that could be automatically retrieved, you must call [Self::new_with_license].
263
309
Err ( InitializationError :: NoLicenseFound )
@@ -270,7 +316,7 @@ impl Session {
270
316
/// can discover by itself, therefor it is expected that you know where your license is when calling this directly.
271
317
pub fn new_with_opts ( options : InitializationOptions ) -> Result < Self , InitializationError > {
272
318
init_with_opts ( options) ?;
273
- Ok ( Self { } )
319
+ Ok ( Self :: registered_session ( ) )
274
320
}
275
321
276
322
/// ```no_run
@@ -364,6 +410,10 @@ impl Session {
364
410
365
411
impl Drop for Session {
366
412
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
+ }
368
418
}
369
419
}
0 commit comments