]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/node/apt.rs
www: show more ACLs in datastore panel
[proxmox-backup.git] / src / api2 / node / apt.rs
CommitLineData
9e61c01c 1use anyhow::{Error, bail, format_err};
a4e86972
SR
2use serde_json::{json, Value};
3
e6513bd5 4use proxmox::list_subdirs_api_method;
fa3f0584
TL
5use proxmox::api::{api, RpcEnvironment, RpcEnvironmentType, Permission};
6use proxmox::api::router::{Router, SubdirMap};
a4e86972 7
fa3f0584 8use crate::server::WorkerTask;
e6513bd5 9use crate::tools::{apt, http};
fa3f0584
TL
10
11use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
e6dc35ac 12use crate::api2::types::{Authid, APTUpdateInfo, NODE_SCHEMA, UPID_SCHEMA};
a4e86972 13
a4e86972
SR
14#[api(
15 input: {
16 properties: {
17 node: {
18 schema: NODE_SCHEMA,
19 },
20 },
21 },
22 returns: {
23 description: "A list of packages with available updates.",
24 type: Array,
e6513bd5
TL
25 items: {
26 type: APTUpdateInfo
27 },
a4e86972 28 },
33508b12 29 protected: true,
a4e86972
SR
30 access: {
31 permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
32 },
33)]
34/// List available APT updates
35fn apt_update_available(_param: Value) -> Result<Value, Error> {
33508b12
TL
36
37 match apt::pkg_cache_expired() {
38 Ok(false) => {
39 if let Ok(Some(cache)) = apt::read_pkg_state() {
40 return Ok(json!(cache.package_status));
41 }
42 },
43 _ => (),
44 }
45
46 let cache = apt::update_cache()?;
47
48 return Ok(json!(cache.package_status));
a4e86972
SR
49}
50
b2825575
TL
51fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
52 if !quiet { worker.log("starting apt-get update") }
53
54 // TODO: set proxy /etc/apt/apt.conf.d/76pbsproxy like PVE
55
56 let mut command = std::process::Command::new("apt-get");
57 command.arg("update");
58
59 // apt "errors" quite easily, and run_command is a bit rigid, so handle this inline for now.
60 let output = command.output()
61 .map_err(|err| format_err!("failed to execute {:?} - {}", command, err))?;
62
63 if !quiet {
64 worker.log(String::from_utf8(output.stdout)?);
65 }
66
67 // TODO: improve run_command to allow outputting both, stderr and stdout
68 if !output.status.success() {
69 if output.status.code().is_some() {
70 let msg = String::from_utf8(output.stderr)
71 .map(|m| if m.is_empty() { String::from("no error message") } else { m })
72 .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
73 worker.warn(msg);
74 } else {
75 bail!("terminated by signal");
76 }
77 }
78 Ok(())
79}
80
fa3f0584 81#[api(
27fde647 82 protected: true,
fa3f0584
TL
83 input: {
84 properties: {
85 node: {
86 schema: NODE_SCHEMA,
87 },
86d60245
TL
88 notify: {
89 type: bool,
90 description: r#"Send notification mail about new package updates availanle to the
91 email address configured for 'root@pam')."#,
92 optional: true,
93 default: false,
94 },
fa3f0584
TL
95 quiet: {
96 description: "Only produces output suitable for logging, omitting progress indicators.",
97 type: bool,
98 default: false,
99 optional: true,
100 },
101 },
102 },
103 returns: {
104 schema: UPID_SCHEMA,
105 },
106 access: {
107 permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
108 },
109)]
110/// Update the APT database
111pub fn apt_update_database(
86d60245 112 notify: Option<bool>,
fa3f0584
TL
113 quiet: Option<bool>,
114 rpcenv: &mut dyn RpcEnvironment,
115) -> Result<String, Error> {
116
e6dc35ac 117 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
fa3f0584 118 let to_stdout = if rpcenv.env_type() == RpcEnvironmentType::CLI { true } else { false };
86d60245 119 // FIXME: change to non-option in signature and drop below once we have proxmox-api-macro 0.2.3
98b17337 120 let quiet = quiet.unwrap_or(API_METHOD_APT_UPDATE_DATABASE_PARAM_DEFAULT_QUIET);
86d60245 121 let notify = notify.unwrap_or(API_METHOD_APT_UPDATE_DATABASE_PARAM_DEFAULT_NOTIFY);
fa3f0584 122
e6dc35ac 123 let upid_str = WorkerTask::new_thread("aptupdate", None, auth_id, to_stdout, move |worker| {
b2825575 124 do_apt_update(&worker, quiet)?;
86d60245
TL
125
126 let mut cache = apt::update_cache()?;
127
128 if notify {
129 let mut notified = match cache.notified {
130 Some(notified) => notified,
131 None => std::collections::HashMap::new(),
132 };
133 let mut to_notify: Vec<&APTUpdateInfo> = Vec::new();
134
135 for pkg in &cache.package_status {
136 match notified.insert(pkg.package.to_owned(), pkg.version.to_owned()) {
137 Some(notified_version) => {
138 if notified_version != pkg.version {
139 to_notify.push(pkg);
140 }
141 },
142 None => to_notify.push(pkg),
143 }
144 }
145 if !to_notify.is_empty() {
0e16f57e 146 to_notify.sort_unstable_by_key(|k| &k.package);
86d60245
TL
147 crate::server::send_updates_available(&to_notify)?;
148 }
149 cache.notified = Some(notified);
150 apt::write_pkg_cache(&cache)?;
151 }
152
fa3f0584
TL
153 Ok(())
154 })?;
155
156 Ok(upid_str)
157}
158
9e61c01c
SR
159#[api(
160 input: {
161 properties: {
162 node: {
163 schema: NODE_SCHEMA,
164 },
165 name: {
166 description: "Package name to get changelog of.",
167 type: String,
168 },
169 version: {
170 description: "Package version to get changelog of. Omit to use candidate version.",
171 type: String,
172 optional: true,
173 },
174 },
175 },
176 returns: {
177 schema: UPID_SCHEMA,
178 },
179 access: {
180 permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
181 },
182)]
183/// Retrieve the changelog of the specified package.
184fn apt_get_changelog(
185 param: Value,
186) -> Result<Value, Error> {
187
188 let name = crate::tools::required_string_param(&param, "name")?.to_owned();
189 let version = param["version"].as_str();
190
e6513bd5 191 let pkg_info = apt::list_installed_apt_packages(|data| {
9e61c01c
SR
192 match version {
193 Some(version) => version == data.active_version,
194 None => data.active_version == data.candidate_version
195 }
196 }, Some(&name));
197
198 if pkg_info.len() == 0 {
199 bail!("Package '{}' not found", name);
200 }
201
202 let changelog_url = &pkg_info[0].change_log_url;
203 // FIXME: use 'apt-get changelog' for proxmox packages as well, once repo supports it
204 if changelog_url.starts_with("http://download.proxmox.com/") {
205 let changelog = crate::tools::runtime::block_on(http::get_string(changelog_url))
6eb41487 206 .map_err(|err| format_err!("Error downloading changelog from '{}': {}", changelog_url, err))?;
9e61c01c
SR
207 return Ok(json!(changelog));
208 } else {
209 let mut command = std::process::Command::new("apt-get");
210 command.arg("changelog");
211 command.arg("-qq"); // don't display download progress
212 command.arg(name);
213 let output = crate::tools::run_command(command, None)?;
214 return Ok(json!(output));
215 }
216}
217
a4e86972 218const SUBDIRS: SubdirMap = &[
9e61c01c 219 ("changelog", &Router::new().get(&API_METHOD_APT_GET_CHANGELOG)),
fa3f0584
TL
220 ("update", &Router::new()
221 .get(&API_METHOD_APT_UPDATE_AVAILABLE)
222 .post(&API_METHOD_APT_UPDATE_DATABASE)
223 ),
a4e86972
SR
224];
225
226pub const ROUTER: Router = Router::new()
227 .get(&list_subdirs_api_method!(SUBDIRS))
228 .subdirs(SUBDIRS);