1 /// Tape command implemented using scsi-generic raw commands
3 /// SCSI-generic command needs root privileges, so this binary need
6 /// This command can use STDIN as tape device handle.
9 use std
::os
::unix
::io
::{AsRawFd, FromRawFd}
;
11 use anyhow
::{bail, Error}
;
12 use serde_json
::Value
;
27 LINUX_DRIVE_PATH_SCHEMA
,
29 TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA
,
30 MEDIA_SET_UUID_SCHEMA
,
37 open_linux_tape_device
,
38 check_tape_is_linux_tape_device
,
43 fn get_tape_handle(param
: &Value
) -> Result
<LinuxTapeHandle
, Error
> {
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
);
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
);
65 let (config
, _digest
) = config
::drive
::config()?
;
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
);
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
);
79 bail
!("no drive/device specified");
90 schema
: DRIVE_NAME_SCHEMA
,
94 schema
: LINUX_DRIVE_PATH_SCHEMA
,
98 description
: "Use standard input as device handle.",
105 /// Tape/Media Status
108 ) -> Result
<(), Error
> {
110 let result
= proxmox
::try_block
!({
111 let mut handle
= get_tape_handle(¶m
)?
;
112 handle
.get_drive_and_media_status()
113 }).map_err(|err
: Error
| err
.to_string());
115 println
!("{}", serde_json
::to_string_pretty(&result
)?
);
124 schema
: DRIVE_NAME_SCHEMA
,
128 schema
: LINUX_DRIVE_PATH_SCHEMA
,
132 description
: "Use standard input as device handle.",
139 /// Read Cartridge Memory (Medium auxiliary memory attributes)
142 ) -> Result
<(), Error
> {
144 let result
= proxmox
::try_block
!({
145 let mut handle
= get_tape_handle(¶m
)?
;
147 handle
.cartridge_memory()
148 }).map_err(|err
| err
.to_string());
150 println
!("{}", serde_json
::to_string_pretty(&result
)?
);
159 schema
: DRIVE_NAME_SCHEMA
,
163 schema
: LINUX_DRIVE_PATH_SCHEMA
,
167 description
: "Use standard input as device handle.",
174 /// Read Tape Alert Flags
177 ) -> Result
<(), Error
> {
179 let result
= proxmox
::try_block
!({
180 let mut handle
= get_tape_handle(¶m
)?
;
182 let flags
= handle
.tape_alert_flags()?
;
184 }).map_err(|err
: Error
| err
.to_string());
186 println
!("{}", serde_json
::to_string_pretty(&result
)?
);
195 schema
: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA
,
199 schema
: MEDIA_SET_UUID_SCHEMA
,
203 schema
: DRIVE_NAME_SCHEMA
,
207 schema
: LINUX_DRIVE_PATH_SCHEMA
,
211 description
: "Use standard input as device handle.",
218 /// Set or clear encryption key
220 fingerprint
: Option
<Fingerprint
>,
223 ) -> Result
<(), Error
> {
225 let result
= proxmox
::try_block
!({
226 let mut handle
= get_tape_handle(¶m
)?
;
228 match (fingerprint
, uuid
) {
229 (Some(fingerprint
), Some(uuid
)) => {
230 handle
.set_encryption(Some((fingerprint
, uuid
)))?
;
233 bail
!("missing media set uuid");
236 handle
.set_encryption(None
)?
;
241 }).map_err(|err
: Error
| err
.to_string());
243 println
!("{}", serde_json
::to_string_pretty(&result
)?
);
252 schema
: DRIVE_NAME_SCHEMA
,
256 schema
: LINUX_DRIVE_PATH_SCHEMA
,
260 description
: "Use standard input as device handle.",
267 /// Read volume statistics
268 fn volume_statistics(
270 ) -> Result
<(), Error
> {
272 let result
= proxmox
::try_block
!({
273 let mut handle
= get_tape_handle(¶m
)?
;
274 handle
.volume_statistics()
275 }).map_err(|err
: Error
| err
.to_string());
277 println
!("{}", serde_json
::to_string_pretty(&result
)?
);
282 fn main() -> Result
<(), Error
> {
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();
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");
295 if !running_uid
.is_root() && (running_uid
!= backup_uid
|| running_gid
!= backup_gid
) {
297 "Not running as backup user or group (got uid {} gid {})",
298 running_uid
, running_gid
,
302 let cmd_def
= CliCommandMap
::new()
305 CliCommand
::new(&API_METHOD_STATUS
)
309 CliCommand
::new(&API_METHOD_CARTRIDGE_MEMORY
)
313 CliCommand
::new(&API_METHOD_TAPE_ALERT_FLAGS
)
317 CliCommand
::new(&API_METHOD_VOLUME_STATISTICS
)
321 CliCommand
::new(&API_METHOD_SET_ENCRYPTION
)
325 let mut rpcenv
= CliEnvironment
::new();
326 rpcenv
.set_auth_id(Some(String
::from("root@pam")));
328 run_cli_command(cmd_def
, rpcenv
, None
);