========
Bugfixes.
+A shared library "libudev" will be installed now, to access udev
+device information. DeviceKit, the successor of HAL, will need this
+library to access the udev database and search sysfs for devices.
+The library is currently in an experimental state, also the API is
+expected to change, as long as the DeviceKit integration isn't
+finished.
+
udev 126
========
We use ./configure now. See INSTALL for details. Current
libudev-utils.c \
libudev-device.c \
libudev-enumerate.c \
+ libudev-monitor.c \
../list.h \
../udev.h \
../udev_utils.c \
udev_device_get_devlinks
udev_device_get_properties
udev_devices_enumerate
+udev_monitor_new_from_socket
+udev_monitor_ref
+udev_monitor_unref
+udev_monitor_get_udev
+udev_monitor_get_fd
+udev_monitor_get_device
#include "libudev-private.h"
#include "../udev.h"
-static struct udev_device *device_init(struct udev *udev)
+struct udev_device *device_init(struct udev *udev)
{
struct udev_device *udev_device;
+ if (udev == NULL)
+ return NULL;
+
udev_device = malloc(sizeof(struct udev_device));
if (udev_device == NULL)
return NULL;
memset(udev_device, 0x00, sizeof(struct udev_device));
udev_device->refcount = 1;
udev_device->udev = udev;
+ INIT_LIST_HEAD(&udev_device->link_list);
+ INIT_LIST_HEAD(&udev_device->env_list);
+ log_info(udev_device->udev, "udev_device: %p created\n", udev_device);
return udev_device;
}
char path[PATH_SIZE];
struct stat statbuf;
struct udev_device *udev_device;
+ struct udevice *udevice;
+ struct name_entry *name_loop;
int err;
+ if (udev == NULL)
+ return NULL;
+ if (devpath == NULL)
+ return NULL;
+
strlcpy(path, udev_get_sys_path(udev), sizeof(path));
strlcat(path, devpath, sizeof(path));
if (stat(path, &statbuf) != 0)
if (udev_device == NULL)
return NULL;
- udev_device->udevice = udev_device_init(NULL);
- if (udev_device->udevice == NULL) {
+ udevice = udev_device_init(NULL);
+ if (udevice == NULL) {
free(udev_device);
return NULL;
}
- log_info(udev, "device %p created\n", udev_device);
+ /* resolve possible symlink to real path */
strlcpy(path, devpath, sizeof(path));
sysfs_resolve_link(path, sizeof(path));
+ udev_device->devpath = strdup(path);
+ log_info(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device));
- err = udev_db_get_device(udev_device->udevice, path);
+ err = udev_db_get_device(udevice, path);
if (err >= 0)
log_info(udev, "device %p filled with udev database data\n", udev_device);
- log_info(udev, "device %p filled with %s data\n", udev_device, udev_device_get_devpath(udev_device));
+
+ if (udevice->name[0] != '\0')
+ asprintf(&udev_device->devname, "%s/%s", udev_get_dev_path(udev), udevice->name);
+
+ list_for_each_entry(name_loop, &udevice->symlink_list, node) {
+ char name[PATH_SIZE];
+
+ strlcpy(name, udev_get_dev_path(udev), sizeof(name));
+ strlcat(name, "/", sizeof(name));
+ strlcat(name, name_loop->name, sizeof(name));
+ name_list_add(&udev_device->link_list, name, 0);
+ }
+
+ list_for_each_entry(name_loop, &udevice->env_list, node)
+ name_list_add(&udev_device->env_list, name_loop->name, 0);
+
+ udev_device_cleanup(udevice);
return udev_device;
}
**/
struct udev *udev_device_get_udev(struct udev_device *udev_device)
{
+ if (udev_device == NULL)
+ return NULL;
return udev_device->udev;
}
**/
struct udev_device *udev_device_ref(struct udev_device *udev_device)
{
+ if (udev_device == NULL)
+ return NULL;
udev_device->refcount++;
return udev_device;
}
**/
void udev_device_unref(struct udev_device *udev_device)
{
+ if (udev_device == NULL)
+ return;
udev_device->refcount--;
if (udev_device->refcount > 0)
return;
- udev_device_cleanup(udev_device->udevice);
+ free(udev_device->devpath);
+ free(udev_device->devname);
+ free(udev_device->subsystem);
+ name_list_cleanup(&udev_device->link_list);
+ name_list_cleanup(&udev_device->env_list);
+ log_info(udev_device->udev, "udev_device: %p released\n", udev_device);
free(udev_device);
}
**/
const char *udev_device_get_devpath(struct udev_device *udev_device)
{
- return udev_device->udevice->dev->devpath;
+ if (udev_device == NULL)
+ return NULL;
+ return udev_device->devpath;
}
/**
* @udev_device: udev device
*
* Retrieve the device node file name belonging to the udev device.
- * The path does not contain the device directory, and does not contain
- * a leading '/'.
+ * The path is an absolute path, and starts with the device directory.
*
* Returns: the device node file name of the udev device, or #NULL if no device node exists
**/
const char *udev_device_get_devname(struct udev_device *udev_device)
{
- if (udev_device->udevice->name[0] == '\0')
+ if (udev_device == NULL)
return NULL;
- return udev_device->udevice->name;
+ return udev_device->devname;
}
/**
**/
const char *udev_device_get_subsystem(struct udev_device *udev_device)
{
- struct sysfs_device *dev = udev_device->udevice->dev;
- if (dev->subsystem[0] != '\0')
- return dev->subsystem;
- if (util_get_sys_subsystem(udev_device->udev, dev->devpath,
- dev->subsystem, sizeof(dev->subsystem)) < 2)
+ char subsystem[NAME_SIZE];
+
+ if (udev_device == NULL)
+ return NULL;
+ if (udev_device->subsystem != NULL)
+ return udev_device->subsystem;
+ if (util_get_sys_subsystem(udev_device->udev, udev_device->devpath, subsystem, sizeof(subsystem)) < 2)
return NULL;
- return dev->subsystem;
+ udev_device->subsystem = strdup(subsystem);
+ return udev_device->subsystem;
}
/**
*
* Retrieve the device links pointing to the device file of the
* udev device. For every device link, the passed function will be
- * called with the device link string. If the function returns 1,
- * remaning device links will be ignored. The device link path
- * does not contain the device directory, and does not contain
- * a leading '/'.
+ * called with the device link string.
+ * The path is an absolute path, and starts with the device directory.
+ * If the function returns 1, remaning device links will be ignored.
*
* Returns: the number of device links passed to the caller, or a negative value on error
**/
struct name_entry *name_loop;
int count = 0;
- list_for_each_entry(name_loop, &udev_device->udevice->symlink_list, node) {
+ if (udev_device == NULL)
+ return -1;
+ list_for_each_entry(name_loop, &udev_device->link_list, node) {
count++;
if (cb(udev_device, name_loop->name, data) != 0)
break;
struct name_entry *name_loop;
int count = 0;
- list_for_each_entry(name_loop, &udev_device->udevice->env_list, node) {
+ if (udev_device == NULL)
+ return -1;
+ list_for_each_entry(name_loop, &udev_device->env_list, node) {
char name[PATH_SIZE];
char *val;
continue;
val[0] = '\0';
val = &val[1];
+ count++;
if (cb(udev_device, name, val, data) != 0)
break;
}
--- /dev/null
+/*
+ * libudev - interface to udev device information
+ *
+ * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "libudev.h"
+#include "libudev-private.h"
+#include "../udev.h"
+
+struct udev_monitor {
+ struct udev *udev;
+ int refcount;
+ int socket;
+};
+
+struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
+{
+ struct udev_monitor *udev_monitor;
+ struct sockaddr_un saddr;
+ socklen_t addrlen;
+ const int on = 1;
+
+ if (udev == NULL)
+ return NULL;
+ if (socket_path == NULL)
+ return NULL;
+ udev_monitor = malloc(sizeof(struct udev_monitor));
+ if (udev_monitor == NULL)
+ return NULL;
+ memset(udev_monitor, 0x00, sizeof(struct udev_monitor));
+ udev_monitor->refcount = 1;
+ udev_monitor->udev = udev;
+
+ memset(&saddr, 0x00, sizeof(saddr));
+ saddr.sun_family = AF_LOCAL;
+ strcpy(saddr.sun_path, socket_path);
+ addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path);
+
+ /* translate leading '@' to abstract namespace */
+ if (saddr.sun_path[0] == '@')
+ saddr.sun_path[0] = '\0';
+
+ udev_monitor->socket = socket(AF_LOCAL, SOCK_DGRAM, 0);
+ if (udev_monitor->socket == -1) {
+ log_err(udev, "error getting socket: %s\n", strerror(errno));
+ free(udev_monitor);
+ return NULL;
+ }
+
+ if (bind(udev_monitor->socket, (struct sockaddr *) &saddr, addrlen) < 0) {
+ log_err(udev, "bind failed: %s\n", strerror(errno));
+ close(udev_monitor->socket);
+ free(udev_monitor);
+ return NULL;
+ }
+
+ /* enable receiving of the sender credentials */
+ setsockopt(udev_monitor->socket, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ log_info(udev_monitor->udev, "udev_monitor: %p created\n", udev_monitor);
+
+ return udev_monitor;
+}
+
+struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return NULL;
+ udev_monitor->refcount++;
+ return udev_monitor;
+}
+
+void udev_monitor_unref(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return;
+ udev_monitor->refcount--;
+ if (udev_monitor->refcount > 0)
+ return;
+ close(udev_monitor->socket);
+ log_info(udev_monitor->udev, "udev_monitor: %p released\n", udev_monitor);
+ free(udev_monitor);
+}
+
+struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return NULL;
+ return udev_monitor->udev;
+}
+
+int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return -1;
+ return udev_monitor->socket;
+}
+
+struct udev_device *udev_monitor_get_device(struct udev_monitor *udev_monitor)
+{
+ struct udev_device *udev_device;
+ struct msghdr smsg;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct ucred *cred;
+ char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+ char buf[4096];
+ size_t bufpos;
+
+ if (udev_monitor == NULL)
+ return NULL;
+ memset(buf, 0x00, sizeof(buf));
+ iov.iov_base = &buf;
+ iov.iov_len = sizeof(buf);
+ memset (&smsg, 0x00, sizeof(struct msghdr));
+ smsg.msg_iov = &iov;
+ smsg.msg_iovlen = 1;
+ smsg.msg_control = cred_msg;
+ smsg.msg_controllen = sizeof(cred_msg);
+
+ if (recvmsg(udev_monitor->socket, &smsg, 0) < 0) {
+ if (errno != EINTR)
+ log_info(udev_monitor->udev, "unable to receive message");
+ return NULL;
+ }
+ cmsg = CMSG_FIRSTHDR(&smsg);
+ cred = (struct ucred *)CMSG_DATA (cmsg);
+
+ if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ log_info(udev_monitor->udev, "no sender credentials received, message ignored");
+ return NULL;
+ }
+
+ if (cred->uid != 0) {
+ log_info(udev_monitor->udev, "sender uid=%d, message ignored", cred->uid);
+ return NULL;
+ }
+
+ /* skip header */
+ bufpos = strlen(buf) + 1;
+ if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
+ log_info(udev_monitor->udev, "invalid message length");
+ return NULL;
+ }
+
+ /* check message header */
+ if (strstr(buf, "@/") == NULL) {
+ log_info(udev_monitor->udev, "unrecognized message header");
+ return NULL;
+ }
+
+ udev_device = device_init(udev_monitor->udev);
+ if (udev_device == NULL) {
+ return NULL;
+ }
+
+ while (bufpos < sizeof(buf)) {
+ char *key;
+ size_t keylen;
+
+ key = &buf[bufpos];
+ keylen = strlen(key);
+ if (keylen == 0)
+ break;
+ bufpos += keylen + 1;
+
+ if (strncmp(key, "DEVPATH=", 8) == 0) {
+ udev_device->devpath = strdup(&key[8]);
+ } else if (strncmp(key, "SUBSYSTEM=", 10) == 0) {
+ udev_device->subsystem = strdup(&key[10]);
+ } else if (strncmp(key, "DEVNAME=", 8) == 0) {
+ udev_device->devname = strdup(&key[8]);
+ } else if (strncmp(key, "DEVLINKS=", 9) == 0) {
+ char *slink = &key[9];
+ char *next = strchr(slink, ' ');
+
+ while (next != NULL) {
+ next[0] = '\0';
+ name_list_add(&udev_device->link_list, slink, 0);
+ slink = &next[1];
+ next = strchr(slink, ' ');
+ }
+ if (slink[0] != '\0')
+ name_list_add(&udev_device->link_list, slink, 0);
+ }
+ name_list_add(&udev_device->env_list, key, 0);
+ }
+
+ return udev_device;
+}
#include "libudev.h"
#include "../udev.h"
-struct udev {
- int refcount;
- void (*log_fn)(struct udev *udev,
- int priority, const char *file, int line, const char *fn,
- const char *format, va_list args);
-};
-
struct udev_device {
int refcount;
struct udev *udev;
- struct udevice *udevice;
+ char *devpath;
+ char *devname;
+ char *subsystem;
+ struct list_head link_list;
+ struct list_head env_list;
};
#ifdef USE_LOG
__attribute__ ((format(printf, 6, 7))) {}
#endif
+extern struct udev_device *device_init(struct udev *udev);
extern ssize_t util_get_sys_subsystem(struct udev *udev, const char *devpath, char *subsystem, size_t size);
#endif
#include "libudev-private.h"
#include "../udev.h"
+struct udev {
+ int refcount;
+ void (*log_fn)(struct udev *udev,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, va_list args);
+};
+
void udev_log(struct udev *udev,
int priority, const char *file, int line, const char *fn,
const char *format, ...)
**/
struct udev *udev_ref(struct udev *udev)
{
+ if (udev == NULL)
+ return NULL;
udev->refcount++;
return udev;
}
**/
void udev_unref(struct udev *udev)
{
+ if (udev == NULL)
+ return;
udev->refcount--;
if (udev->refcount > 0)
return;
**/
const char *udev_get_sys_path(struct udev *udev)
{
+ if (udev == NULL)
+ return NULL;
return sysfs_path;
}
**/
const char *udev_get_dev_path(struct udev *udev)
{
+ if (udev == NULL)
+ return NULL;
return udev_root;
}
struct udev;
struct udev_device;
+struct udev_monitor;
extern struct udev *udev_new(void);
extern struct udev *udev_ref(struct udev *udev);
const char *devpath, const char *subsystem, const char *name, void *data),
void *data);
+extern struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path);
+extern struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor);
+extern void udev_monitor_unref(struct udev_monitor *udev_monitor);
+extern struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor);
+extern int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
+extern struct udev_device *udev_monitor_get_device(struct udev_monitor *udev_monitor);
+
#endif
#include <stdio.h>
#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+
#include "libudev.h"
static void log_fn(struct udev *udev,
vprintf(format, args);
}
-static int devlinks_cb(struct udev_device *udev_device, const char *value, void *data)
+static int print_devlinks_cb(struct udev_device *udev_device, const char *value, void *data)
{
- printf("link: %s\n", value);
+ printf("link: '%s'\n", value);
+ return 0;
+}
+
+static int print_properties_cb(struct udev_device *udev_device, const char *key, const char *value, void *data)
+{
+ printf("property: '%s=%s'\n", key, value);
+ return 0;
+}
+
+static void print_device(struct udev_device *device)
+{
+ const char *str;
+ int count;
+
+ printf("*** device: %p ***\n", device);
+ str = udev_device_get_devpath(device);
+ printf("devpath: '%s'\n", str);
+ str = udev_device_get_subsystem(device);
+ printf("subsystem: '%s'\n", str);
+ str = udev_device_get_devname(device);
+ printf("devname: '%s'\n", str);
+ count = udev_device_get_devlinks(device, print_devlinks_cb, NULL);
+ printf("found %i links\n", count);
+ count = udev_device_get_properties(device, print_properties_cb, NULL);
+ printf("found %i properties\n", count);
+ printf("\n");
+}
+
+static int test_device(struct udev *udev, const char *devpath)
+{
+ struct udev_device *device;
+
+ printf("looking at device: %s\n", devpath);
+ device = udev_device_new_from_devpath(udev, devpath);
+ if (device == NULL) {
+ printf("no device\n");
+ return -1;
+ }
+ print_device(device);
+ udev_device_unref(device);
return 0;
}
const char *devpath, const char *subsystem, const char *name,
void *data)
{
- printf("device: %s (%s) %s\n", devpath, subsystem, name);
+ printf("device: '%s' (%s) '%s'\n", devpath, subsystem, name);
return 0;
}
-static int properties_cb(struct udev_device *udev_device, const char *key, const char *value, void *data)
+static int test_enumerate(struct udev *udev, const char *subsystem)
{
- printf("property: %s=%s\n", key, value);
+ int count;
+
+ count = udev_devices_enumerate(udev, subsystem, devices_enum_cb, NULL);
+ printf("found %i devices\n\n", count);
+ return count;
+}
+
+static int test_monitor(struct udev *udev, const char *socket_path)
+{
+ struct udev_monitor *udev_monitor;
+ fd_set readfds;
+ int fd;
+
+ udev_monitor = udev_monitor_new_from_socket(udev, socket_path);
+ if (udev_monitor == NULL) {
+ printf("no socket\n");
+ return -1;
+ }
+
+ fd = udev_monitor_get_fd(udev_monitor);
+ FD_ZERO(&readfds);
+
+ while (1) {
+ struct udev_device *device;
+ int fdcount;
+
+ FD_SET(STDIN_FILENO, &readfds);
+ FD_SET(fd, &readfds);
+
+ printf("waiting for events on %s, press ENTER to exit\n", socket_path);
+ fdcount = select(fd+1, &readfds, NULL, NULL, NULL);
+ printf("select fd count: %i\n", fdcount);
+
+ if (FD_ISSET(fd, &readfds)) {
+ device = udev_monitor_get_device(udev_monitor);
+ if (device == NULL) {
+ printf("no device from socket\n");
+ continue;
+ }
+ print_device(device);
+ udev_device_unref(device);
+ }
+
+ if (FD_ISSET(STDIN_FILENO, &readfds)) {
+ printf("exiting loop\n");
+ break;
+ }
+ }
+
+ udev_monitor_unref(udev_monitor);
return 0;
}
int main(int argc, char *argv[], char *envp[])
{
struct udev *udev;
- struct udev_device *device;
- const char *str;
const char *devpath = "/devices/virtual/mem/null";
const char *subsystem = NULL;
+ const char *socket = "@/org/kernel/udev/monitor";
+ const char *str;
if (argv[1] != NULL) {
devpath = argv[1];
if (argv[2] != NULL)
subsystem = argv[2];
+ if (argv[3] != NULL)
+ socket = argv[3];
}
udev = udev_new();
printf("set log: %p\n", log_fn);
str = udev_get_sys_path(udev);
- printf("sys_path: %s\n", str);
+ printf("sys_path: '%s'\n", str);
str = udev_get_dev_path(udev);
- printf("dev_path: %s\n", str);
-
- printf("looking at device: %s\n", devpath);
- device = udev_device_new_from_devpath(udev, devpath);
- printf("device: %p\n", device);
- if (device == NULL) {
- printf("no device\n");
- return 1;
- }
- str = udev_device_get_devpath(device);
- printf("devpath: %s\n", str);
- str = udev_device_get_subsystem(device);
- printf("subsystem: %s\n", str);
- str = udev_device_get_devname(device);
- printf("devname: %s\n", str);
- udev_device_get_devlinks(device, devlinks_cb, NULL);
- udev_device_get_properties(device, properties_cb, NULL);
- udev_device_unref(device);
+ printf("dev_path: '%s'\n", str);
- if (subsystem == NULL)
- printf("enumerating devices from all subsystems\n");
- else
- printf("enumerating devices from subsystem: %s\n", subsystem);
- udev_devices_enumerate(udev, subsystem, devices_enum_cb, NULL);
+ test_device(udev, devpath);
+ test_enumerate(udev, subsystem);
+ test_monitor(udev, socket);
udev_unref(udev);
return 0;