]> git.proxmox.com Git - proxmox-apt.git/blame - src/repositories/mod.rs
standard repos: drop product acronym from repo name
[proxmox-apt.git] / src / repositories / mod.rs
CommitLineData
b6be0f39
FE
1use std::collections::BTreeMap;
2use std::path::PathBuf;
3
4use anyhow::{bail, Error};
5
6mod repository;
7pub use repository::{
8 APTRepository, APTRepositoryFileType, APTRepositoryOption, APTRepositoryPackageType,
9};
10
11mod file;
76d3a5ba
FE
12pub use file::{APTRepositoryFile, APTRepositoryFileError, APTRepositoryInfo};
13
14mod release;
8ada1785
FE
15use release::get_current_release_codename;
16
17mod standard;
18pub use standard::{APTRepositoryHandle, APTStandardRepository};
b6be0f39
FE
19
20const APT_SOURCES_LIST_FILENAME: &str = "/etc/apt/sources.list";
21const APT_SOURCES_LIST_DIRECTORY: &str = "/etc/apt/sources.list.d/";
22
23/// Calculates a common digest for successfully parsed repository files.
24///
25/// The digest is invariant with respect to file order.
26///
27/// Files without a digest are ignored.
28fn common_digest(files: &[APTRepositoryFile]) -> [u8; 32] {
29 let mut digests = BTreeMap::new();
30
31 for file in files.iter() {
32 digests.insert(file.path.clone(), &file.digest);
33 }
34
35 let mut common_raw = Vec::<u8>::with_capacity(digests.len() * 32);
36 for digest in digests.values() {
37 match digest {
38 Some(digest) => common_raw.extend_from_slice(&digest[..]),
39 None => (),
40 }
41 }
42
43 openssl::sha::sha256(&common_raw[..])
44}
45
76d3a5ba
FE
46/// Provides additional information about the repositories.
47///
48/// The kind of information can be:
49/// `warnings` for bad suites.
50/// `ignore-pre-upgrade-warning` when the next stable suite is configured.
51/// `badge` for official URIs.
52pub fn check_repositories(files: &[APTRepositoryFile]) -> Result<Vec<APTRepositoryInfo>, Error> {
53 let mut infos = vec![];
54
55 for file in files.iter() {
56 infos.append(&mut file.check_suites()?);
57 infos.append(&mut file.check_uris());
58 }
59
60 Ok(infos)
61}
62
8ada1785
FE
63/// Get the repository associated to the handle and the path where its usually configured.
64pub fn get_standard_repository(
65 handle: APTRepositoryHandle,
66 product: &str,
67) -> Result<(APTRepository, String), Error> {
68 let suite = get_current_release_codename()?;
69
70 let repo = handle.to_repository(product, &suite);
71 let path = handle.path(product);
72
73 Ok((repo, path))
74}
75
76/// Return handles for standard Proxmox repositories and whether their status, where
77/// None means not configured, and Some(bool) indicates enabled or disabled
78pub fn standard_repositories(
79 product: &str,
80 files: &[APTRepositoryFile],
81) -> Vec<APTStandardRepository> {
82 let mut result = vec![
83 APTStandardRepository {
84 handle: APTRepositoryHandle::Enterprise,
85 status: None,
3f715238 86 name: APTRepositoryHandle::Enterprise.name(),
8ada1785
FE
87 },
88 APTStandardRepository {
89 handle: APTRepositoryHandle::NoSubscription,
90 status: None,
3f715238 91 name: APTRepositoryHandle::NoSubscription.name(),
8ada1785
FE
92 },
93 APTStandardRepository {
94 handle: APTRepositoryHandle::Test,
95 status: None,
3f715238 96 name: APTRepositoryHandle::Test.name(),
8ada1785
FE
97 },
98 ];
99
100 if product == "pve" {
101 result.append(&mut vec![
102 APTStandardRepository {
103 handle: APTRepositoryHandle::CephPacific,
104 status: None,
3f715238 105 name: APTRepositoryHandle::CephPacific.name(),
8ada1785
FE
106 },
107 APTStandardRepository {
108 handle: APTRepositoryHandle::CephPacificTest,
109 status: None,
3f715238 110 name: APTRepositoryHandle::CephPacificTest.name(),
8ada1785
FE
111 },
112 APTStandardRepository {
113 handle: APTRepositoryHandle::CephOctopus,
114 status: None,
3f715238 115 name: APTRepositoryHandle::CephOctopus.name(),
8ada1785
FE
116 },
117 APTStandardRepository {
118 handle: APTRepositoryHandle::CephOctopusTest,
119 status: None,
3f715238 120 name: APTRepositoryHandle::CephOctopusTest.name(),
8ada1785
FE
121 },
122 ]);
123 }
124
125 for file in files.iter() {
126 for repo in file.repositories.iter() {
127 for entry in result.iter_mut() {
128 if entry.status == Some(true) {
129 continue;
130 }
131
132 if repo.is_referenced_repository(entry.handle, product) {
133 entry.status = Some(repo.enabled);
134 }
135 }
136 }
137 }
138
139 result
140}
141
b6be0f39
FE
142/// Returns all APT repositories configured in `/etc/apt/sources.list` and
143/// in `/etc/apt/sources.list.d` including disabled repositories.
144///
145/// Returns the succesfully parsed files, a list of errors for files that could
146/// not be read or parsed and a common digest for the succesfully parsed files.
147///
148/// The digest is guaranteed to be set for each successfully parsed file.
149pub fn repositories() -> Result<
150 (
151 Vec<APTRepositoryFile>,
152 Vec<APTRepositoryFileError>,
153 [u8; 32],
154 ),
155 Error,
156> {
157 let to_result = |files: Vec<APTRepositoryFile>, errors: Vec<APTRepositoryFileError>| {
158 let common_digest = common_digest(&files);
159
160 (files, errors, common_digest)
161 };
162
163 let mut files = vec![];
164 let mut errors = vec![];
165
166 let sources_list_path = PathBuf::from(APT_SOURCES_LIST_FILENAME);
167
168 let sources_list_d_path = PathBuf::from(APT_SOURCES_LIST_DIRECTORY);
169
170 match APTRepositoryFile::new(sources_list_path) {
171 Ok(Some(mut file)) => match file.parse() {
172 Ok(()) => files.push(file),
173 Err(err) => errors.push(err),
174 },
175 _ => bail!("internal error with '{}'", APT_SOURCES_LIST_FILENAME),
176 }
177
178 if !sources_list_d_path.exists() {
179 return Ok(to_result(files, errors));
180 }
181
182 if !sources_list_d_path.is_dir() {
183 errors.push(APTRepositoryFileError {
184 path: APT_SOURCES_LIST_DIRECTORY.to_string(),
185 error: "not a directory!".to_string(),
186 });
187 return Ok(to_result(files, errors));
188 }
189
190 for entry in std::fs::read_dir(sources_list_d_path)? {
191 let path = entry?.path();
192
193 match APTRepositoryFile::new(path) {
194 Ok(Some(mut file)) => match file.parse() {
195 Ok(()) => {
196 if file.digest.is_none() {
197 bail!("internal error - digest not set");
198 }
199 files.push(file);
200 }
201 Err(err) => errors.push(err),
202 },
203 Ok(None) => (),
204 Err(err) => errors.push(err),
205 }
206 }
207
208 Ok(to_result(files, errors))
209}