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:
Ed Page 2025-06-04 16:00:42 +00:00 committed by GitHub
commit f1bf94d3b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 51 additions and 18 deletions

2
Cargo.lock generated
View File

@ -407,7 +407,7 @@ dependencies = [
[[package]]
name = "cargo-credential-libsecret"
version = "0.4.15"
version = "0.5.0"
dependencies = [
"anyhow",
"cargo-credential",

View File

@ -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" }

View File

@ -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

View File

@ -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();

View File

@ -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 {})
}