mirror of
https://github.com/nushell/nushell
synced 2024-12-27 13:33:16 +00:00
add a new command to query the registry on windows (#6670)
* add a new command to query the registry on windows * cross platform tweaks * return nushell datatype * change visibility of exec and registry commands
This commit is contained in:
parent
e1d5180e6d
commit
7910d20e50
6 changed files with 294 additions and 26 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2759,6 +2759,7 @@ dependencies = [
|
|||
"wax",
|
||||
"which",
|
||||
"windows",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -93,6 +93,9 @@ wax = { version = "0.5.0", features = ["diagnostics"] }
|
|||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winreg = "0.10.1"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "2.0.0"
|
||||
users = "0.11.0"
|
||||
|
|
|
@ -158,12 +158,17 @@ pub fn create_default_context() -> EngineState {
|
|||
bind_command! {
|
||||
Benchmark,
|
||||
Complete,
|
||||
Exec,
|
||||
External,
|
||||
NuCheck,
|
||||
Sys,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
bind_command! { Exec }
|
||||
|
||||
#[cfg(windows)]
|
||||
bind_command! { RegistryQuery }
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use super::run_external::ExternalCommand;
|
||||
use nu_engine::{current_dir, env_to_strings, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
ast::{Call, Expr},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
||||
};
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Exec;
|
||||
|
@ -57,19 +60,11 @@ impl Command for Exec {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn exec(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
use super::run_external::ExternalCommand;
|
||||
use nu_engine::{current_dir, env_to_strings, CallExt};
|
||||
use nu_protocol::ast::Expr;
|
||||
use nu_protocol::Spanned;
|
||||
|
||||
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let name_span = name.span;
|
||||
|
||||
|
@ -113,18 +108,3 @@ fn exec(
|
|||
Vec::new(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn exec(
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
Err(ShellError::GenericError(
|
||||
"Error on exec".to_string(),
|
||||
"exec is not supported on your platform".to_string(),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod benchmark;
|
||||
mod complete;
|
||||
#[cfg(unix)]
|
||||
mod exec;
|
||||
mod nu_check;
|
||||
#[cfg(any(
|
||||
|
@ -9,12 +10,15 @@ mod nu_check;
|
|||
target_os = "windows"
|
||||
))]
|
||||
mod ps;
|
||||
#[cfg(windows)]
|
||||
mod registry_query;
|
||||
mod run_external;
|
||||
mod sys;
|
||||
mod which_;
|
||||
|
||||
pub use benchmark::Benchmark;
|
||||
pub use complete::Complete;
|
||||
#[cfg(unix)]
|
||||
pub use exec::Exec;
|
||||
pub use nu_check::NuCheck;
|
||||
#[cfg(any(
|
||||
|
@ -24,6 +28,8 @@ pub use nu_check::NuCheck;
|
|||
target_os = "windows"
|
||||
))]
|
||||
pub use ps::Ps;
|
||||
#[cfg(windows)]
|
||||
pub use registry_query::RegistryQuery;
|
||||
pub use run_external::{External, ExternalCommand};
|
||||
pub use sys::Sys;
|
||||
pub use which_::Which;
|
||||
|
|
273
crates/nu-command/src/system/registry_query.rs
Normal file
273
crates/nu-command/src/system/registry_query.rs
Normal file
|
@ -0,0 +1,273 @@
|
|||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, Spanned, SyntaxShape, Value,
|
||||
};
|
||||
use winreg::{enums::*, RegKey};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RegistryQuery;
|
||||
|
||||
struct RegistryQueryArgs {
|
||||
hkcr: bool,
|
||||
hkcu: bool,
|
||||
hklm: bool,
|
||||
hku: bool,
|
||||
hkpd: bool,
|
||||
hkpt: bool,
|
||||
hkpnls: bool,
|
||||
hkcc: bool,
|
||||
hkdd: bool,
|
||||
hkculs: bool,
|
||||
key: String,
|
||||
}
|
||||
|
||||
impl Command for RegistryQuery {
|
||||
fn name(&self) -> &str {
|
||||
"registry query"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("registry query")
|
||||
.switch("hkcr", "query the hkey_classes_root hive", None)
|
||||
.switch("hkcu", "query the hkey_current_user hive", None)
|
||||
.switch("hklm", "query the hkey_local_machine hive", None)
|
||||
.switch("hku", "query the hkey_users hive", None)
|
||||
.switch("hkpd", "query the hkey_performance_data hive", None)
|
||||
.switch("hkpt", "query the hkey_performance_text hive", None)
|
||||
.switch("hkpnls", "query the hkey_performance_nls_text hive", None)
|
||||
.switch("hkcc", "query the hkey_current_config hive", None)
|
||||
.switch("hkdd", "query the hkey_dyn_data hive", None)
|
||||
.switch(
|
||||
"hkculs",
|
||||
"query the hkey_current_user_local_settings hive",
|
||||
None,
|
||||
)
|
||||
.required("key", SyntaxShape::String, "registry key to query")
|
||||
.optional(
|
||||
"value",
|
||||
SyntaxShape::String,
|
||||
"optionally supply a registry value to query",
|
||||
)
|
||||
.category(Category::System)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Query the Windows registry."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"Currently supported only on Windows systems."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
registry_query(engine_state, stack, call)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Query the HKEY_CURRENT_USER hive",
|
||||
example: "registry query --hkcu environment",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Query the HKEY_LOCAL_MACHINE hive",
|
||||
example: r"registry query --hklm 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn registry_query(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let registry_key: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let registry_key_span = ®istry_key.clone().span;
|
||||
let registry_value: Option<Spanned<String>> = call.opt(engine_state, stack, 1)?;
|
||||
|
||||
let reg_params = RegistryQueryArgs {
|
||||
hkcr: call.has_flag("hkcr"),
|
||||
hkcu: call.has_flag("hkcu"),
|
||||
hklm: call.has_flag("hklm"),
|
||||
hku: call.has_flag("hku"),
|
||||
hkpd: call.has_flag("hkpd"),
|
||||
hkpt: call.has_flag("hkpt"),
|
||||
hkpnls: call.has_flag("hkpnls"),
|
||||
hkcc: call.has_flag("hkcc"),
|
||||
hkdd: call.has_flag("hkdd"),
|
||||
hkculs: call.has_flag("hkculs"),
|
||||
key: registry_key.item,
|
||||
};
|
||||
|
||||
let reg_key = get_reg_key(reg_params)?;
|
||||
|
||||
if registry_value.is_none() {
|
||||
let mut reg_values = vec![];
|
||||
for (name, val) in reg_key.enum_values().flatten() {
|
||||
let (nu_value, reg_type) = reg_value_to_nu_value(val);
|
||||
reg_values.push(Value::Record {
|
||||
cols: vec!["name".to_string(), "value".to_string(), "type".to_string()],
|
||||
vals: vec![
|
||||
Value::string(name, Span::test_data()),
|
||||
nu_value,
|
||||
Value::string(format!("{:?}", reg_type), Span::test_data()),
|
||||
],
|
||||
span: *registry_key_span,
|
||||
})
|
||||
}
|
||||
Ok(reg_values.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
} else {
|
||||
match registry_value {
|
||||
Some(value) => {
|
||||
let reg_value = reg_key.get_raw_value(value.item.as_str());
|
||||
match reg_value {
|
||||
Ok(val) => {
|
||||
let (nu_value, reg_type) = reg_value_to_nu_value(val);
|
||||
Ok(Value::Record {
|
||||
cols: vec!["name".to_string(), "value".to_string(), "type".to_string()],
|
||||
vals: vec![
|
||||
Value::string(value.item, Span::test_data()),
|
||||
nu_value,
|
||||
Value::string(format!("{:?}", reg_type), Span::test_data()),
|
||||
],
|
||||
span: value.span,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
Err(_) => Ok(Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"Unable to find registry key/value".to_string(),
|
||||
format!("Registry value: {} was not found", value.item),
|
||||
Some(value.span),
|
||||
None,
|
||||
Vec::new(),
|
||||
),
|
||||
}
|
||||
.into_pipeline_data()),
|
||||
}
|
||||
}
|
||||
None => Ok(Value::nothing(Span::test_data()).into_pipeline_data()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reg_key(reg_params: RegistryQueryArgs) -> Result<RegKey, ShellError> {
|
||||
let mut key_count = 0;
|
||||
let registry_key = if reg_params.hkcr {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_CLASSES_ROOT).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hkcu {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_CURRENT_USER).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hklm {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hku {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_USERS).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hkpd {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_PERFORMANCE_DATA).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hkpt {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_PERFORMANCE_TEXT).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hkpnls {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_PERFORMANCE_NLSTEXT).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hkcc {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_CURRENT_CONFIG).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hkdd {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_DYN_DATA).open_subkey(reg_params.key)?
|
||||
} else if reg_params.hkculs {
|
||||
key_count += 1;
|
||||
RegKey::predef(HKEY_CURRENT_USER_LOCAL_SETTINGS).open_subkey(reg_params.key)?
|
||||
} else {
|
||||
RegKey::predef(HKEY_CURRENT_USER).open_subkey(reg_params.key)?
|
||||
};
|
||||
|
||||
if key_count > 1 {
|
||||
return Err(ShellError::GenericError(
|
||||
"Only one registry key can be specified".into(),
|
||||
"Only one registry key can be specified".into(),
|
||||
Some(Span::test_data()),
|
||||
None,
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
Ok(registry_key)
|
||||
}
|
||||
|
||||
fn reg_value_to_nu_value(
|
||||
reg_value: winreg::RegValue,
|
||||
) -> (nu_protocol::Value, winreg::enums::RegType) {
|
||||
match reg_value.vtype {
|
||||
REG_NONE => (Value::nothing(Span::test_data()), reg_value.vtype),
|
||||
REG_SZ => (
|
||||
Value::string(reg_value.to_string(), Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_EXPAND_SZ => (
|
||||
Value::string(reg_value.to_string(), Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_BINARY => (
|
||||
Value::binary(reg_value.bytes, Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_DWORD => (
|
||||
Value::int(
|
||||
unsafe { *(reg_value.bytes.as_ptr() as *const u32) } as i64,
|
||||
Span::test_data(),
|
||||
),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_DWORD_BIG_ENDIAN => (
|
||||
Value::int(
|
||||
unsafe { *(reg_value.bytes.as_ptr() as *const u32) } as i64,
|
||||
Span::test_data(),
|
||||
),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_LINK => (
|
||||
Value::string(reg_value.to_string(), Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_MULTI_SZ => (
|
||||
Value::string(reg_value.to_string(), Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_RESOURCE_LIST => (
|
||||
Value::string(reg_value.to_string(), Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_FULL_RESOURCE_DESCRIPTOR => (
|
||||
Value::string(reg_value.to_string(), Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_RESOURCE_REQUIREMENTS_LIST => (
|
||||
Value::string(reg_value.to_string(), Span::test_data()),
|
||||
reg_value.vtype,
|
||||
),
|
||||
REG_QWORD => (
|
||||
Value::int(
|
||||
unsafe { *(reg_value.bytes.as_ptr() as *const u32) } as i64,
|
||||
Span::test_data(),
|
||||
),
|
||||
reg_value.vtype,
|
||||
),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue