]> git.proxmox.com Git - proxmox-backup.git/blob - src/bin/sg-tape-cmd.rs
clippy: misc. fixes
[proxmox-backup.git] / src / bin / sg-tape-cmd.rs
1 /// Tape command implemented using scsi-generic raw commands
2 ///
3 /// SCSI-generic command needs root priviledges, so this binary need
4 /// to be setuid root.
5 ///
6 /// This command can use STDIN as tape device handle.
7
8 use std::fs::File;
9 use std::os::unix::io::{AsRawFd, FromRawFd};
10
11 use anyhow::{bail, Error};
12 use serde_json::Value;
13
14 use proxmox::{
15 api::{
16 api,
17 cli::*,
18 RpcEnvironment,
19 },
20 };
21
22 use proxmox_backup::{
23 config,
24 backup::Fingerprint,
25 api2::types::{
26 LINUX_DRIVE_PATH_SCHEMA,
27 DRIVE_NAME_SCHEMA,
28 TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
29 LinuxTapeDrive,
30 },
31 tape::{
32 TapeDriver,
33 linux_tape::{
34 LinuxTapeHandle,
35 open_linux_tape_device,
36 check_tape_is_linux_tape_device,
37 },
38 },
39 };
40
41 fn get_tape_handle(param: &Value) -> Result<LinuxTapeHandle, Error> {
42
43 let handle = if let Some(name) = param["drive"].as_str() {
44 let (config, _digest) = config::drive::config()?;
45 let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
46 eprintln!("using device {}", drive.path);
47 drive.open()?
48 } else if let Some(device) = param["device"].as_str() {
49 eprintln!("using device {}", device);
50 LinuxTapeHandle::new(open_linux_tape_device(&device)?)
51 } else if let Some(true) = param["stdin"].as_bool() {
52 eprintln!("using stdin");
53 let fd = std::io::stdin().as_raw_fd();
54 let file = unsafe { File::from_raw_fd(fd) };
55 check_tape_is_linux_tape_device(&file)?;
56 LinuxTapeHandle::new(file)
57 } else if let Ok(name) = std::env::var("PROXMOX_TAPE_DRIVE") {
58 let (config, _digest) = config::drive::config()?;
59 let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
60 eprintln!("using device {}", drive.path);
61 drive.open()?
62 } else {
63 let (config, _digest) = config::drive::config()?;
64
65 let mut drive_names = Vec::new();
66 for (name, (section_type, _)) in config.sections.iter() {
67 if section_type != "linux" { continue; }
68 drive_names.push(name);
69 }
70
71 if drive_names.len() == 1 {
72 let name = drive_names[0];
73 let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
74 eprintln!("using device {}", drive.path);
75 drive.open()?
76 } else {
77 bail!("no drive/device specified");
78 }
79 };
80
81 Ok(handle)
82 }
83
84 #[api(
85 input: {
86 properties: {
87 drive: {
88 schema: DRIVE_NAME_SCHEMA,
89 optional: true,
90 },
91 device: {
92 schema: LINUX_DRIVE_PATH_SCHEMA,
93 optional: true,
94 },
95 stdin: {
96 description: "Use standard input as device handle.",
97 type: bool,
98 optional: true,
99 },
100 },
101 },
102 )]
103 /// Tape/Media Status
104 fn status(
105 param: Value,
106 ) -> Result<(), Error> {
107
108 let result = proxmox::try_block!({
109 let mut handle = get_tape_handle(&param)?;
110 handle.get_drive_and_media_status()
111 }).map_err(|err: Error| err.to_string());
112
113 println!("{}", serde_json::to_string_pretty(&result)?);
114
115 Ok(())
116 }
117
118 #[api(
119 input: {
120 properties: {
121 drive: {
122 schema: DRIVE_NAME_SCHEMA,
123 optional: true,
124 },
125 device: {
126 schema: LINUX_DRIVE_PATH_SCHEMA,
127 optional: true,
128 },
129 stdin: {
130 description: "Use standard input as device handle.",
131 type: bool,
132 optional: true,
133 },
134 },
135 },
136 )]
137 /// Read Cartridge Memory (Medium auxiliary memory attributes)
138 fn cartridge_memory(
139 param: Value,
140 ) -> Result<(), Error> {
141
142 let result = proxmox::try_block!({
143 let mut handle = get_tape_handle(&param)?;
144
145 handle.cartridge_memory()
146 }).map_err(|err| err.to_string());
147
148 println!("{}", serde_json::to_string_pretty(&result)?);
149
150 Ok(())
151 }
152
153 #[api(
154 input: {
155 properties: {
156 drive: {
157 schema: DRIVE_NAME_SCHEMA,
158 optional: true,
159 },
160 device: {
161 schema: LINUX_DRIVE_PATH_SCHEMA,
162 optional: true,
163 },
164 stdin: {
165 description: "Use standard input as device handle.",
166 type: bool,
167 optional: true,
168 },
169 },
170 },
171 )]
172 /// Read Tape Alert Flags
173 fn tape_alert_flags(
174 param: Value,
175 ) -> Result<(), Error> {
176
177 let result = proxmox::try_block!({
178 let mut handle = get_tape_handle(&param)?;
179
180 let flags = handle.tape_alert_flags()?;
181 Ok(flags.bits())
182 }).map_err(|err: Error| err.to_string());
183
184 println!("{}", serde_json::to_string_pretty(&result)?);
185
186 Ok(())
187 }
188
189 #[api(
190 input: {
191 properties: {
192 fingerprint: {
193 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
194 optional: true,
195 },
196 drive: {
197 schema: DRIVE_NAME_SCHEMA,
198 optional: true,
199 },
200 device: {
201 schema: LINUX_DRIVE_PATH_SCHEMA,
202 optional: true,
203 },
204 stdin: {
205 description: "Use standard input as device handle.",
206 type: bool,
207 optional: true,
208 },
209 },
210 },
211 )]
212 /// Set or clear encryption key
213 fn set_encryption(
214 fingerprint: Option<Fingerprint>,
215 param: Value,
216 ) -> Result<(), Error> {
217
218 let result = proxmox::try_block!({
219 let mut handle = get_tape_handle(&param)?;
220
221 handle.set_encryption(fingerprint)?;
222 Ok(())
223 }).map_err(|err: Error| err.to_string());
224
225 println!("{}", serde_json::to_string_pretty(&result)?);
226
227 Ok(())
228 }
229
230 #[api(
231 input: {
232 properties: {
233 drive: {
234 schema: DRIVE_NAME_SCHEMA,
235 optional: true,
236 },
237 device: {
238 schema: LINUX_DRIVE_PATH_SCHEMA,
239 optional: true,
240 },
241 stdin: {
242 description: "Use standard input as device handle.",
243 type: bool,
244 optional: true,
245 },
246 },
247 },
248 )]
249 /// Read volume statistics
250 fn volume_statistics(
251 param: Value,
252 ) -> Result<(), Error> {
253
254 let result = proxmox::try_block!({
255 let mut handle = get_tape_handle(&param)?;
256 handle.volume_statistics()
257 }).map_err(|err: Error| err.to_string());
258
259 println!("{}", serde_json::to_string_pretty(&result)?);
260
261 Ok(())
262 }
263
264 fn main() -> Result<(), Error> {
265
266 // check if we are user root or backup
267 let backup_uid = proxmox_backup::backup::backup_user()?.uid;
268 let backup_gid = proxmox_backup::backup::backup_group()?.gid;
269 let running_uid = nix::unistd::Uid::current();
270 let running_gid = nix::unistd::Gid::current();
271
272 let effective_uid = nix::unistd::Uid::effective();
273 if !effective_uid.is_root() {
274 bail!("this program needs to be run with setuid root");
275 }
276
277 if !running_uid.is_root() && (running_uid != backup_uid || running_gid != backup_gid) {
278 bail!(
279 "Not running as backup user or group (got uid {} gid {})",
280 running_uid, running_gid,
281 );
282 }
283
284 let cmd_def = CliCommandMap::new()
285 .insert(
286 "status",
287 CliCommand::new(&API_METHOD_STATUS)
288 )
289 .insert(
290 "cartridge-memory",
291 CliCommand::new(&API_METHOD_CARTRIDGE_MEMORY)
292 )
293 .insert(
294 "tape-alert-flags",
295 CliCommand::new(&API_METHOD_TAPE_ALERT_FLAGS)
296 )
297 .insert(
298 "volume-statistics",
299 CliCommand::new(&API_METHOD_VOLUME_STATISTICS)
300 )
301 .insert(
302 "encryption",
303 CliCommand::new(&API_METHOD_SET_ENCRYPTION)
304 )
305 ;
306
307 let mut rpcenv = CliEnvironment::new();
308 rpcenv.set_auth_id(Some(String::from("root@pam")));
309
310 run_cli_command(cmd_def, rpcenv, None);
311
312 Ok(())
313 }