Skip to main content

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