gpio_utils/
export.rs

1// Copyright (c) 2016, The gpio-utils Authors.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option.  This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use config::PinConfig;
10use error::*;
11use nix::unistd::{chown, Gid, Uid};
12use std::fs;
13use std::io::ErrorKind;
14use std::os::unix::fs as unix_fs;
15use std::os::unix::fs::PermissionsExt;
16use std::path;
17use std::sync::Mutex;
18use sysfs_gpio;
19use users::{Groups, Users, UsersCache};
20
21lazy_static! {
22    static ref USERS_CACHE: Mutex<UsersCache> = Mutex::new(UsersCache::new());
23}
24
25/// Unexport the pin specified in the provided config
26///
27/// Unexporting a config (in this context) involves a few different
28/// actions:
29///
30/// 1. For each GPIO name/alias, the corresponding symlink is remvoed from
31///    `/var/run/gpio/<name>` (or an alternate configured `symlink_root`).
32/// 2. The GPIO pin istself is unexported (vai /sys/class/gpio/unexport)
33///
34/// If the GPIO was already unexported, this function will continue
35/// without an error as the desired end state is achieved.
36pub fn unexport(pin_config: &PinConfig, symlink_root: Option<&str>) -> Result<()> {
37    if let Some(symroot) = symlink_root {
38        // create symlink for each name
39        for name in &pin_config.names {
40            let mut dst = path::PathBuf::from(symroot);
41            dst.push(name);
42            match fs::remove_file(dst) {
43                Ok(_) => (),
44                Err(ref e) if e.kind() == ErrorKind::NotFound => (),
45                Err(e) => return Err(e.into()),
46            };
47        }
48    }
49
50    // unexport the pin itself.  On many boards, it turns out, some pins are
51    // exported by the kernel itself but we might still be assigning names.  In
52    // those cases we will get an error here.  We handle that rather than
53    // exposing the error up the chain. (EINVAL)
54    let pin = pin_config.get_pin();
55    match pin.unexport() {
56        Ok(_) => Ok(()),
57        Err(sysfs_gpio::Error::Io(ref e)) if e.kind() == ErrorKind::InvalidInput => Ok(()),
58        Err(e) => Err(e.into()),
59    }
60}
61
62/// Export the pin specified in the provided config
63///
64/// Exporting a pin (in this context) involves, a few different
65/// actions:
66///
67/// 1. The GPIO pin itself is exported (via /sys/class/gpio/export)
68/// 2. For each GPIO name/alias, a symlink is created from
69///     `/var/run/gpio/<name>` -> `/sys/class/gpio<num>`.
70///
71/// If the GPIO is already exported, this function will continue
72/// without an error as the desired end state is achieved.
73pub fn export(pin_config: &PinConfig, symlink_root: Option<&str>) -> Result<()> {
74    let pin = pin_config.get_pin();
75    pin.export()?;
76
77    let uid = if let Some(username) = pin_config.user.as_ref() {
78        Some(
79            USERS_CACHE
80                .lock()
81                .unwrap()
82                .get_user_by_name(username)
83                .map(|u| Uid::from_raw(u.uid()))
84                .ok_or_else(|| format!("Unable to find user {:?}", username))?,
85        )
86    } else {
87        None
88    };
89
90    let gid = if let Some(groupname) = pin_config.group.as_ref() {
91        Some(
92            USERS_CACHE
93                .lock()
94                .unwrap()
95                .get_group_by_name(groupname)
96                .map(|g| Gid::from_raw(g.gid()))
97                .ok_or_else(|| format!("Unable to find group {:?}", groupname))?,
98        )
99    } else {
100        None
101    };
102
103    // change user, group, mode for files in gpio directory
104    if uid.is_some() || gid.is_some() || pin_config.mode.is_some() {
105        for entry in fs::read_dir(format!("/sys/class/gpio/gpio{}", &pin_config.num))? {
106            let e = entry?;
107            let metadata = e.metadata()?;
108
109            if metadata.is_file() {
110                if uid.is_some() || gid.is_some() {
111                    chown(e.path().as_path(), uid, gid)?;
112                }
113
114                if let Some(mode) = pin_config.mode {
115                    let mut permissions = metadata.permissions();
116                    permissions.set_mode(mode);
117                    fs::set_permissions(e.path().as_path(), permissions)?;
118                }
119            }
120        }
121    }
122
123    // if there is a symlink root provided, create symlink
124    if let Some(symroot) = symlink_root {
125        // create root directory if not exists
126        fs::create_dir_all(symroot)?;
127
128        // set active low
129        pin_config
130            .get_pin()
131            .set_active_low(pin_config.active_low)?;
132
133        // set the pin direction
134        pin_config
135            .get_pin()
136            .set_direction(pin_config.direction)?;
137
138        // create symlink for each name
139        for name in &pin_config.names {
140            let mut dst = path::PathBuf::from(symroot);
141            dst.push(name);
142            match unix_fs::symlink(format!("/sys/class/gpio/gpio{}", pin_config.num), dst) {
143                Err(ref e) if e.kind() == ErrorKind::AlreadyExists => (),
144                Err(e) => return Err(e.into()),
145                _ => (),
146            };
147        }
148    }
149
150    Ok(())
151}