Implemented chcpu.

This commit is contained in:
Koutheir Attouchi 2025-02-25 15:14:57 -05:00
parent b66945dee8
commit 31795f2729
7 changed files with 580 additions and 145 deletions

4
.gitignore vendored

@ -1 +1,3 @@
/target
syntax: glob
/target/

29
Cargo.lock generated

@ -888,6 +888,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syscall-numbers"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e88dcf8be2fd556b3cebd02689c424dced834317c7e38328eadfcfcab00b785"
[[package]]
name = "sysinfo"
version = "0.33.1"
@ -937,6 +943,26 @@ dependencies = [
"unicode-width 0.2.0",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.40"
@ -1062,7 +1088,10 @@ name = "uu_chcpu"
version = "0.0.1"
dependencies = [
"clap",
"libc",
"rangemap",
"syscall-numbers",
"thiserror",
"uucore",
]

@ -61,9 +61,11 @@ serde_json = { version = "1.0.122", features = ["preserve_order"] }
sysinfo = "0.33"
tempfile = "3.9.0"
textwrap = { version = "0.16.0", features = ["terminal_size"] }
thiserror = "2.0"
uucore = "0.0.30"
xattr = "1.3.1"
rangemap = "1.5.1"
syscall-numbers = "4.0.2"
[dependencies]
clap = { workspace = true }

@ -1,5 +1,5 @@
[package]
name = "uu_chcpu"
name = "uu_chcpu"
version = "0.0.1"
edition = "2024"
@ -11,6 +11,9 @@ name = "chcpu"
path = "src/main.rs"
[dependencies]
uucore = { workspace = true }
clap = { workspace = true }
rangemap = { workspace = true }
clap = { workspace = true }
libc = { workspace = true }
rangemap = { workspace = true }
syscall-numbers = { workspace = true }
thiserror = { workspace = true }
uucore = { workspace = true }

@ -3,9 +3,15 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// Remove this if the tool is ported to Non-UNIX platforms.
#![cfg_attr(not(unix), allow(dead_code))]
mod errors;
#[cfg(unix)]
mod sysfs;
use std::str::FromStr;
use std::{fmt, str};
use clap::builder::{EnumValueParser, PossibleValue};
use clap::{Arg, ArgAction, ArgGroup, Command, ValueEnum, crate_version};
@ -28,10 +34,47 @@ mod options {
const ABOUT: &str = help_about!("chcpu.md");
const USAGE: &str = help_usage!("chcpu.md");
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum DispatchMode {
Horizontal,
Vertical,
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = uu_app().try_get_matches_from_mut(args)?;
if args.contains_id(options::ENABLE) {
let cpu_list = args
.get_one::<CpuList>(options::ENABLE)
.expect("cpu-list is required");
enable_cpu(cpu_list, true)?;
} else if args.contains_id(options::DISABLE) {
let cpu_list = args
.get_one::<CpuList>(options::DISABLE)
.expect("cpu-list is required");
enable_cpu(cpu_list, false)?;
} else if args.contains_id(options::CONFIGURE) {
let cpu_list = args
.get_one::<CpuList>(options::CONFIGURE)
.expect("cpu-list is required");
configure_cpu(cpu_list, true)?;
} else if args.contains_id(options::DECONFIGURE) {
let cpu_list = args
.get_one::<CpuList>(options::DECONFIGURE)
.expect("cpu-list is required");
configure_cpu(cpu_list, false)?;
} else if args.contains_id(options::DISPATCH) {
let dispatch_mode = args
.get_one::<DispatchMode>(options::DISPATCH)
.expect("mode is required");
set_dispatch_mode(*dispatch_mode)?;
} else if args.get_flag(options::RESCAN) {
rescan_cpus()?;
} else {
unimplemented!();
}
Ok(())
}
impl ValueEnum for DispatchMode {
@ -51,121 +94,6 @@ impl ValueEnum for DispatchMode {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct CpuList(RangeInclusiveSet<usize>);
impl FromStr for CpuList {
type Err = ChCpuError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let set: RangeInclusiveSet<usize> = s
.split(',')
.map(|element| {
// Parsing: ...,element,...
let mut iter = element.splitn(2, '-').map(str::trim);
let first = iter.next();
(first, iter.next())
})
.map(|(first, last)| {
let first: usize = first
.ok_or(ChCpuError::EmptyCpuList)?
.parse()
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?;
if let Some(last) = last {
// Parsing: ...,first-last,...
let last =
str::parse(last).map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?;
if first <= last {
Ok(first..=last)
} else {
Err(ChCpuError::CpuSpecFirstAfterLast)
}
} else {
Ok(first..=first) // Parsing: ...,first,...
}
})
.collect::<Result<_, _>>()?;
if set.is_empty() {
Err(ChCpuError::EmptyCpuList)
} else {
Ok(Self(set))
}
}
}
fn enable_cpu_list(_cpu_list: &CpuList) -> UResult<()> {
dbg!(_cpu_list);
todo!()
}
fn disable_cpu_list(_cpu_list: &CpuList) -> UResult<()> {
dbg!(_cpu_list);
todo!()
}
fn configure_cpu_list(_cpu_list: &CpuList) -> UResult<()> {
dbg!(_cpu_list);
todo!()
}
fn deconfigure_cpu_list(_cpu_list: &CpuList) -> UResult<()> {
dbg!(_cpu_list);
todo!()
}
fn set_dispatch_mode(_mode: DispatchMode) -> UResult<()> {
dbg!(_mode);
todo!()
}
fn rescan_cpus() -> UResult<()> {
todo!()
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = uu_app().try_get_matches_from_mut(args)?;
if args.contains_id(options::ENABLE) {
let cpu_list = args
.get_one::<CpuList>(options::ENABLE)
.expect("cpu-list is required");
enable_cpu_list(cpu_list)
} else if args.contains_id(options::DISABLE) {
let cpu_list = args
.get_one::<CpuList>(options::DISABLE)
.expect("cpu-list is required");
disable_cpu_list(cpu_list)
} else if args.contains_id(options::CONFIGURE) {
let cpu_list = args
.get_one::<CpuList>(options::CONFIGURE)
.expect("cpu-list is required");
configure_cpu_list(cpu_list)
} else if args.contains_id(options::DECONFIGURE) {
let cpu_list = args
.get_one::<CpuList>(options::DECONFIGURE)
.expect("cpu-list is required");
deconfigure_cpu_list(cpu_list)
} else if args.contains_id(options::DISPATCH) {
let dispatch_mode = args
.get_one::<DispatchMode>(options::DISPATCH)
.expect("mode is required");
set_dispatch_mode(*dispatch_mode)
} else if args.get_flag(options::RESCAN) {
rescan_cpus()
} else {
unimplemented!();
}
}
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
@ -255,3 +183,156 @@ pub fn uu_app() -> Command {
For example, 0,2,7,10-13 refers to CPUs whose addresses are: 0, 2, 7, 10, 11, 12, and 13.",
)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
enum DispatchMode {
Horizontal = 0,
Vertical = 1,
}
impl fmt::Display for DispatchMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Horizontal => write!(f, "horizontal"),
Self::Vertical => write!(f, "vertical"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CpuList(RangeInclusiveSet<usize>);
impl CpuList {
fn run(&self, f: &mut dyn FnMut(usize) -> Result<(), ChCpuError>) -> Result<(), ChCpuError> {
use std::ops::RangeInclusive;
let iter = self.0.iter().flat_map(RangeInclusive::to_owned).map(f);
let (success_occurred, first_error) =
iter.fold((false, None), |(success_occurred, first_error), result| {
if let Err(err) = result {
eprintln!("{err}");
(success_occurred, first_error.or(Some(err)))
} else {
(true, first_error)
}
});
if let Some(err) = first_error {
if success_occurred {
uucore::error::set_exit_code(64); // Partial success.
Ok(())
} else {
Err(err)
}
} else {
Ok(())
}
}
}
impl TryFrom<&[u8]> for CpuList {
type Error = ChCpuError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let set: RangeInclusiveSet<usize> = bytes
.split(|&b| b == b',')
.map(|element| {
// Parsing: ...,element,...
let mut iter = element.splitn(2, |&b| b == b'-').map(<[u8]>::trim_ascii);
let first = iter.next();
(first, iter.next())
})
.map(|(first, last)| {
let first = first.ok_or(ChCpuError::EmptyCpuList)?;
let first: usize = str::from_utf8(first)
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?
.parse()
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?;
if let Some(last) = last {
// Parsing: ...,first-last,...
let last = str::from_utf8(last)
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?
.parse()
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?;
if first <= last {
Ok(first..=last)
} else {
Err(ChCpuError::CpuSpecFirstAfterLast)
}
} else {
Ok(first..=first) // Parsing: ...,first,...
}
})
.collect::<Result<_, _>>()?;
if set.is_empty() {
Err(ChCpuError::EmptyCpuList)
} else {
Ok(Self(set))
}
}
}
impl FromStr for CpuList {
type Err = ChCpuError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s.as_bytes())
}
}
#[cfg(unix)]
fn enable_cpu(cpu_list: &CpuList, enable: bool) -> Result<(), ChCpuError> {
let sysfs_cpu = sysfs::SysFSCpu::open()?;
let mut enabled_cpu_list = sysfs_cpu.enabled_cpu_list().ok();
cpu_list.run(&mut move |cpu_index| {
sysfs_cpu.enable_cpu(enabled_cpu_list.as_mut(), cpu_index, enable)
})
}
#[cfg(not(unix))]
fn enable_cpu(_cpu_list: &CpuList, _enable: bool) -> Result<(), ChCpuError> {
unimplemented!()
}
#[cfg(unix)]
fn configure_cpu(cpu_list: &CpuList, configure: bool) -> Result<(), ChCpuError> {
let sysfs_cpu = sysfs::SysFSCpu::open()?;
let enabled_cpu_list = sysfs_cpu.enabled_cpu_list().ok();
cpu_list.run(&mut move |cpu_index| {
sysfs_cpu.configure_cpu(enabled_cpu_list.as_ref(), cpu_index, configure)
})
}
#[cfg(not(unix))]
fn configure_cpu(_cpu_list: &CpuList, _configure: bool) -> Result<(), ChCpuError> {
unimplemented!()
}
#[cfg(unix)]
fn set_dispatch_mode(dispatch_mode: DispatchMode) -> Result<(), ChCpuError> {
sysfs::SysFSCpu::open()?.set_dispatch_mode(dispatch_mode)
}
#[cfg(not(unix))]
fn set_dispatch_mode(_dispatch_mode: DispatchMode) -> Result<(), ChCpuError> {
unimplemented!()
}
#[cfg(unix)]
fn rescan_cpus() -> Result<(), ChCpuError> {
sysfs::SysFSCpu::open()?.rescan_cpus()
}
#[cfg(not(unix))]
fn rescan_cpus() -> Result<(), ChCpuError> {
unimplemented!()
}

@ -3,35 +3,85 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::fmt;
use std::path::PathBuf;
use uucore::error::UError;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, thiserror::Error)]
pub enum ChCpuError {
EmptyCpuList,
CpuSpecNotPositiveInteger,
#[error("CPU {0} is enabled")]
CpuIsEnabled(usize),
#[error("CPU {0} is not configurable")]
CpuNotConfigurable(usize),
#[error("CPU {0} is not hot pluggable")]
CpuNotHotPluggable(usize),
#[error("this system does not support rescanning of CPUs")]
CpuRescanUnsupported,
#[error("first element of CPU list range is greater than its last element")]
CpuSpecFirstAfterLast,
#[error("CPU list element is not a positive number")]
CpuSpecNotPositiveInteger,
#[error("CPU list is empty")]
EmptyCpuList,
#[error("CPU {0} does not exist")]
InvalidCpuIndex(usize),
#[error("{0}: {1}")]
IO0(String, std::io::Error),
#[error("{0} '{path}': {2}", path = .1.display())]
IO1(String, PathBuf, std::io::Error),
#[error("only one CPU is enabled")]
OneCpuIsEnabled,
#[error("data is not an integer '{0}'")]
NotInteger(String),
#[error("this system does not support setting the dispatching mode of CPUs")]
SetCpuDispatchUnsupported,
}
impl fmt::Display for ChCpuError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl ChCpuError {
pub(crate) fn io0(message: impl Into<String>, error: std::io::Error) -> Self {
Self::IO0(message.into(), error)
}
pub(crate) fn io1(
message: impl Into<String>,
path: impl Into<PathBuf>,
error: std::io::Error,
) -> Self {
Self::IO1(message.into(), path.into(), error)
}
pub(crate) fn with_io_message(self, message: impl Into<String>) -> Self {
match self {
Self::EmptyCpuList => write!(f, "CPU list is empty"),
Self::CpuSpecNotPositiveInteger => {
write!(f, "CPU list element is not a positive number")
}
Self::CpuSpecFirstAfterLast => {
write!(
f,
"first element of CPU list range is greater than its last element"
)
}
Self::IO0(_, err) => Self::IO0(message.into(), err),
Self::IO1(_, path, err) => Self::IO1(message.into(), path, err),
Self::CpuIsEnabled(_)
| Self::CpuNotConfigurable(_)
| Self::CpuNotHotPluggable(_)
| Self::CpuRescanUnsupported
| Self::CpuSpecFirstAfterLast
| Self::CpuSpecNotPositiveInteger
| Self::EmptyCpuList
| Self::InvalidCpuIndex(_)
| Self::OneCpuIsEnabled
| Self::NotInteger(_)
| Self::SetCpuDispatchUnsupported => self,
}
}
}
impl UError for ChCpuError {
impl uucore::error::UError for ChCpuError {
fn code(&self) -> i32 {
1
}
@ -40,5 +90,3 @@ impl UError for ChCpuError {
false
}
}
impl std::error::Error for ChCpuError {}

270
src/uu/chcpu/src/sysfs.rs Normal file

@ -0,0 +1,270 @@
// This file is part of the uutils hostname package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::ffi::{CString, c_int};
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write, stdout};
use std::os::fd::{AsRawFd, FromRawFd};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::OpenOptionsExt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fmt, str};
use crate::errors::ChCpuError;
use crate::{CpuList, DispatchMode};
pub(crate) const PATH_SYS_CPU: &str = "/sys/devices/system/cpu";
pub(crate) struct SysFSCpu(File);
impl SysFSCpu {
pub(crate) fn open() -> Result<Self, ChCpuError> {
OpenOptions::new()
.read(true)
.custom_flags(libc::O_CLOEXEC)
.open(PATH_SYS_CPU)
.map(Self)
.map_err(|err| ChCpuError::io1("failed to open", PATH_SYS_CPU, err))
}
fn inner_path(name: impl AsRef<Path>) -> PathBuf {
Path::new(PATH_SYS_CPU).join(name)
}
pub(crate) fn ensure_accessible(
&self,
name: impl AsRef<Path>,
access: c_int,
) -> Result<(), ChCpuError> {
use std::io::Error;
let name = name.as_ref();
let c_name = c_string_from_path(name)?;
if unsafe { libc::faccessat(self.0.as_raw_fd(), c_name.as_ptr(), access, 0) } == 0 {
Ok(())
} else {
let path = Self::inner_path(name);
let err = Error::last_os_error();
Err(ChCpuError::io1("file/directory is inaccessible", path, err))
}
}
pub(crate) fn open_inner(
&self,
name: impl AsRef<Path>,
flags: c_int,
) -> Result<File, ChCpuError> {
use std::io::Error;
let name = name.as_ref();
let c_name = c_string_from_path(name)?;
unsafe {
let fd = libc::openat(self.0.as_raw_fd(), c_name.as_ptr(), flags);
if fd >= 0 {
return Ok(File::from_raw_fd(fd));
}
}
let path = Self::inner_path(name);
let err = Error::last_os_error();
Err(ChCpuError::io1("failed to open", path, err))
}
pub(crate) fn read_value<T>(&self, name: impl AsRef<Path>) -> Result<T, ChCpuError>
where
T: FromStr,
{
let name = name.as_ref();
let mut line = String::default();
self.open_inner(name, libc::O_RDONLY | libc::O_CLOEXEC)
.map(BufReader::new)?
.read_line(&mut line)
.map_err(|err| ChCpuError::io1("failed to read file", Self::inner_path(name), err))?;
line.trim()
.parse()
.map_err(|_r| ChCpuError::NotInteger(line.trim().into()))
}
pub(crate) fn write_value(
&self,
name: impl AsRef<Path>,
value: impl fmt::Display,
) -> Result<(), ChCpuError> {
let name = name.as_ref();
self.open_inner(name, libc::O_WRONLY | libc::O_CLOEXEC)?
.write_all(format!("{value}").as_bytes())
.map_err(|err| ChCpuError::io1("failed to write file", Self::inner_path(name), err))
}
pub(crate) fn enabled_cpu_list(&self) -> Result<CpuList, ChCpuError> {
let mut buffer = Vec::default();
self.open_inner("online", libc::O_RDONLY | libc::O_CLOEXEC)?
.read_to_end(&mut buffer)
.map_err(|err| {
ChCpuError::io1("failed to read file", Self::inner_path("online"), err)
})?;
CpuList::try_from(buffer.as_slice())
}
pub(crate) fn cpu_dir_path(&self, cpu_index: usize) -> Result<PathBuf, ChCpuError> {
let dir_name = PathBuf::from(format!("cpu{cpu_index}"));
self.ensure_accessible(&dir_name, libc::F_OK)
.map(|()| dir_name)
.map_err(|_r| ChCpuError::InvalidCpuIndex(cpu_index))
}
pub(crate) fn enable_cpu(
&self,
enabled_cpu_list: Option<&mut CpuList>,
cpu_index: usize,
enable: bool,
) -> Result<(), ChCpuError> {
use std::ops::RangeInclusive;
let dir_name = self.cpu_dir_path(cpu_index)?;
let online_path = dir_name.join("online");
self.ensure_accessible(&online_path, libc::F_OK)
.map_err(|_r| ChCpuError::CpuNotHotPluggable(cpu_index))?;
let online = self
.read_value::<i32>(&online_path)
.map(|value| value != 0)?;
let new_state = if enable { "enabled" } else { "disabled" };
if enable == online {
let mut stdout = stdout().lock();
return writeln!(&mut stdout, "CPU {cpu_index} is already {new_state}")
.map_err(|err| ChCpuError::io0("write standard output", err));
}
if let Some(enabled_cpu_list) = &enabled_cpu_list {
let iter = enabled_cpu_list
.0
.iter()
.flat_map(RangeInclusive::to_owned)
.take(2);
if !enable && iter.count() <= 1 {
return Err(ChCpuError::OneCpuIsEnabled);
}
}
let configured = self.read_value::<i32>(dir_name.join("configure"));
if let Err(err) = self.write_value(&online_path, u8::from(enable)) {
let operation = if enable { "enable" } else { "disable" };
let reason = if enable && configured.is_ok_and(|value| value == 0) {
" (CPU is deconfigured)"
} else {
""
};
return Err(err.with_io_message(format!("CPU {cpu_index} {operation} failed{reason}")));
}
if let Some(enabled_cpu_list) = enabled_cpu_list {
if enable {
enabled_cpu_list.0.insert(cpu_index..=cpu_index);
} else {
enabled_cpu_list.0.remove(cpu_index..=cpu_index);
}
}
let mut stdout = stdout().lock();
writeln!(&mut stdout, "CPU {cpu_index} {new_state}",)
.map_err(|err| ChCpuError::io0("write standard output", err))
}
pub(crate) fn configure_cpu(
&self,
enabled_cpu_list: Option<&CpuList>,
cpu_index: usize,
configure: bool,
) -> Result<(), ChCpuError> {
let dir_name = self.cpu_dir_path(cpu_index)?;
let configure_path = dir_name.join("configure");
self.ensure_accessible(&configure_path, libc::F_OK)
.map_err(|_r| ChCpuError::CpuNotConfigurable(cpu_index))?;
let previous_config = self
.read_value::<i32>(&configure_path)
.map(|value| value != 0)?;
let new_state = if configure {
"configured"
} else {
"deconfigured"
};
if configure == previous_config {
let mut stdout = stdout().lock();
return writeln!(&mut stdout, "CPU {cpu_index} is already {new_state}")
.map_err(|err| ChCpuError::io0("write standard output", err));
}
if let Some(enabled_cpu_list) = enabled_cpu_list {
if previous_config && !configure && enabled_cpu_list.0.contains(&cpu_index) {
return Err(ChCpuError::CpuIsEnabled(cpu_index));
}
}
if let Err(err) = self.write_value(&configure_path, u8::from(configure)) {
let operation = if configure {
"configure"
} else {
"deconfigure"
};
Err(err.with_io_message(format!("CPU {cpu_index} {operation} failed")))
} else {
let mut stdout = stdout().lock();
writeln!(&mut stdout, "CPU {cpu_index} {new_state}",)
.map_err(|err| ChCpuError::io0("write standard output", err))
}
}
pub(crate) fn set_dispatch_mode(&self, mode: DispatchMode) -> Result<(), ChCpuError> {
self.ensure_accessible("dispatching", libc::F_OK)
.map_err(|_r| ChCpuError::SetCpuDispatchUnsupported)?;
self.write_value("dispatching", mode as u8)
.map_err(|err| err.with_io_message("failed to set dispatch mode"))?;
let mut stdout = stdout().lock();
writeln!(&mut stdout, "Successfully set {mode} dispatching mode")
.map_err(|err| ChCpuError::io0("write standard output", err))
}
pub(crate) fn rescan_cpus(&self) -> Result<(), ChCpuError> {
self.ensure_accessible("rescan", libc::F_OK)
.map_err(|_r| ChCpuError::CpuRescanUnsupported)?;
self.write_value("rescan", "1")
.map_err(|err| err.with_io_message("failed to trigger rescan of CPUs"))?;
let mut stdout = stdout().lock();
writeln!(&mut stdout, "Triggered rescan of CPUs")
.map_err(|err| ChCpuError::io0("write standard output", err))
}
}
fn c_string_from_path(path: &Path) -> Result<CString, ChCpuError> {
use std::io::{Error, ErrorKind};
CString::new(path.as_os_str().as_bytes())
.map_err(|_r| ChCpuError::io1("invalid name", path, Error::from(ErrorKind::InvalidInput)))
}