]> git.proxmox.com Git - proxmox-backup.git/blob - src/bin/sg-tape-cmd.rs
ee4c32c277752fcbbe6eaa381fa730a4433eae50
[proxmox-backup.git] / src / bin / sg-tape-cmd.rs
1 /// Helper to run tape commands as root. Currently only required
2 /// to read and set the encryption key.
3 ///
4 /// This command can use STDIN as tape device handle.
5
6 use std::fs::File;
7 use std::os::unix::io::{AsRawFd, FromRawFd};
8
9 use anyhow::{bail, Error};
10 use serde_json::Value;
11
12 use proxmox::{
13 api::{
14 api,
15 cli::*,
16 RpcEnvironment,
17 },
18 tools::Uuid,
19 };
20
21 use pbs_api_types::{
22 Fingerprint, LTO_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
23 MEDIA_SET_UUID_SCHEMA, LtoTapeDrive,
24 };
25
26 use pbs_tape::linux_list_drives::{open_lto_tape_device, check_tape_is_lto_tape_device};
27
28 use proxmox_backup::{
29 tape::{
30 drive::{
31 TapeDriver,
32 LtoTapeHandle,
33 open_lto_tape_drive,
34 },
35 },
36 };
37
38 fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
39
40 let handle = if let Some(name) = param["drive"].as_str() {
41 let (config, _digest) = pbs_config::drive::config()?;
42 let drive: LtoTapeDrive = config.lookup("lto", &name)?;
43 eprintln!("using device {}", drive.path);
44 open_lto_tape_drive(&drive)?
45 } else if let Some(device) = param["device"].as_str() {
46 eprintln!("using device {}", device);
47 LtoTapeHandle::new(open_lto_tape_device(&device)?)?
48 } else if let Some(true) = param["stdin"].as_bool() {
49 eprintln!("using stdin");
50 let fd = std::io::stdin().as_raw_fd();
51 let file = unsafe { File::from_raw_fd(fd) };
52 check_tape_is_lto_tape_device(&file)?;
53 LtoTapeHandle::new(file)?
54 } else if let Ok(name) = std::env::var("PROXMOX_TAPE_DRIVE") {
55 let (config, _digest) = pbs_config::drive::config()?;
56 let drive: LtoTapeDrive = config.lookup("lto", &name)?;
57 eprintln!("using device {}", drive.path);
58 open_lto_tape_drive(&drive)?
59 } else {
60 let (config, _digest) = pbs_config::drive::config()?;
61
62 let mut drive_names = Vec::new();
63 for (name, (section_type, _)) in config.sections.iter() {
64 if section_type != "lto" { continue; }
65 drive_names.push(name);
66 }
67
68 if drive_names.len() == 1 {
69 let name = drive_names[0];
70 let drive: LtoTapeDrive = config.lookup("lto", &name)?;
71 eprintln!("using device {}", drive.path);
72 open_lto_tape_drive(&drive)?
73 } else {
74 bail!("no drive/device specified");
75 }
76 };
77
78 Ok(handle)
79 }
80
81 #[api(
82 input: {
83 properties: {
84 fingerprint: {
85 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
86 optional: true,
87 },
88 uuid: {
89 schema: MEDIA_SET_UUID_SCHEMA,
90 optional: true,
91 },
92 drive: {
93 schema: DRIVE_NAME_SCHEMA,
94 optional: true,
95 },
96 device: {
97 schema: LTO_DRIVE_PATH_SCHEMA,
98 optional: true,
99 },
100 stdin: {
101 description: "Use standard input as device handle.",
102 type: bool,
103 optional: true,
104 },
105 },
106 },
107 )]
108 /// Set or clear encryption key
109 fn set_encryption(
110 fingerprint: Option<Fingerprint>,
111 uuid: Option<Uuid>,
112 param: Value,
113 ) -> Result<(), Error> {
114
115 let result = proxmox::try_block!({
116 let mut handle = get_tape_handle(&param)?;
117
118 match (fingerprint, uuid) {
119 (Some(fingerprint), Some(uuid)) => {
120 handle.set_encryption(Some((fingerprint, uuid)))?;
121 }
122 (Some(_), None) => {
123 bail!("missing media set uuid");
124 }
125 (None, _) => {
126 handle.set_encryption(None)?;
127 }
128 }
129
130 Ok(())
131 }).map_err(|err: Error| err.to_string());
132
133 println!("{}", serde_json::to_string_pretty(&result)?);
134
135 Ok(())
136 }
137
138 fn main() -> Result<(), Error> {
139
140 // check if we are user root or backup
141 let backup_uid = pbs_config::backup_user()?.uid;
142 let backup_gid = pbs_config::backup_group()?.gid;
143 let running_uid = nix::unistd::Uid::current();
144 let running_gid = nix::unistd::Gid::current();
145
146 let effective_uid = nix::unistd::Uid::effective();
147 if !effective_uid.is_root() {
148 bail!("this program needs to be run with setuid root");
149 }
150
151 if !running_uid.is_root() && (running_uid != backup_uid || running_gid != backup_gid) {
152 bail!(
153 "Not running as backup user or group (got uid {} gid {})",
154 running_uid, running_gid,
155 );
156 }
157
158 let cmd_def = CliCommandMap::new()
159 .insert(
160 "encryption",
161 CliCommand::new(&API_METHOD_SET_ENCRYPTION)
162 )
163 ;
164
165 let mut rpcenv = CliEnvironment::new();
166 rpcenv.set_auth_id(Some(String::from("root@pam")));
167
168 run_cli_command(cmd_def, rpcenv, None);
169
170 Ok(())
171 }