diff --git a/rust/zypp-agama/fixtures/zypp_services_root/service/repo/repoindex.xml b/rust/zypp-agama/fixtures/zypp_services_root/service/repo/repoindex.xml
new file mode 100644
index 0000000000..b5251e05b1
--- /dev/null
+++ b/rust/zypp-agama/fixtures/zypp_services_root/service/repo/repoindex.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/rust/zypp-agama/src/lib.rs b/rust/zypp-agama/src/lib.rs
index 510049550a..dbd9297116 100644
--- a/rust/zypp-agama/src/lib.rs
+++ b/rust/zypp-agama/src/lib.rs
@@ -474,6 +474,29 @@ impl Zypp {
}
}
+ pub fn add_service(&self, alias: &str, url: &str) -> ZyppResult<()> {
+ unsafe {
+ let mut status: Status = Status::default();
+ let status_ptr = &mut status as *mut _;
+ let c_alias = CString::new(alias).unwrap();
+ let c_url = CString::new(url).unwrap();
+ zypp_agama_sys::add_service(self.ptr, c_alias.as_ptr(), c_url.as_ptr(), status_ptr);
+
+ helpers::status_to_result_void(status)
+ }
+ }
+
+ pub fn refresh_service(&self, alias: &str) -> ZyppResult<()> {
+ unsafe {
+ let mut status: Status = Status::default();
+ let status_ptr = &mut status as *mut _;
+ let c_alias = CString::new(alias).unwrap();
+ zypp_agama_sys::refresh_service(self.ptr, c_alias.as_ptr(), status_ptr);
+
+ helpers::status_to_result_void(status)
+ }
+ }
+
pub fn create_repo_cache(&self, alias: &str, progress: F) -> ZyppResult<()>
where
F: FnMut(i64, String) -> bool,
diff --git a/rust/zypp-agama/tests/services.rs b/rust/zypp-agama/tests/services.rs
new file mode 100644
index 0000000000..c87cfbbb01
--- /dev/null
+++ b/rust/zypp-agama/tests/services.rs
@@ -0,0 +1,63 @@
+// Copyright (c) [2025] SUSE LLC
+//
+// All Rights Reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, contact SUSE LLC.
+//
+// To contact SUSE LLC about this file by physical or electronic mail, you may
+// find current contact information at www.suse.com.
+
+use std::{fs, io, path::Path};
+
+fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> {
+ fs::create_dir_all(&dst)?;
+ for entry in fs::read_dir(src)? {
+ let entry = entry?;
+ let ty = entry.file_type()?;
+ if ty.is_dir() {
+ copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
+ } else {
+ fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
+ }
+ }
+ Ok(())
+}
+
+#[test]
+fn test_services() {
+ let root_dir = env!("CARGO_MANIFEST_DIR").to_string() + "/fixtures/zypp_services_root";
+
+ let zypp_root = env!("CARGO_MANIFEST_DIR").to_string() + "/fixtures/zypp_root_tmp";
+ if fs::exists(&zypp_root).unwrap() {
+ fs::remove_dir_all(&zypp_root).unwrap();
+ }
+ fs::create_dir_all(&zypp_root).unwrap();
+
+ copy_dir_all(Path::new(&root_dir), Path::new(&zypp_root)).unwrap();
+
+ let zypp = zypp_agama::Zypp::init_target(&zypp_root, |_, _, _| {}).unwrap();
+
+ let service_url = "file:///service";
+ println!("{}", service_url);
+
+ zypp.add_service("test", service_url).unwrap();
+ zypp.refresh_service("test").unwrap();
+
+ let repos = zypp.list_repositories().unwrap();
+ assert!(
+ repos.len() == 2,
+ "Unexpected repos count. Repos: {:#?}",
+ repos
+ );
+}
diff --git a/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h b/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h
index a00dce78be..b2018d3565 100644
--- a/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h
+++ b/rust/zypp-agama/zypp-agama-sys/c-layer/include/repository.h
@@ -65,6 +65,14 @@ void remove_repository(struct Zypp *zypp, const char *alias,
struct Status *status, ZyppProgressCallback callback,
void *user_data) noexcept;
+/// Adds service to repo manager
+/// @param zypp see \ref init_target
+/// @param alias have to be unique
+/// @param url
+/// @param[out] status (will overwrite existing contents)
+void add_service(struct Zypp *zypp, const char *alias, const char *url,
+ struct Status *status) noexcept;
+
///
/// @param zypp see \ref init_target
/// @param alias alias of repository to refresh
@@ -77,6 +85,13 @@ void refresh_repository(struct Zypp *zypp, const char *alias,
struct DownloadProgressCallbacks *progress,
struct SecurityCallbacks *security) noexcept;
+///
+/// @param zypp see \ref init_target
+/// @param alias alias of service to refresh
+/// @param[out] status (will overwrite existing contents)
+void refresh_service(struct Zypp *zypp, const char *alias,
+ struct Status *status) noexcept;
+
void build_repository_cache(struct Zypp *zypp, const char *alias,
struct Status *status,
ZyppProgressCallback callback,
diff --git a/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx b/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx
index 2e177fa724..d194fc7f9e 100644
--- a/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx
+++ b/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx
@@ -150,7 +150,8 @@ void switch_target(struct Zypp *zypp, const char *root,
false /* rebuild rpmdb: no */);
// switch cache for repositories, otherwise we run out of space in tmpfs
- // see https://github.com/yast/yast-pkg-bindings/blob/853496f527543e6d51730fd7e3126ad94b13c303/src/PkgFunctions.cc#L496
+ // see
+ // https://github.com/yast/yast-pkg-bindings/blob/853496f527543e6d51730fd7e3126ad94b13c303/src/PkgFunctions.cc#L496
zypp::RepoManagerOptions repo_options(root);
zypp::Pathname packages_prefix = repo_options.repoPackagesCachePath;
@@ -424,6 +425,44 @@ bool run_solver(struct Zypp *zypp, struct Status *status) noexcept {
}
}
+void add_service(struct Zypp *zypp, const char *alias, const char *url,
+ struct Status *status) noexcept {
+ if (zypp->repo_manager == NULL) {
+ STATUS_ERROR(status, "Internal Error: Repo manager is not initialized.");
+ return;
+ }
+ try {
+ zypp::ServiceInfo zypp_service = zypp::ServiceInfo(alias);
+ zypp_service.setUrl(zypp::Url(url));
+
+ zypp->repo_manager->addService(zypp_service);
+ STATUS_OK(status);
+ } catch (zypp::Exception &excpt) {
+ STATUS_EXCEPT(status, excpt);
+ }
+}
+
+void refresh_service(struct Zypp *zypp, const char *alias,
+ struct Status *status) noexcept {
+ if (zypp->repo_manager == NULL) {
+ STATUS_ERROR(status, "Internal Error: Repo manager is not initialized.");
+ return;
+ }
+ try {
+ zypp::ServiceInfo service = zypp->repo_manager->getService(alias);
+ if (service == zypp::ServiceInfo::noService) {
+ STATUS_ERROR(status,
+ "Cannot refresh service with alias %s. Service not found.",
+ alias);
+ return;
+ }
+ zypp->repo_manager->refreshService(service);
+ STATUS_OK(status);
+ } catch (zypp::Exception &excpt) {
+ STATUS_EXCEPT(status, excpt);
+ }
+}
+
void refresh_repository(struct Zypp *zypp, const char *alias,
struct Status *status,
struct DownloadProgressCallbacks *callbacks,
diff --git a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs
index 7dd95d4855..8191764010 100644
--- a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs
+++ b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs
@@ -683,6 +683,13 @@ unsafe extern "C" {
callback: ZyppProgressCallback,
user_data: *mut ::std::os::raw::c_void,
);
+ #[doc = " Adds service to repo manager\n @param zypp see \\ref init_target\n @param alias have to be unique\n @param url\n @param[out] status (will overwrite existing contents)"]
+ pub fn add_service(
+ zypp: *mut Zypp,
+ alias: *const ::std::os::raw::c_char,
+ url: *const ::std::os::raw::c_char,
+ status: *mut Status,
+ );
#[doc = "\n @param zypp see \\ref init_target\n @param alias alias of repository to refresh\n @param[out] status (will overwrite existing contents)\n @param progress pointer to struct with callbacks or NULL if no progress is\n needed\n @param security pointer to struct with security callbacks"]
pub fn refresh_repository(
zypp: *mut Zypp,
@@ -691,6 +698,12 @@ unsafe extern "C" {
progress: *mut DownloadProgressCallbacks,
security: *mut SecurityCallbacks,
);
+ #[doc = "\n @param zypp see \\ref init_target\n @param alias alias of service to refresh\n @param[out] status (will overwrite existing contents)"]
+ pub fn refresh_service(
+ zypp: *mut Zypp,
+ alias: *const ::std::os::raw::c_char,
+ status: *mut Status,
+ );
pub fn build_repository_cache(
zypp: *mut Zypp,
alias: *const ::std::os::raw::c_char,