]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-tape/src/bin/pmt.rs
update to first proxmox crate split
[proxmox-backup.git] / pbs-tape / src / bin / pmt.rs
CommitLineData
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
16use std::convert::TryInto;
17
1e041082
DM
18use anyhow::{bail, Error};
19use serde_json::Value;
20
6ef1b649
WB
21use proxmox_schema::{api, ArraySchema, IntegerSchema, Schema, StringSchema};
22use proxmox_router::cli::*;
23use proxmox_router::RpcEnvironment;
1e041082 24
1ce8e905
DM
25use pbs_api_types::{
26 LTO_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, LtoTapeDrive,
27};
28use pbs_config::drive::complete_drive_name;
048b43af
DM
29use 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
34pub 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
40pub 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
46pub const RECORD_COUNT_SCHEMA: Schema =
47 IntegerSchema::new("Record count.")
48 .minimum(1)
49 .maximum(i32::MAX as isize)
50 .schema();
51
8937c659 52pub const DRIVE_OPTION_SCHEMA: Schema = StringSchema::new(
a79082a0 53 "Lto Tape Driver Option, either numeric value or option name.")
8937c659
DM
54 .schema();
55
56pub 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 61fn 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)
124fn asf(count: u64, param: Value) -> Result<(), Error> {
85ef6244
DM
125
126 let mut handle = get_tape_handle(&param)?;
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 154fn bsf(count: usize, param: Value) -> Result<(), Error> {
1f31d06f
DM
155
156 let mut handle = get_tape_handle(&param)?;
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 185fn bsfm(count: usize, param: Value) -> Result<(), Error> {
8e6ad430
DM
186
187 let mut handle = get_tape_handle(&param)?;
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 214fn bsr(count: usize, param: Value) -> Result<(), Error> {
d690d145
DM
215
216 let mut handle = get_tape_handle(&param)?;
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
243fn cartridge_memory(param: Value) -> Result<(), Error> {
244
245 let output_format = get_output_format(&param);
246
247 let mut handle = get_tape_handle(&param)?;
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
294fn tape_alert_flags(param: Value) -> Result<(), Error> {
295
296 let output_format = get_output_format(&param);
297
298 let mut handle = get_tape_handle(&param)?;
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
339fn eject(param: Value) -> Result<(), Error> {
340
341 let mut handle = get_tape_handle(&param)?;
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
363fn eod(param: Value) -> Result<(), Error> {
364
365 let mut handle = get_tape_handle(&param)?;
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
393fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> {
394
395 let mut handle = get_tape_handle(&param)?;
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
422fn format(fast: Option<bool>, param: Value) -> Result<(), Error> {
423
424 let mut handle = get_tape_handle(&param)?;
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 450fn fsf(count: usize, param: Value) -> Result<(), Error> {
2f2e83c8
DM
451
452 let mut handle = get_tape_handle(&param)?;
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 480fn fsfm(count: usize, param: Value) -> Result<(), Error> {
8e6ad430
DM
481
482 let mut handle = get_tape_handle(&param)?;
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 509fn fsr(count: usize, param: Value) -> Result<(), Error> {
d690d145
DM
510
511 let mut handle = get_tape_handle(&param)?;
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
534fn load(param: Value) -> Result<(), Error> {
535
536 let mut handle = get_tape_handle(&param)?;
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
558fn lock(param: Value) -> Result<(), Error> {
559
560 let mut handle = get_tape_handle(&param)?;
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
583fn rewind(param: Value) -> Result<(), Error> {
584
585 let mut handle = get_tape_handle(&param)?;
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
603fn scan(param: Value) -> Result<(), Error> {
604
605 let output_format = get_output_format(&param);
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
649fn status(param: Value) -> Result<(), Error> {
650
651 let output_format = get_output_format(&param);
652
653 let mut handle = get_tape_handle(&param)?;
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
696fn unlock(param: Value) -> Result<(), Error> {
697
698 let mut handle = get_tape_handle(&param)?;
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
725fn volume_statistics(param: Value) -> Result<(), Error> {
726
727 let output_format = get_output_format(&param);
728
729 let mut handle = get_tape_handle(&param)?;
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
774fn 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(&param)?;
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
821fn 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(&param)?;
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
840fn 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}