use config::PinConfig;
use error::*;
use nix::unistd::{chown, Gid, Uid};
use std::fs;
use std::io::ErrorKind;
use std::os::unix::fs as unix_fs;
use std::os::unix::fs::PermissionsExt;
use std::path;
use std::sync::Mutex;
use sysfs_gpio;
use users::{Groups, Users, UsersCache};
lazy_static! {
static ref USERS_CACHE: Mutex<UsersCache> = Mutex::new(UsersCache::new());
}
pub fn unexport(pin_config: &PinConfig, symlink_root: Option<&str>) -> Result<()> {
if let Some(symroot) = symlink_root {
for name in &pin_config.names {
let mut dst = path::PathBuf::from(symroot);
dst.push(name);
match fs::remove_file(dst) {
Ok(_) => (),
Err(ref e) if e.kind() == ErrorKind::NotFound => (),
Err(e) => return Err(e.into()),
};
}
}
let pin = pin_config.get_pin();
match pin.unexport() {
Ok(_) => Ok(()),
Err(sysfs_gpio::Error::Io(ref e)) if e.kind() == ErrorKind::InvalidInput => Ok(()),
Err(e) => Err(e.into()),
}
}
pub fn export(pin_config: &PinConfig, symlink_root: Option<&str>) -> Result<()> {
let pin = pin_config.get_pin();
pin.export()?;
let uid = if let Some(username) = pin_config.user.as_ref() {
Some(
USERS_CACHE
.lock()
.unwrap()
.get_user_by_name(username)
.map(|u| Uid::from_raw(u.uid()))
.ok_or_else(|| format!("Unable to find user {:?}", username))?,
)
} else {
None
};
let gid = if let Some(groupname) = pin_config.group.as_ref() {
Some(
USERS_CACHE
.lock()
.unwrap()
.get_group_by_name(groupname)
.map(|g| Gid::from_raw(g.gid()))
.ok_or_else(|| format!("Unable to find group {:?}", groupname))?,
)
} else {
None
};
if uid.is_some() || gid.is_some() || pin_config.mode.is_some() {
for entry in fs::read_dir(format!("/sys/class/gpio/gpio{}", &pin_config.num))? {
let e = entry?;
let metadata = e.metadata()?;
if metadata.is_file() {
if uid.is_some() || gid.is_some() {
chown(e.path().as_path(), uid, gid)?;
}
if let Some(mode) = pin_config.mode {
let mut permissions = metadata.permissions();
permissions.set_mode(mode);
fs::set_permissions(e.path().as_path(), permissions)?;
}
}
}
}
if let Some(symroot) = symlink_root {
fs::create_dir_all(symroot)?;
pin_config
.get_pin()
.set_active_low(pin_config.active_low)?;
pin_config
.get_pin()
.set_direction(pin_config.direction)?;
for name in &pin_config.names {
let mut dst = path::PathBuf::from(symroot);
dst.push(name);
match unix_fs::symlink(format!("/sys/class/gpio/gpio{}", pin_config.num), dst) {
Err(ref e) if e.kind() == ErrorKind::AlreadyExists => (),
Err(e) => return Err(e.into()),
_ => (),
};
}
}
Ok(())
}