mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +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",
|
"wax",
|
||||||
"which",
|
"which",
|
||||||
"windows",
|
"windows",
|
||||||
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -93,6 +93,9 @@ wax = { version = "0.5.0", features = ["diagnostics"] }
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||||
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
|
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winreg = "0.10.1"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
umask = "2.0.0"
|
umask = "2.0.0"
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
|
|
|
@ -158,12 +158,17 @@ pub fn create_default_context() -> EngineState {
|
||||||
bind_command! {
|
bind_command! {
|
||||||
Benchmark,
|
Benchmark,
|
||||||
Complete,
|
Complete,
|
||||||
Exec,
|
|
||||||
External,
|
External,
|
||||||
NuCheck,
|
NuCheck,
|
||||||
Sys,
|
Sys,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
bind_command! { Exec }
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
bind_command! { RegistryQuery }
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
target_os = "android",
|
target_os = "android",
|
||||||
target_os = "linux",
|
target_os = "linux",
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
use super::run_external::ExternalCommand;
|
||||||
|
use nu_engine::{current_dir, env_to_strings, CallExt};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::{Call, Expr},
|
||||||
engine::{Command, EngineState, Stack},
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct Exec;
|
pub struct Exec;
|
||||||
|
@ -57,19 +60,11 @@ impl Command for Exec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
fn exec(
|
fn exec(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> 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: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let name_span = name.span;
|
let name_span = name.span;
|
||||||
|
|
||||||
|
@ -113,18 +108,3 @@ fn exec(
|
||||||
Vec::new(),
|
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 benchmark;
|
||||||
mod complete;
|
mod complete;
|
||||||
|
#[cfg(unix)]
|
||||||
mod exec;
|
mod exec;
|
||||||
mod nu_check;
|
mod nu_check;
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
|
@ -9,12 +10,15 @@ mod nu_check;
|
||||||
target_os = "windows"
|
target_os = "windows"
|
||||||
))]
|
))]
|
||||||
mod ps;
|
mod ps;
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod registry_query;
|
||||||
mod run_external;
|
mod run_external;
|
||||||
mod sys;
|
mod sys;
|
||||||
mod which_;
|
mod which_;
|
||||||
|
|
||||||
pub use benchmark::Benchmark;
|
pub use benchmark::Benchmark;
|
||||||
pub use complete::Complete;
|
pub use complete::Complete;
|
||||||
|
#[cfg(unix)]
|
||||||
pub use exec::Exec;
|
pub use exec::Exec;
|
||||||
pub use nu_check::NuCheck;
|
pub use nu_check::NuCheck;
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
|
@ -24,6 +28,8 @@ pub use nu_check::NuCheck;
|
||||||
target_os = "windows"
|
target_os = "windows"
|
||||||
))]
|
))]
|
||||||
pub use ps::Ps;
|
pub use ps::Ps;
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub use registry_query::RegistryQuery;
|
||||||
pub use run_external::{External, ExternalCommand};
|
pub use run_external::{External, ExternalCommand};
|
||||||
pub use sys::Sys;
|
pub use sys::Sys;
|
||||||
pub use which_::Which;
|
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