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