FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Ubuntu > Ubuntu User

 
 
LinkBack Thread Tools
 
Old 11-10-2009, 02:19 PM
Martin Gracik
 
Default Added the libudev python bindings

Also rewrote the baseudev.py to use the bindings instead of parsing
the udev db file
---
baseudev.py | 66 ++++++----------------
pyudev.py | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++
2 files changed, 197 insertions(+), 49 deletions(-)
create mode 100644 pyudev.py

diff --git a/baseudev.py b/baseudev.py
index 76b14cc..978bae0 100644
--- a/baseudev.py
+++ b/baseudev.py
@@ -23,6 +23,7 @@

import iutil
import os
+import pyudev

import logging
log = logging.getLogger("storage")
@@ -43,20 +44,24 @@ def udev_get_device(sysfs_path):
log.debug("%s does not exist" % sysfs_path)
return None

- db_entry = sysfs_path.replace("/", "x2f")
- db_root = "/dev/.udev/db"
- db_path = os.path.normpath("%s/%s" % (db_root, db_entry))
- if not os.access(db_path, os.R_OK):
- log.debug("db entry %s does not exist" % db_path)
- return None
+ udev = pyudev.Udev()
+ # XXX we remove the /sys part when enumerating devices,
+ # so we have to prepend it when creating the device
+ udev_device = udev.create_device("/sys" + sysfs_path)
+ udev.teardown()
+
+ dev = udev_device.properties
+
+ if dev:
+ # remove the /dev/ part from beginning of devnode and set as name
+ dev["name"] = udev_device.devnode[5:]
+
+ dev["symlinks"] = dev["DEVLINKS"].split()
+ dev["sysfs_path"] = sysfs_path

- entry = open(db_path).read()
- dev = udev_parse_entry(entry)
- if dev.has_key("name"):
- dev['sysfs_path'] = sysfs_path
+ # now add in the contents of the uevent file since they're handy
dev = udev_parse_uevent_file(dev)

- # now add in the contents of the uevent file since they're handy
return dev

def udev_get_devices(deviceClass="block"):
@@ -68,45 +73,8 @@ def udev_get_devices(deviceClass="block"):
entries.append(entry)
return entries

-def udev_parse_entry(buf):
- dev = {}
-
- for line in buf.splitlines():
- line.strip()
- (tag, sep, val) = line.partition(":")
- if not sep:
- continue
-
- if tag == "N":
- dev['name'] = val
- elif tag == "S":
- if dev.has_key('symlinks'):
- dev['symlinks'].append(val)
- else:
- dev['symlinks'] = [val]
- elif tag == "E":
- if val.count("=") > 1 and val.count(" ") > 0:
- # eg: LVM2_LV_NAME when querying the VG for its LVs
- vars = val.split()
- vals = []
- var_name = None
- for (index, subval) in enumerate(vars):
- (var_name, sep, var_val) = subval.partition("=")
- if sep:
- vals.append(var_val)
-
- dev[var_name] = vals
- else:
- (var_name, sep, var_val) = val.partition("=")
- if not sep:
- continue
-
- dev[var_name] = var_val
-
- return dev
-
def udev_parse_uevent_file(dev):
- path = os.path.normpath("/sys/%s/uevent" % dev['sysfs_path'])
+ path = os.path.normpath("/sys/%s/uevent" % dev["sysfs_path"])
if not os.access(path, os.R_OK):
return dev

diff --git a/pyudev.py b/pyudev.py
new file mode 100644
index 0000000..8d5a7c2
--- /dev/null
+++ b/pyudev.py
@@ -0,0 +1,180 @@
+import sys
+import os
+import fnmatch
+from ctypes import *
+
+
+# XXX this one may need some tweaking...
+def find_library(name):
+ libdirs = ("/lib64", "/lib")
+
+ for dir in libdirs:
+ files = fnmatch.filter(os.listdir(dir), "*%s*" % name)
+ files = [os.path.join(dir, file) for file in files]
+
+ if files:
+ break
+
+ if files:
+ return files[0]
+ else:
+ return None
+
+
+# find the udev library
+libudev = find_library("udev")
+
+if not libudev or not os.path.exists(libudev):
+ raise ImportError, "No library named %s" % libudev
+
+# load the udev library
+libudev = CDLL(libudev)
+
+
+# create aliases for needed functions and set the return types where needed
+libudev_udev_new = libudev.udev_new
+libudev_udev_unref = libudev.udev_unref
+
+libudev_udev_device_new_from_syspath = libudev.udev_device_new_from_syspath
+libudev_udev_device_unref = libudev.udev_device_unref
+
+libudev_udev_device_get_syspath = libudev.udev_device_get_syspath
+libudev_udev_device_get_sysname = libudev.udev_device_get_sysname
+libudev_udev_device_get_syspath.restype = c_char_p
+libudev_udev_device_get_sysname.restype = c_char_p
+
+libudev_udev_device_get_properties_list_entry = libudev.udev_device_get_properties_list_entry
+libudev_udev_list_entry_get_next = libudev.udev_list_entry_get_next
+
+libudev_udev_list_entry_get_name = libudev.udev_list_entry_get_name
+libudev_udev_list_entry_get_value = libudev.udev_list_entry_get_value
+libudev_udev_list_entry_get_name.restype = c_char_p
+libudev_udev_list_entry_get_value.restype = c_char_p
+
+libudev_udev_enumerate_new = libudev.udev_enumerate_new
+libudev_udev_enumerate_unref = libudev.udev_enumerate_unref
+
+libudev_udev_enumerate_add_match_subsystem = libudev.udev_enumerate_add_match_subsystem
+libudev_udev_enumerate_scan_devices = libudev.udev_enumerate_scan_devices
+libudev_udev_enumerate_get_list_entry = libudev.udev_enumerate_get_list_entry
+
+
+class UdevDevice(object):
+
+ def __init__(self, udev, sysfs_path):
+ self.syspath = None
+ self.sysname = None
+ self.properties = {}
+
+ # create new udev device from syspath
+ udev_device = libudev_udev_device_new_from_syspath(udev, sysfs_path)
+ if not udev_device:
+ # device does not exist
+ return
+
+ # set syspath and sysname properties
+ self.syspath = libudev_udev_device_get_syspath(udev_device)
+ self.sysname = libudev_udev_device_get_sysname(udev_device)
+
+ # get the first property entry
+ property_entry = libudev_udev_device_get_properties_list_entry(udev _device)
+
+ while property_entry:
+ name = libudev_udev_list_entry_get_name(property_entry)
+ value = libudev_udev_list_entry_get_value(property_entry)
+
+ # XXX we have to split some of the values into a list,
+ # the libudev is not so smart
+ fields = value.split()
+
+ if len(fields) > 1:
+ value = [fields[0]]
+
+ for item in fields[1:]:
+ (key, sep, val) = item.partition("=")
+ if sep:
+ value.append(val)
+
+ if len(value) == 1:
+ value = value[0]
+
+ self.properties[name] = value
+
+ # get next property entry
+ property_entry = libudev_udev_list_entry_get_next(property_entry)
+
+ # set additional properties
+ libudev.udev_device_get_devpath.restype = c_char_p
+ self.devpath = libudev.udev_device_get_devpath(udev_device)
+
+ libudev.udev_device_get_subsystem.restype = c_char_p
+ self.subsystem = libudev.udev_device_get_subsystem(udev_device)
+
+ libudev.udev_device_get_devtype.restype = c_char_p
+ self.devtype = libudev.udev_device_get_devtype(udev_device)
+
+ libudev.udev_device_get_sysnum.restype = c_char_p
+ self.sysnum = libudev.udev_device_get_sysnum(udev_device)
+
+ libudev.udev_device_get_devnode.restype = c_char_p
+ self.devnode = libudev.udev_device_get_devnode(udev_device)
+
+ # cleanup
+ libudev_udev_device_unref(udev_device)
+
+ def __str__(self):
+ s = "SYSPATH: %s
" % self.syspath
+ s += "SYSNAME: %s" % self.sysname
+
+ return s
+
+
+class Udev(object):
+
+ def __init__(self):
+ self.udev = libudev_udev_new()
+
+ def create_device(self, sysfs_path):
+ return UdevDevice(self.udev, sysfs_path)
+
+ def scan_devices(self, subsystem=None):
+ enumerate = libudev_udev_enumerate_new(self.udev)
+
+ # add the match subsystem
+ if subsystem is not None:
+ rc = libudev_udev_enumerate_add_match_subsystem(enumera te, subsystem)
+ if not rc == 0:
+ print >> sys.stderr, "error: unable to add the match subsystem"
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # scan the devices
+ rc = libudev_udev_enumerate_scan_devices(enumerate)
+ if not rc == 0:
+ print >> sys.stderr, "error: unable to scan the devices"
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # create the list of devices
+ devices = []
+
+ # get the first device entry
+ device_entry = libudev_udev_enumerate_get_list_entry(enumerate)
+
+ while device_entry:
+ sysfs_path = libudev_udev_list_entry_get_name(device_entry)
+ device = self.create_device(sysfs_path)
+
+ if device.syspath is not None:
+ devices.append(device)
+
+ # get next device entry
+ device_entry = libudev_udev_list_entry_get_next(device_entry)
+
+ # cleanup
+ libudev_udev_enumerate_unref(enumerate)
+
+ return devices
+
+ def teardown(self):
+ libudev_udev_unref(self.udev)
--
1.6.5.2

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-10-2009, 03:24 PM
Hans de Goede
 
Default Added the libudev python bindings

Hi,

On 11/10/2009 04:19 PM, Martin Gracik wrote:

Also rewrote the baseudev.py to use the bindings instead of parsing
the udev db file
---
baseudev.py | 66 ++++++----------------
pyudev.py | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++
2 files changed, 197 insertions(+), 49 deletions(-)
create mode 100644 pyudev.py

diff --git a/baseudev.py b/baseudev.py
index 76b14cc..978bae0 100644
--- a/baseudev.py
+++ b/baseudev.py
@@ -23,6 +23,7 @@

import iutil
import os
+import pyudev

import logging
log = logging.getLogger("storage")
@@ -43,20 +44,24 @@ def udev_get_device(sysfs_path):
log.debug("%s does not exist" % sysfs_path)
return None

- db_entry = sysfs_path.replace("/", "x2f")
- db_root = "/dev/.udev/db"
- db_path = os.path.normpath("%s/%s" % (db_root, db_entry))
- if not os.access(db_path, os.R_OK):
- log.debug("db entry %s does not exist" % db_path)
- return None
+ udev = pyudev.Udev()
+ # XXX we remove the /sys part when enumerating devices,
+ # so we have to prepend it when creating the device
+ udev_device = udev.create_device("/sys" + sysfs_path)
+ udev.teardown()
+


Not sure if I like the teardown() name, this implies that after this
things can be setup again, which is not true.


+ dev = udev_device.properties
+
+ if dev:
+ # remove the /dev/ part from beginning of devnode and set as name
+ dev["name"] = udev_device.devnode[5:]
+


This is not correct, this will return mapper/Volgroup-Volname for LV's where it
should return just Volgroup-Volname, please find out which libudev function return
the actual internal libudev name and use that instead.


+ dev["symlinks"] = dev["DEVLINKS"].split()
+ dev["sysfs_path"] = sysfs_path

- entry = open(db_path).read()
- dev = udev_parse_entry(entry)
- if dev.has_key("name"):
- dev['sysfs_path'] = sysfs_path
+ # now add in the contents of the uevent file since they're handy
dev = udev_parse_uevent_file(dev)

- # now add in the contents of the uevent file since they're handy
return dev

def udev_get_devices(deviceClass="block"):
@@ -68,45 +73,8 @@ def udev_get_devices(deviceClass="block"):
entries.append(entry)
return entries

-def udev_parse_entry(buf):
- dev = {}
-
- for line in buf.splitlines():
- line.strip()
- (tag, sep, val) = line.partition(":")
- if not sep:
- continue
-
- if tag == "N":
- dev['name'] = val
- elif tag == "S":
- if dev.has_key('symlinks'):
- dev['symlinks'].append(val)
- else:
- dev['symlinks'] = [val]
- elif tag == "E":
- if val.count("=")> 1 and val.count(" ")> 0:
- # eg: LVM2_LV_NAME when querying the VG for its LVs
- vars = val.split()
- vals = []
- var_name = None
- for (index, subval) in enumerate(vars):
- (var_name, sep, var_val) = subval.partition("=")
- if sep:
- vals.append(var_val)
-
- dev[var_name] = vals
- else:
- (var_name, sep, var_val) = val.partition("=")
- if not sep:
- continue
-
- dev[var_name] = var_val
-
- return dev
-
def udev_parse_uevent_file(dev):
- path = os.path.normpath("/sys/%s/uevent" % dev['sysfs_path'])
+ path = os.path.normpath("/sys/%s/uevent" % dev["sysfs_path"])


This is a meaningless change, which does not belong in this patch


if not os.access(path, os.R_OK):
return dev

diff --git a/pyudev.py b/pyudev.py
new file mode 100644
index 0000000..8d5a7c2
--- /dev/null
+++ b/pyudev.py
@@ -0,0 +1,180 @@
+import sys
+import os
+import fnmatch
+from ctypes import *
+
+
+# XXX this one may need some tweaking...
+def find_library(name):
+ libdirs = ("/lib64", "/lib")
+
+ for dir in libdirs:
+ files = fnmatch.filter(os.listdir(dir), "*%s*" % name)
+ files = [os.path.join(dir, file) for file in files]
+
+ if files:
+ break
+
+ if files:
+ return files[0]
+ else:
+ return None
+


Please make the matching here less flexible, we want something
mathching "lib%s.so.%d" % (name, somajor), where somajor is a
new parameter to find_library, this way we can also detect if the ABI
ever changes underneed us, as then the somajor will change and we
will no longer find the library.


+
+# find the udev library
+libudev = find_library("udev")
+
+if not libudev or not os.path.exists(libudev):
+ raise ImportError, "No library named %s" % libudev
+
+# load the udev library
+libudev = CDLL(libudev)
+
+
+# create aliases for needed functions and set the return types where needed
+libudev_udev_new = libudev.udev_new
+libudev_udev_unref = libudev.udev_unref
+
+libudev_udev_device_new_from_syspath = libudev.udev_device_new_from_syspath
+libudev_udev_device_unref = libudev.udev_device_unref
+
+libudev_udev_device_get_syspath = libudev.udev_device_get_syspath
+libudev_udev_device_get_sysname = libudev.udev_device_get_sysname
+libudev_udev_device_get_syspath.restype = c_char_p
+libudev_udev_device_get_sysname.restype = c_char_p
+
+libudev_udev_device_get_properties_list_entry = libudev.udev_device_get_properties_list_entry
+libudev_udev_list_entry_get_next = libudev.udev_list_entry_get_next
+
+libudev_udev_list_entry_get_name = libudev.udev_list_entry_get_name
+libudev_udev_list_entry_get_value = libudev.udev_list_entry_get_value
+libudev_udev_list_entry_get_name.restype = c_char_p
+libudev_udev_list_entry_get_value.restype = c_char_p
+
+libudev_udev_enumerate_new = libudev.udev_enumerate_new
+libudev_udev_enumerate_unref = libudev.udev_enumerate_unref
+
+libudev_udev_enumerate_add_match_subsystem = libudev.udev_enumerate_add_match_subsystem
+libudev_udev_enumerate_scan_devices = libudev.udev_enumerate_scan_devices
+libudev_udev_enumerate_get_list_entry = libudev.udev_enumerate_get_list_entry
+
+
+class UdevDevice(object):
+
+ def __init__(self, udev, sysfs_path):
+ self.syspath = None
+ self.sysname = None
+ self.properties = {}
+
+ # create new udev device from syspath
+ udev_device = libudev_udev_device_new_from_syspath(udev, sysfs_path)
+ if not udev_device:
+ # device does not exist
+ return
+
+ # set syspath and sysname properties
+ self.syspath = libudev_udev_device_get_syspath(udev_device)
+ self.sysname = libudev_udev_device_get_sysname(udev_device)
+
+ # get the first property entry
+ property_entry = libudev_udev_device_get_properties_list_entry(udev _device)
+
+ while property_entry:
+ name = libudev_udev_list_entry_get_name(property_entry)
+ value = libudev_udev_list_entry_get_value(property_entry)
+
+ # XXX we have to split some of the values into a list,
+ # the libudev is not so smart
+ fields = value.split()
+
+ if len(fields)> 1:
+ value = [fields[0]]
+
+ for item in fields[1:]:
+ (key, sep, val) = item.partition("=")
+ if sep:
+ value.append(val)
+
+ if len(value) == 1:
+ value = value[0]
+
+ self.properties[name] = value
+
+ # get next property entry
+ property_entry = libudev_udev_list_entry_get_next(property_entry)
+
+ # set additional properties
+ libudev.udev_device_get_devpath.restype = c_char_p
+ self.devpath = libudev.udev_device_get_devpath(udev_device)
+
+ libudev.udev_device_get_subsystem.restype = c_char_p
+ self.subsystem = libudev.udev_device_get_subsystem(udev_device)
+
+ libudev.udev_device_get_devtype.restype = c_char_p
+ self.devtype = libudev.udev_device_get_devtype(udev_device)
+
+ libudev.udev_device_get_sysnum.restype = c_char_p
+ self.sysnum = libudev.udev_device_get_sysnum(udev_device)
+
+ libudev.udev_device_get_devnode.restype = c_char_p
+ self.devnode = libudev.udev_device_get_devnode(udev_device)
+
+ # cleanup
+ libudev_udev_device_unref(udev_device)
+
+ def __str__(self):
+ s = "SYSPATH: %s
" % self.syspath
+ s += "SYSNAME: %s" % self.sysname
+
+ return s
+
+
+class Udev(object):
+
+ def __init__(self):
+ self.udev = libudev_udev_new()
+
+ def create_device(self, sysfs_path):
+ return UdevDevice(self.udev, sysfs_path)
+
+ def scan_devices(self, subsystem=None):
+ enumerate = libudev_udev_enumerate_new(self.udev)
+
+ # add the match subsystem
+ if subsystem is not None:
+ rc = libudev_udev_enumerate_add_match_subsystem(enumera te, subsystem)
+ if not rc == 0:
+ print>> sys.stderr, "error: unable to add the match subsystem"
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # scan the devices
+ rc = libudev_udev_enumerate_scan_devices(enumerate)
+ if not rc == 0:
+ print>> sys.stderr, "error: unable to scan the devices"
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # create the list of devices
+ devices = []
+
+ # get the first device entry
+ device_entry = libudev_udev_enumerate_get_list_entry(enumerate)
+
+ while device_entry:
+ sysfs_path = libudev_udev_list_entry_get_name(device_entry)
+ device = self.create_device(sysfs_path)
+
+ if device.syspath is not None:
+ devices.append(device)
+
+ # get next device entry
+ device_entry = libudev_udev_list_entry_get_next(device_entry)
+
+ # cleanup
+ libudev_udev_enumerate_unref(enumerate)
+
+ return devices
+
+ def teardown(self):
+ libudev_udev_unref(self.udev)


You will want to set self.udev to None after this, and check all other uses for it to be not None, other
wise you may cause a segfault (referring freed memory) !

Regards,

Hana

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-10-2009, 07:22 PM
Chris Lumens
 
Default Added the libudev python bindings

> Also rewrote the baseudev.py to use the bindings instead of parsing
> the udev db file
> ---
> baseudev.py | 66 ++++++----------------
> pyudev.py | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++
> 2 files changed, 197 insertions(+), 49 deletions(-)

Will pyudev.py eventually be moving into another package, like perhaps
in the udev package or one of its subpackages?

> @@ -43,20 +44,24 @@ def udev_get_device(sysfs_path):
> log.debug("%s does not exist" % sysfs_path)
> return None
>
> - db_entry = sysfs_path.replace("/", "x2f")
> - db_root = "/dev/.udev/db"
> - db_path = os.path.normpath("%s/%s" % (db_root, db_entry))
> - if not os.access(db_path, os.R_OK):
> - log.debug("db entry %s does not exist" % db_path)
> - return None
> + udev = pyudev.Udev()
> + # XXX we remove the /sys part when enumerating devices,
> + # so we have to prepend it when creating the device
> + udev_device = udev.create_device("/sys" + sysfs_path)
> + udev.teardown()
> +
> + dev = udev_device.properties
> +
> + if dev:
> + # remove the /dev/ part from beginning of devnode and set as name
> + dev["name"] = udev_device.devnode[5:]
> +
> + dev["symlinks"] = dev["DEVLINKS"].split()
> + dev["sysfs_path"] = sysfs_path
>
> - entry = open(db_path).read()
> - dev = udev_parse_entry(entry)
> - if dev.has_key("name"):
> - dev['sysfs_path'] = sysfs_path
> + # now add in the contents of the uevent file since they're handy
> dev = udev_parse_uevent_file(dev)
>
> - # now add in the contents of the uevent file since they're handy
> return dev
>
> def udev_get_devices(deviceClass="block"):

We could potentially be calling udev_get_device() on several hundreds or
even thousands of devices, which is a whole lot of:

pyudev.Udev()
udev.create_device()
udev.teardown()

Can we instead only call pyudev.Udev() once and stash an instance to the
object as a global in baseudev.py? I'm concerned that we're going to be
introducing another big slowdown here in device discovery on very large
configurations.

> +# XXX this one may need some tweaking...
> +def find_library(name):
> + libdirs = ("/lib64", "/lib")
> +
> + for dir in libdirs:
> + files = fnmatch.filter(os.listdir(dir), "*%s*" % name)
> + files = [os.path.join(dir, file) for file in files]
> +
> + if files:
> + break
> +
> + if files:
> + return files[0]
> + else:
> + return None

Would be nice to search the $LD_LIBRARY_PATH, since we use that to do
updates.img stuff:

def find_library(name):
env = os.environ.get("LD_LIBRARY_PATH")
if env:
libdirs = env.split(":")
else:
libdirs = ["/lib64", "/lib"]

for dir in libdirs:
...

> +class UdevDevice(object):

If you really want to be fancy, you can add some special methods to
UdevDevice so people don't have to go through the properties dict to
access udev properties. You could do something like the following:

def __getattr__(self, name):
return self.properties[name]

Then it's just a simple matter of calling it like this:

udev_device = udev.create_device("/sys" + sysfs_path)
print udev_device["symlinks"]
print udev_device["busted"]

The first print will return udev_device.properties["symlinks"]. The
second will raise AttributeError, like you'd expect. I think doing
something like this makes for a much nicer API and hides some of the
implementation details.

More information at:

http://docs.python.org/reference/datamodel.html?highlight=__get__#customizing-attribute-access

> +class Udev(object):
> +
> + def __init__(self):
> + self.udev = libudev_udev_new()
> +
> + def create_device(self, sysfs_path):
> + return UdevDevice(self.udev, sysfs_path)
> +
> + def scan_devices(self, subsystem=None):
> + enumerate = libudev_udev_enumerate_new(self.udev)

scan_devices could take a while, I suppose. Is it worth making it
return an iterator so it doesn't have to grab everything all at once and
can instead grab each individual device as needed? Or am I just making
things too complicated now?

> + # add the match subsystem
> + if subsystem is not None:
> + rc = libudev_udev_enumerate_add_match_subsystem(enumera te, subsystem)
> + if not rc == 0:
> + print >> sys.stderr, "error: unable to add the match subsystem"
> + libudev_udev_enumerate_unref(enumerate)
> + return []

print is a function in python3 (also python2.6?). For future
compatibility, you'll want to:

print("error: whatever", file=sys.stderr)

One final minor point: This is a fairly low level module in anaconda.
Whenever we add new things like this, it's good to think about how it
could be automatically tested so we have more trust in what gets built
on top of it. Have you given any thought to this yet?'

Overall, looks interesting.

- Chris

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-11-2009, 07:09 AM
Martin Gracik
 
Default Added the libudev python bindings

Hi,

please check my answers below your suggestions.

--

Martin Gracik

----- Original Message -----
From: "Hans de Goede" <hdegoede@redhat.com>
To: anaconda-devel-list@redhat.com
Sent: Tuesday, November 10, 2009 5:24:29 PM GMT +01:00 Amsterdam / Berlin / Bern / Rome / Stockholm / Vienna
Subject: Re: [PATCH] Added the libudev python bindings

Hi,

On 11/10/2009 04:19 PM, Martin Gracik wrote:
> Also rewrote the baseudev.py to use the bindings instead of parsing
> the udev db file
> ---
> baseudev.py | 66 ++++++----------------
> pyudev.py | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++
> 2 files changed, 197 insertions(+), 49 deletions(-)
> create mode 100644 pyudev.py
>
> diff --git a/baseudev.py b/baseudev.py
> index 76b14cc..978bae0 100644
> --- a/baseudev.py
> +++ b/baseudev.py
> @@ -23,6 +23,7 @@
>
> import iutil
> import os
> +import pyudev
>
> import logging
> log = logging.getLogger("storage")
> @@ -43,20 +44,24 @@ def udev_get_device(sysfs_path):
> log.debug("%s does not exist" % sysfs_path)
> return None
>
> - db_entry = sysfs_path.replace("/", "x2f")
> - db_root = "/dev/.udev/db"
> - db_path = os.path.normpath("%s/%s" % (db_root, db_entry))
> - if not os.access(db_path, os.R_OK):
> - log.debug("db entry %s does not exist" % db_path)
> - return None
> + udev = pyudev.Udev()
> + # XXX we remove the /sys part when enumerating devices,
> + # so we have to prepend it when creating the device
> + udev_device = udev.create_device("/sys" + sysfs_path)
> + udev.teardown()
> +

Not sure if I like the teardown() name, this implies that after this
things can be setup again, which is not true.

Ok, I have no problem with changing the name, suggest one for me that would be appropriate.

> + dev = udev_device.properties
> +
> + if dev:
> + # remove the /dev/ part from beginning of devnode and set as name
> + dev["name"] = udev_device.devnode[5:]
> +

This is not correct, this will return mapper/Volgroup-Volname for LV's where it
should return just Volgroup-Volname, please find out which libudev function return
the actual internal libudev name and use that instead.

It is correct if we want to have the same stuff the old baseudev.py returns.
I was using just the SYSNAME, but that's dm-0, dm-1 etc. But the old baseudev.py returns mapper/vg-something,
so I checked all the properties, and libudev functions, and the only one getting this kind of output was
devnode(), but with the /dev/ at the start.

> + dev["symlinks"] = dev["DEVLINKS"].split()
> + dev["sysfs_path"] = sysfs_path
>
> - entry = open(db_path).read()
> - dev = udev_parse_entry(entry)
> - if dev.has_key("name"):
> - dev['sysfs_path'] = sysfs_path
> + # now add in the contents of the uevent file since they're handy
> dev = udev_parse_uevent_file(dev)
>
> - # now add in the contents of the uevent file since they're handy
> return dev
>
> def udev_get_devices(deviceClass="block"):
> @@ -68,45 +73,8 @@ def udev_get_devices(deviceClass="block"):
> entries.append(entry)
> return entries
>
> -def udev_parse_entry(buf):
> - dev = {}
> -
> - for line in buf.splitlines():
> - line.strip()
> - (tag, sep, val) = line.partition(":")
> - if not sep:
> - continue
> -
> - if tag == "N":
> - dev['name'] = val
> - elif tag == "S":
> - if dev.has_key('symlinks'):
> - dev['symlinks'].append(val)
> - else:
> - dev['symlinks'] = [val]
> - elif tag == "E":
> - if val.count("=")> 1 and val.count(" ")> 0:
> - # eg: LVM2_LV_NAME when querying the VG for its LVs
> - vars = val.split()
> - vals = []
> - var_name = None
> - for (index, subval) in enumerate(vars):
> - (var_name, sep, var_val) = subval.partition("=")
> - if sep:
> - vals.append(var_val)
> -
> - dev[var_name] = vals
> - else:
> - (var_name, sep, var_val) = val.partition("=")
> - if not sep:
> - continue
> -
> - dev[var_name] = var_val
> -
> - return dev
> -
> def udev_parse_uevent_file(dev):
> - path = os.path.normpath("/sys/%s/uevent" % dev['sysfs_path'])
> + path = os.path.normpath("/sys/%s/uevent" % dev["sysfs_path"])

This is a meaningless change, which does not belong in this patch

Ok, I know it's meaningless, but when I was editing the file, I wanted to have the code consistent.
Didn't know that's a problem. Will change it back.

> if not os.access(path, os.R_OK):
> return dev
>
> diff --git a/pyudev.py b/pyudev.py
> new file mode 100644
> index 0000000..8d5a7c2
> --- /dev/null
> +++ b/pyudev.py
> @@ -0,0 +1,180 @@
> +import sys
> +import os
> +import fnmatch
> +from ctypes import *
> +
> +
> +# XXX this one may need some tweaking...
> +def find_library(name):
> + libdirs = ("/lib64", "/lib")
> +
> + for dir in libdirs:
> + files = fnmatch.filter(os.listdir(dir), "*%s*" % name)
> + files = [os.path.join(dir, file) for file in files]
> +
> + if files:
> + break
> +
> + if files:
> + return files[0]
> + else:
> + return None
> +

Please make the matching here less flexible, we want something
mathching "lib%s.so.%d" % (name, somajor), where somajor is a
new parameter to find_library, this way we can also detect if the ABI
ever changes underneed us, as then the somajor will change and we
will no longer find the library.

I was concerned myself about this function, wasn't sure what all kind of names
the library can have, so I just put some default matching expression.
Will change to what you proposed.

> +
> +# find the udev library
> +libudev = find_library("udev")
> +
> +if not libudev or not os.path.exists(libudev):
> + raise ImportError, "No library named %s" % libudev
> +
> +# load the udev library
> +libudev = CDLL(libudev)
> +
> +
> +# create aliases for needed functions and set the return types where needed
> +libudev_udev_new = libudev.udev_new
> +libudev_udev_unref = libudev.udev_unref
> +
> +libudev_udev_device_new_from_syspath = libudev.udev_device_new_from_syspath
> +libudev_udev_device_unref = libudev.udev_device_unref
> +
> +libudev_udev_device_get_syspath = libudev.udev_device_get_syspath
> +libudev_udev_device_get_sysname = libudev.udev_device_get_sysname
> +libudev_udev_device_get_syspath.restype = c_char_p
> +libudev_udev_device_get_sysname.restype = c_char_p
> +
> +libudev_udev_device_get_properties_list_entry = libudev.udev_device_get_properties_list_entry
> +libudev_udev_list_entry_get_next = libudev.udev_list_entry_get_next
> +
> +libudev_udev_list_entry_get_name = libudev.udev_list_entry_get_name
> +libudev_udev_list_entry_get_value = libudev.udev_list_entry_get_value
> +libudev_udev_list_entry_get_name.restype = c_char_p
> +libudev_udev_list_entry_get_value.restype = c_char_p
> +
> +libudev_udev_enumerate_new = libudev.udev_enumerate_new
> +libudev_udev_enumerate_unref = libudev.udev_enumerate_unref
> +
> +libudev_udev_enumerate_add_match_subsystem = libudev.udev_enumerate_add_match_subsystem
> +libudev_udev_enumerate_scan_devices = libudev.udev_enumerate_scan_devices
> +libudev_udev_enumerate_get_list_entry = libudev.udev_enumerate_get_list_entry
> +
> +
> +class UdevDevice(object):
> +
> + def __init__(self, udev, sysfs_path):
> + self.syspath = None
> + self.sysname = None
> + self.properties = {}
> +
> + # create new udev device from syspath
> + udev_device = libudev_udev_device_new_from_syspath(udev, sysfs_path)
> + if not udev_device:
> + # device does not exist
> + return
> +
> + # set syspath and sysname properties
> + self.syspath = libudev_udev_device_get_syspath(udev_device)
> + self.sysname = libudev_udev_device_get_sysname(udev_device)
> +
> + # get the first property entry
> + property_entry = libudev_udev_device_get_properties_list_entry(udev _device)
> +
> + while property_entry:
> + name = libudev_udev_list_entry_get_name(property_entry)
> + value = libudev_udev_list_entry_get_value(property_entry)
> +
> + # XXX we have to split some of the values into a list,
> + # the libudev is not so smart
> + fields = value.split()
> +
> + if len(fields)> 1:
> + value = [fields[0]]
> +
> + for item in fields[1:]:
> + (key, sep, val) = item.partition("=")
> + if sep:
> + value.append(val)
> +
> + if len(value) == 1:
> + value = value[0]
> +
> + self.properties[name] = value
> +
> + # get next property entry
> + property_entry = libudev_udev_list_entry_get_next(property_entry)
> +
> + # set additional properties
> + libudev.udev_device_get_devpath.restype = c_char_p
> + self.devpath = libudev.udev_device_get_devpath(udev_device)
> +
> + libudev.udev_device_get_subsystem.restype = c_char_p
> + self.subsystem = libudev.udev_device_get_subsystem(udev_device)
> +
> + libudev.udev_device_get_devtype.restype = c_char_p
> + self.devtype = libudev.udev_device_get_devtype(udev_device)
> +
> + libudev.udev_device_get_sysnum.restype = c_char_p
> + self.sysnum = libudev.udev_device_get_sysnum(udev_device)
> +
> + libudev.udev_device_get_devnode.restype = c_char_p
> + self.devnode = libudev.udev_device_get_devnode(udev_device)
> +
> + # cleanup
> + libudev_udev_device_unref(udev_device)
> +
> + def __str__(self):
> + s = "SYSPATH: %s
" % self.syspath
> + s += "SYSNAME: %s" % self.sysname
> +
> + return s
> +
> +
> +class Udev(object):
> +
> + def __init__(self):
> + self.udev = libudev_udev_new()
> +
> + def create_device(self, sysfs_path):
> + return UdevDevice(self.udev, sysfs_path)
> +
> + def scan_devices(self, subsystem=None):
> + enumerate = libudev_udev_enumerate_new(self.udev)
> +
> + # add the match subsystem
> + if subsystem is not None:
> + rc = libudev_udev_enumerate_add_match_subsystem(enumera te, subsystem)
> + if not rc == 0:
> + print>> sys.stderr, "error: unable to add the match subsystem"
> + libudev_udev_enumerate_unref(enumerate)
> + return []
> +
> + # scan the devices
> + rc = libudev_udev_enumerate_scan_devices(enumerate)
> + if not rc == 0:
> + print>> sys.stderr, "error: unable to scan the devices"
> + libudev_udev_enumerate_unref(enumerate)
> + return []
> +
> + # create the list of devices
> + devices = []
> +
> + # get the first device entry
> + device_entry = libudev_udev_enumerate_get_list_entry(enumerate)
> +
> + while device_entry:
> + sysfs_path = libudev_udev_list_entry_get_name(device_entry)
> + device = self.create_device(sysfs_path)
> +
> + if device.syspath is not None:
> + devices.append(device)
> +
> + # get next device entry
> + device_entry = libudev_udev_list_entry_get_next(device_entry)
> +
> + # cleanup
> + libudev_udev_enumerate_unref(enumerate)
> +
> + return devices
> +
> + def teardown(self):
> + libudev_udev_unref(self.udev)

You will want to set self.udev to None after this, and check all other uses for it to be not None, other
wise you may cause a segfault (referring freed memory) !

Will do.

Thanks for the review.


Regards,

Hana

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-11-2009, 07:24 AM
Martin Gracik
 
Default Added the libudev python bindings

Hello,

please see my answers below.

--

Martin Gracik

----- "Chris Lumens" <clumens@redhat.com> wrote:

> > Also rewrote the baseudev.py to use the bindings instead of parsing
> > the udev db file
> > ---
> > baseudev.py | 66 ++++++----------------
> > pyudev.py | 180
> ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++
> > 2 files changed, 197 insertions(+), 49 deletions(-)
>
> Will pyudev.py eventually be moving into another package, like
> perhaps
> in the udev package or one of its subpackages?

Well, I cannot answer this now, because I don't know if we want
this to happen, but I think it is possible. Later maybe I would like
to add code for udevadm settle and trigger, but as I was looking at
the C code for that, it's not so trivial, so I didn't want to break
anything for RHEL6. Maybe I can do it later, for F13, and when it
will have more features, we can move it to some other package.
Didn't want to make a lot of different changes to break some things now.

>
> > @@ -43,20 +44,24 @@ def udev_get_device(sysfs_path):
> > log.debug("%s does not exist" % sysfs_path)
> > return None
> >
> > - db_entry = sysfs_path.replace("/", "x2f")
> > - db_root = "/dev/.udev/db"
> > - db_path = os.path.normpath("%s/%s" % (db_root, db_entry))
> > - if not os.access(db_path, os.R_OK):
> > - log.debug("db entry %s does not exist" % db_path)
> > - return None
> > + udev = pyudev.Udev()
> > + # XXX we remove the /sys part when enumerating devices,
> > + # so we have to prepend it when creating the device
> > + udev_device = udev.create_device("/sys" + sysfs_path)
> > + udev.teardown()
> > +
> > + dev = udev_device.properties
> > +
> > + if dev:
> > + # remove the /dev/ part from beginning of devnode and set
> as name
> > + dev["name"] = udev_device.devnode[5:]
> > +
> > + dev["symlinks"] = dev["DEVLINKS"].split()
> > + dev["sysfs_path"] = sysfs_path
> >
> > - entry = open(db_path).read()
> > - dev = udev_parse_entry(entry)
> > - if dev.has_key("name"):
> > - dev['sysfs_path'] = sysfs_path
> > + # now add in the contents of the uevent file since they're
> handy
> > dev = udev_parse_uevent_file(dev)
> >
> > - # now add in the contents of the uevent file since they're
> handy
> > return dev
> >
> > def udev_get_devices(deviceClass="block"):
>
> We could potentially be calling udev_get_device() on several hundreds
> or
> even thousands of devices, which is a whole lot of:
>
> pyudev.Udev()
> udev.create_device()
> udev.teardown()
>
> Can we instead only call pyudev.Udev() once and stash an instance to
> the
> object as a global in baseudev.py? I'm concerned that we're going to
> be
> introducing another big slowdown here in device discovery on very
> large
> configurations.

Propably you're right, I have no idea how many times this get's called.
What I wanted to do first was rewrite the whole baseudev.py to something "better".
But Hans stopped me, to rewrite just needed parts, not to break anything.
So I was just trying to make as little changes as possible to the original code.

My concern is, where will we call the "teardown" method?

>
> > +# XXX this one may need some tweaking...
> > +def find_library(name):
> > + libdirs = ("/lib64", "/lib")
> > +
> > + for dir in libdirs:
> > + files = fnmatch.filter(os.listdir(dir), "*%s*" % name)
> > + files = [os.path.join(dir, file) for file in files]
> > +
> > + if files:
> > + break
> > +
> > + if files:
> > + return files[0]
> > + else:
> > + return None
>
> Would be nice to search the $LD_LIBRARY_PATH, since we use that to do
> updates.img stuff:
>
> def find_library(name):
> env = os.environ.get("LD_LIBRARY_PATH")
> if env:
> libdirs = env.split(":")
> else:
> libdirs = ["/lib64", "/lib"]
>
> for dir in libdirs:
> ...

Will add this to the function, along with the changes Hans proposed.

>
> > +class UdevDevice(object):
>
> If you really want to be fancy, you can add some special methods to
> UdevDevice so people don't have to go through the properties dict to
> access udev properties. You could do something like the following:
>
> def __getattr__(self, name):
> return self.properties[name]
>
> Then it's just a simple matter of calling it like this:
>
> udev_device = udev.create_device("/sys" + sysfs_path)
> print udev_device["symlinks"]
> print udev_device["busted"]
>
> The first print will return udev_device.properties["symlinks"]. The
> second will raise AttributeError, like you'd expect. I think doing
> something like this makes for a much nicer API and hides some of the
> implementation details.
>
> More information at:
>
>
> http://docs.python.org/reference/datamodel.html?highlight=__get__#customizing-attribute-access

I know about this python feature. But when using this, I would have to return the whole UdevDevice object,
from the udev_get_devices().
Because the old functions return just a dict, with properties. Will this be OK ?

>
> > +class Udev(object):
> > +
> > + def __init__(self):
> > + self.udev = libudev_udev_new()
> > +
> > + def create_device(self, sysfs_path):
> > + return UdevDevice(self.udev, sysfs_path)
> > +
> > + def scan_devices(self, subsystem=None):
> > + enumerate = libudev_udev_enumerate_new(self.udev)
>
> scan_devices could take a while, I suppose. Is it worth making it
> return an iterator so it doesn't have to grab everything all at once
> and
> can instead grab each individual device as needed? Or am I just
> making
> things too complicated now?

Don't have much experience with iterators. I will take a look at that.

>
> > + # add the match subsystem
> > + if subsystem is not None:
> > + rc =
> libudev_udev_enumerate_add_match_subsystem(enumera te, subsystem)
> > + if not rc == 0:
> > + print >> sys.stderr, "error: unable to add the
> match subsystem"
> > + libudev_udev_enumerate_unref(enumerate)
> > + return []
>
> print is a function in python3 (also python2.6?). For future
> compatibility, you'll want to:
>
> print("error: whatever", file=sys.stderr)

OK, I will change the print stuff.

>
> One final minor point: This is a fairly low level module in
> anaconda.
> Whenever we add new things like this, it's good to think about how it
> could be automatically tested so we have more trust in what gets
> built
> on top of it. Have you given any thought to this yet?'

OK, I will try to come up with something. But I don't promise anything yet

>
> Overall, looks interesting.
>
> - Chris

Thank you for the comments.

>
> _______________________________________________
> Anaconda-devel-list mailing list
> Anaconda-devel-list@redhat.com
> https://www.redhat.com/mailman/listinfo/anaconda-devel-list

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-11-2009, 07:50 AM
Hans de Goede
 
Default Added the libudev python bindings

Hi,

On 11/11/2009 09:24 AM, Martin Gracik wrote:

Hello,


Can we instead only call pyudev.Udev() once and stash an instance to
the
object as a global in baseudev.py? I'm concerned that we're going to
be
introducing another big slowdown here in device discovery on very
large
configurations.


Propably you're right, I have no idea how many times this get's called.
What I wanted to do first was rewrite the whole baseudev.py to something "better".
But Hans stopped me, to rewrite just needed parts, not to break anything.
So I was just trying to make as little changes as possible to the original code.

My concern is, where will we call the "teardown" method?



We would not, we would do one call to libudev_new_udev on import of pyudev.py,
and then never release it, its memory will then get returned to the OS
when python exits.

Regards,

Hans

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-11-2009, 07:55 AM
Martin Gracik
 
Default Added the libudev python bindings

Ok, so then I can remove the whole method, and we have no problem with the naming

--

Martin Gracik

----- "Hans de Goede" <hdegoede@redhat.com> wrote:

> Hi,
>
> On 11/11/2009 09:24 AM, Martin Gracik wrote:
> > Hello,
> >
> >> Can we instead only call pyudev.Udev() once and stash an instance
> to
> >> the
> >> object as a global in baseudev.py? I'm concerned that we're going
> to
> >> be
> >> introducing another big slowdown here in device discovery on very
> >> large
> >> configurations.
> >
> > Propably you're right, I have no idea how many times this get's
> called.
> > What I wanted to do first was rewrite the whole baseudev.py to
> something "better".
> > But Hans stopped me, to rewrite just needed parts, not to break
> anything.
> > So I was just trying to make as little changes as possible to the
> original code.
> >
> > My concern is, where will we call the "teardown" method?
> >
>
> We would not, we would do one call to libudev_new_udev on import of
> pyudev.py,
> and then never release it, its memory will then get returned to the
> OS
> when python exits.
>
> Regards,
>
> Hans
>
> _______________________________________________
> Anaconda-devel-list mailing list
> Anaconda-devel-list@redhat.com
> https://www.redhat.com/mailman/listinfo/anaconda-devel-list

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-11-2009, 08:03 AM
Martin Gracik
 
Default Added the libudev python bindings

> > +class Udev(object):
> > +
> > + def __init__(self):
> > + self.udev = libudev_udev_new()
> > +
> > + def create_device(self, sysfs_path):
> > + return UdevDevice(self.udev, sysfs_path)
> > +
> > + def scan_devices(self, subsystem=None):
> > + enumerate = libudev_udev_enumerate_new(self.udev)
>
> scan_devices could take a while, I suppose. Is it worth making it
> return an iterator so it doesn't have to grab everything all at once
> and
> can instead grab each individual device as needed? Or am I just
> making
> things too complicated now?


For now, we are not even using this scan_devices function.
We're using the udev_enumerate_devices from baseudev, and using the bindings
just to get the properties, not to build the whole devices list,
because maybe it returns different devices, than the old function,
so we don't want to break anything that's not broken.

I know you love iterators so I can think about it later, for F13 maybe?
When we will want to use it instead of listing the files in /sys.

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-11-2009, 08:34 AM
Hans de Goede
 
Default Added the libudev python bindings

Hi,

On 11/11/2009 09:09 AM, Martin Gracik wrote:

Hi,

please check my answers below your suggestions.


+ # so we have to prepend it when creating the device
+ udev_device = udev.create_device("/sys" + sysfs_path)
+ udev.teardown()
+


Not sure if I like the teardown() name, this implies that after this
things can be setup again, which is not true.

Ok, I have no problem with changing the name, suggest one for me that would be appropriate.



delete or unref ?



+ dev = udev_device.properties
+
+ if dev:
+ # remove the /dev/ part from beginning of devnode and set as name
+ dev["name"] = udev_device.devnode[5:]
+


This is not correct, this will return mapper/Volgroup-Volname for LV's where it
should return just Volgroup-Volname, please find out which libudev function return
the actual internal libudev name and use that instead.

It is correct if we want to have the same stuff the old baseudev.py returns.
I was using just the SYSNAME, but that's dm-0, dm-1 etc. But the old baseudev.py returns mapper/vg-something,
so I checked all the properties, and libudev functions, and the only one getting this kind of output was
devnode(), but with the /dev/ at the start.



Actually we were both wrong, I just checked and for device mapper stuff the name in the dict should be just
dm-#, so you should just use SYSNAME, which makes sense.

Regards,

Hans

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 11-11-2009, 01:47 PM
Martin Gracik
 
Default Added the libudev python bindings

Also rewrote the baseudev.py to use the bindings instead of parsing
the udev db file
---
baseudev.py | 60 ++++---------------
pyudev.py | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++
2 files changed, 203 insertions(+), 48 deletions(-)
create mode 100644 pyudev.py

diff --git a/baseudev.py b/baseudev.py
index 76b14cc..d88d7fc 100644
--- a/baseudev.py
+++ b/baseudev.py
@@ -24,6 +24,9 @@
import iutil
import os

+import pyudev
+global_udev = pyudev.Udev()
+
import logging
log = logging.getLogger("storage")

@@ -43,20 +46,18 @@ def udev_get_device(sysfs_path):
log.debug("%s does not exist" % sysfs_path)
return None

- db_entry = sysfs_path.replace("/", "x2f")
- db_root = "/dev/.udev/db"
- db_path = os.path.normpath("%s/%s" % (db_root, db_entry))
- if not os.access(db_path, os.R_OK):
- log.debug("db entry %s does not exist" % db_path)
- return None
+ # XXX we remove the /sys part when enumerating devices,
+ # so we have to prepend it when creating the device
+ dev = global_udev.create_device("/sys" + sysfs_path)
+
+ if dev:
+ dev["name"] = dev.sysname
+ dev["symlinks"] = dev["DEVLINKS"]
+ dev["sysfs_path"] = sysfs_path

- entry = open(db_path).read()
- dev = udev_parse_entry(entry)
- if dev.has_key("name"):
- dev['sysfs_path'] = sysfs_path
+ # now add in the contents of the uevent file since they're handy
dev = udev_parse_uevent_file(dev)

- # now add in the contents of the uevent file since they're handy
return dev

def udev_get_devices(deviceClass="block"):
@@ -68,43 +69,6 @@ def udev_get_devices(deviceClass="block"):
entries.append(entry)
return entries

-def udev_parse_entry(buf):
- dev = {}
-
- for line in buf.splitlines():
- line.strip()
- (tag, sep, val) = line.partition(":")
- if not sep:
- continue
-
- if tag == "N":
- dev['name'] = val
- elif tag == "S":
- if dev.has_key('symlinks'):
- dev['symlinks'].append(val)
- else:
- dev['symlinks'] = [val]
- elif tag == "E":
- if val.count("=") > 1 and val.count(" ") > 0:
- # eg: LVM2_LV_NAME when querying the VG for its LVs
- vars = val.split()
- vals = []
- var_name = None
- for (index, subval) in enumerate(vars):
- (var_name, sep, var_val) = subval.partition("=")
- if sep:
- vals.append(var_val)
-
- dev[var_name] = vals
- else:
- (var_name, sep, var_val) = val.partition("=")
- if not sep:
- continue
-
- dev[var_name] = var_val
-
- return dev
-
def udev_parse_uevent_file(dev):
path = os.path.normpath("/sys/%s/uevent" % dev['sysfs_path'])
if not os.access(path, os.R_OK):
diff --git a/pyudev.py b/pyudev.py
new file mode 100644
index 0000000..b6e5ea9
--- /dev/null
+++ b/pyudev.py
@@ -0,0 +1,191 @@
+from __future__ import print_function
+
+import sys
+import os
+import fnmatch
+from ctypes import *
+
+
+# XXX this one may need some tweaking...
+def find_library(name, somajor=0):
+ env = os.environ.get("LD_LIBRARY_PATH")
+
+ if env:
+ libdirs = env.split(":")
+ else:
+ libdirs = ["/lib64", "/lib"]
+
+ libdirs = filter(os.path.isdir, libdirs)
+
+ for dir in libdirs:
+ files = fnmatch.filter(os.listdir(dir), "lib%s.so.%d" % (name, somajor))
+ files = [os.path.join(dir, file) for file in files]
+
+ if files:
+ break
+
+ if files:
+ return files[0]
+ else:
+ return None
+
+
+# find the udev library
+libudev = find_library(name="udev", somajor=0)
+
+if not libudev or not os.path.exists(libudev):
+ raise ImportError, "No library named %s" % libudev
+
+# load the udev library
+libudev = CDLL(libudev)
+
+
+# create aliases for needed functions and set the return types where needed
+libudev_udev_new = libudev.udev_new
+libudev_udev_unref = libudev.udev_unref
+
+libudev_udev_device_new_from_syspath = libudev.udev_device_new_from_syspath
+libudev_udev_device_unref = libudev.udev_device_unref
+
+libudev_udev_device_get_syspath = libudev.udev_device_get_syspath
+libudev_udev_device_get_sysname = libudev.udev_device_get_sysname
+libudev_udev_device_get_syspath.restype = c_char_p
+libudev_udev_device_get_sysname.restype = c_char_p
+
+libudev_udev_device_get_properties_list_entry = libudev.udev_device_get_properties_list_entry
+libudev_udev_list_entry_get_next = libudev.udev_list_entry_get_next
+
+libudev_udev_list_entry_get_name = libudev.udev_list_entry_get_name
+libudev_udev_list_entry_get_value = libudev.udev_list_entry_get_value
+libudev_udev_list_entry_get_name.restype = c_char_p
+libudev_udev_list_entry_get_value.restype = c_char_p
+
+libudev_udev_enumerate_new = libudev.udev_enumerate_new
+libudev_udev_enumerate_unref = libudev.udev_enumerate_unref
+
+libudev_udev_enumerate_add_match_subsystem = libudev.udev_enumerate_add_match_subsystem
+libudev_udev_enumerate_scan_devices = libudev.udev_enumerate_scan_devices
+libudev_udev_enumerate_get_list_entry = libudev.udev_enumerate_get_list_entry
+
+
+class UdevDevice(dict):
+
+ def __init__(self, udev, sysfs_path):
+ dict.__init__(self)
+
+ # create new udev device from syspath
+ udev_device = libudev_udev_device_new_from_syspath(udev, sysfs_path)
+ if not udev_device:
+ # device does not exist
+ return
+
+ # set syspath and sysname properties
+ self.syspath = libudev_udev_device_get_syspath(udev_device)
+ self.sysname = libudev_udev_device_get_sysname(udev_device)
+
+ # get the first property entry
+ property_entry = libudev_udev_device_get_properties_list_entry(udev _device)
+
+ while property_entry:
+ name = libudev_udev_list_entry_get_name(property_entry)
+ value = libudev_udev_list_entry_get_value(property_entry)
+
+ # XXX we have to split some of the values into a list,
+ # the libudev is not so smart
+ fields = value.split()
+
+ if len(fields) > 1:
+ value = [fields[0]]
+
+ for item in fields[1:]:
+ (key, sep, val) = item.partition("=")
+ if sep:
+ value.append(val)
+ else:
+ value.append(key)
+
+ if len(value) == 1:
+ value = value[0]
+
+ self[name] = value
+
+ # get next property entry
+ property_entry = libudev_udev_list_entry_get_next(property_entry)
+
+ # set additional properties
+ libudev.udev_device_get_devpath.restype = c_char_p
+ self.devpath = libudev.udev_device_get_devpath(udev_device)
+
+ libudev.udev_device_get_subsystem.restype = c_char_p
+ self.subsystem = libudev.udev_device_get_subsystem(udev_device)
+
+ libudev.udev_device_get_devtype.restype = c_char_p
+ self.devtype = libudev.udev_device_get_devtype(udev_device)
+
+ libudev.udev_device_get_sysnum.restype = c_char_p
+ self.sysnum = libudev.udev_device_get_sysnum(udev_device)
+
+ libudev.udev_device_get_devnode.restype = c_char_p
+ self.devnode = libudev.udev_device_get_devnode(udev_device)
+
+ # cleanup
+ libudev_udev_device_unref(udev_device)
+
+
+class Udev(object):
+
+ def __init__(self):
+ self.udev = libudev_udev_new()
+
+ def create_device(self, sysfs_path):
+ return UdevDevice(self.udev, sysfs_path)
+
+ def enumerate_devices(self, subsystem=None):
+ enumerate = libudev_udev_enumerate_new(self.udev)
+
+ # add the match subsystem
+ if subsystem is not None:
+ rc = libudev_udev_enumerate_add_match_subsystem(enumera te, subsystem)
+ if not rc == 0:
+ print("error: unable to add the match subsystem", file=sys.stderr)
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # scan the devices
+ rc = libudev_udev_enumerate_scan_devices(enumerate)
+ if not rc == 0:
+ print("error: unable to enumerate the devices", file=sys.stderr)
+ libudev_udev_enumerate_unref(enumerate)
+ return []
+
+ # create the list of sysfs paths
+ sysfs_paths = []
+
+ # get the first list entry
+ list_entry = libudev_udev_enumerate_get_list_entry(enumerate)
+
+ while list_entry:
+ sysfs_path = libudev_udev_list_entry_get_name(list_entry)
+ sysfs_paths.append(sysfs_path)
+
+ # get next list entry
+ list_entry = libudev_udev_list_entry_get_next(list_entry)
+
+ # cleanup
+ libudev_udev_enumerate_unref(enumerate)
+
+ return sysfs_paths
+
+ def scan_devices(self, sysfs_paths=None):
+ if sysfs_paths is None:
+ sysfs_paths = self.enumerate_devices()
+
+ for sysfs_path in sysfs_paths:
+ device = self.create_device(sysfs_path)
+
+ if device:
+ yield device
+
+ def unref(self):
+ libudev_udev_unref(self.udev)
+ self.udev = None
--
1.6.5.2

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 

Thread Tools




All times are GMT. The time now is 11:03 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright 2007 - 2008, www.linux-archive.org