]>
Commit | Line | Data |
---|---|---|
1e041082 DM |
1 | /// Control magnetic tape drive operation |
2 | /// | |
a79082a0 DM |
3 | /// This is a Rust implementation, using the Proxmox userspace tape |
4 | /// driver. This is meant as replacement fot the 'mt' command line | |
5 | /// tool. | |
1e041082 DM |
6 | /// |
7 | /// Features: | |
8 | /// | |
9 | /// - written in Rust | |
a79082a0 | 10 | /// - use Proxmox userspace driver (using SG_IO) |
1e041082 DM |
11 | /// - optional json output format |
12 | /// - support tape alert flags | |
13 | /// - support volume statistics | |
14 | /// - read cartridge memory | |
15 | ||
048b43af DM |
16 | use std::convert::TryInto; |
17 | ||
1e041082 DM |
18 | use anyhow::{bail, Error}; |
19 | use serde_json::Value; | |
20 | ||
6ef1b649 WB |
21 | use proxmox_schema::{api, ArraySchema, IntegerSchema, Schema, StringSchema}; |
22 | use proxmox_router::cli::*; | |
23 | use proxmox_router::RpcEnvironment; | |
1e041082 | 24 | |
1ce8e905 DM |
25 | use pbs_api_types::{ |
26 | LTO_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, LtoTapeDrive, | |
27 | }; | |
28 | use pbs_config::drive::complete_drive_name; | |
048b43af DM |
29 | use pbs_tape::{ |
30 | sg_tape::SgTape, | |
31 | linux_list_drives::{complete_drive_path, lto_tape_device_list, open_lto_tape_device}, | |
1ce8e905 DM |
32 | }; |
33 | ||
83b8949a DM |
34 | pub const FILE_MARK_COUNT_SCHEMA: Schema = |
35 | IntegerSchema::new("File mark count.") | |
36 | .minimum(1) | |
e9959962 | 37 | .maximum(i32::MAX as isize) |
83b8949a DM |
38 | .schema(); |
39 | ||
5d6379f8 DM |
40 | pub const FILE_MARK_POSITION_SCHEMA: Schema = |
41 | IntegerSchema::new("File mark position (0 is BOT).") | |
42 | .minimum(0) | |
43 | .maximum(i32::MAX as isize) | |
44 | .schema(); | |
45 | ||
d690d145 DM |
46 | pub const RECORD_COUNT_SCHEMA: Schema = |
47 | IntegerSchema::new("Record count.") | |
48 | .minimum(1) | |
49 | .maximum(i32::MAX as isize) | |
50 | .schema(); | |
51 | ||
8937c659 | 52 | pub const DRIVE_OPTION_SCHEMA: Schema = StringSchema::new( |
a79082a0 | 53 | "Lto Tape Driver Option, either numeric value or option name.") |
8937c659 DM |
54 | .schema(); |
55 | ||
56 | pub const DRIVE_OPTION_LIST_SCHEMA: Schema = | |
57 | ArraySchema::new("Drive Option List.", &DRIVE_OPTION_SCHEMA) | |
0d4e4cae | 58 | .min_length(1) |
8937c659 DM |
59 | .schema(); |
60 | ||
048b43af | 61 | fn get_tape_handle(param: &Value) -> Result<SgTape, Error> { |
1e041082 DM |
62 | |
63 | if let Some(name) = param["drive"].as_str() { | |
1ce8e905 | 64 | let (config, _digest) = pbs_config::drive::config()?; |
a79082a0 | 65 | let drive: LtoTapeDrive = config.lookup("lto", &name)?; |
1e041082 | 66 | eprintln!("using device {}", drive.path); |
048b43af | 67 | return SgTape::new(open_lto_tape_device(&drive.path)?); |
1e041082 DM |
68 | } |
69 | ||
70 | if let Some(device) = param["device"].as_str() { | |
71 | eprintln!("using device {}", device); | |
048b43af | 72 | return SgTape::new(open_lto_tape_device(&device)?); |
1e041082 DM |
73 | } |
74 | ||
75 | if let Ok(name) = std::env::var("PROXMOX_TAPE_DRIVE") { | |
1ce8e905 | 76 | let (config, _digest) = pbs_config::drive::config()?; |
a79082a0 | 77 | let drive: LtoTapeDrive = config.lookup("lto", &name)?; |
1e041082 | 78 | eprintln!("using device {}", drive.path); |
048b43af | 79 | return SgTape::new(open_lto_tape_device(&drive.path)?); |
1e041082 DM |
80 | } |
81 | ||
82 | if let Ok(device) = std::env::var("TAPE") { | |
83 | eprintln!("using device {}", device); | |
048b43af | 84 | return SgTape::new(open_lto_tape_device(&device)?); |
1e041082 DM |
85 | } |
86 | ||
1ce8e905 | 87 | let (config, _digest) = pbs_config::drive::config()?; |
1e041082 DM |
88 | |
89 | let mut drive_names = Vec::new(); | |
90 | for (name, (section_type, _)) in config.sections.iter() { | |
a79082a0 | 91 | if section_type != "lto" { continue; } |
1e041082 DM |
92 | drive_names.push(name); |
93 | } | |
94 | ||
95 | if drive_names.len() == 1 { | |
96 | let name = drive_names[0]; | |
a79082a0 | 97 | let drive: LtoTapeDrive = config.lookup("lto", &name)?; |
1e041082 | 98 | eprintln!("using device {}", drive.path); |
048b43af | 99 | return SgTape::new(open_lto_tape_device(&drive.path)?); |
1e041082 DM |
100 | } |
101 | ||
102 | bail!("no drive/device specified"); | |
103 | } | |
104 | ||
85ef6244 DM |
105 | #[api( |
106 | input: { | |
107 | properties: { | |
108 | drive: { | |
109 | schema: DRIVE_NAME_SCHEMA, | |
110 | optional: true, | |
111 | }, | |
112 | device: { | |
a79082a0 | 113 | schema: LTO_DRIVE_PATH_SCHEMA, |
85ef6244 DM |
114 | optional: true, |
115 | }, | |
116 | count: { | |
5d6379f8 | 117 | schema: FILE_MARK_POSITION_SCHEMA, |
85ef6244 DM |
118 | }, |
119 | }, | |
120 | }, | |
121 | )] | |
5d6379f8 DM |
122 | /// Position the tape at the beginning of the count file (after |
123 | /// filemark count) | |
124 | fn asf(count: u64, param: Value) -> Result<(), Error> { | |
85ef6244 DM |
125 | |
126 | let mut handle = get_tape_handle(¶m)?; | |
127 | ||
5d6379f8 | 128 | handle.locate_file(count)?; |
85ef6244 DM |
129 | |
130 | Ok(()) | |
131 | } | |
132 | ||
133 | ||
1f31d06f DM |
134 | #[api( |
135 | input: { | |
136 | properties: { | |
137 | drive: { | |
138 | schema: DRIVE_NAME_SCHEMA, | |
139 | optional: true, | |
140 | }, | |
141 | device: { | |
a79082a0 | 142 | schema: LTO_DRIVE_PATH_SCHEMA, |
1f31d06f DM |
143 | optional: true, |
144 | }, | |
145 | count: { | |
83b8949a | 146 | schema: FILE_MARK_COUNT_SCHEMA, |
1f31d06f | 147 | }, |
83b8949a | 148 | }, |
1f31d06f DM |
149 | }, |
150 | )] | |
151 | /// Backward space count files (position before file mark). | |
152 | /// | |
153 | /// The tape is positioned on the last block of the previous file. | |
589c4dad | 154 | fn bsf(count: usize, param: Value) -> Result<(), Error> { |
1f31d06f DM |
155 | |
156 | let mut handle = get_tape_handle(¶m)?; | |
157 | ||
048b43af | 158 | handle.space_filemarks(-count.try_into()?)?; |
1f31d06f DM |
159 | |
160 | Ok(()) | |
161 | } | |
162 | ||
8e6ad430 DM |
163 | |
164 | #[api( | |
165 | input: { | |
166 | properties: { | |
167 | drive: { | |
168 | schema: DRIVE_NAME_SCHEMA, | |
169 | optional: true, | |
170 | }, | |
171 | device: { | |
a79082a0 | 172 | schema: LTO_DRIVE_PATH_SCHEMA, |
8e6ad430 DM |
173 | optional: true, |
174 | }, | |
175 | count: { | |
176 | schema: FILE_MARK_COUNT_SCHEMA, | |
177 | }, | |
178 | }, | |
179 | }, | |
180 | )] | |
181 | /// Backward space count files, then forward space one record (position after file mark). | |
182 | /// | |
183 | /// This leaves the tape positioned at the first block of the file | |
184 | /// that is count - 1 files before the current file. | |
a79082a0 | 185 | fn bsfm(count: usize, param: Value) -> Result<(), Error> { |
8e6ad430 DM |
186 | |
187 | let mut handle = get_tape_handle(¶m)?; | |
188 | ||
048b43af DM |
189 | handle.space_filemarks(-count.try_into()?)?; |
190 | handle.space_filemarks(1)?; | |
8e6ad430 DM |
191 | |
192 | Ok(()) | |
193 | } | |
194 | ||
195 | ||
d690d145 DM |
196 | #[api( |
197 | input: { | |
198 | properties: { | |
199 | drive: { | |
200 | schema: DRIVE_NAME_SCHEMA, | |
201 | optional: true, | |
202 | }, | |
203 | device: { | |
a79082a0 | 204 | schema: LTO_DRIVE_PATH_SCHEMA, |
d690d145 DM |
205 | optional: true, |
206 | }, | |
207 | count: { | |
208 | schema: RECORD_COUNT_SCHEMA, | |
209 | }, | |
210 | }, | |
211 | }, | |
212 | )] | |
213 | /// Backward space records. | |
7f745967 | 214 | fn bsr(count: usize, param: Value) -> Result<(), Error> { |
d690d145 DM |
215 | |
216 | let mut handle = get_tape_handle(¶m)?; | |
217 | ||
048b43af | 218 | handle.space_blocks(-count.try_into()?)?; |
d690d145 DM |
219 | |
220 | Ok(()) | |
221 | } | |
222 | ||
223 | ||
1e041082 DM |
224 | #[api( |
225 | input: { | |
226 | properties: { | |
227 | drive: { | |
228 | schema: DRIVE_NAME_SCHEMA, | |
229 | optional: true, | |
230 | }, | |
231 | device: { | |
a79082a0 | 232 | schema: LTO_DRIVE_PATH_SCHEMA, |
1e041082 DM |
233 | optional: true, |
234 | }, | |
235 | "output-format": { | |
236 | schema: OUTPUT_FORMAT, | |
237 | optional: true, | |
238 | }, | |
239 | }, | |
240 | }, | |
241 | )] | |
242 | /// Read Cartridge Memory | |
243 | fn cartridge_memory(param: Value) -> Result<(), Error> { | |
244 | ||
245 | let output_format = get_output_format(¶m); | |
246 | ||
247 | let mut handle = get_tape_handle(¶m)?; | |
248 | let result = handle.cartridge_memory(); | |
249 | ||
250 | if output_format == "json-pretty" { | |
251 | let result = result.map_err(|err: Error| err.to_string()); | |
252 | println!("{}", serde_json::to_string_pretty(&result)?); | |
253 | return Ok(()); | |
254 | } | |
255 | ||
256 | if output_format == "json" { | |
257 | let result = result.map_err(|err: Error| err.to_string()); | |
258 | println!("{}", serde_json::to_string(&result)?); | |
259 | return Ok(()); | |
260 | } | |
261 | ||
262 | if output_format != "text" { | |
263 | bail!("unknown output format '{}'", output_format); | |
264 | } | |
265 | ||
266 | let list = result?; | |
267 | ||
268 | for item in list { | |
269 | println!("{}|{}|{}", item.id, item.name, item.value); | |
270 | } | |
271 | ||
272 | Ok(()) | |
273 | } | |
274 | ||
5ca5f8da DM |
275 | #[api( |
276 | input: { | |
277 | properties: { | |
278 | drive: { | |
279 | schema: DRIVE_NAME_SCHEMA, | |
280 | optional: true, | |
281 | }, | |
282 | device: { | |
a79082a0 | 283 | schema: LTO_DRIVE_PATH_SCHEMA, |
5ca5f8da DM |
284 | optional: true, |
285 | }, | |
286 | "output-format": { | |
287 | schema: OUTPUT_FORMAT, | |
288 | optional: true, | |
289 | }, | |
290 | }, | |
291 | }, | |
292 | )] | |
293 | /// Read Tape Alert Flags | |
294 | fn tape_alert_flags(param: Value) -> Result<(), Error> { | |
295 | ||
296 | let output_format = get_output_format(¶m); | |
297 | ||
298 | let mut handle = get_tape_handle(¶m)?; | |
299 | let result = handle.tape_alert_flags() | |
300 | .map(|flags| format!("{:?}", flags)); | |
301 | ||
302 | if output_format == "json-pretty" { | |
303 | let result = result.map_err(|err: Error| err.to_string()); | |
304 | println!("{}", serde_json::to_string_pretty(&result)?); | |
305 | return Ok(()); | |
306 | } | |
307 | ||
308 | if output_format == "json" { | |
309 | let result = result.map_err(|err: Error| err.to_string()); | |
310 | println!("{}", serde_json::to_string(&result)?); | |
311 | return Ok(()); | |
312 | } | |
313 | ||
314 | if output_format != "text" { | |
315 | bail!("unknown output format '{}'", output_format); | |
316 | } | |
317 | ||
318 | let flags = result?; | |
319 | println!("Tape Alert Flags: {}", flags); | |
320 | ||
321 | Ok(()) | |
322 | } | |
323 | ||
1e041082 DM |
324 | #[api( |
325 | input: { | |
326 | properties: { | |
327 | drive: { | |
328 | schema: DRIVE_NAME_SCHEMA, | |
329 | optional: true, | |
330 | }, | |
331 | device: { | |
a79082a0 | 332 | schema: LTO_DRIVE_PATH_SCHEMA, |
1e041082 DM |
333 | optional: true, |
334 | }, | |
335 | }, | |
336 | }, | |
337 | )] | |
338 | /// Eject drive media | |
339 | fn eject(param: Value) -> Result<(), Error> { | |
340 | ||
341 | let mut handle = get_tape_handle(¶m)?; | |
048b43af | 342 | handle.eject()?; |
1e041082 DM |
343 | |
344 | Ok(()) | |
345 | } | |
346 | ||
347 | ||
348 | #[api( | |
349 | input: { | |
350 | properties: { | |
351 | drive: { | |
352 | schema: DRIVE_NAME_SCHEMA, | |
353 | optional: true, | |
354 | }, | |
355 | device: { | |
a79082a0 | 356 | schema: LTO_DRIVE_PATH_SCHEMA, |
1e041082 DM |
357 | optional: true, |
358 | }, | |
359 | }, | |
360 | }, | |
361 | )] | |
362 | /// Move to end of media | |
363 | fn eod(param: Value) -> Result<(), Error> { | |
364 | ||
365 | let mut handle = get_tape_handle(¶m)?; | |
7b11a809 | 366 | handle.move_to_eom(false)?; |
1e041082 DM |
367 | |
368 | Ok(()) | |
369 | } | |
370 | ||
371 | ||
b22c6187 DM |
372 | #[api( |
373 | input: { | |
374 | properties: { | |
375 | drive: { | |
376 | schema: DRIVE_NAME_SCHEMA, | |
377 | optional: true, | |
378 | }, | |
379 | device: { | |
a79082a0 | 380 | schema: LTO_DRIVE_PATH_SCHEMA, |
b22c6187 DM |
381 | optional: true, |
382 | }, | |
383 | fast: { | |
384 | description: "Use fast erase.", | |
385 | type: bool, | |
386 | optional: true, | |
387 | default: true, | |
388 | }, | |
389 | }, | |
390 | }, | |
391 | )] | |
e29f456e | 392 | /// Erase media (from current position) |
b22c6187 DM |
393 | fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> { |
394 | ||
395 | let mut handle = get_tape_handle(¶m)?; | |
396 | handle.erase_media(fast.unwrap_or(true))?; | |
397 | ||
398 | Ok(()) | |
399 | } | |
400 | ||
e29f456e DM |
401 | #[api( |
402 | input: { | |
403 | properties: { | |
404 | drive: { | |
405 | schema: DRIVE_NAME_SCHEMA, | |
406 | optional: true, | |
407 | }, | |
408 | device: { | |
409 | schema: LTO_DRIVE_PATH_SCHEMA, | |
410 | optional: true, | |
411 | }, | |
412 | fast: { | |
413 | description: "Use fast erase.", | |
414 | type: bool, | |
415 | optional: true, | |
416 | default: true, | |
417 | }, | |
418 | }, | |
419 | }, | |
420 | )] | |
421 | /// Format media, single partition | |
422 | fn format(fast: Option<bool>, param: Value) -> Result<(), Error> { | |
423 | ||
424 | let mut handle = get_tape_handle(¶m)?; | |
425 | handle.format_media(fast.unwrap_or(true))?; | |
426 | ||
427 | Ok(()) | |
428 | } | |
429 | ||
2f2e83c8 DM |
430 | #[api( |
431 | input: { | |
432 | properties: { | |
433 | drive: { | |
434 | schema: DRIVE_NAME_SCHEMA, | |
435 | optional: true, | |
436 | }, | |
437 | device: { | |
a79082a0 | 438 | schema: LTO_DRIVE_PATH_SCHEMA, |
2f2e83c8 DM |
439 | optional: true, |
440 | }, | |
441 | count: { | |
83b8949a | 442 | schema: FILE_MARK_COUNT_SCHEMA, |
2f2e83c8 DM |
443 | }, |
444 | }, | |
445 | }, | |
446 | )] | |
447 | /// Forward space count files (position after file mark). | |
448 | /// | |
449 | /// The tape is positioned on the first block of the next file. | |
589c4dad | 450 | fn fsf(count: usize, param: Value) -> Result<(), Error> { |
2f2e83c8 DM |
451 | |
452 | let mut handle = get_tape_handle(¶m)?; | |
453 | ||
048b43af | 454 | handle.space_filemarks(count.try_into()?)?; |
2f2e83c8 DM |
455 | |
456 | Ok(()) | |
457 | } | |
458 | ||
8e6ad430 DM |
459 | #[api( |
460 | input: { | |
461 | properties: { | |
462 | drive: { | |
463 | schema: DRIVE_NAME_SCHEMA, | |
464 | optional: true, | |
465 | }, | |
466 | device: { | |
a79082a0 | 467 | schema: LTO_DRIVE_PATH_SCHEMA, |
8e6ad430 DM |
468 | optional: true, |
469 | }, | |
470 | count: { | |
471 | schema: FILE_MARK_COUNT_SCHEMA, | |
472 | }, | |
473 | }, | |
474 | }, | |
475 | )] | |
476 | /// Forward space count files, then backward space one record (position before file mark). | |
477 | /// | |
478 | /// This leaves the tape positioned at the last block of the file that | |
479 | /// is count - 1 files past the current file. | |
a79082a0 | 480 | fn fsfm(count: usize, param: Value) -> Result<(), Error> { |
8e6ad430 DM |
481 | |
482 | let mut handle = get_tape_handle(¶m)?; | |
483 | ||
048b43af DM |
484 | handle.space_filemarks(count.try_into()?)?; |
485 | handle.space_filemarks(-1)?; | |
8e6ad430 DM |
486 | |
487 | Ok(()) | |
488 | } | |
489 | ||
b22c6187 | 490 | |
d690d145 DM |
491 | #[api( |
492 | input: { | |
493 | properties: { | |
494 | drive: { | |
495 | schema: DRIVE_NAME_SCHEMA, | |
496 | optional: true, | |
497 | }, | |
498 | device: { | |
a79082a0 | 499 | schema: LTO_DRIVE_PATH_SCHEMA, |
d690d145 DM |
500 | optional: true, |
501 | }, | |
502 | count: { | |
503 | schema: RECORD_COUNT_SCHEMA, | |
504 | }, | |
505 | }, | |
506 | }, | |
507 | )] | |
508 | /// Forward space records. | |
7f745967 | 509 | fn fsr(count: usize, param: Value) -> Result<(), Error> { |
d690d145 DM |
510 | |
511 | let mut handle = get_tape_handle(¶m)?; | |
512 | ||
048b43af | 513 | handle.space_blocks(count.try_into()?)?; |
d690d145 DM |
514 | |
515 | Ok(()) | |
516 | } | |
517 | ||
518 | ||
1e041082 DM |
519 | #[api( |
520 | input: { | |
521 | properties: { | |
522 | drive: { | |
523 | schema: DRIVE_NAME_SCHEMA, | |
524 | optional: true, | |
525 | }, | |
526 | device: { | |
a79082a0 | 527 | schema: LTO_DRIVE_PATH_SCHEMA, |
1e041082 DM |
528 | optional: true, |
529 | }, | |
530 | }, | |
531 | }, | |
532 | )] | |
533 | /// Load media | |
534 | fn load(param: Value) -> Result<(), Error> { | |
535 | ||
536 | let mut handle = get_tape_handle(¶m)?; | |
a79082a0 | 537 | handle.load()?; |
1e041082 DM |
538 | |
539 | Ok(()) | |
540 | } | |
541 | ||
542 | ||
90769e56 DM |
543 | #[api( |
544 | input: { | |
545 | properties: { | |
546 | drive: { | |
547 | schema: DRIVE_NAME_SCHEMA, | |
548 | optional: true, | |
549 | }, | |
550 | device: { | |
a79082a0 | 551 | schema: LTO_DRIVE_PATH_SCHEMA, |
90769e56 DM |
552 | optional: true, |
553 | }, | |
554 | }, | |
555 | }, | |
556 | )] | |
557 | /// Lock the tape drive door | |
558 | fn lock(param: Value) -> Result<(), Error> { | |
559 | ||
560 | let mut handle = get_tape_handle(¶m)?; | |
561 | ||
048b43af | 562 | handle.set_medium_removal(false)?; |
90769e56 DM |
563 | |
564 | Ok(()) | |
565 | } | |
566 | ||
567 | ||
1e041082 DM |
568 | #[api( |
569 | input: { | |
570 | properties: { | |
571 | drive: { | |
572 | schema: DRIVE_NAME_SCHEMA, | |
573 | optional: true, | |
574 | }, | |
575 | device: { | |
a79082a0 | 576 | schema: LTO_DRIVE_PATH_SCHEMA, |
1e041082 DM |
577 | optional: true, |
578 | }, | |
579 | }, | |
580 | }, | |
581 | )] | |
582 | /// Rewind the tape | |
583 | fn rewind(param: Value) -> Result<(), Error> { | |
584 | ||
585 | let mut handle = get_tape_handle(¶m)?; | |
586 | handle.rewind()?; | |
587 | ||
588 | Ok(()) | |
589 | } | |
590 | ||
591 | ||
592 | #[api( | |
593 | input: { | |
594 | properties: { | |
595 | "output-format": { | |
596 | schema: OUTPUT_FORMAT, | |
597 | optional: true, | |
598 | }, | |
599 | }, | |
600 | }, | |
601 | )] | |
602 | /// Scan for existing tape changer devices | |
603 | fn scan(param: Value) -> Result<(), Error> { | |
604 | ||
605 | let output_format = get_output_format(¶m); | |
606 | ||
a79082a0 | 607 | let list = lto_tape_device_list(); |
1e041082 DM |
608 | |
609 | if output_format == "json-pretty" { | |
610 | println!("{}", serde_json::to_string_pretty(&list)?); | |
611 | return Ok(()); | |
612 | } | |
613 | ||
614 | if output_format == "json" { | |
615 | println!("{}", serde_json::to_string(&list)?); | |
616 | return Ok(()); | |
617 | } | |
618 | ||
619 | if output_format != "text" { | |
620 | bail!("unknown output format '{}'", output_format); | |
621 | } | |
622 | ||
623 | for item in list.iter() { | |
624 | println!("{} ({}/{}/{})", item.path, item.vendor, item.model, item.serial); | |
625 | } | |
626 | ||
627 | Ok(()) | |
628 | } | |
629 | ||
630 | #[api( | |
631 | input: { | |
632 | properties: { | |
633 | drive: { | |
634 | schema: DRIVE_NAME_SCHEMA, | |
635 | optional: true, | |
636 | }, | |
637 | device: { | |
a79082a0 | 638 | schema: LTO_DRIVE_PATH_SCHEMA, |
1e041082 DM |
639 | optional: true, |
640 | }, | |
641 | "output-format": { | |
642 | schema: OUTPUT_FORMAT, | |
643 | optional: true, | |
644 | }, | |
645 | }, | |
646 | }, | |
647 | )] | |
648 | /// Drive Status | |
649 | fn status(param: Value) -> Result<(), Error> { | |
650 | ||
651 | let output_format = get_output_format(¶m); | |
652 | ||
653 | let mut handle = get_tape_handle(¶m)?; | |
048b43af | 654 | |
1e041082 DM |
655 | let result = handle.get_drive_and_media_status(); |
656 | ||
657 | if output_format == "json-pretty" { | |
658 | let result = result.map_err(|err: Error| err.to_string()); | |
659 | println!("{}", serde_json::to_string_pretty(&result)?); | |
660 | return Ok(()); | |
661 | } | |
662 | ||
663 | if output_format == "json" { | |
664 | let result = result.map_err(|err: Error| err.to_string()); | |
665 | println!("{}", serde_json::to_string(&result)?); | |
666 | return Ok(()); | |
667 | } | |
668 | ||
669 | if output_format != "text" { | |
670 | bail!("unknown output format '{}'", output_format); | |
671 | } | |
672 | ||
673 | let status = result?; | |
674 | ||
675 | println!("{}", serde_json::to_string_pretty(&status)?); | |
676 | ||
677 | Ok(()) | |
678 | } | |
679 | ||
90769e56 DM |
680 | |
681 | #[api( | |
682 | input: { | |
683 | properties: { | |
684 | drive: { | |
685 | schema: DRIVE_NAME_SCHEMA, | |
686 | optional: true, | |
687 | }, | |
688 | device: { | |
a79082a0 | 689 | schema: LTO_DRIVE_PATH_SCHEMA, |
90769e56 DM |
690 | optional: true, |
691 | }, | |
692 | }, | |
693 | }, | |
694 | )] | |
695 | /// Unlock the tape drive door | |
696 | fn unlock(param: Value) -> Result<(), Error> { | |
697 | ||
698 | let mut handle = get_tape_handle(¶m)?; | |
699 | ||
048b43af | 700 | handle.set_medium_removal(true)?; |
90769e56 DM |
701 | |
702 | Ok(()) | |
703 | } | |
704 | ||
705 | ||
1e041082 DM |
706 | #[api( |
707 | input: { | |
708 | properties: { | |
709 | drive: { | |
710 | schema: DRIVE_NAME_SCHEMA, | |
711 | optional: true, | |
712 | }, | |
713 | device: { | |
a79082a0 | 714 | schema: LTO_DRIVE_PATH_SCHEMA, |
1e041082 DM |
715 | optional: true, |
716 | }, | |
717 | "output-format": { | |
718 | schema: OUTPUT_FORMAT, | |
719 | optional: true, | |
720 | }, | |
721 | }, | |
722 | }, | |
723 | )] | |
724 | /// Volume Statistics | |
725 | fn volume_statistics(param: Value) -> Result<(), Error> { | |
726 | ||
727 | let output_format = get_output_format(¶m); | |
728 | ||
729 | let mut handle = get_tape_handle(¶m)?; | |
730 | let result = handle.volume_statistics(); | |
731 | ||
732 | if output_format == "json-pretty" { | |
733 | let result = result.map_err(|err: Error| err.to_string()); | |
734 | println!("{}", serde_json::to_string_pretty(&result)?); | |
735 | return Ok(()); | |
736 | } | |
737 | ||
738 | if output_format == "json" { | |
739 | let result = result.map_err(|err: Error| err.to_string()); | |
740 | println!("{}", serde_json::to_string(&result)?); | |
741 | return Ok(()); | |
742 | } | |
743 | ||
744 | if output_format != "text" { | |
745 | bail!("unknown output format '{}'", output_format); | |
746 | } | |
747 | ||
748 | let data = result?; | |
749 | ||
750 | println!("{}", serde_json::to_string_pretty(&data)?); | |
751 | ||
752 | Ok(()) | |
753 | } | |
754 | ||
83b8949a DM |
755 | #[api( |
756 | input: { | |
757 | properties: { | |
758 | drive: { | |
759 | schema: DRIVE_NAME_SCHEMA, | |
760 | optional: true, | |
761 | }, | |
762 | device: { | |
a79082a0 | 763 | schema: LTO_DRIVE_PATH_SCHEMA, |
83b8949a DM |
764 | optional: true, |
765 | }, | |
766 | count: { | |
767 | schema: FILE_MARK_COUNT_SCHEMA, | |
768 | optional: true, | |
769 | }, | |
770 | }, | |
771 | }, | |
772 | )] | |
773 | /// Write count (default 1) EOF marks at current position. | |
a79082a0 DM |
774 | fn weof(count: Option<usize>, param: Value) -> Result<(), Error> { |
775 | ||
776 | let count = count.unwrap_or(1); | |
83b8949a DM |
777 | |
778 | let mut handle = get_tape_handle(¶m)?; | |
a79082a0 | 779 | |
048b43af | 780 | handle.write_filemarks(count, false)?; |
83b8949a DM |
781 | |
782 | Ok(()) | |
783 | } | |
80ea23e1 DM |
784 | #[api( |
785 | input: { | |
786 | properties: { | |
787 | drive: { | |
788 | schema: DRIVE_NAME_SCHEMA, | |
789 | optional: true, | |
790 | }, | |
791 | device: { | |
792 | schema: LTO_DRIVE_PATH_SCHEMA, | |
793 | optional: true, | |
794 | }, | |
795 | compression: { | |
796 | description: "Enable/disable compression.", | |
797 | type: bool, | |
798 | optional: true, | |
799 | }, | |
800 | blocksize: { | |
801 | description: "Set tape drive block_length (0 is variable length).", | |
802 | type: u32, | |
803 | minimum: 0, | |
804 | maximum: 0x80_00_00, | |
805 | optional: true, | |
806 | }, | |
807 | buffer_mode: { | |
808 | description: "Use drive buffer.", | |
809 | type: bool, | |
810 | optional: true, | |
811 | }, | |
812 | defaults: { | |
813 | description: "Set default options", | |
814 | type: bool, | |
815 | optional: true, | |
816 | }, | |
817 | }, | |
818 | }, | |
819 | )] | |
820 | /// Set varios drive options | |
821 | fn options( | |
822 | compression: Option<bool>, | |
823 | blocksize: Option<u32>, | |
824 | buffer_mode: Option<bool>, | |
825 | defaults: Option<bool>, | |
826 | param: Value, | |
827 | ) -> Result<(), Error> { | |
828 | ||
829 | let mut handle = get_tape_handle(¶m)?; | |
830 | ||
831 | if let Some(true) = defaults { | |
832 | handle.set_default_options()?; | |
833 | } | |
834 | ||
835 | handle.set_drive_options(compression, blocksize, buffer_mode)?; | |
836 | ||
837 | Ok(()) | |
838 | } | |
83b8949a | 839 | |
1e041082 DM |
840 | fn main() -> Result<(), Error> { |
841 | ||
842 | let uid = nix::unistd::Uid::current(); | |
843 | ||
844 | let username = match nix::unistd::User::from_uid(uid)? { | |
845 | Some(user) => user.name, | |
846 | None => bail!("unable to get user name"), | |
847 | }; | |
848 | ||
849 | let std_cmd = |method| { | |
850 | CliCommand::new(method) | |
851 | .completion_cb("drive", complete_drive_name) | |
852 | .completion_cb("device", complete_drive_path) | |
853 | }; | |
854 | ||
855 | let cmd_def = CliCommandMap::new() | |
4c209d6b | 856 | .usage_skip_options(&["device", "drive", "output-format"]) |
85ef6244 | 857 | .insert("asf", std_cmd(&API_METHOD_ASF).arg_param(&["count"])) |
8e6ad430 DM |
858 | .insert("bsf", std_cmd(&API_METHOD_BSF).arg_param(&["count"])) |
859 | .insert("bsfm", std_cmd(&API_METHOD_BSFM).arg_param(&["count"])) | |
d690d145 | 860 | .insert("bsr", std_cmd(&API_METHOD_BSR).arg_param(&["count"])) |
1e041082 DM |
861 | .insert("cartridge-memory", std_cmd(&API_METHOD_CARTRIDGE_MEMORY)) |
862 | .insert("eject", std_cmd(&API_METHOD_EJECT)) | |
863 | .insert("eod", std_cmd(&API_METHOD_EOD)) | |
b22c6187 | 864 | .insert("erase", std_cmd(&API_METHOD_ERASE)) |
e29f456e | 865 | .insert("format", std_cmd(&API_METHOD_FORMAT)) |
8e6ad430 DM |
866 | .insert("fsf", std_cmd(&API_METHOD_FSF).arg_param(&["count"])) |
867 | .insert("fsfm", std_cmd(&API_METHOD_FSFM).arg_param(&["count"])) | |
d690d145 | 868 | .insert("fsr", std_cmd(&API_METHOD_FSR).arg_param(&["count"])) |
1e041082 | 869 | .insert("load", std_cmd(&API_METHOD_LOAD)) |
90769e56 | 870 | .insert("lock", std_cmd(&API_METHOD_LOCK)) |
80ea23e1 | 871 | .insert("options", std_cmd(&API_METHOD_OPTIONS)) |
1e041082 DM |
872 | .insert("rewind", std_cmd(&API_METHOD_REWIND)) |
873 | .insert("scan", CliCommand::new(&API_METHOD_SCAN)) | |
874 | .insert("status", std_cmd(&API_METHOD_STATUS)) | |
5ca5f8da | 875 | .insert("tape-alert-flags", std_cmd(&API_METHOD_TAPE_ALERT_FLAGS)) |
90769e56 | 876 | .insert("unlock", std_cmd(&API_METHOD_UNLOCK)) |
1e041082 | 877 | .insert("volume-statistics", std_cmd(&API_METHOD_VOLUME_STATISTICS)) |
8e6ad430 | 878 | .insert("weof", std_cmd(&API_METHOD_WEOF).arg_param(&["count"])) |
1e041082 DM |
879 | ; |
880 | ||
881 | let mut rpcenv = CliEnvironment::new(); | |
882 | rpcenv.set_auth_id(Some(format!("{}@pam", username))); | |
883 | ||
884 | run_cli_command(cmd_def, rpcenv, None); | |
885 | ||
886 | Ok(()) | |
887 | } |