1#![allow(clippy::missing_safety_doc)]
2
3use std::{os::raw::c_char, ptr::null_mut};
4
5use crate::*;
6
7pub type ExtismMemoryHandle = u64;
8pub type Size = u64;
9pub struct ExtismFunction(std::cell::Cell<Option<Function>>);
10
11pub static EXTISM_SUCCESS: i32 = 0;
13
14fn make_error_msg(s: String) -> Vec<u8> {
15 let mut s = s.into_bytes();
16 s.push(0);
17 s
18}
19
20#[repr(C)]
22pub union ValUnion {
23 i32: i32,
24 i64: i64,
25 f32: f32,
26 f64: f64,
27 }
29
30#[repr(C)]
32pub struct ExtismVal {
33 t: ValType,
34 v: ValUnion,
35}
36
37pub type ExtismFunctionType = extern "C" fn(
39 plugin: *mut CurrentPlugin,
40 inputs: *const ExtismVal,
41 n_inputs: Size,
42 outputs: *mut ExtismVal,
43 n_outputs: Size,
44 data: *mut std::ffi::c_void,
45);
46
47pub type ExtismLogDrainFunctionType = extern "C" fn(data: *const std::ffi::c_char, size: Size);
49
50impl ExtismVal {
51 fn from_val(value: &wasmtime::Val, ctx: impl AsContext) -> Result<Self, Error> {
52 match value.ty(ctx)? {
53 wasmtime::ValType::I32 => Ok(ExtismVal {
54 t: ValType::I32,
55 v: ValUnion {
56 i32: value.unwrap_i32(),
57 },
58 }),
59 wasmtime::ValType::I64 => Ok(ExtismVal {
60 t: ValType::I64,
61 v: ValUnion {
62 i64: value.unwrap_i64(),
63 },
64 }),
65 wasmtime::ValType::F32 => Ok(ExtismVal {
66 t: ValType::F32,
67 v: ValUnion {
68 f32: value.unwrap_f32(),
69 },
70 }),
71 wasmtime::ValType::F64 => Ok(ExtismVal {
72 t: ValType::F64,
73 v: ValUnion {
74 f64: value.unwrap_f64(),
75 },
76 }),
77 t => todo!("{}", t),
78 }
79 }
80}
81
82#[no_mangle]
84pub unsafe extern "C" fn extism_plugin_id(plugin: *mut Plugin) -> *const u8 {
85 if plugin.is_null() {
86 return std::ptr::null_mut();
87 }
88
89 let plugin = &mut *plugin;
90 plugin.id.as_bytes().as_ptr()
91}
92
93#[no_mangle]
96pub unsafe extern "C" fn extism_current_plugin_host_context(
97 plugin: *mut CurrentPlugin,
98) -> *mut std::ffi::c_void {
99 if plugin.is_null() {
100 return std::ptr::null_mut();
101 }
102
103 let plugin = &mut *plugin;
104 if let Ok(CVoidContainer(ptr)) = plugin.host_context::<CVoidContainer>() {
105 *ptr
106 } else {
107 std::ptr::null_mut()
108 }
109}
110
111#[no_mangle]
114pub unsafe extern "C" fn extism_current_plugin_memory(plugin: *mut CurrentPlugin) -> *mut u8 {
115 if plugin.is_null() {
116 return std::ptr::null_mut();
117 }
118
119 let plugin = &mut *plugin;
120 plugin.memory_ptr()
121}
122
123#[no_mangle]
126pub unsafe extern "C" fn extism_current_plugin_memory_alloc(
127 plugin: *mut CurrentPlugin,
128 n: Size,
129) -> ExtismMemoryHandle {
130 if plugin.is_null() {
131 return 0;
132 }
133
134 let plugin = &mut *plugin;
135 match plugin.memory_alloc(n) {
136 Ok(x) => x.offset(),
137 Err(_) => 0,
138 }
139}
140
141#[no_mangle]
144pub unsafe extern "C" fn extism_current_plugin_memory_length(
145 plugin: *mut CurrentPlugin,
146 n: ExtismMemoryHandle,
147) -> Size {
148 if plugin.is_null() {
149 return 0;
150 }
151
152 let plugin = &mut *plugin;
153 plugin.memory_length(n).unwrap_or_default()
154}
155
156#[no_mangle]
159pub unsafe extern "C" fn extism_current_plugin_memory_free(
160 plugin: *mut CurrentPlugin,
161 ptr: ExtismMemoryHandle,
162) {
163 if plugin.is_null() {
164 return;
165 }
166
167 let plugin = &mut *plugin;
168 if let Some(handle) = plugin.memory_handle(ptr) {
169 let _ = plugin.memory_free(handle);
170 }
171}
172
173#[no_mangle]
189pub unsafe extern "C" fn extism_function_new(
190 name: *const std::ffi::c_char,
191 inputs: *const ValType,
192 n_inputs: Size,
193 outputs: *const ValType,
194 n_outputs: Size,
195 func: ExtismFunctionType,
196 user_data: *mut std::ffi::c_void,
197 free_user_data: Option<extern "C" fn(_: *mut std::ffi::c_void)>,
198) -> *mut ExtismFunction {
199 let name = match std::ffi::CStr::from_ptr(name).to_str() {
200 Ok(x) => x.to_string(),
201 Err(_) => {
202 return std::ptr::null_mut();
203 }
204 };
205
206 let inputs = if inputs.is_null() || n_inputs == 0 {
207 &[]
208 } else {
209 std::slice::from_raw_parts(inputs, n_inputs as usize)
210 }
211 .to_vec();
212
213 let output_types = if outputs.is_null() || n_outputs == 0 {
214 &[]
215 } else {
216 std::slice::from_raw_parts(outputs, n_outputs as usize)
217 }
218 .to_vec();
219
220 let user_data: UserData<()> = UserData::new_pointer(user_data, free_user_data);
221 let f = Function::new(
222 name,
223 inputs,
224 output_types.clone(),
225 user_data,
226 move |plugin, inputs, outputs, user_data| {
227 let store = &*plugin.store;
228 let inputs: Vec<_> = inputs
229 .iter()
230 .map(|x| ExtismVal::from_val(x, store).unwrap())
231 .collect();
232 let mut output_tmp: Vec<_> = output_types
233 .iter()
234 .map(|t| ExtismVal {
235 t: t.clone(),
236 v: ValUnion { i64: 0 },
237 })
238 .collect();
239
240 let (inputs_ptr, inputs_len) = if inputs.is_empty() {
245 (core::ptr::null(), 0 as Size)
246 } else {
247 (inputs.as_ptr(), inputs.len() as Size)
248 };
249
250 let (output_ptr, output_len) = if output_tmp.is_empty() {
251 (null_mut(), 0 as Size)
252 } else {
253 (output_tmp.as_mut_ptr(), output_tmp.len() as Size)
254 };
255
256 func(
257 plugin,
258 inputs_ptr,
259 inputs_len,
260 output_ptr,
261 output_len,
262 user_data.as_ptr(),
263 );
264
265 for (tmp, out) in output_tmp.iter().zip(outputs.iter_mut()) {
266 match tmp.t {
267 ValType::I32 => *out = Val::I32(tmp.v.i32),
268 ValType::I64 => *out = Val::I64(tmp.v.i64),
269 ValType::F32 => *out = Val::F32(tmp.v.f32.to_bits()),
270 ValType::F64 => *out = Val::F64(tmp.v.f64.to_bits()),
271 _ => todo!(),
272 }
273 }
274 Ok(())
275 },
276 );
277 Box::into_raw(Box::new(ExtismFunction(std::cell::Cell::new(Some(f)))))
278}
279
280#[no_mangle]
282pub unsafe extern "C" fn extism_function_free(f: *mut ExtismFunction) {
283 if f.is_null() {
284 return;
285 }
286
287 drop(Box::from_raw(f))
288}
289
290#[no_mangle]
292pub unsafe extern "C" fn extism_function_set_namespace(
293 ptr: *mut ExtismFunction,
294 namespace: *const std::ffi::c_char,
295) {
296 let namespace = std::ffi::CStr::from_ptr(namespace);
297 let f = &mut *ptr;
298 if let Some(x) = f.0.get_mut() {
299 x.set_namespace(namespace.to_string_lossy().to_string());
300 } else {
301 debug!("Trying to set namespace of already registered function")
302 }
303}
304
305#[no_mangle]
307pub unsafe extern "C" fn extism_compiled_plugin_new(
308 wasm: *const u8,
309 wasm_size: Size,
310 functions: *mut *const ExtismFunction,
311 n_functions: Size,
312 with_wasi: bool,
313 errmsg: *mut *mut std::ffi::c_char,
314) -> *mut CompiledPlugin {
315 trace!("Call to extism_plugin_new with wasm pointer {:?}", wasm);
316 let data = std::slice::from_raw_parts(wasm, wasm_size as usize);
317
318 let mut builder = PluginBuilder::new(data).with_wasi(with_wasi);
319
320 if !functions.is_null() {
321 let funcs = (0..n_functions)
322 .map(|i| unsafe { *functions.add(i as usize) })
323 .map(|ptr| {
324 if ptr.is_null() {
325 return Err("Cannot pass null pointer");
326 }
327
328 let ExtismFunction(func) = &*ptr;
329 let Some(func) = func.take() else {
330 return Err("Function cannot be registered with multiple different Plugins");
331 };
332
333 Ok(func)
334 })
335 .collect::<Result<Vec<_>, _>>()
336 .unwrap_or_else(|e| {
337 if !errmsg.is_null() {
338 let e = std::ffi::CString::new(e.to_string()).unwrap();
339 *errmsg = e.into_raw();
340 }
341 Vec::new()
342 });
343
344 if funcs.len() != n_functions as usize {
345 return std::ptr::null_mut();
346 }
347
348 builder = builder.with_functions(funcs);
349 }
350
351 CompiledPlugin::new(builder)
352 .map(|v| Box::into_raw(Box::new(v)))
353 .unwrap_or_else(|e| {
354 if !errmsg.is_null() {
355 let e = std::ffi::CString::new(format!(
356 "Unable to compile Extism plugin: {}",
357 e.root_cause(),
358 ))
359 .unwrap();
360 *errmsg = e.into_raw();
361 }
362 std::ptr::null_mut()
363 })
364}
365
366#[no_mangle]
368pub unsafe extern "C" fn extism_compiled_plugin_free(plugin: *mut CompiledPlugin) {
369 if plugin.is_null() {
370 return;
371 }
372
373 let plugin = Box::from_raw(plugin);
374 trace!("called extism_compiled_plugin_free");
375 drop(plugin)
376}
377
378#[no_mangle]
386pub unsafe extern "C" fn extism_plugin_new(
387 wasm: *const u8,
388 wasm_size: Size,
389 functions: *mut *const ExtismFunction,
390 n_functions: Size,
391 with_wasi: bool,
392 errmsg: *mut *mut std::ffi::c_char,
393) -> *mut Plugin {
394 let data = std::slice::from_raw_parts(wasm, wasm_size as usize);
395 let funcs = if functions.is_null() {
396 vec![]
397 } else {
398 let funcs = (0..n_functions)
399 .map(|i| unsafe { *functions.add(i as usize) })
400 .map(|ptr| {
401 if ptr.is_null() {
402 return Err("Cannot pass null pointer");
403 }
404
405 let ExtismFunction(func) = &*ptr;
406 let Some(func) = func.take() else {
407 return Err("Function cannot be registered with multiple different Plugins");
408 };
409
410 Ok(func)
411 })
412 .collect::<Result<Vec<_>, _>>()
413 .unwrap_or_else(|e| {
414 if !errmsg.is_null() {
415 let e = std::ffi::CString::new(e.to_string()).unwrap();
416 *errmsg = e.into_raw();
417 }
418 Vec::new()
419 });
420
421 if funcs.len() != n_functions as usize {
422 return std::ptr::null_mut();
423 }
424
425 funcs
426 };
427
428 Plugin::new(data, funcs, with_wasi)
429 .map(|v| Box::into_raw(Box::new(v)))
430 .unwrap_or_else(|e| {
431 if !errmsg.is_null() {
432 let e = std::ffi::CString::new(format!(
433 "Unable to compile Extism plugin: {}",
434 e.root_cause(),
435 ))
436 .unwrap();
437 *errmsg = e.into_raw();
438 }
439 std::ptr::null_mut()
440 })
441}
442
443#[no_mangle]
445pub unsafe extern "C" fn extism_plugin_new_from_compiled(
446 compiled: *const CompiledPlugin,
447 errmsg: *mut *mut std::ffi::c_char,
448) -> *mut Plugin {
449 let plugin = Plugin::new_from_compiled(&*compiled);
450 match plugin {
451 Err(e) => {
452 if !errmsg.is_null() {
453 let e = std::ffi::CString::new(format!(
454 "Unable to create Extism plugin: {}",
455 e.root_cause(),
456 ))
457 .unwrap();
458 *errmsg = e.into_raw();
459 }
460 std::ptr::null_mut()
461 }
462 Ok(p) => Box::into_raw(Box::new(p)),
463 }
464}
465
466#[no_mangle]
468pub unsafe extern "C" fn extism_plugin_new_with_fuel_limit(
469 wasm: *const u8,
470 wasm_size: Size,
471 functions: *mut *const ExtismFunction,
472 n_functions: Size,
473 with_wasi: bool,
474 fuel_limit: u64,
475 errmsg: *mut *mut std::ffi::c_char,
476) -> *mut Plugin {
477 trace!(
478 "Call to extism_plugin_new_with_fuel_limit with wasm pointer {:?}",
479 wasm
480 );
481 let data = std::slice::from_raw_parts(wasm, wasm_size as usize);
482 let funcs = if functions.is_null() {
483 vec![]
484 } else {
485 let funcs = (0..n_functions)
486 .map(|i| unsafe { *functions.add(i as usize) })
487 .map(|ptr| {
488 if ptr.is_null() {
489 return Err("Cannot pass null pointer");
490 }
491
492 let ExtismFunction(func) = &*ptr;
493 let Some(func) = func.take() else {
494 return Err("Function cannot be registered with multiple different Plugins");
495 };
496
497 Ok(func)
498 })
499 .collect::<Result<Vec<_>, _>>()
500 .unwrap_or_else(|e| {
501 if !errmsg.is_null() {
502 let e = std::ffi::CString::new(e.to_string()).unwrap();
503 *errmsg = e.into_raw();
504 }
505 Vec::new()
506 });
507
508 if funcs.len() != n_functions as usize {
509 return std::ptr::null_mut();
510 }
511
512 funcs
513 };
514
515 let compiled = match CompiledPlugin::new(
516 PluginBuilder::new(data)
517 .with_functions(funcs)
518 .with_wasi(with_wasi)
519 .with_fuel_limit(fuel_limit),
520 ) {
521 Ok(x) => x,
522 Err(e) => {
523 if !errmsg.is_null() {
524 let e = std::ffi::CString::new(format!(
525 "Unable to compile Extism plugin: {}",
526 e.root_cause(),
527 ))
528 .unwrap();
529 *errmsg = e.into_raw();
530 }
531 return std::ptr::null_mut();
532 }
533 };
534
535 let plugin = Plugin::new_from_compiled(&compiled);
536
537 match plugin {
538 Err(e) => {
539 if !errmsg.is_null() {
540 let e = std::ffi::CString::new(format!(
541 "Unable to create Extism plugin: {}",
542 e.root_cause(),
543 ))
544 .unwrap();
545 *errmsg = e.into_raw();
546 }
547 std::ptr::null_mut()
548 }
549 Ok(p) => Box::into_raw(Box::new(p)),
550 }
551}
552
553#[no_mangle]
555pub unsafe extern "C" fn extism_plugin_allow_http_response_headers(plugin: *mut Plugin) {
556 let plugin = &mut *plugin;
557 plugin.store.data_mut().http_headers = Some(BTreeMap::new());
558}
559
560#[no_mangle]
562pub unsafe extern "C" fn extism_plugin_new_error_free(err: *mut std::ffi::c_char) {
563 if err.is_null() {
564 return;
565 }
566 drop(std::ffi::CString::from_raw(err))
567}
568
569#[no_mangle]
571pub unsafe extern "C" fn extism_plugin_free(plugin: *mut Plugin) {
572 if plugin.is_null() {
573 return;
574 }
575
576 let plugin = Box::from_raw(plugin);
577 trace!(plugin = plugin.id.to_string(), "called extism_plugin_free");
578 drop(plugin)
579}
580
581#[no_mangle]
583pub unsafe extern "C" fn extism_plugin_cancel_handle(plugin: *const Plugin) -> *const CancelHandle {
584 if plugin.is_null() {
585 return std::ptr::null();
586 }
587 let plugin = &*plugin;
588 trace!(
589 plugin = plugin.id.to_string(),
590 "called extism_plugin_cancel_handle"
591 );
592 &plugin.cancel_handle as *const _
593}
594
595#[no_mangle]
597pub unsafe extern "C" fn extism_plugin_cancel(handle: *const CancelHandle) -> bool {
598 let handle = &*handle;
599 trace!(
600 plugin = handle.id.to_string(),
601 "called extism_plugin_cancel"
602 );
603 handle.cancel().is_ok()
604}
605
606#[no_mangle]
611pub unsafe extern "C" fn extism_plugin_config(
612 plugin: *mut Plugin,
613 json: *const u8,
614 json_size: Size,
615) -> bool {
616 if plugin.is_null() {
617 return false;
618 }
619 let plugin = &mut *plugin;
620
621 trace!(
622 plugin = plugin.id.to_string(),
623 "call to extism_plugin_config with pointer {:?}",
624 json
625 );
626 let data = std::slice::from_raw_parts(json, json_size as usize);
627 let json: std::collections::BTreeMap<String, Option<String>> =
628 match serde_json::from_slice(data) {
629 Ok(x) => x,
630 Err(_) => {
631 return false;
632 }
633 };
634
635 let id = plugin.id;
636 let config = &mut plugin.current_plugin_mut().manifest.config;
637 for (k, v) in json.into_iter() {
638 match v {
639 Some(v) => {
640 trace!(plugin = id.to_string(), "config, adding {k}");
641 config.insert(k, v);
642 }
643 None => {
644 trace!(plugin = id.to_string(), "config, removing {k}");
645 config.remove(&k);
646 }
647 }
648 }
649
650 let _ = plugin.clear_error();
651 true
652}
653
654#[no_mangle]
656pub unsafe extern "C" fn extism_plugin_function_exists(
657 plugin: *mut Plugin,
658 func_name: *const c_char,
659) -> bool {
660 if plugin.is_null() {
661 return false;
662 }
663 let plugin = &mut *plugin;
664 let name = std::ffi::CStr::from_ptr(func_name);
665 trace!(
666 plugin = plugin.id.to_string(),
667 "extism_plugin_function_exists: {:?}",
668 name
669 );
670
671 let name = match name.to_str() {
672 Ok(x) => x,
673 Err(_) => {
674 return false;
675 }
676 };
677
678 let _ = plugin.clear_error();
679 plugin.function_exists(name)
680}
681
682#[no_mangle]
688pub unsafe extern "C" fn extism_plugin_call(
689 plugin: *mut Plugin,
690 func_name: *const c_char,
691 data: *const u8,
692 data_len: Size,
693) -> i32 {
694 extism_plugin_call_with_host_context(plugin, func_name, data, data_len, std::ptr::null_mut())
695}
696
697#[derive(Clone)]
698#[repr(transparent)]
699struct CVoidContainer(*mut std::ffi::c_void);
700
701unsafe impl Send for CVoidContainer {}
703unsafe impl Sync for CVoidContainer {}
704
705#[no_mangle]
712pub unsafe extern "C" fn extism_plugin_call_with_host_context(
713 plugin: *mut Plugin,
714 func_name: *const c_char,
715 data: *const u8,
716 data_len: Size,
717 host_context: *mut std::ffi::c_void,
718) -> i32 {
719 if plugin.is_null() {
720 return -1;
721 }
722 let plugin = &mut *plugin;
723 let lock = plugin.instance.clone();
724 let mut lock = lock.lock().unwrap();
725
726 let name = std::ffi::CStr::from_ptr(func_name);
728 let name = match name.to_str() {
729 Ok(name) => name,
730 Err(e) => {
731 plugin.error_msg = Some(make_error_msg(e.to_string()));
732 return -1;
733 }
734 };
735
736 trace!(
737 plugin = plugin.id.to_string(),
738 "calling function {} using extism_plugin_call",
739 name
740 );
741 let input = std::slice::from_raw_parts(data, data_len as usize);
742 let r = if host_context.is_null() {
743 None
744 } else {
745 Some(CVoidContainer(host_context))
746 };
747 let res = plugin.raw_call(&mut lock, name, input, r);
748 match res {
749 Err((e, rc)) => {
750 plugin.error_msg = Some(make_error_msg(e.to_string()));
751 rc
752 }
753 Ok(x) => x,
754 }
755}
756
757#[no_mangle]
759#[deprecated]
760pub unsafe extern "C" fn extism_error(plugin: *mut Plugin) -> *const c_char {
761 extism_plugin_error(plugin)
762}
763
764#[no_mangle]
766pub unsafe extern "C" fn extism_plugin_error(plugin: *mut Plugin) -> *const c_char {
767 if plugin.is_null() {
768 return std::ptr::null();
769 }
770 let plugin = &mut *plugin;
771 let _lock = plugin.instance.clone();
772 let _lock = _lock.lock().unwrap();
773
774 if plugin.output.error_offset == 0 {
775 if let Some(err) = &plugin.error_msg {
776 return err.as_ptr() as *const _;
777 }
778 trace!(plugin = plugin.id.to_string(), "error is NULL");
779 return std::ptr::null();
780 }
781
782 let offs = plugin.output.error_offset;
783
784 let ptr = plugin.current_plugin_mut().memory_ptr().add(offs as usize) as *const _;
785
786 let len = plugin
787 .current_plugin_mut()
788 .memory_length(offs)
789 .unwrap_or_default();
790
791 let mut data = std::slice::from_raw_parts(ptr, len as usize).to_vec();
792 data.push(0);
793 plugin.error_msg = Some(data);
794 plugin.error_msg.as_ref().unwrap().as_ptr() as *const _
795}
796
797#[no_mangle]
799pub unsafe extern "C" fn extism_plugin_output_length(plugin: *mut Plugin) -> Size {
800 if plugin.is_null() {
801 return 0;
802 }
803 let plugin = &mut *plugin;
804 let _lock = plugin.instance.clone();
805 let _lock = _lock.lock().unwrap();
806 plugin.output.length
807}
808
809#[no_mangle]
811pub unsafe extern "C" fn extism_plugin_output_data(plugin: *mut Plugin) -> *const u8 {
812 if plugin.is_null() {
813 return std::ptr::null();
814 }
815 let plugin = &mut *plugin;
816 let _lock = plugin.instance.clone();
817 let _lock = _lock.lock().unwrap();
818 trace!(
819 plugin = plugin.id.to_string(),
820 "extism_plugin_output_data: offset={}, length={}",
821 plugin.output.offset,
822 plugin.output.length
823 );
824
825 let ptr = plugin.current_plugin_mut().memory_ptr();
826 ptr.add(plugin.output.offset as usize)
827}
828
829#[no_mangle]
834pub unsafe extern "C" fn extism_log_file(
835 filename: *const c_char,
836 log_level: *const c_char,
837) -> bool {
838 let file = if !filename.is_null() {
839 let file = std::ffi::CStr::from_ptr(filename);
840 match file.to_str() {
841 Ok(x) => x,
842 Err(_) => {
843 return false;
844 }
845 }
846 } else {
847 "stderr"
848 };
849
850 let level = if !log_level.is_null() {
851 let level = std::ffi::CStr::from_ptr(log_level);
852 match level.to_str() {
853 Ok(x) => x,
854 Err(_) => {
855 return false;
856 }
857 }
858 } else {
859 "error"
860 };
861
862 set_log_file(file, level).is_ok()
863}
864
865fn set_log_file(log_file: impl Into<std::path::PathBuf>, filter: &str) -> Result<(), Error> {
867 let log_file = log_file.into();
868 let s = log_file.to_str();
869 let is_level = tracing::Level::from_str(filter).is_ok();
870 let cfg = tracing_subscriber::FmtSubscriber::builder().with_env_filter({
871 let x = tracing_subscriber::EnvFilter::builder()
872 .with_default_directive(tracing::Level::ERROR.into());
873 if is_level {
874 x.parse_lossy(format!("extism={filter}"))
875 } else {
876 x.parse_lossy(filter)
877 }
878 });
879
880 let res = if s == Some("-") || s == Some("stderr") {
881 cfg.with_ansi(true).with_writer(std::io::stderr).try_init()
882 } else if s == Some("stdout") {
883 cfg.with_ansi(true).with_writer(std::io::stdout).try_init()
884 } else {
885 let log_file = log_file.to_path_buf();
886 let f = std::fs::OpenOptions::new()
887 .create(true)
888 .append(true)
889 .open(log_file)
890 .expect("Open log file");
891 cfg.with_ansi(false)
892 .with_writer(move || f.try_clone().unwrap())
893 .try_init()
894 };
895
896 if let Err(e) = res {
897 return Err(Error::msg(e.to_string()));
898 }
899 Ok(())
900}
901
902static LOG_BUFFER: std::sync::Mutex<Option<LogBuffer>> = std::sync::Mutex::new(None);
903
904#[no_mangle]
907pub unsafe extern "C" fn extism_log_custom(log_level: *const c_char) -> bool {
908 let level = if !log_level.is_null() {
909 let level = std::ffi::CStr::from_ptr(log_level);
910 match level.to_str() {
911 Ok(x) => x,
912 Err(_) => {
913 return false;
914 }
915 }
916 } else {
917 "error"
918 };
919
920 set_log_buffer(level).is_ok()
921}
922
923unsafe fn set_log_buffer(filter: &str) -> Result<(), Error> {
924 let is_level = tracing::Level::from_str(filter).is_ok();
925 let cfg = tracing_subscriber::FmtSubscriber::builder().with_env_filter({
926 let x = tracing_subscriber::EnvFilter::builder()
927 .with_default_directive(tracing::Level::ERROR.into());
928 if is_level {
929 x.parse_lossy(format!("extism={filter}"))
930 } else {
931 x.parse_lossy(filter)
932 }
933 });
934 *LOG_BUFFER.lock().unwrap() = Some(LogBuffer::default());
935 let buf = LOG_BUFFER.lock().unwrap().clone().unwrap();
936 cfg.with_ansi(false)
937 .with_writer(move || buf.clone())
938 .try_init()
939 .map_err(|x| Error::msg(x.to_string()))?;
940 Ok(())
941}
942
943#[no_mangle]
944pub unsafe extern "C" fn extism_log_drain(handler: ExtismLogDrainFunctionType) {
947 if let Some(buf) = LOG_BUFFER.lock().unwrap().as_mut() {
948 if let Ok(mut buf) = buf.buffer.lock() {
949 for (line, len) in buf.drain(..) {
950 handler(line.as_ptr(), len as u64);
951 }
952 }
953 }
954}
955
956#[derive(Default, Clone)]
957struct LogBuffer {
958 buffer:
959 std::sync::Arc<std::sync::Mutex<std::collections::VecDeque<(std::ffi::CString, usize)>>>,
960}
961
962unsafe impl Send for LogBuffer {}
963unsafe impl Sync for LogBuffer {}
964
965impl std::io::Write for LogBuffer {
966 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
967 if let Ok(s) = std::str::from_utf8(buf) {
968 if let Ok(mut buf) = self.buffer.lock() {
969 buf.push_back((std::ffi::CString::new(s)?, s.len()));
970 }
971 }
972
973 Ok(buf.len())
974 }
975
976 fn flush(&mut self) -> std::io::Result<()> {
977 Ok(())
978 }
979}
980
981#[no_mangle]
983pub unsafe extern "C" fn extism_plugin_reset(plugin: *mut Plugin) -> bool {
984 let plugin = &mut *plugin;
985
986 if let Err(e) = plugin.reset() {
987 error!(
988 plugin = plugin.id.to_string(),
989 "unable to reset plugin: {}",
990 e.to_string()
991 );
992 if let Err(e) = plugin.current_plugin_mut().set_error(e.to_string()) {
993 error!(
994 plugin = plugin.id.to_string(),
995 "unable to set error after failed plugin reset: {}",
996 e.to_string()
997 );
998 }
999 false
1000 } else {
1001 true
1002 }
1003}
1004
1005#[no_mangle]
1007pub unsafe extern "C" fn extism_version() -> *const c_char {
1008 VERSION.as_ptr() as *const _
1009}