mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
cargo-credential-libsecret: load libsecret only once (#15295)
Previously, libsecret was repeatedly loaded and unloaded—at least twice during cargo login (once for action get and once for action login). This caused issues where calls could hang indefinitely due to glib failing to clean up properly (some threads still running after unloading) and generating numerous error messages: ``` cargo login --registry ownreg Updating `ownreg` index please paste the token for ownreg below (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: cannot register existing type 'SecretService' (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: cannot add private field to invalid (non-instantiatable) type '<invalid>' (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: g_type_add_interface_static: assertion 'G_TYPE_IS_INSTANTIATABLE (instance_type)' failed (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: g_type_add_interface_static: assertion 'G_TYPE_IS_INSTANTIATABLE (instance_type)' failed (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: cannot register existing type 'SecretBackend' (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: g_type_interface_add_prerequisite: assertion 'G_TYPE_IS_INTERFACE (interface_type)' failed (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: g_type_interface_add_prerequisite: assertion 'G_TYPE_IS_INTERFACE (interface_type)' failed (process:100727): GLib-CRITICAL **: 08:59:54.568: g_once_init_leave_pointer: assertion 'result != 0' failed (process:100727): GLib-GObject-CRITICAL **: 08:59:54.568: g_type_add_interface_static: assertion 'G_TYPE_IS_INSTANTIATABLE (instance_type)' failed ``` Now, libsecret is stored in a OnceLock, ensuring it is only loaded once, preventing unnecessary unload/reload cycles. Fixes #15603
This commit is contained in:
commit
f1bf94d3b2
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -407,7 +407,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cargo-credential-libsecret"
|
||||
version = "0.4.15"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo-credential",
|
||||
|
@ -27,7 +27,7 @@ blake3 = "1.5.5"
|
||||
build-rs = { version = "0.3.1", path = "crates/build-rs" }
|
||||
cargo = { path = "" }
|
||||
cargo-credential = { version = "0.4.2", path = "credential/cargo-credential" }
|
||||
cargo-credential-libsecret = { version = "0.4.15", path = "credential/cargo-credential-libsecret" }
|
||||
cargo-credential-libsecret = { version = "0.5.0", path = "credential/cargo-credential-libsecret" }
|
||||
cargo-credential-macos-keychain = { version = "0.4.15", path = "credential/cargo-credential-macos-keychain" }
|
||||
cargo-credential-wincred = { version = "0.4.15", path = "credential/cargo-credential-wincred" }
|
||||
cargo-platform = { path = "crates/cargo-platform", version = "0.3.0" }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cargo-credential-libsecret"
|
||||
version = "0.4.15"
|
||||
version = "0.5.0"
|
||||
rust-version = "1.87" # MSRV:1
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -83,7 +83,9 @@ mod linux {
|
||||
...
|
||||
) -> *mut gchar;
|
||||
|
||||
pub struct LibSecretCredential;
|
||||
pub struct LibSecretCredential {
|
||||
libsecret: Library,
|
||||
}
|
||||
|
||||
fn label(index_url: &str) -> CString {
|
||||
CString::new(format!("cargo-registry:{}", index_url)).unwrap()
|
||||
@ -105,31 +107,41 @@ mod linux {
|
||||
}
|
||||
}
|
||||
|
||||
impl Credential for LibSecretCredential {
|
||||
impl LibSecretCredential {
|
||||
pub fn new() -> Result<LibSecretCredential, Error> {
|
||||
// Dynamically load libsecret to avoid users needing to install
|
||||
// additional -dev packages when building this provider.
|
||||
let libsecret = unsafe { Library::new("libsecret-1.so.0") }.context(
|
||||
"failed to load libsecret: try installing the `libsecret` \
|
||||
or `libsecret-1-0` package with the system package manager",
|
||||
)?;
|
||||
Ok(Self { libsecret })
|
||||
}
|
||||
}
|
||||
|
||||
impl Credential for &LibSecretCredential {
|
||||
fn perform(
|
||||
&self,
|
||||
registry: &RegistryInfo<'_>,
|
||||
action: &Action<'_>,
|
||||
_args: &[&str],
|
||||
) -> Result<CredentialResponse, Error> {
|
||||
// Dynamically load libsecret to avoid users needing to install
|
||||
// additional -dev packages when building this provider.
|
||||
let lib;
|
||||
let secret_password_lookup_sync: Symbol<'_, SecretPasswordLookupSync>;
|
||||
let secret_password_store_sync: Symbol<'_, SecretPasswordStoreSync>;
|
||||
let secret_password_clear_sync: Symbol<'_, SecretPasswordClearSync>;
|
||||
unsafe {
|
||||
lib = Library::new("libsecret-1.so.0").context(
|
||||
"failed to load libsecret: try installing the `libsecret` \
|
||||
or `libsecret-1-0` package with the system package manager",
|
||||
)?;
|
||||
secret_password_lookup_sync = lib
|
||||
secret_password_lookup_sync = self
|
||||
.libsecret
|
||||
.get(b"secret_password_lookup_sync\0")
|
||||
.map_err(Box::new)?;
|
||||
secret_password_store_sync =
|
||||
lib.get(b"secret_password_store_sync\0").map_err(Box::new)?;
|
||||
secret_password_clear_sync =
|
||||
lib.get(b"secret_password_clear_sync\0").map_err(Box::new)?;
|
||||
secret_password_store_sync = self
|
||||
.libsecret
|
||||
.get(b"secret_password_store_sync\0")
|
||||
.map_err(Box::new)?;
|
||||
secret_password_clear_sync = self
|
||||
.libsecret
|
||||
.get(b"secret_password_clear_sync\0")
|
||||
.map_err(Box::new)?;
|
||||
}
|
||||
|
||||
let index_url_c = CString::new(registry.index_url).unwrap();
|
||||
|
@ -508,6 +508,27 @@ static BUILT_IN_PROVIDERS: &[&'static str] = &[
|
||||
"cargo:libsecret",
|
||||
];
|
||||
|
||||
/// Retrieves a cached instance of `LibSecretCredential`.
|
||||
/// Must be cached to avoid repeated load/unload cycles, which are not supported by `glib`.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn get_credential_libsecret(
|
||||
) -> CargoResult<&'static cargo_credential_libsecret::LibSecretCredential> {
|
||||
static CARGO_CREDENTIAL_LIBSECRET: std::sync::OnceLock<
|
||||
cargo_credential_libsecret::LibSecretCredential,
|
||||
> = std::sync::OnceLock::new();
|
||||
// Unfortunately `get_or_try_init` is not yet stable. This workaround is not threadsafe but
|
||||
// loading libsecret twice will only temporary increment the ref counter, which is decrement
|
||||
// again when `drop` is called.
|
||||
match CARGO_CREDENTIAL_LIBSECRET.get() {
|
||||
Some(lib) => Ok(lib),
|
||||
None => {
|
||||
let _ = CARGO_CREDENTIAL_LIBSECRET
|
||||
.set(cargo_credential_libsecret::LibSecretCredential::new()?);
|
||||
Ok(CARGO_CREDENTIAL_LIBSECRET.get().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn credential_action(
|
||||
gctx: &GlobalContext,
|
||||
sid: &SourceId,
|
||||
@ -545,7 +566,7 @@ fn credential_action(
|
||||
#[cfg(target_os = "macos")]
|
||||
"cargo:macos-keychain" => Box::new(cargo_credential_macos_keychain::MacKeychain {}),
|
||||
#[cfg(target_os = "linux")]
|
||||
"cargo:libsecret" => Box::new(cargo_credential_libsecret::LibSecretCredential {}),
|
||||
"cargo:libsecret" => Box::new(get_credential_libsecret()?),
|
||||
name if BUILT_IN_PROVIDERS.contains(&name) => {
|
||||
Box::new(cargo_credential::UnsupportedCredential {})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user