]> git.proxmox.com Git - proxmox-backup.git/blob - src/bin/sg-tape-cmd.rs
tape: install new sg-tape-cmd setuid binary
[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 priviledges, 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
13 use proxmox::{
14 api::{
15 api,
16 cli::*,
17 RpcEnvironment,
18 },
19 };
20
21 use proxmox_backup::{
22 api2::types::{
23 LINUX_DRIVE_PATH_SCHEMA,
24 LinuxDriveAndMediaStatus,
25 },
26 tape::{
27 mam_extract_media_usage,
28 linux_tape::{
29 LinuxTapeHandle,
30 open_linux_tape_device,
31 check_tape_is_linux_tape_device,
32 },
33 },
34 };
35
36 fn get_tape_handle(device: Option<String>) -> Result<LinuxTapeHandle, Error> {
37
38 let file = if let Some(device) = device {
39 open_linux_tape_device(&device)?
40 } else {
41 let fd = std::io::stdin().as_raw_fd();
42 let file = unsafe { File::from_raw_fd(fd) };
43 check_tape_is_linux_tape_device(&file)?;
44 file
45 };
46 Ok(LinuxTapeHandle::new(file))
47 }
48
49 #[api(
50 input: {
51 properties: {
52 device: {
53 schema: LINUX_DRIVE_PATH_SCHEMA,
54 optional: true,
55 },
56 },
57 },
58 )]
59 /// Tape/Media Status
60 fn status(
61 device: Option<String>,
62 ) -> Result<(), Error> {
63
64 let result = proxmox::try_block!({
65 let mut handle = get_tape_handle(device)?;
66
67 let drive_status = handle.get_drive_status()?;
68
69 let mam = handle.cartridge_memory()?;
70
71 let usage = mam_extract_media_usage(&mam)?;
72
73 Ok(LinuxDriveAndMediaStatus {
74 blocksize: drive_status.blocksize,
75 density: drive_status.density,
76 status: format!("{:?}", drive_status.status),
77 file_number: drive_status.file_number,
78 block_number: drive_status.block_number,
79 manufactured: usage.manufactured,
80 bytes_read: usage.bytes_read,
81 bytes_written: usage.bytes_written,
82 })
83 }).map_err(|err: Error| err.to_string());
84
85 println!("{}", serde_json::to_string_pretty(&result)?);
86
87 Ok(())
88 }
89
90 #[api(
91 input: {
92 properties: {
93 device: {
94 schema: LINUX_DRIVE_PATH_SCHEMA,
95 optional: true,
96 },
97 },
98 },
99 )]
100 /// Read Cartridge Memory (Medium auxiliary memory attributes)
101 fn cartridge_memory(
102 device: Option<String>,
103 ) -> Result<(), Error> {
104
105 let result = proxmox::try_block!({
106 let mut handle = get_tape_handle(device)?;
107
108 handle.cartridge_memory()
109 }).map_err(|err| err.to_string());
110
111 println!("{}", serde_json::to_string_pretty(&result)?);
112
113 Ok(())
114 }
115
116 fn main() -> Result<(), Error> {
117
118 // check if we are user root or backup
119 let backup_uid = proxmox_backup::backup::backup_user()?.uid;
120 let backup_gid = proxmox_backup::backup::backup_group()?.gid;
121 let running_uid = nix::unistd::Uid::current();
122 let running_gid = nix::unistd::Gid::current();
123
124 let effective_uid = nix::unistd::Uid::effective();
125 if !effective_uid.is_root() {
126 bail!("this program needs to be run with setuid root");
127 }
128
129 if !running_uid.is_root() {
130 if running_uid != backup_uid || running_gid != backup_gid {
131 bail!(
132 "Not running as backup user or group (got uid {} gid {})",
133 running_uid, running_gid,
134 );
135 }
136 }
137
138 let cmd_def = CliCommandMap::new()
139 .insert(
140 "status",
141 CliCommand::new(&API_METHOD_STATUS)
142 )
143 .insert(
144 "cartridge-memory",
145 CliCommand::new(&API_METHOD_CARTRIDGE_MEMORY)
146 )
147 ;
148
149 let mut rpcenv = CliEnvironment::new();
150 rpcenv.set_auth_id(Some(String::from("root@pam")));
151
152 run_cli_command(cmd_def, rpcenv, None);
153
154 Ok(())
155 }