-/*
- * usb-db, pci-db - lookup vendor/product database
- *
- * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net>
- * Copyright (C) 2011 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/>.
- */
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Kay Sievers <kay@vrfy.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
#include <ctype.h>
#include <stdlib.h>
+#include <fnmatch.h>
+#include <getopt.h>
#include "udev.h"
-static int get_id_attr(
- struct udev_device *parent,
- const char *name,
- uint16_t *value) {
+static struct udev_hwdb *hwdb;
- const char *t;
- unsigned u;
+int udev_builtin_hwdb_lookup(struct udev_device *dev,
+ const char *prefix, const char *modalias,
+ const char *filter, bool test) {
+ struct udev_list_entry *list;
+ struct udev_list_entry *entry;
+ int n = 0;
- if (!(t = udev_device_get_sysattr_value(parent, name))) {
- fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name);
- return -1;
- }
+ if (!hwdb)
+ return -ENOENT;
- if (!strncmp(t, "0x", 2))
- t += 2;
+ if (prefix) {
+ _cleanup_free_ const char *lookup;
- if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) {
- fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent));
- return -1;
- }
+ lookup = strjoin(prefix, modalias, NULL);
+ if (!lookup)
+ return -ENOMEM;
+ list = udev_hwdb_get_properties_list_entry(hwdb, lookup, 0);
+ } else
+ list = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0);
- *value = (uint16_t) u;
- return 0;
-}
+ udev_list_entry_foreach(entry, list) {
+ if (filter && fnmatch(filter, udev_list_entry_get_name(entry), FNM_NOESCAPE) != 0)
+ continue;
-static int get_vid_pid(
- struct udev_device *parent,
- const char *vendor_attr,
- const char *product_attr,
- uint16_t *vid,
- uint16_t *pid) {
-
- if (get_id_attr(parent, vendor_attr, vid) < 0)
- return -1;
- else if (*vid <= 0) {
- fprintf(stderr, "Invalid vendor id.\n");
- return -1;
+ if (udev_builtin_add_property(dev, test,
+ udev_list_entry_get_name(entry),
+ udev_list_entry_get_value(entry)) < 0)
+ return -ENOMEM;
+ n++;
}
-
- if (get_id_attr(parent, product_attr, pid) < 0)
- return -1;
-
- return 0;
+ return n;
}
-static void rstrip(char *n) {
- size_t i;
-
- for (i = strlen(n); i > 0 && isspace(n[i-1]); i--)
- n[i-1] = 0;
+static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) {
+ const char *v, *p;
+ int vn, pn;
+
+ v = udev_device_get_sysattr_value(dev, "idVendor");
+ if (!v)
+ return NULL;
+ p = udev_device_get_sysattr_value(dev, "idProduct");
+ if (!p)
+ return NULL;
+ vn = strtol(v, NULL, 16);
+ if (vn <= 0)
+ return NULL;
+ pn = strtol(p, NULL, 16);
+ if (pn <= 0)
+ return NULL;
+ snprintf(s, size, "usb:v%04Xp%04X*", vn, pn);
+ return s;
}
-#define HEXCHARS "0123456789abcdefABCDEF"
-#define WHITESPACE " \t\n\r"
-static int lookup_vid_pid(const char *database,
- uint16_t vid, uint16_t pid,
- char **vendor, char **product)
-{
-
- FILE *f;
- int ret = -1;
- int found_vendor = 0;
- char *line = NULL;
-
- *vendor = *product = NULL;
-
- if (!(f = fopen(database, "rme"))) {
- fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno));
- return -1;
- }
-
- for (;;) {
- size_t n;
+static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device *srcdev,
+ const char *subsystem, const char *prefix,
+ const char *filter, bool test) {
+ struct udev_device *d;
+ char s[16];
+ int n = 0;
- if (getline(&line, &n, f) < 0)
- break;
+ for (d = srcdev; d; d = udev_device_get_parent(d)) {
+ const char *dsubsys;
+ const char *modalias = NULL;
- rstrip(line);
+ dsubsys = udev_device_get_subsystem(d);
+ if (!dsubsys)
+ continue;
- if (line[0] == '#' || line[0] == 0)
+ /* look only at devices of a specific subsystem */
+ if (subsystem && !streq(dsubsys, subsystem))
continue;
- if (strspn(line, HEXCHARS) == 4) {
- unsigned u;
+ modalias = udev_device_get_property_value(d, "MODALIAS");
- if (found_vendor)
- break;
+ /* the usb_device does not have a modalias, compose one */
+ if (!modalias && streq(dsubsys, "usb"))
+ modalias = modalias_usb(d, s, sizeof(s));
- if (sscanf(line, "%04x", &u) == 1 && u == vid) {
- char *t;
+ if (!modalias)
+ continue;
- t = line+4;
- t += strspn(t, WHITESPACE);
+ n = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
+ if (n > 0)
+ break;
+ }
- if (!(*vendor = strdup(t))) {
- fprintf(stderr, "Out of memory.\n");
- goto finish;
- }
+ return n;
+}
- found_vendor = 1;
- }
+static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
+ static const struct option options[] = {
+ { "filter", required_argument, NULL, 'f' },
+ { "device", required_argument, NULL, 'd' },
+ { "subsystem", required_argument, NULL, 's' },
+ { "lookup-prefix", required_argument, NULL, 'p' },
+ {}
+ };
+ const char *filter = NULL;
+ const char *device = NULL;
+ const char *subsystem = NULL;
+ const char *prefix = NULL;
+ struct udev_device *srcdev;
+
+ if (!hwdb)
+ return EXIT_FAILURE;
- continue;
- }
+ for (;;) {
+ int option;
- if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) {
- unsigned u;
+ option = getopt_long(argc, argv, "f:d:s:p:", options, NULL);
+ if (option == -1)
+ break;
- if (sscanf(line+1, "%04x", &u) == 1 && u == pid) {
- char *t;
+ switch (option) {
+ case 'f':
+ filter = optarg;
+ break;
- t = line+5;
- t += strspn(t, WHITESPACE);
+ case 'd':
+ device = optarg;
+ break;
- if (!(*product = strdup(t))) {
- fprintf(stderr, "Out of memory.\n");
- goto finish;
- }
+ case 's':
+ subsystem = optarg;
+ break;
- break;
- }
+ case 'p':
+ prefix = optarg;
+ break;
}
}
- ret = 0;
-
-finish:
- free(line);
- fclose(f);
-
- if (ret < 0) {
- free(*product);
- free(*vendor);
-
- *product = *vendor = NULL;
+ /* query a specific key given as argument */
+ if (argv[optind]) {
+ if (udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test) > 0)
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
}
- return ret;
+ /* read data from another device than the device we will store the data */
+ if (device) {
+ srcdev = udev_device_new_from_device_id(udev_device_get_udev(dev), device);
+ if (!srcdev)
+ return EXIT_FAILURE;
+ } else
+ srcdev = dev;
+
+ if (udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test) > 0)
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
}
-static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype)
+/* called at udev startup and reload */
+static int builtin_hwdb_init(struct udev *udev)
{
- const char *str;
-
- str = udev_device_get_subsystem(dev);
- if (str == NULL)
- goto try_parent;
- if (strcmp(str, subsys) != 0)
- goto try_parent;
-
- if (devtype != NULL) {
- str = udev_device_get_devtype(dev);
- if (str == NULL)
- goto try_parent;
- if (strcmp(str, devtype) != 0)
- goto try_parent;
- }
- return dev;
-try_parent:
- return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype);
-}
-
-
-static int builtin_db(struct udev_device *dev, bool test,
- const char *database,
- const char *vendor_attr, const char *product_attr,
- const char *subsys, const char *devtype)
-{
- struct udev_device *parent;
- uint16_t vid = 0, pid = 0;
- char *vendor = NULL, *product = NULL;
-
- parent = find_device(dev, subsys, devtype);
- if (!parent) {
- fprintf(stderr, "Failed to find device.\n");
- goto finish;
- }
-
- if (get_vid_pid(parent, vendor_attr, product_attr, &vid, &pid) < 0)
- goto finish;
-
- if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0)
- goto finish;
-
- if (vendor)
- udev_builtin_add_property(dev, test, "ID_VENDOR_FROM_DATABASE", vendor);
- if (product)
- udev_builtin_add_property(dev, test, "ID_MODEL_FROM_DATABASE", product);
-
-finish:
- free(vendor);
- free(product);
+ if (hwdb)
+ return 0;
+ hwdb = udev_hwdb_new(udev);
+ if (!hwdb)
+ return -ENOMEM;
return 0;
}
-static int builtin_usb_db(struct udev_device *dev, int argc, char *argv[], bool test)
+/* called on udev shutdown and reload request */
+static void builtin_hwdb_exit(struct udev *udev)
{
- return builtin_db(dev, test, USB_DATABASE, "idVendor", "idProduct", "usb", "usb_device");
+ hwdb = udev_hwdb_unref(hwdb);
}
-static int builtin_pci_db(struct udev_device *dev, int argc, char *argv[], bool test)
+/* called every couple of seconds during event activity; 'true' if config has changed */
+static bool builtin_hwdb_validate(struct udev *udev)
{
- return builtin_db(dev, test, PCI_DATABASE, "vendor", "device", "pci", NULL);
+ return udev_hwdb_validate(hwdb);
}
-const struct udev_builtin udev_builtin_usb_db = {
- .name = "usb-db",
- .cmd = builtin_usb_db,
- .help = "USB vendor/product database",
- .run_once = true,
-};
-
-const struct udev_builtin udev_builtin_pci_db = {
- .name = "pci-db",
- .cmd = builtin_pci_db,
- .help = "PCI vendor/product database",
- .run_once = true,
+const struct udev_builtin udev_builtin_hwdb = {
+ .name = "hwdb",
+ .cmd = builtin_hwdb,
+ .init = builtin_hwdb_init,
+ .exit = builtin_hwdb_exit,
+ .validate = builtin_hwdb_validate,
+ .help = "hardware database",
};