X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=libudev%2Flibudev-device.c;h=0ef7260de223a78615b3831cd83906953e090a6e;hp=8b0cccd533f01a76ff7ae5108c12691c999f9f34;hb=2181d30a342dd9fb168b7077ae5095849e328689;hpb=ff0e1f4e5d897a0ddbf365e920c0e28a6657abaa diff --git a/libudev/libudev-device.c b/libudev/libudev-device.c index 8b0cccd53..0ef7260de 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" @@ -60,10 +64,11 @@ struct udev_device { size_t monitor_buf_len; struct udev_list_node devlinks_list; struct udev_list_node properties_list; + struct udev_list_node sysattr_value_list; struct udev_list_node sysattr_list; struct udev_list_node tags_list; unsigned long long int seqnum; - int event_timeout; + unsigned long long int usec_initialized; int timeout; int devlink_priority; int refcount; @@ -81,6 +86,9 @@ struct udev_device { bool info_loaded; bool db_loaded; bool uevent_loaded; + bool is_initialized; + bool sysattr_list_read; + bool db_persist; }; struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) @@ -229,26 +237,33 @@ const char *udev_device_get_property_value(struct udev_device *udev_device, cons 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) { - const char *id; char filename[UTIL_PATH_SIZE]; char line[UTIL_LINE_SIZE]; FILE *f; - if (udev_device->db_loaded) - return 0; - udev_device->db_loaded = true; + /* providing a database file will always force-load it */ + if (dbfile == NULL) { + const char *id; - id = udev_device_get_id_filename(udev_device); - if (id == NULL) - return -1; - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/.udev/db/", id, NULL); - f = fopen(filename, "re"); + if (udev_device->db_loaded) + return 0; + udev_device->db_loaded = true; + + id = udev_device_get_id_filename(udev_device); + if (id == NULL) + return -1; + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_device->udev), "/data/", id, NULL); + dbfile = filename; + } + + f = fopen(dbfile, "re"); if (f == NULL) { - info(udev_device->udev, "no db file to read %s: %m\n", filename); + info(udev_device->udev, "no db file to read %s: %m\n", dbfile); return -1; } + udev_device->is_initialized = true; while (fgets(line, sizeof(line), f)) { ssize_t len; @@ -282,6 +297,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); @@ -354,9 +372,10 @@ struct udev_device *udev_device_new(struct udev *udev) udev_device->udev = udev; udev_list_init(&udev_device->devlinks_list); udev_list_init(&udev_device->properties_list); + udev_list_init(&udev_device->sysattr_value_list); udev_list_init(&udev_device->sysattr_list); udev_list_init(&udev_device->tags_list); - udev_device->event_timeout = -1; + udev_device->timeout = -1; udev_device->watch_handle = -1; /* copy global properties */ udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) @@ -488,6 +507,35 @@ struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id 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, ':'); @@ -778,6 +826,7 @@ void udev_device_unref(struct udev_device *udev_device) 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_value_list); udev_list_cleanup_entries(udev_device->udev, &udev_device->sysattr_list); udev_list_cleanup_entries(udev_device->udev, &udev_device->tags_list); free(udev_device->action); @@ -865,7 +914,7 @@ const char *udev_device_get_devnode(struct udev_device *udev_device) 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); } /* we might get called before we handled an event and have a db, use the kernel-provided name */ @@ -959,7 +1008,7 @@ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct 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); } @@ -987,7 +1036,7 @@ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device 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]; @@ -1092,6 +1141,44 @@ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) return udev_device->seqnum; } +/** + * udev_device_get_usec_since_initialized: + * @udev_device: udev device + * + * Return the number of microseconds passed since udev set up the + * device for the first time. + * + * 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_usec_since_initialized(struct udev_device *udev_device) +{ + unsigned long long now; + + if (udev_device == NULL) + return 0; + 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) +{ + udev_device->usec_initialized = usec_initialized; +} + /** * udev_device_get_sysattr_value: * @udev_device: udev device @@ -1118,7 +1205,7 @@ 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)) { + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_device->sysattr_value_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)); @@ -1129,7 +1216,7 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const 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->udev, &udev_device->sysattr_value_list, sysattr, NULL, 0, 0); goto out; } @@ -1153,7 +1240,7 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const 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); + list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_value_list, sysattr, pos, 0, 0); val = udev_list_entry_get_value(list_entry); } @@ -1185,12 +1272,78 @@ 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->udev, &udev_device->sysattr_value_list, sysattr, value, 0, 0); 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->udev, &udev_device->sysattr_list, + dent->d_name, NULL, 0, 0); + 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 + **/ +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; @@ -1308,6 +1461,31 @@ const char *udev_device_get_id_filename(struct udev_device *udev_device) 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. + **/ +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) @@ -1347,7 +1525,7 @@ int udev_device_has_tag(struct udev_device *udev_device, const char *tag) struct udev_list_entry *list_entry; 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) @@ -1510,23 +1688,11 @@ int udev_device_get_timeout(struct udev_device *udev_device) } 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) { char num[32]; - udev_device->event_timeout = event_timeout; - snprintf(num, sizeof(num), "%u", event_timeout); + udev_device->timeout = timeout; + snprintf(num, sizeof(num), "%u", timeout); udev_device_add_property(udev_device, "TIMEOUT", num); return 0; } @@ -1557,7 +1723,7 @@ int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) 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; } @@ -1570,7 +1736,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; } @@ -1596,3 +1762,13 @@ int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) udev_device_add_property(udev_device, "IFINDEX", num); return 0; } + +bool udev_device_get_db_persist(struct udev_device *udev_device) +{ + return udev_device->db_persist; +} + +void udev_device_set_db_persist(struct udev_device *udev_device) +{ + udev_device->db_persist = true; +}