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