libudev/
device.rs

1use std::str;
2
3use std::ffi::{CStr, OsStr};
4use std::marker::PhantomData;
5use std::path::Path;
6use std::str::FromStr;
7
8use libc::{c_char, dev_t};
9
10use ::context::Context;
11use ::handle::Handle;
12
13
14pub unsafe fn from_raw(device: *mut ::ffi::udev_device) -> Device {
15    ::ffi::udev_ref(::ffi::udev_device_get_udev(device));
16
17    Device { device: device }
18}
19
20
21/// A structure that provides access to sysfs/kernel devices.
22pub struct Device {
23    device: *mut ::ffi::udev_device,
24}
25
26impl Drop for Device {
27    fn drop(&mut self) {
28        unsafe {
29            let udev = ::ffi::udev_device_get_udev(self.device);
30
31            ::ffi::udev_device_unref(self.device);
32            ::ffi::udev_unref(udev);
33        }
34    }
35}
36
37#[doc(hidden)]
38impl Handle<::ffi::udev_device> for Device {
39    fn as_ptr(&self) -> *mut ::ffi::udev_device {
40        self.device
41    }
42}
43
44impl Device {
45    /// Creates a device for a given syspath.
46    ///
47    /// The `syspath` parameter should be a path to the device file within the `sysfs` file system,
48    /// e.g., `/sys/devices/virtual/tty/tty0`.
49    pub fn from_syspath(context: &Context, syspath: &Path) -> ::Result<Self> {
50        let syspath = try!(::util::os_str_to_cstring(syspath));
51
52        Ok(unsafe {
53            from_raw(try_alloc!(
54                ::ffi::udev_device_new_from_syspath(context.as_ptr(), syspath.as_ptr())
55            ))
56        })
57    }
58
59    /// Checks whether the device has already been handled by udev.
60    ///
61    /// When a new device is connected to the system, udev initializes the device by setting
62    /// permissions, renaming network devices, and possibly other initialization routines. This
63    /// method returns `true` if udev has performed all of its work to initialize this device.
64    ///
65    /// This method only applies to devices with device nodes or network interfaces. All other
66    /// devices return `true` by default.
67    pub fn is_initialized(&self) -> bool {
68        unsafe {
69            ::ffi::udev_device_get_is_initialized(self.device) > 0
70        }
71    }
72
73    /// Gets the device's major/minor number.
74    pub fn devnum(&self) -> Option<dev_t> {
75        match unsafe { ::ffi::udev_device_get_devnum(self.device) } {
76            0 => None,
77            n => Some(n),
78        }
79    }
80
81    /// Returns the syspath of the device.
82    ///
83    /// The path is an absolute path and includes the sys mount point. For example, the syspath for
84    /// `tty0` could be `/sys/devices/virtual/tty/tty0`, which includes the sys mount point,
85    /// `/sys`.
86    pub fn syspath(&self) -> Option<&Path> {
87        ::util::ptr_to_path(unsafe {
88            ::ffi::udev_device_get_syspath(self.device)
89        })
90    }
91
92    /// Returns the kernel devpath value of the device.
93    ///
94    /// The path does not contain the sys mount point, but does start with a `/`. For example, the
95    /// devpath for `tty0` could be `/devices/virtual/tty/tty0`.
96    pub fn devpath(&self) -> Option<&OsStr> {
97        ::util::ptr_to_os_str(unsafe {
98            ::ffi::udev_device_get_devpath(self.device)
99        })
100    }
101
102    /// Returns the path to the device node belonging to the device.
103    ///
104    /// The path is an absolute path and starts with the device directory. For example, the device
105    /// node for `tty0` could be `/dev/tty0`.
106    pub fn devnode(&self) -> Option<&Path> {
107        ::util::ptr_to_path(unsafe {
108            ::ffi::udev_device_get_devnode(self.device)
109        })
110    }
111
112    /// Returns the parent of the device.
113    pub fn parent(&self) -> Option<Device> {
114        let ptr = unsafe { ::ffi::udev_device_get_parent(self.device) };
115
116        if !ptr.is_null() {
117            unsafe {
118                ::ffi::udev_device_ref(ptr);
119
120                Some(from_raw(ptr))
121            }
122        }
123        else {
124            None
125        }
126    }
127
128    /// Returns the subsystem name of the device.
129    ///
130    /// The subsystem name is a string that indicates which kernel subsystem the device belongs to.
131    /// Examples of subsystem names are `tty`, `vtconsole`, `block`, `scsi`, and `net`.
132    pub fn subsystem(&self) -> Option<&OsStr> {
133        ::util::ptr_to_os_str(unsafe {
134            ::ffi::udev_device_get_subsystem(self.device)
135        })
136    }
137
138    /// Returns the kernel device name for the device.
139    ///
140    /// The sysname is a string that differentiates the device from others in the same subsystem.
141    /// For example, `tty0` is the sysname for a TTY device that differentiates it from others,
142    /// such as `tty1`.
143    pub fn sysname(&self) -> Option<&OsStr> {
144        ::util::ptr_to_os_str(unsafe {
145            ::ffi::udev_device_get_sysname(self.device)
146        })
147    }
148
149    /// Returns the instance number of the device.
150    ///
151    /// The instance number is used to differentiate many devices of the same type. For example,
152    /// `/dev/tty0` and `/dev/tty1` are both TTY devices but have instance numbers of 0 and 1,
153    /// respectively.
154    ///
155    /// Some devices don't have instance numbers, such as `/dev/console`, in which case the method
156    /// returns `None`.
157    pub fn sysnum(&self) -> Option<usize> {
158        let ptr = unsafe { ::ffi::udev_device_get_sysnum(self.device) };
159
160        if !ptr.is_null() {
161            match str::from_utf8(unsafe { CStr::from_ptr(ptr) }.to_bytes()) {
162                Ok(s) => FromStr::from_str(s).ok(),
163                Err(_) => None,
164            }
165        }
166        else {
167            None
168        }
169    }
170
171    /// Returns the devtype name of the device.
172    pub fn devtype(&self) -> Option<&OsStr> {
173        ::util::ptr_to_os_str(unsafe { ::ffi::udev_device_get_devtype(self.device) })
174    }
175
176    /// Returns the name of the kernel driver attached to the device.
177    pub fn driver(&self) -> Option<&OsStr> {
178        ::util::ptr_to_os_str(unsafe { ::ffi::udev_device_get_driver(self.device) })
179    }
180
181    /// Retrieves the value of a device property.
182    pub fn property_value<T: AsRef<OsStr>>(&self, property: T) -> Option<&OsStr> {
183        match ::util::os_str_to_cstring(property) {
184            Ok(prop) => {
185                ::util::ptr_to_os_str(unsafe {
186                    ::ffi::udev_device_get_property_value(self.device, prop.as_ptr())
187                })
188            },
189            Err(_) => None,
190        }
191
192    }
193
194    /// Retrieves the value of a device attribute.
195    pub fn attribute_value<T: AsRef<OsStr>>(&self, attribute: T) -> Option<&OsStr> {
196        match ::util::os_str_to_cstring(attribute) {
197            Ok(attr) => {
198                ::util::ptr_to_os_str(unsafe {
199                    ::ffi::udev_device_get_sysattr_value(self.device, attr.as_ptr())
200                })
201            },
202            Err(_) => None,
203        }
204    }
205
206    /// Sets the value of a device attribute.
207    pub fn set_attribute_value<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, attribute: T, value: U) -> ::Result<()> {
208        let attribute = try!(::util::os_str_to_cstring(attribute));
209        let value = try!(::util::os_str_to_cstring(value));
210
211        ::util::errno_to_result(unsafe {
212            ::ffi::udev_device_set_sysattr_value(self.device, attribute.as_ptr(), value.as_ptr() as *mut c_char)
213        })
214    }
215
216    /// Returns an iterator over the device's properties.
217    ///
218    /// ## Example
219    ///
220    /// This example prints out all of a device's properties:
221    ///
222    /// ```no_run
223    /// # use std::path::Path;
224    /// # let mut context = libudev::Context::new().unwrap();
225    /// # let device = libudev::Device::from_syspath(&context, Path::new("/sys/devices/virtual/tty/tty0")).unwrap();
226    /// for property in device.properties() {
227    ///     println!("{:?} = {:?}", property.name(), property.value());
228    /// }
229    /// ```
230    pub fn properties(&self) -> Properties {
231        Properties {
232            _device: PhantomData,
233            entry: unsafe { ::ffi::udev_device_get_properties_list_entry(self.device) },
234        }
235    }
236
237    /// Returns an iterator over the device's attributes.
238    ///
239    /// ## Example
240    ///
241    /// This example prints out all of a device's attributes:
242    ///
243    /// ```no_run
244    /// # use std::path::Path;
245    /// # let mut context = libudev::Context::new().unwrap();
246    /// # let device = libudev::Device::from_syspath(&context, Path::new("/sys/devices/virtual/tty/tty0")).unwrap();
247    /// for attribute in device.attributes() {
248    ///     println!("{:?} = {:?}", attribute.name(), attribute.value());
249    /// }
250    /// ```
251    pub fn attributes(&self) -> Attributes {
252        Attributes {
253            device: self,
254            entry: unsafe { ::ffi::udev_device_get_sysattr_list_entry(self.device) },
255        }
256    }
257}
258
259
260/// Iterator over a device's properties.
261pub struct Properties<'a> {
262    _device: PhantomData<&'a Device>,
263    entry: *mut ::ffi::udev_list_entry,
264}
265
266impl<'a> Iterator for Properties<'a> {
267    type Item = Property<'a>;
268
269    fn next(&mut self) -> Option<Property<'a>> {
270        if !self.entry.is_null() {
271            unsafe {
272                let name = ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_name(self.entry));
273                let value = ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_value(self.entry));
274
275                self.entry = ::ffi::udev_list_entry_get_next(self.entry);
276
277                Some(Property {
278                    name: name,
279                    value: value,
280                })
281            }
282        }
283        else {
284            None
285        }
286    }
287
288    fn size_hint(&self) -> (usize, Option<usize>) {
289        (0, None)
290    }
291}
292
293/// A device property.
294pub struct Property<'a> {
295    name: &'a OsStr,
296    value: &'a OsStr,
297}
298
299impl<'a> Property<'a> {
300    /// Returns the property name.
301    pub fn name(&self) -> &OsStr {
302        self.name
303    }
304
305    /// Returns the property value.
306    pub fn value(&self) -> &OsStr {
307        self.value
308    }
309}
310
311
312/// Iterator over a device's attributes.
313pub struct Attributes<'a> {
314    device: &'a Device,
315    entry: *mut ::ffi::udev_list_entry,
316}
317
318impl<'a> Iterator for Attributes<'a> {
319    type Item = Attribute<'a>;
320
321    fn next(&mut self) -> Option<Attribute<'a>> {
322        if !self.entry.is_null() {
323            let name = unsafe { ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_name(self.entry)) };
324
325            self.entry = unsafe { ::ffi::udev_list_entry_get_next(self.entry) };
326
327            Some(Attribute {
328                device: self.device,
329                name: name,
330            })
331        }
332        else {
333            None
334        }
335    }
336
337    fn size_hint(&self) -> (usize, Option<usize>) {
338        (0, None)
339    }
340}
341
342/// A device attribute.
343pub struct Attribute<'a> {
344    device: &'a Device,
345    name: &'a OsStr,
346}
347
348impl<'a> Attribute<'a> {
349    /// Returns the attribute name.
350    pub fn name(&self) -> &OsStr {
351        self.name
352    }
353
354    /// Returns the attribute value.
355    pub fn value(&self) -> Option<&OsStr> {
356        self.device.attribute_value(self.name)
357    }
358}