X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=libudev%2Flibudev-device.c;h=991398c76d4df2c22a9267c3b515658feee8395e;hp=9b5d79ff4bf7e7caadd179794fa810f2497c16e5;hb=2fdaa983a958822d79821c1afe51e22e7a3e00ff;hpb=cdb1d7608a2e2ba708a890eeab6e5e99409a1953 diff --git a/libudev/libudev-device.c b/libudev/libudev-device.c index 9b5d79ff4..991398c76 100644 --- a/libudev/libudev-device.c +++ b/libudev/libudev-device.c @@ -19,7 +19,11 @@ #include #include #include +#include #include +#include +#include +#include #include "libudev.h" #include "libudev-private.h" @@ -47,22 +51,24 @@ struct udev_device { char *sysname; const char *sysnum; char *devnode; + mode_t devnode_mode; char *subsystem; char *devtype; char *driver; char *action; char *devpath_old; - char *sysname_old; char *knodename; + char *id_filename; char **envp; char *monitor_buf; size_t monitor_buf_len; - struct udev_list_node devlinks_list; - struct udev_list_node properties_list; - struct udev_list_node sysattr_list; - struct udev_list_node tags_list; + struct udev_list devlinks_list; + struct udev_list properties_list; + struct udev_list sysattr_value_list; + struct udev_list sysattr_list; + struct udev_list tags_list; unsigned long long int seqnum; - int event_timeout; + unsigned long long int usec_initialized; int timeout; int devlink_priority; int refcount; @@ -80,8 +86,265 @@ struct udev_device { bool info_loaded; bool db_loaded; bool uevent_loaded; + bool is_initialized; + bool sysattr_list_read; + bool db_persist; }; +/** + * udev_device_get_devnum: + * @udev_device: udev device + * + * This is only valid if the device was received through a monitor. Devices read from + * sys do not have a sequence number. + * + * Returns: the kernel event sequence number, or 0 if there is no sequence number available. + **/ +UDEV_EXPORT unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return 0; + return udev_device->seqnum; +} + +static int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum) +{ + char num[32]; + + udev_device->seqnum = seqnum; + snprintf(num, sizeof(num), "%llu", seqnum); + udev_device_add_property(udev_device, "SEQNUM", num); + return 0; +} + +int udev_device_get_ifindex(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->ifindex; +} + +static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) +{ + char num[32]; + + udev_device->ifindex = ifindex; + snprintf(num, sizeof(num), "%u", ifindex); + udev_device_add_property(udev_device, "IFINDEX", num); + return 0; +} + +/** + * udev_device_get_devnum: + * @udev_device: udev device + * + * Returns: the device major/minor number. + **/ +UDEV_EXPORT dev_t udev_device_get_devnum(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return makedev(0, 0); + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->devnum; +} + +static int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) +{ + char num[32]; + + udev_device->devnum = devnum; + + snprintf(num, sizeof(num), "%u", major(devnum)); + udev_device_add_property(udev_device, "MAJOR", num); + snprintf(num, sizeof(num), "%u", minor(devnum)); + udev_device_add_property(udev_device, "MINOR", num); + return 0; +} + +int udev_device_get_timeout(struct udev_device *udev_device) +{ + return udev_device->timeout; +} + +static int udev_device_set_timeout(struct udev_device *udev_device, int timeout) +{ + char num[32]; + + udev_device->timeout = timeout; + snprintf(num, sizeof(num), "%u", timeout); + udev_device_add_property(udev_device, "TIMEOUT", num); + return 0; +} + +const char *udev_device_get_knodename(struct udev_device *udev_device) +{ + return udev_device->knodename; +} + +static int udev_device_set_knodename(struct udev_device *udev_device, const char *knodename) +{ + free(udev_device->knodename); + udev_device->knodename = strdup(knodename); + if (udev_device->knodename == NULL) + return -ENOMEM; + /* do not overwrite the udev property with the kernel property */ + if (udev_device->devnode == NULL) + udev_device_add_property(udev_device, "DEVNAME", udev_device->knodename); + return 0; +} + +const char *udev_device_get_devpath_old(struct udev_device *udev_device) +{ + return udev_device->devpath_old; +} + +static int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old) +{ + const char *pos; + + free(udev_device->devpath_old); + udev_device->devpath_old = strdup(devpath_old); + if (udev_device->devpath_old == NULL) + return -ENOMEM; + udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old); + + pos = strrchr(udev_device->devpath_old, '/'); + if (pos == NULL) + return -EINVAL; + return 0; +} + +/** + * udev_device_get_driver: + * @udev_device: udev device + * + * Returns: the driver string, or #NULL if there is no driver attached. + **/ +UDEV_EXPORT const char *udev_device_get_driver(struct udev_device *udev_device) +{ + char driver[UTIL_NAME_SIZE]; + + if (udev_device == NULL) + return NULL; + if (!udev_device->driver_set) { + udev_device->driver_set = true; + if (util_get_sys_core_link_value(udev_device->udev, "driver", udev_device->syspath, driver, sizeof(driver)) > 0) + udev_device->driver = strdup(driver); + } + return udev_device->driver; +} + +static int udev_device_set_driver(struct udev_device *udev_device, const char *driver) +{ + free(udev_device->driver); + udev_device->driver = strdup(driver); + if (udev_device->driver == NULL) + return -ENOMEM; + udev_device->driver_set = true; + udev_device_add_property(udev_device, "DRIVER", udev_device->driver); + return 0; +} + +/** + * udev_device_get_devtype: + * @udev_device: udev device + * + * Retrieve the devtype string of the udev device. + * + * Returns: the devtype name of the udev device, or #NULL if it can not be determined + **/ +UDEV_EXPORT const char *udev_device_get_devtype(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + if (!udev_device->devtype_set) { + udev_device->devtype_set = true; + udev_device_read_uevent_file(udev_device); + } + return udev_device->devtype; +} + +static int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype) +{ + free(udev_device->devtype); + udev_device->devtype = strdup(devtype); + if (udev_device->devtype == NULL) + return -ENOMEM; + udev_device->devtype_set = true; + udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype); + return 0; +} + +static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) +{ + free(udev_device->subsystem); + udev_device->subsystem = strdup(subsystem); + if (udev_device->subsystem == NULL) + return -ENOMEM; + udev_device->subsystem_set = true; + udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem); + return 0; +} + +/** + * udev_device_get_subsystem: + * @udev_device: udev device + * + * Retrieve the subsystem string of the udev device. The string does not + * contain any "/". + * + * Returns: the subsystem name of the udev device, or #NULL if it can not be determined + **/ +UDEV_EXPORT const char *udev_device_get_subsystem(struct udev_device *udev_device) +{ + char subsystem[UTIL_NAME_SIZE]; + + if (udev_device == NULL) + return NULL; + if (!udev_device->subsystem_set) { + udev_device->subsystem_set = true; + /* read "subsystem" link */ + if (util_get_sys_core_link_value(udev_device->udev, "subsystem", udev_device->syspath, subsystem, sizeof(subsystem)) > 0) { + udev_device_set_subsystem(udev_device, subsystem); + return udev_device->subsystem; + } + /* implicit names */ + if (strncmp(udev_device->devpath, "/module/", 8) == 0) { + udev_device_set_subsystem(udev_device, "module"); + return udev_device->subsystem; + } + if (strstr(udev_device->devpath, "/drivers/") != NULL) { + udev_device_set_subsystem(udev_device, "drivers"); + return udev_device->subsystem; + } + if (strncmp(udev_device->devpath, "/subsystem/", 11) == 0 || + strncmp(udev_device->devpath, "/class/", 7) == 0 || + strncmp(udev_device->devpath, "/bus/", 5) == 0) { + udev_device_set_subsystem(udev_device, "subsystem"); + return udev_device->subsystem; + } + } + return udev_device->subsystem; +} + +mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->devnode_mode; +} + +static int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t mode) +{ + char num[32]; + + udev_device->devnode_mode = mode; + snprintf(num, sizeof(num), "%#o", mode); + udev_device_add_property(udev_device, "DEVMODE", num); + return 0; +} + struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) { udev_device->envp_uptodate = false; @@ -94,7 +357,7 @@ struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device udev_list_entry_delete(list_entry); return NULL; } - return udev_list_entry_add(udev_device->udev, &udev_device->properties_list, key, value, 1, 0); + return udev_list_entry_add(&udev_device->properties_list, key, value); } static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property) @@ -174,6 +437,8 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device, udev_device_add_tag(udev_device, tag); } } + } else if (strncmp(property, "USEC_INITIALIZED=", 19) == 0) { + udev_device_set_usec_initialized(udev_device, strtoull(&property[19], NULL, 10)); } else if (strncmp(property, "DRIVER=", 7) == 0) { udev_device_set_driver(udev_device, &property[7]); } else if (strncmp(property, "ACTION=", 7) == 0) { @@ -190,6 +455,8 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device, udev_device_set_timeout(udev_device, strtoull(&property[8], NULL, 10)); } else if (strncmp(property, "IFINDEX=", 8) == 0) { udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10)); + } else if (strncmp(property, "DEVMODE=", 8) == 0) { + udev_device_set_devnode_mode(udev_device, strtoul(&property[8], NULL, 8)); } else { udev_device_add_property_from_string(udev_device, property); } @@ -214,7 +481,7 @@ int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_d * * Returns: the value of a device property, or #NULL if there is no such property. **/ -const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) +UDEV_EXPORT const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) { struct udev_list_entry *list_entry; @@ -224,74 +491,42 @@ const char *udev_device_get_property_value(struct udev_device *udev_device, cons return NULL; list_entry = udev_device_get_properties_list_entry(udev_device); - list_entry = udev_list_entry_get_by_name(list_entry, key); + list_entry = udev_list_entry_get_by_name(list_entry, key); return udev_list_entry_get_value(list_entry); } -int udev_device_read_db(struct udev_device *udev_device) +int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) { - struct stat stats; char filename[UTIL_PATH_SIZE]; char line[UTIL_LINE_SIZE]; FILE *f; - if (udev_device->db_loaded) - return 0; - - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/.udev/db/", - udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL); + /* providing a database file will always force-load it */ + if (dbfile == NULL) { + const char *id; - if (lstat(filename, &stats) != 0) { - dbg(udev_device->udev, "no db file to read %s: %m\n", filename); - return -1; - } - if ((stats.st_mode & S_IFMT) == S_IFLNK) { - char target[UTIL_PATH_SIZE]; - char devnode[UTIL_PATH_SIZE]; - int target_len; - char *next; + if (udev_device->db_loaded) + return 0; + udev_device->db_loaded = true; - target_len = readlink(filename, target, sizeof(target)); - if (target_len <= 0 || target_len == sizeof(target)) { - info(udev_device->udev, "error reading db link %s: %m\n", filename); + id = udev_device_get_id_filename(udev_device); + if (id == NULL) return -1; - } - target[target_len] = '\0'; - - next = strchr(target, ' '); - if (next != NULL) { - next[0] = '\0'; - next = &next[1]; - } - util_strscpyl(devnode, sizeof(devnode), udev_get_dev_path(udev_device->udev), "/", target, NULL); - udev_device_set_devnode(udev_device, devnode); - while (next != NULL) { - char devlink[UTIL_PATH_SIZE]; - const char *lnk; - - lnk = next; - next = strchr(next, ' '); - if (next != NULL) { - next[0] = '\0'; - next = &next[1]; - } - util_strscpyl(devlink, sizeof(devlink), udev_get_dev_path(udev_device->udev), "/", lnk, NULL); - udev_device_add_devlink(udev_device, devlink, 0); - } - info(udev_device->udev, "device %p filled with db symlink data '%s'\n", udev_device, udev_device->devnode); - return 0; + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_device->udev), "/data/", id, NULL); + dbfile = filename; } - f = fopen(filename, "re"); + f = fopen(dbfile, "re"); if (f == NULL) { - dbg(udev_device->udev, "error reading db file %s: %m\n", filename); + info(udev_device->udev, "no db file to read %s: %m\n", dbfile); return -1; } - udev_device->db_loaded = true; + udev_device->is_initialized = true; while (fgets(line, sizeof(line), f)) { ssize_t len; const char *val; + struct udev_list_entry *entry; len = strlen(line); if (len < 4) @@ -310,11 +545,9 @@ int udev_device_read_db(struct udev_device *udev_device) case 'L': udev_device_set_devlink_priority(udev_device, atoi(val)); break; - case 'T': - udev_device_set_event_timeout(udev_device, atoi(val)); - break; case 'E': - udev_device_add_property_from_string(udev_device, val); + entry = udev_device_add_property_from_string(udev_device, val); + udev_list_entry_set_num(entry, true); break; case 'G': udev_device_add_tag(udev_device, val); @@ -322,6 +555,9 @@ int udev_device_read_db(struct udev_device *udev_device) case 'W': udev_device_set_watch_handle(udev_device, atoi(val)); break; + case 'I': + udev_device_set_usec_initialized(udev_device, strtoull(val, NULL, 10)); + break; } } fclose(f); @@ -365,6 +601,8 @@ int udev_device_read_uevent_file(struct udev_device *udev_device) udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10)); else if (strncmp(line, "DEVNAME=", 8) == 0) udev_device_set_knodename(udev_device, &line[8]); + else if (strncmp(line, "DEVMODE=", 8) == 0) + udev_device->devnode_mode = strtoul(&line[8], NULL, 8); udev_device_add_property_from_string(udev_device, line); } @@ -392,11 +630,12 @@ struct udev_device *udev_device_new(struct udev *udev) return NULL; udev_device->refcount = 1; udev_device->udev = udev; - udev_list_init(&udev_device->devlinks_list); - udev_list_init(&udev_device->properties_list); - udev_list_init(&udev_device->sysattr_list); - udev_list_init(&udev_device->tags_list); - udev_device->event_timeout = -1; + udev_list_init(udev, &udev_device->devlinks_list, true); + udev_list_init(udev, &udev_device->properties_list, true); + udev_list_init(udev, &udev_device->sysattr_value_list, true); + udev_list_init(udev, &udev_device->sysattr_list, false); + udev_list_init(udev, &udev_device->tags_list, true); + udev_device->timeout = -1; udev_device->watch_handle = -1; /* copy global properties */ udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) @@ -421,7 +660,7 @@ struct udev_device *udev_device_new(struct udev *udev) * * Returns: a new udev device, or #NULL, if it does not exist **/ -struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) +UDEV_EXPORT struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) { size_t len; const char *subdir; @@ -497,7 +736,7 @@ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char * * * Returns: a new udev device, or #NULL, if it does not exist **/ -struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) +UDEV_EXPORT struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) { char path[UTIL_PATH_SIZE]; const char *type_str; @@ -515,6 +754,61 @@ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, de return udev_device_new_from_syspath(udev, path); } +struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id) +{ + char type; + int maj, min; + char subsys[UTIL_PATH_SIZE]; + char *sysname; + + switch(id[0]) { + case 'b': + case 'c': + if (sscanf(id, "%c%i:%i", &type, &maj, &min) != 3) + return NULL; + return udev_device_new_from_devnum(udev, type, makedev(maj, min)); + case 'n': { + int sk; + struct ifreq ifr; + struct udev_device *dev; + int ifindex; + + ifindex = strtoul(&id[1], NULL, 10); + if (ifindex <= 0) + return NULL; + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) + return NULL; + memset(&ifr, 0x00, sizeof(struct ifreq)); + ifr.ifr_ifindex = ifindex; + if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) { + close(sk); + return NULL; + } + close(sk); + + dev = udev_device_new_from_subsystem_sysname(udev, "net", ifr.ifr_name); + if (dev == NULL) + return NULL; + if (udev_device_get_ifindex(dev) == ifindex) + return dev; + udev_device_unref(dev); + return NULL; + } + case '+': + util_strscpy(subsys, sizeof(subsys), &id[1]); + sysname = strchr(subsys, ':'); + if (sysname == NULL) + return NULL; + sysname[0] = '\0'; + sysname = &sysname[1]; + return udev_device_new_from_subsystem_sysname(udev, subsys, sysname); + default: + return NULL; + } +} + /** * udev_device_new_from_subsystem_sysname: * @udev: udev library context @@ -530,7 +824,7 @@ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, de * * Returns: a new udev device, or #NULL, if it does not exist **/ -struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) +UDEV_EXPORT struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) { char path_full[UTIL_PATH_SIZE]; char *path; @@ -614,7 +908,7 @@ found: * * Returns: a new udev device, or #NULL, if it does not exist **/ -struct udev_device *udev_device_new_from_environment(struct udev *udev) +UDEV_EXPORT struct udev_device *udev_device_new_from_environment(struct udev *udev) { int i; struct udev_device *udev_device; @@ -677,7 +971,7 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic * * Returns: a new udev device, or #NULL, if it no parent exist. **/ -struct udev_device *udev_device_get_parent(struct udev_device *udev_device) +UDEV_EXPORT struct udev_device *udev_device_get_parent(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; @@ -712,7 +1006,7 @@ struct udev_device *udev_device_get_parent(struct udev_device *udev_device) * * Returns: a new udev device, or #NULL if no matching parent exists. **/ -struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) +UDEV_EXPORT struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) { struct udev_device *parent; @@ -745,7 +1039,7 @@ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_de * * Returns: the udev library context **/ -struct udev *udev_device_get_udev(struct udev_device *udev_device) +UDEV_EXPORT struct udev *udev_device_get_udev(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; @@ -760,7 +1054,7 @@ struct udev *udev_device_get_udev(struct udev_device *udev_device) * * Returns: the passed udev device **/ -struct udev_device *udev_device_ref(struct udev_device *udev_device) +UDEV_EXPORT struct udev_device *udev_device_ref(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; @@ -776,7 +1070,7 @@ struct udev_device *udev_device_ref(struct udev_device *udev_device) * the resources of the device will be released. * **/ -void udev_device_unref(struct udev_device *udev_device) +UDEV_EXPORT void udev_device_unref(struct udev_device *udev_device) { if (udev_device == NULL) return; @@ -790,15 +1084,16 @@ void udev_device_unref(struct udev_device *udev_device) free(udev_device->devnode); free(udev_device->subsystem); free(udev_device->devtype); - udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list); - udev_list_cleanup_entries(udev_device->udev, &udev_device->properties_list); - udev_list_cleanup_entries(udev_device->udev, &udev_device->sysattr_list); - udev_list_cleanup_entries(udev_device->udev, &udev_device->tags_list); + udev_list_cleanup(&udev_device->devlinks_list); + udev_list_cleanup(&udev_device->properties_list); + udev_list_cleanup(&udev_device->sysattr_value_list); + udev_list_cleanup(&udev_device->sysattr_list); + udev_list_cleanup(&udev_device->tags_list); free(udev_device->action); free(udev_device->driver); free(udev_device->devpath_old); - free(udev_device->sysname_old); free(udev_device->knodename); + free(udev_device->id_filename); free(udev_device->envp); free(udev_device->monitor_buf); dbg(udev_device->udev, "udev_device: %p released\n", udev_device); @@ -814,7 +1109,7 @@ void udev_device_unref(struct udev_device *udev_device) * * Returns: the devpath of the udev device **/ -const char *udev_device_get_devpath(struct udev_device *udev_device) +UDEV_EXPORT const char *udev_device_get_devpath(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; @@ -830,7 +1125,7 @@ const char *udev_device_get_devpath(struct udev_device *udev_device) * * Returns: the sys path of the udev device **/ -const char *udev_device_get_syspath(struct udev_device *udev_device) +UDEV_EXPORT const char *udev_device_get_syspath(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; @@ -843,7 +1138,7 @@ const char *udev_device_get_syspath(struct udev_device *udev_device) * * Returns: the sys name of the device device **/ -const char *udev_device_get_sysname(struct udev_device *udev_device) +UDEV_EXPORT const char *udev_device_get_sysname(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; @@ -854,102 +1149,44 @@ const char *udev_device_get_sysname(struct udev_device *udev_device) * udev_device_get_sysnum: * @udev_device: udev device * - * Returns: the trailing number of of the device name - **/ -const char *udev_device_get_sysnum(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->sysnum; -} - -/** - * udev_device_get_devnode: - * @udev_device: udev device - * - * Retrieve the device node file name belonging to the udev device. - * 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_devnode(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - if (!udev_device->info_loaded) { - udev_device_read_uevent_file(udev_device); - udev_device_read_db(udev_device); - } - - /* we might get called before we handled an event and have a db, use the kernel-provided name */ - if (udev_device->devnode == NULL && udev_device_get_knodename(udev_device) != NULL) { - if (asprintf(&udev_device->devnode, "%s/%s", - udev_get_dev_path(udev_device->udev), udev_device_get_knodename(udev_device)) < 0) - return NULL; - return udev_device->devnode; - } - - return udev_device->devnode; -} - -/** - * udev_device_get_subsystem: - * @udev_device: udev device - * - * Retrieve the subsystem string of the udev device. The string does not - * contain any "/". - * - * Returns: the subsystem name of the udev device, or #NULL if it can not be determined - **/ -const char *udev_device_get_subsystem(struct udev_device *udev_device) -{ - char subsystem[UTIL_NAME_SIZE]; - - if (udev_device == NULL) - return NULL; - if (!udev_device->subsystem_set) { - udev_device->subsystem_set = true; - /* read "subsystem" link */ - if (util_get_sys_subsystem(udev_device->udev, udev_device->syspath, subsystem, sizeof(subsystem)) > 0) { - udev_device_set_subsystem(udev_device, subsystem); - return udev_device->subsystem; - } - /* implicit names */ - if (strncmp(udev_device->devpath, "/module/", 8) == 0) { - udev_device_set_subsystem(udev_device, "module"); - return udev_device->subsystem; - } - if (strstr(udev_device->devpath, "/drivers/") != NULL) { - udev_device_set_subsystem(udev_device, "drivers"); - return udev_device->subsystem; - } - if (strncmp(udev_device->devpath, "/subsystem/", 11) == 0 || - strncmp(udev_device->devpath, "/class/", 7) == 0 || - strncmp(udev_device->devpath, "/bus/", 5) == 0) { - udev_device_set_subsystem(udev_device, "subsystem"); - return udev_device->subsystem; - } - } - return udev_device->subsystem; + * Returns: the trailing number of of the device name + **/ +UDEV_EXPORT const char *udev_device_get_sysnum(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + return udev_device->sysnum; } /** - * udev_device_get_devtype: + * udev_device_get_devnode: * @udev_device: udev device * - * Retrieve the devtype string of the udev device. + * Retrieve the device node file name belonging to the udev device. + * The path is an absolute path, and starts with the device directory. * - * Returns: the devtype name of the udev device, or #NULL if it can not be determined + * Returns: the device node file name of the udev device, or #NULL if no device node exists **/ -const char *udev_device_get_devtype(struct udev_device *udev_device) +UDEV_EXPORT const char *udev_device_get_devnode(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; - if (!udev_device->devtype_set) { - udev_device->devtype_set = true; + if (!udev_device->info_loaded) { udev_device_read_uevent_file(udev_device); + udev_device_read_db(udev_device, NULL); } - return udev_device->devtype; + + /* we might get called before we handled an event and have a db, use the kernel-provided name */ + if (udev_device->devnode == NULL && udev_device_get_knodename(udev_device) != NULL) { + char filename[UTIL_NAME_SIZE]; + + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/", + udev_device_get_knodename(udev_device), NULL); + udev_device_set_devnode(udev_device, filename); + return udev_device->devnode; + } + + return udev_device->devnode; } /** @@ -965,19 +1202,19 @@ const char *udev_device_get_devtype(struct udev_device *udev_device) * * Returns: the first entry of the device node link list **/ -struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) +UDEV_EXPORT struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; if (!udev_device->info_loaded) - udev_device_read_db(udev_device); + udev_device_read_db(udev_device, NULL); return udev_list_get_entry(&udev_device->devlinks_list); } void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) { udev_device->devlinks_uptodate = false; - udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list); + udev_list_cleanup(&udev_device->devlinks_list); } /** @@ -992,13 +1229,13 @@ void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) * * Returns: the first entry of the property list **/ -struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) +UDEV_EXPORT struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; if (!udev_device->info_loaded) { udev_device_read_uevent_file(udev_device); - udev_device_read_db(udev_device); + udev_device_read_db(udev_device, NULL); } if (!udev_device->devlinks_uptodate) { char symlinks[UTIL_PATH_SIZE]; @@ -1035,41 +1272,6 @@ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device return udev_list_get_entry(&udev_device->properties_list); } -/** - * udev_device_get_driver: - * @udev_device: udev device - * - * Returns: the driver string, or #NULL if there is no driver attached. - **/ -const char *udev_device_get_driver(struct udev_device *udev_device) -{ - char driver[UTIL_NAME_SIZE]; - - if (udev_device == NULL) - return NULL; - if (!udev_device->driver_set) { - udev_device->driver_set = true; - if (util_get_sys_driver(udev_device->udev, udev_device->syspath, driver, sizeof(driver)) > 0) - udev_device->driver = strdup(driver); - } - return udev_device->driver; -} - -/** - * udev_device_get_devnum: - * @udev_device: udev device - * - * Returns: the device major/minor number. - **/ -dev_t udev_device_get_devnum(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return makedev(0, 0); - if (!udev_device->info_loaded) - udev_device_read_uevent_file(udev_device); - return udev_device->devnum; -} - /** * udev_device_get_action: * @udev_device: udev device @@ -1080,7 +1282,7 @@ dev_t udev_device_get_devnum(struct udev_device *udev_device) * * Returns: the kernel action value, or #NULL if there is no action value available. **/ -const char *udev_device_get_action(struct udev_device *udev_device) +UDEV_EXPORT const char *udev_device_get_action(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; @@ -1088,19 +1290,45 @@ const char *udev_device_get_action(struct udev_device *udev_device) } /** - * udev_device_get_devnum: + * udev_device_get_usec_since_initialized: * @udev_device: udev device * - * This is only valid if the device was received through a monitor. Devices read from - * sys do not have a sequence number. + * Return the number of microseconds passed since udev set up the + * device for the first time. * - * Returns: the kernel event sequence number, or 0 if there is no sequence number available. + * This is only implemented for devices with need to store properties + * in the udev database. All other devices return 0 here. + * + * Returns: the number of microseconds since the device was first seen. **/ -unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) +UDEV_EXPORT unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) { + unsigned long long now; + if (udev_device == NULL) return 0; - return udev_device->seqnum; + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + if (udev_device->usec_initialized == 0) + return 0; + now = now_usec(); + if (now == 0) + return 0; + return now - udev_device->usec_initialized; +} + +unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device) +{ + return udev_device->usec_initialized; +} + +void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized) +{ + char num[32]; + + udev_device->usec_initialized = usec_initialized; + snprintf(num, sizeof(num), "%llu", usec_initialized); + udev_device_add_property(udev_device, "USEC_INITIALIZED", num); } /** @@ -1113,7 +1341,7 @@ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) * * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. **/ -const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) +UDEV_EXPORT const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) { struct udev_list_entry *list_entry; char path[UTIL_PATH_SIZE]; @@ -1129,43 +1357,48 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const return NULL; /* look for possibly already cached result */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_device->sysattr_list)) { - if (strcmp(udev_list_entry_get_name(list_entry), sysattr) == 0) { - dbg(udev_device->udev, "got '%s' (%s) from cache\n", - sysattr, udev_list_entry_get_value(list_entry)); - return udev_list_entry_get_value(list_entry); - } + list_entry = udev_list_get_entry(&udev_device->sysattr_value_list); + list_entry = udev_list_entry_get_by_name(list_entry, sysattr); + if (list_entry != NULL) { + dbg(udev_device->udev, "got '%s' (%s) from cache\n", + sysattr, udev_list_entry_get_value(list_entry)); + return udev_list_entry_get_value(list_entry); } util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); if (lstat(path, &statbuf) != 0) { dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path); - udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, NULL, 0, 0); + udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, NULL); goto out; } if (S_ISLNK(statbuf.st_mode)) { - char target[UTIL_NAME_SIZE]; - int len; - char *pos; - - /* some core links return the last element of the target path */ - if (strcmp(sysattr, "driver") != 0 && - strcmp(sysattr, "subsystem") != 0 && - strcmp(sysattr, "module") != 0) - goto out; - - len = readlink(path, target, sizeof(target)); - if (len <= 0 || len == sizeof(target)) + struct udev_device *dev; + + /* + * Some core links return only the last element of the target path, + * these are just values, the paths should not be exposed. + */ + if (strcmp(sysattr, "driver") == 0 || + strcmp(sysattr, "subsystem") == 0 || + strcmp(sysattr, "module") == 0) { + if (util_get_sys_core_link_value(udev_device->udev, sysattr, + udev_device->syspath, value, sizeof(value)) < 0) + return NULL; + dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, value); + list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); + val = udev_list_entry_get_value(list_entry); goto out; - target[len] = '\0'; + } - pos = strrchr(target, '/'); - if (pos != NULL) { - pos = &pos[1]; - dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, pos); - list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, pos, 0, 0); + /* resolve link to a device and return its syspath */ + util_strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL); + dev = udev_device_new_from_syspath(udev_device->udev, path); + if (dev != NULL) { + list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, + udev_device_get_syspath(dev)); val = udev_list_entry_get_value(list_entry); + udev_device_unref(dev); } goto out; @@ -1196,12 +1429,77 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const value[size] = '\0'; util_remove_trailing_chars(value, '\n'); dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value); - list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, value, 0, 0); + list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); val = udev_list_entry_get_value(list_entry); out: return val; } +static int udev_device_sysattr_list_read(struct udev_device *udev_device) +{ + struct dirent *dent; + DIR *dir; + int num = 0; + + if (udev_device == NULL) + return -1; + if (udev_device->sysattr_list_read) + return 0; + + dir = opendir(udev_device_get_syspath(udev_device)); + if (!dir) { + dbg(udev_device->udev, "sysfs dir '%s' can not be opened\n", + udev_device_get_syspath(udev_device)); + return -1; + } + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char path[UTIL_PATH_SIZE]; + struct stat statbuf; + + /* only handle symlinks and regular files */ + if (dent->d_type != DT_LNK && dent->d_type != DT_REG) + continue; + + util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL); + if (lstat(path, &statbuf) != 0) + continue; + if ((statbuf.st_mode & S_IRUSR) == 0) + continue; + + udev_list_entry_add(&udev_device->sysattr_list, dent->d_name, NULL); + num++; + } + + closedir(dir); + dbg(udev_device->udev, "found %d sysattrs for '%s'\n", num, udev_device_get_syspath(udev_device)); + udev_device->sysattr_list_read = true; + + return num; +} + +/** + * udev_device_get_sysattr_list_entry: + * @udev_device: udev device + * + * Retrieve the list of available sysattrs, with value being empty; + * This just return all available sysfs attributes for a particular + * device without reading their values. + * + * Returns: the first entry of the property list + **/ +UDEV_EXPORT struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) +{ + if (!udev_device->sysattr_list_read) { + int ret; + ret = udev_device_sysattr_list_read(udev_device); + if (0 > ret) + return NULL; + } + + return udev_list_get_entry(&udev_device->sysattr_list); +} + int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath) { const char *pos; @@ -1240,34 +1538,10 @@ int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath return 0; } -int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) -{ - free(udev_device->subsystem); - udev_device->subsystem = strdup(subsystem); - if (udev_device->subsystem == NULL) - return -ENOMEM; - udev_device->subsystem_set = true; - udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem); - return 0; -} - -int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype) -{ - free(udev_device->devtype); - udev_device->devtype = strdup(devtype); - if (udev_device->devtype == NULL) - return -ENOMEM; - udev_device->devtype_set = true; - udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype); - return 0; -} - int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) { free(udev_device->devnode); udev_device->devnode = strdup(devnode); - if (devnode == NULL) - return 0; if (udev_device->devnode == NULL) return -ENOMEM; udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode); @@ -1279,20 +1553,79 @@ int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink struct udev_list_entry *list_entry; udev_device->devlinks_uptodate = false; - list_entry = udev_list_entry_add(udev_device->udev, &udev_device->devlinks_list, devlink, NULL, 1, 0); + list_entry = udev_list_entry_add(&udev_device->devlinks_list, devlink, NULL); if (list_entry == NULL) return -ENOMEM; if (unique) - udev_list_entry_set_flags(list_entry, 1); + udev_list_entry_set_num(list_entry, true); return 0; } +const char *udev_device_get_id_filename(struct udev_device *udev_device) +{ + if (udev_device->id_filename == NULL) { + if (udev_device_get_subsystem(udev_device) == NULL) + return NULL; + + if (major(udev_device_get_devnum(udev_device)) > 0) { + /* use dev_t -- b259:131072, c254:0 */ + if (asprintf(&udev_device->id_filename, "%c%u:%u", + strcmp(udev_device_get_subsystem(udev_device), "block") == 0 ? 'b' : 'c', + major(udev_device_get_devnum(udev_device)), + minor(udev_device_get_devnum(udev_device))) < 0) + udev_device->id_filename = NULL; + } else if (udev_device_get_ifindex(udev_device) > 0) { + /* use netdev ifindex -- n3 */ + if (asprintf(&udev_device->id_filename, "n%u", udev_device_get_ifindex(udev_device)) < 0) + udev_device->id_filename = NULL; + } else { + /* + * use $subsys:$syname -- pci:0000:00:1f.2 + * sysname() has '!' translated, get it from devpath + */ + const char *sysname; + sysname = strrchr(udev_device->devpath, '/'); + if (sysname == NULL) + return NULL; + sysname = &sysname[1]; + if (asprintf(&udev_device->id_filename, "+%s:%s", udev_device_get_subsystem(udev_device), sysname) < 0) + udev_device->id_filename = NULL; + } + } + return udev_device->id_filename; +} + +/** + * udev_device_get_is_initialized: + * @udev_device: udev device + * + * Check if udev has already handled the device and has set up + * device node permissions and context, or has renamed a network + * device. + * + * This is only implemented for devices with a device node + * or network interfaces. All other devices return 1 here. + * + * Returns: 1 if the device is set up. 0 otherwise. + **/ +UDEV_EXPORT int udev_device_get_is_initialized(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + return udev_device->is_initialized; +} + +void udev_device_set_is_initialized(struct udev_device *udev_device) +{ + udev_device->is_initialized = true; +} + int udev_device_add_tag(struct udev_device *udev_device, const char *tag) { if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL) return -EINVAL; udev_device->tags_uptodate = false; - if (udev_list_entry_add(udev_device->udev, &udev_device->tags_list, tag, NULL, 1, 0) != NULL) + if (udev_list_entry_add(&udev_device->tags_list, tag, NULL) != NULL) return 0; return -ENOMEM; } @@ -1300,7 +1633,7 @@ int udev_device_add_tag(struct udev_device *udev_device, const char *tag) void udev_device_cleanup_tags_list(struct udev_device *udev_device) { udev_device->tags_uptodate = false; - udev_list_cleanup_entries(udev_device->udev, &udev_device->tags_list); + udev_list_cleanup(&udev_device->tags_list); } /** @@ -1314,24 +1647,27 @@ void udev_device_cleanup_tags_list(struct udev_device *udev_device) * * Returns: the first entry of the tag list **/ -struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) +UDEV_EXPORT struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) { if (udev_device == NULL) return NULL; + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); return udev_list_get_entry(&udev_device->tags_list); } -int udev_device_has_tag(struct udev_device *udev_device, const char *tag) +UDEV_EXPORT int udev_device_has_tag(struct udev_device *udev_device, const char *tag) { struct udev_list_entry *list_entry; + if (udev_device == NULL) + return false; if (!udev_device->info_loaded) - udev_device_read_db(udev_device); + udev_device_read_db(udev_device, NULL); list_entry = udev_device_get_tags_list_entry(udev_device); - list_entry = udev_list_entry_get_by_name(list_entry, tag); - if (list_entry != NULL) - return 1; - return 0; + if (udev_list_entry_get_by_name(list_entry, tag) != NULL) + return true; + return false; } #define ENVP_SIZE 128 @@ -1415,122 +1751,10 @@ int udev_device_set_action(struct udev_device *udev_device, const char *action) return 0; } -int udev_device_set_driver(struct udev_device *udev_device, const char *driver) -{ - free(udev_device->driver); - udev_device->driver = strdup(driver); - if (udev_device->driver == NULL) - return -ENOMEM; - udev_device->driver_set = true; - udev_device_add_property(udev_device, "DRIVER", udev_device->driver); - return 0; -} - -const char *udev_device_get_devpath_old(struct udev_device *udev_device) -{ - return udev_device->devpath_old; -} - -int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old) -{ - const char *pos; - size_t len; - - free(udev_device->devpath_old); - udev_device->devpath_old = strdup(devpath_old); - if (udev_device->devpath_old == NULL) - return -ENOMEM; - udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old); - - pos = strrchr(udev_device->devpath_old, '/'); - if (pos == NULL) - return -EINVAL; - udev_device->sysname_old = strdup(&pos[1]); - if (udev_device->sysname_old == NULL) - return -ENOMEM; - - /* some devices have '!' in their name, change that to '/' */ - len = 0; - while (udev_device->sysname_old[len] != '\0') { - if (udev_device->sysname_old[len] == '!') - udev_device->sysname_old[len] = '/'; - len++; - } - return 0; -} - -const char *udev_device_get_sysname_old(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->sysname_old; -} - -const char *udev_device_get_knodename(struct udev_device *udev_device) -{ - return udev_device->knodename; -} - -int udev_device_set_knodename(struct udev_device *udev_device, const char *knodename) -{ - free(udev_device->knodename); - udev_device->knodename = strdup(knodename); - if (udev_device->knodename == NULL) - return -ENOMEM; - udev_device_add_property(udev_device, "DEVNAME", udev_device->knodename); - return 0; -} - -int udev_device_get_timeout(struct udev_device *udev_device) -{ - return udev_device->timeout; -} - -int udev_device_set_timeout(struct udev_device *udev_device, int timeout) -{ - udev_device->timeout = timeout; - return 0; -} -int udev_device_get_event_timeout(struct udev_device *udev_device) -{ - if (!udev_device->info_loaded) - udev_device_read_db(udev_device); - return udev_device->event_timeout; -} - -int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout) -{ - udev_device->event_timeout = event_timeout; - return 0; -} - -int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum) -{ - char num[32]; - - udev_device->seqnum = seqnum; - snprintf(num, sizeof(num), "%llu", seqnum); - udev_device_add_property(udev_device, "SEQNUM", num); - return 0; -} - -int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) -{ - char num[32]; - - udev_device->devnum = devnum; - - snprintf(num, sizeof(num), "%u", major(devnum)); - udev_device_add_property(udev_device, "MAJOR", num); - snprintf(num, sizeof(num), "%u", minor(devnum)); - udev_device_add_property(udev_device, "MINOR", num); - return 0; -} - int udev_device_get_devlink_priority(struct udev_device *udev_device) { if (!udev_device->info_loaded) - udev_device_read_db(udev_device); + udev_device_read_db(udev_device, NULL); return udev_device->devlink_priority; } @@ -1543,7 +1767,7 @@ int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio) int udev_device_get_watch_handle(struct udev_device *udev_device) { if (!udev_device->info_loaded) - udev_device_read_db(udev_device); + udev_device_read_db(udev_device, NULL); return udev_device->watch_handle; } @@ -1553,13 +1777,12 @@ int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) return 0; } -int udev_device_get_ifindex(struct udev_device *udev_device) +bool udev_device_get_db_persist(struct udev_device *udev_device) { - return udev_device->ifindex; + return udev_device->db_persist; } -int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) +void udev_device_set_db_persist(struct udev_device *udev_device) { - udev_device->ifindex = ifindex; - return 0; + udev_device->db_persist = true; }