mirror of
https://github.com/AuxXxilium/eudev.git
synced 2024-12-03 21:27:00 +07:00
udev: add hardware database support
This commit is contained in:
parent
59bb9d9a14
commit
796b06c21b
18
Makefile.am
18
Makefile.am
@ -80,6 +80,7 @@ systempresetdir=$(rootprefix)/lib/systemd/system-preset
|
|||||||
udevlibexecdir=$(rootprefix)/lib/udev
|
udevlibexecdir=$(rootprefix)/lib/udev
|
||||||
udevhomedir = $(udevlibexecdir)
|
udevhomedir = $(udevlibexecdir)
|
||||||
udevrulesdir = $(udevlibexecdir)/rules.d
|
udevrulesdir = $(udevlibexecdir)/rules.d
|
||||||
|
udevhwdbdir = $(udevlibexecdir)/hwdb.d
|
||||||
|
|
||||||
# And these are the special ones for /
|
# And these are the special ones for /
|
||||||
rootprefix=@rootprefix@
|
rootprefix=@rootprefix@
|
||||||
@ -1758,6 +1759,7 @@ man/systemd-udevd-kernel.socket.8: man/systemd-udevd.service.8
|
|||||||
|
|
||||||
udev-confdirs:
|
udev-confdirs:
|
||||||
-$(MKDIR_P) $(DESTDIR)$(sysconfdir)/udev/rules.d
|
-$(MKDIR_P) $(DESTDIR)$(sysconfdir)/udev/rules.d
|
||||||
|
-$(MKDIR_P) $(DESTDIR)$(sysconfdir)/udev/hwdb.d
|
||||||
|
|
||||||
INSTALL_DATA_HOOKS += udev-confdirs
|
INSTALL_DATA_HOOKS += udev-confdirs
|
||||||
|
|
||||||
@ -1777,6 +1779,10 @@ dist_udevrules_DATA += \
|
|||||||
rules/80-drivers.rules \
|
rules/80-drivers.rules \
|
||||||
rules/95-udev-late.rules
|
rules/95-udev-late.rules
|
||||||
|
|
||||||
|
dist_udevhwdb_DATA = \
|
||||||
|
hwdb/20-pci-vendor-product.hwdb \
|
||||||
|
hwdb/20-usb-vendor-product.hwdb
|
||||||
|
|
||||||
udevconfdir = $(sysconfdir)/udev
|
udevconfdir = $(sysconfdir)/udev
|
||||||
dist_udevconf_DATA = \
|
dist_udevconf_DATA = \
|
||||||
src/udev/udev.conf
|
src/udev/udev.conf
|
||||||
@ -1824,6 +1830,7 @@ noinst_LTLIBRARIES += \
|
|||||||
|
|
||||||
libudev_core_la_SOURCES = \
|
libudev_core_la_SOURCES = \
|
||||||
src/udev/udev.h \
|
src/udev/udev.h \
|
||||||
|
src/udev/udev-hwdb.h \
|
||||||
src/udev/udev-event.c \
|
src/udev/udev-event.c \
|
||||||
src/udev/udev-watch.c \
|
src/udev/udev-watch.c \
|
||||||
src/udev/udev-node.c \
|
src/udev/udev-node.c \
|
||||||
@ -1854,8 +1861,7 @@ libudev_core_la_LIBADD = \
|
|||||||
|
|
||||||
libudev_core_la_CPPFLAGS = \
|
libudev_core_la_CPPFLAGS = \
|
||||||
$(AM_CPPFLAGS) \
|
$(AM_CPPFLAGS) \
|
||||||
-DFIRMWARE_PATH="$(FIRMWARE_PATH)" \
|
-DFIRMWARE_PATH="$(FIRMWARE_PATH)"
|
||||||
-DUSB_DATABASE=\"$(USB_DATABASE)\" -DPCI_DATABASE=\"$(PCI_DATABASE)\"
|
|
||||||
|
|
||||||
if HAVE_ACL
|
if HAVE_ACL
|
||||||
libudev_core_la_SOURCES += \
|
libudev_core_la_SOURCES += \
|
||||||
@ -1878,6 +1884,7 @@ udevadm_SOURCES = \
|
|||||||
src/udev/udevadm-info.c \
|
src/udev/udevadm-info.c \
|
||||||
src/udev/udevadm-control.c \
|
src/udev/udevadm-control.c \
|
||||||
src/udev/udevadm-monitor.c \
|
src/udev/udevadm-monitor.c \
|
||||||
|
src/udev/udevadm-hwdb.c \
|
||||||
src/udev/udevadm-settle.c \
|
src/udev/udevadm-settle.c \
|
||||||
src/udev/udevadm-trigger.c \
|
src/udev/udevadm-trigger.c \
|
||||||
src/udev/udevadm-test.c \
|
src/udev/udevadm-test.c \
|
||||||
@ -3951,6 +3958,7 @@ distclean-local: $(DISTCLEAN_LOCAL_HOOKS)
|
|||||||
|
|
||||||
clean-local:
|
clean-local:
|
||||||
rm -rf $(abs_srcdir)/install-tree
|
rm -rf $(abs_srcdir)/install-tree
|
||||||
|
rm -f $(abs_srcdir)/hwdb/usb.ids $(abs_srcdir)/hwdb/pci.ids
|
||||||
|
|
||||||
DISTCHECK_CONFIGURE_FLAGS = \
|
DISTCHECK_CONFIGURE_FLAGS = \
|
||||||
--with-sysvinit-path=$$dc_install_base/$(sysvinitdir) \
|
--with-sysvinit-path=$$dc_install_base/$(sysvinitdir) \
|
||||||
@ -3967,6 +3975,12 @@ DISTCHECK_CONFIGURE_FLAGS += \
|
|||||||
--enable-gtk-doc
|
--enable-gtk-doc
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
hwdb-update:
|
||||||
|
( cd hwdb && \
|
||||||
|
wget -N http://www.linux-usb.org/usb.ids && \
|
||||||
|
wget -N http://pciids.sourceforge.net/v2.2/pci.ids && \
|
||||||
|
./ids-update.pl )
|
||||||
|
|
||||||
upload: all distcheck
|
upload: all distcheck
|
||||||
cp -v systemd-$(VERSION).tar.xz /home/lennart/git.fedora/systemd/
|
cp -v systemd-$(VERSION).tar.xz /home/lennart/git.fedora/systemd/
|
||||||
scp systemd-$(VERSION).tar.xz fdo:/srv/www.freedesktop.org/www/software/systemd/
|
scp systemd-$(VERSION).tar.xz fdo:/srv/www.freedesktop.org/www/software/systemd/
|
||||||
|
1
TODO
1
TODO
@ -448,6 +448,7 @@ Features:
|
|||||||
* GC unreferenced jobs (such as .device jobs)
|
* GC unreferenced jobs (such as .device jobs)
|
||||||
|
|
||||||
* write blog stories about:
|
* write blog stories about:
|
||||||
|
- hwdb: what belongs into it, lsusb
|
||||||
- enabling dbus services
|
- enabling dbus services
|
||||||
- status update
|
- status update
|
||||||
- how to make changes to sysctl and sysfs attributes
|
- how to make changes to sysctl and sysfs attributes
|
||||||
|
29
configure.ac
29
configure.ac
@ -567,33 +567,6 @@ if test "x$enable_coredump" != "xno"; then
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"])
|
AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"])
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
AC_ARG_WITH(usb-ids-path,
|
|
||||||
[AS_HELP_STRING([--with-usb-ids-path=DIR], [Path to usb.ids file])],
|
|
||||||
[USB_DATABASE=${withval}],
|
|
||||||
[if test -n "$usbids" ; then
|
|
||||||
USB_DATABASE="$usbids"
|
|
||||||
else
|
|
||||||
PKG_CHECK_MODULES(USBUTILS, usbutils >= 0.82)
|
|
||||||
AC_SUBST([USB_DATABASE], [$($PKG_CONFIG --variable=usbids usbutils)])
|
|
||||||
fi])
|
|
||||||
AC_MSG_CHECKING([for USB database location])
|
|
||||||
AC_MSG_RESULT([$USB_DATABASE])
|
|
||||||
AC_SUBST(USB_DATABASE)
|
|
||||||
|
|
||||||
AC_ARG_WITH(pci-ids-path,
|
|
||||||
[AS_HELP_STRING([--with-pci-ids-path=DIR], [Path to pci.ids file])],
|
|
||||||
[PCI_DATABASE=${withval}],
|
|
||||||
[if test -n "$pciids" ; then
|
|
||||||
PCI_DATABASE="$pciids"
|
|
||||||
else
|
|
||||||
PKG_CHECK_MODULES(LIBPCI, libpci >= 3)
|
|
||||||
AC_SUBST([PCI_DATABASE], [$($PKG_CONFIG --variable=idsdir libpci)/pci.ids])
|
|
||||||
fi])
|
|
||||||
AC_MSG_CHECKING([for PCI database location])
|
|
||||||
AC_MSG_RESULT([$PCI_DATABASE])
|
|
||||||
AC_SUBST(PCI_DATABASE)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
AC_ARG_WITH(firmware-path,
|
AC_ARG_WITH(firmware-path,
|
||||||
AS_HELP_STRING([--with-firmware-path=DIR[[[:DIR[...]]]]],
|
AS_HELP_STRING([--with-firmware-path=DIR[[[:DIR[...]]]]],
|
||||||
@ -878,8 +851,6 @@ AC_MSG_RESULT([
|
|||||||
localed: ${have_localed}
|
localed: ${have_localed}
|
||||||
coredump: ${have_coredump}
|
coredump: ${have_coredump}
|
||||||
firmware path: ${FIRMWARE_PATH}
|
firmware path: ${FIRMWARE_PATH}
|
||||||
usb.ids: ${USB_DATABASE}
|
|
||||||
pci.ids: ${PCI_DATABASE}
|
|
||||||
gudev: ${enable_gudev}
|
gudev: ${enable_gudev}
|
||||||
gintrospection: ${enable_introspection}
|
gintrospection: ${enable_introspection}
|
||||||
keymap: ${enable_keymap}
|
keymap: ${enable_keymap}
|
||||||
|
2
hwdb/.gitignore
vendored
Normal file
2
hwdb/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/pci.ids
|
||||||
|
/usb.ids
|
63420
hwdb/20-pci-vendor-product.hwdb
Normal file
63420
hwdb/20-pci-vendor-product.hwdb
Normal file
File diff suppressed because it is too large
Load Diff
47304
hwdb/20-usb-vendor-product.hwdb
Normal file
47304
hwdb/20-usb-vendor-product.hwdb
Normal file
File diff suppressed because it is too large
Load Diff
80
hwdb/ids-update.pl
Executable file
80
hwdb/ids-update.pl
Executable file
@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
my $vendor;
|
||||||
|
|
||||||
|
open(IN, "<", "usb.ids");
|
||||||
|
open(OUT, ">", "20-usb-vendor-product.hwdb");
|
||||||
|
print(OUT "# This file is part of systemd.\n" .
|
||||||
|
"#\n" .
|
||||||
|
"# Data imported and updated from: http://www.linux-usb.org/usb.ids\n");
|
||||||
|
|
||||||
|
while (my $line = <IN>) {
|
||||||
|
$line =~ s/\s+$//;
|
||||||
|
$line =~ m/^([0-9a-f]{4})\s*(.*)$/;
|
||||||
|
if (defined $1) {
|
||||||
|
$vendor = uc $1;
|
||||||
|
my $text = $2;
|
||||||
|
print(OUT "\n");
|
||||||
|
print(OUT "usb:v" . $vendor . "*\n");
|
||||||
|
print(OUT " ID_VENDOR_FROM_DATABASE=" . $text . "\n");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$line =~ m/^\t([0-9a-f]{4})\s*(.*)$/;
|
||||||
|
if (defined $1) {
|
||||||
|
my $product = uc $1;
|
||||||
|
my $text = $2;
|
||||||
|
print(OUT "\n");
|
||||||
|
print(OUT "usb:v" . $vendor . "p" . $product . "*\n");
|
||||||
|
print(OUT " ID_PRODUCT_FROM_DATABASE=" . $text . "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(INP);
|
||||||
|
close(OUTP);
|
||||||
|
|
||||||
|
|
||||||
|
my $device;
|
||||||
|
|
||||||
|
open(IN, "<", "pci.ids");
|
||||||
|
open(OUT, ">", "20-pci-vendor-product.hwdb");
|
||||||
|
print(OUT "# This file is part of systemd.\n" .
|
||||||
|
"#\n" .
|
||||||
|
"# Data imported and updated from: http://pciids.sourceforge.net/v2.2/pci.ids\n");
|
||||||
|
|
||||||
|
while (my $line = <IN>) {
|
||||||
|
$line =~ s/\s+$//;
|
||||||
|
$line =~ m/^([0-9a-f]{4})\s*(.*)$/;
|
||||||
|
if (defined $1) {
|
||||||
|
$vendor = uc $1;
|
||||||
|
my $text = $2;
|
||||||
|
print(OUT "\n");
|
||||||
|
print(OUT "pci:v0000" . $vendor . "*\n");
|
||||||
|
print(OUT " ID_VENDOR_FROM_DATABASE=" . $text . "\n");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$line =~ m/^\t([0-9a-f]{4})\s*(.*)$/;
|
||||||
|
if (defined $1) {
|
||||||
|
$device = uc $1;
|
||||||
|
my $text = $2;
|
||||||
|
print(OUT "\n");
|
||||||
|
print(OUT "pci:v0000" . $vendor . "d0000" . $device . "*\n");
|
||||||
|
print(OUT " ID_PRODUCT_FROM_DATABASE=" . $text . "\n");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$line =~ m/^\t\t([0-9a-f]{4})\s*([0-9a-f]{4})\s*(.*)$/;
|
||||||
|
if (defined $1) {
|
||||||
|
my $sub_vendor = uc $1;
|
||||||
|
my $sub_device = uc $2;
|
||||||
|
my $text = $3;
|
||||||
|
print(OUT "\n");
|
||||||
|
print(OUT "pci:v0000" . $vendor . "d0000" . $device . "sv0000" . $sub_vendor . "sd0000" . $sub_device . "*\n");
|
||||||
|
print(OUT " ID_PRODUCT_FROM_DATABASE=" . $text . "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(INP);
|
||||||
|
close(OUTP);
|
@ -43,7 +43,7 @@ SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video"
|
|||||||
|
|
||||||
# 'libusb' device nodes
|
# 'libusb' device nodes
|
||||||
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664"
|
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664"
|
||||||
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id"
|
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb"
|
||||||
|
|
||||||
# printer
|
# printer
|
||||||
KERNEL=="parport[0-9]*", GROUP="lp"
|
KERNEL=="parport[0-9]*", GROUP="lp"
|
||||||
|
@ -4,11 +4,11 @@ ACTION=="remove", GOTO="net_end"
|
|||||||
SUBSYSTEM!="net", GOTO="net_end"
|
SUBSYSTEM!="net", GOTO="net_end"
|
||||||
|
|
||||||
SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
|
SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
|
||||||
SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db"
|
SUBSYSTEMS=="usb", IMPORT{builtin}="hwdb"
|
||||||
SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}"
|
SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}"
|
||||||
SUBSYSTEMS=="usb", GOTO="net_end"
|
SUBSYSTEMS=="usb", GOTO="net_end"
|
||||||
|
|
||||||
SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db"
|
IMPORT{builtin}="hwdb"
|
||||||
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
|
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
|
||||||
|
|
||||||
LABEL="net_end"
|
LABEL="net_end"
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
ACTION=="remove", GOTO="tty_end"
|
ACTION=="remove", GOTO="tty_end"
|
||||||
SUBSYSTEM!="tty", GOTO="tty_end"
|
SUBSYSTEM!="tty", GOTO="tty_end"
|
||||||
|
|
||||||
|
IMPORT{builtin}="hwdb"
|
||||||
|
|
||||||
SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
|
SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
|
||||||
SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db"
|
|
||||||
SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}"
|
SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}"
|
||||||
SUBSYSTEMS=="usb", GOTO="tty_end"
|
SUBSYSTEMS=="usb", GOTO="tty_end"
|
||||||
|
|
||||||
SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db"
|
|
||||||
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
|
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
|
||||||
|
|
||||||
LABEL="tty_end"
|
LABEL="tty_end"
|
||||||
|
@ -37,8 +37,8 @@ KERNEL!="card*", GOTO="sound_end"
|
|||||||
|
|
||||||
ENV{SOUND_INITIALIZED}="1"
|
ENV{SOUND_INITIALIZED}="1"
|
||||||
|
|
||||||
|
IMPORT{builtin}="hwdb"
|
||||||
SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
|
SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
|
||||||
SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db"
|
|
||||||
SUBSYSTEMS=="usb", GOTO="skip_pci"
|
SUBSYSTEMS=="usb", GOTO="skip_pci"
|
||||||
|
|
||||||
SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \
|
SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \
|
||||||
@ -46,10 +46,7 @@ SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \
|
|||||||
SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", ENV{ID_ID}="firewire-$attr{guid}"
|
SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", ENV{ID_ID}="firewire-$attr{guid}"
|
||||||
SUBSYSTEMS=="firewire", GOTO="skip_pci"
|
SUBSYSTEMS=="firewire", GOTO="skip_pci"
|
||||||
|
|
||||||
|
|
||||||
SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db"
|
|
||||||
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
|
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
|
||||||
|
|
||||||
LABEL="skip_pci"
|
LABEL="skip_pci"
|
||||||
|
|
||||||
ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}-$attr{id}"
|
ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}-$attr{id}"
|
||||||
|
@ -1379,8 +1379,7 @@ int main(int argc, char *argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
char **files, **f;
|
char **files, **f;
|
||||||
|
|
||||||
r = conf_files_list_strv(&files, ".conf",
|
r = conf_files_list_strv(&files, ".conf", (const char **)conf_file_dirs);
|
||||||
(const char **) conf_file_dirs);
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
|
log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
|
||||||
r = EXIT_FAILURE;
|
r = EXIT_FAILURE;
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
/*
|
/***
|
||||||
* usb-db, pci-db - lookup vendor/product database
|
This file is part of systemd.
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Lennart Poettering <lennart@poettering.net>
|
Copyright 2012 Kay Sievers <kay.sievers@vrfy.org>
|
||||||
* Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org>
|
Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
systemd is free software; you can redistribute it and/or modify it
|
||||||
* it under the terms of the GNU General Public License as published by
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
* (at your option) any later version.
|
(at your option) any later version.
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
systemd is distributed in the hope that it will be useful, but
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
* GNU General Public License for more details.
|
Lesser General Public License for more details.
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
***/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -24,224 +24,352 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include "udev.h"
|
#include "udev.h"
|
||||||
|
#include "udev-hwdb.h"
|
||||||
|
|
||||||
static int get_id_attr(
|
struct linebuf {
|
||||||
struct udev_device *parent,
|
char bytes[LINE_MAX];
|
||||||
const char *name,
|
size_t size;
|
||||||
uint16_t *value) {
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
const char *t;
|
static void linebuf_init(struct linebuf *buf) {
|
||||||
unsigned u;
|
buf->size = 0;
|
||||||
|
buf->len = 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 (startswith(t, "0x"))
|
static const char *linebuf_get(struct linebuf *buf) {
|
||||||
t += 2;
|
if (buf->len + 1 >= sizeof(buf->bytes))
|
||||||
|
return NULL;
|
||||||
if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) {
|
buf->bytes[buf->len] = '\0';
|
||||||
fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent));
|
return buf->bytes;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*value = (uint16_t) u;
|
static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
|
||||||
return 0;
|
if (buf->len + len >= sizeof(buf->bytes))
|
||||||
|
return false;
|
||||||
|
memcpy(buf->bytes + buf->len, s, len);
|
||||||
|
buf->len += len;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_vid_pid(
|
static bool linebuf_add_char(struct linebuf *buf, char c)
|
||||||
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 (get_id_attr(parent, product_attr, pid) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rstrip(char *n) {
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = strlen(n); i > 0 && isspace(n[i-1]); i--)
|
|
||||||
n[i-1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#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)
|
|
||||||
{
|
{
|
||||||
|
if (buf->len + 1 >= sizeof(buf->bytes))
|
||||||
FILE *f;
|
return false;
|
||||||
int ret = -1;
|
buf->bytes[buf->len++] = c;
|
||||||
int found_vendor = 0;
|
return true;
|
||||||
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 (;;) {
|
static void linebuf_rem(struct linebuf *buf, size_t count) {
|
||||||
|
assert(buf->len >= count);
|
||||||
|
buf->len -= count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void linebuf_rem_char(struct linebuf *buf) {
|
||||||
|
linebuf_rem(buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct trie_f {
|
||||||
|
struct udev_device *dev;
|
||||||
|
bool test;
|
||||||
|
FILE *f;
|
||||||
|
uint64_t file_time_usec;
|
||||||
|
union {
|
||||||
|
struct trie_header_f *head;
|
||||||
|
const char *map;
|
||||||
|
};
|
||||||
|
size_t map_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct trie_child_entry_f *trie_node_children(struct trie_f *trie, const struct trie_node_f *node) {
|
||||||
|
return (const struct trie_child_entry_f *)((const char *)node + le64toh(trie->head->node_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct trie_value_entry_f *trie_node_values(struct trie_f *trie, const struct trie_node_f *node) {
|
||||||
|
const char *base = (const char *)node;
|
||||||
|
|
||||||
|
base += le64toh(trie->head->node_size);
|
||||||
|
base += node->children_count * le64toh(trie->head->child_entry_size);
|
||||||
|
return (const struct trie_value_entry_f *)base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct trie_node_f *trie_node_from_off(struct trie_f *trie, le64_t off) {
|
||||||
|
return (const struct trie_node_f *)(trie->map + le64toh(off));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *trie_string(struct trie_f *trie, le64_t off) {
|
||||||
|
return trie->map + le64toh(off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trie_children_cmp_f(const void *v1, const void *v2) {
|
||||||
|
const struct trie_child_entry_f *n1 = v1;
|
||||||
|
const struct trie_child_entry_f *n2 = v2;
|
||||||
|
|
||||||
|
return n1->c - n2->c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct trie_node_f *node_lookup_f(struct trie_f *trie, const struct trie_node_f *node, uint8_t c) {
|
||||||
|
struct trie_child_entry_f *child;
|
||||||
|
struct trie_child_entry_f search;
|
||||||
|
|
||||||
|
search.c = c;
|
||||||
|
child = bsearch(&search, trie_node_children(trie, node), node->children_count,
|
||||||
|
le64toh(trie->head->child_entry_size), trie_children_cmp_f);
|
||||||
|
if (child)
|
||||||
|
return trie_node_from_off(trie, child->child_off);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trie_fnmatch_f(struct trie_f *trie, const struct trie_node_f *node, size_t p,
|
||||||
|
struct linebuf *buf, const char *search,
|
||||||
|
void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
|
||||||
|
size_t len;
|
||||||
|
size_t i;
|
||||||
|
const char *prefix;
|
||||||
|
|
||||||
|
prefix = trie_string(trie, node->prefix_off);
|
||||||
|
len = strlen(prefix + p);
|
||||||
|
linebuf_add(buf, prefix + p, len);
|
||||||
|
|
||||||
|
for (i = 0; i < node->children_count; i++) {
|
||||||
|
const struct trie_child_entry_f *child = &trie_node_children(trie, node)[i];
|
||||||
|
|
||||||
|
linebuf_add_char(buf, child->c);
|
||||||
|
trie_fnmatch_f(trie, trie_node_from_off(trie, child->child_off), 0, buf, search, cb);
|
||||||
|
linebuf_rem_char(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->values_count && fnmatch(linebuf_get(buf), search, 0) == 0)
|
||||||
|
for (i = 0; i < node->values_count; i++)
|
||||||
|
cb(trie, trie_string(trie, trie_node_values(trie, node)[i].key_off),
|
||||||
|
trie_string(trie, trie_node_values(trie, node)[i].value_off));
|
||||||
|
|
||||||
|
linebuf_rem(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trie_search_f(struct trie_f *trie, const char *search,
|
||||||
|
void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
|
||||||
|
struct linebuf buf;
|
||||||
|
const struct trie_node_f *node;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
linebuf_init(&buf);
|
||||||
|
|
||||||
|
node = trie_node_from_off(trie, trie->head->nodes_root_off);
|
||||||
|
while (node) {
|
||||||
|
const struct trie_node_f *child;
|
||||||
|
size_t p = 0;
|
||||||
|
|
||||||
|
if (node->prefix_off) {
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
for (; (c = trie_string(trie, node->prefix_off)[p]); p++) {
|
||||||
|
if (c == '*' || c == '?' || c == '[') {
|
||||||
|
trie_fnmatch_f(trie, node, p, &buf, search + i + p, cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c != search[i + p])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i += p;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = node_lookup_f(trie, node, '*');
|
||||||
|
if (child) {
|
||||||
|
linebuf_add_char(&buf, '*');
|
||||||
|
trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
|
||||||
|
linebuf_rem_char(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
child = node_lookup_f(trie, node, '?');
|
||||||
|
if (child) {
|
||||||
|
linebuf_add_char(&buf, '?');
|
||||||
|
trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
|
||||||
|
linebuf_rem_char(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
child = node_lookup_f(trie, node, '[');
|
||||||
|
if (child) {
|
||||||
|
linebuf_add_char(&buf, '[');
|
||||||
|
trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
|
||||||
|
linebuf_rem_char(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search[i] == '\0') {
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
if (getline(&line, &n, f) < 0)
|
for (n = 0; n < node->values_count; n++)
|
||||||
break;
|
cb(trie, trie_string(trie, trie_node_values(trie, node)[n].key_off),
|
||||||
|
trie_string(trie, trie_node_values(trie, node)[n].value_off));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rstrip(line);
|
child = node_lookup_f(trie, node, search[i]);
|
||||||
|
node = child;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (line[0] == '#' || line[0] == 0)
|
static void value_cb(struct trie_f *trie, const char *key, const char *value) {
|
||||||
|
/* TODO: add sub-matches (+) against DMI data */
|
||||||
|
if (key[0] == ' ')
|
||||||
|
udev_builtin_add_property(trie->dev, trie->test, key + 1, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct trie_f trie;
|
||||||
|
|
||||||
|
static int hwdb_lookup(struct udev_device *dev, const char *subsys) {
|
||||||
|
struct udev_device *d;
|
||||||
|
const char *modalias;
|
||||||
|
char str[UTIL_NAME_SIZE];
|
||||||
|
int rc = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
/* search the first parent device with a modalias */
|
||||||
|
for (d = dev; d; d = udev_device_get_parent(d)) {
|
||||||
|
const char *dsubsys = udev_device_get_subsystem(d);
|
||||||
|
|
||||||
|
/* look only at devices of a specific subsystem */
|
||||||
|
if (subsys && dsubsys && !streq(dsubsys, subsys))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (strspn(line, HEXCHARS) == 4) {
|
modalias = udev_device_get_property_value(d, "MODALIAS");
|
||||||
unsigned u;
|
if (modalias)
|
||||||
|
|
||||||
if (found_vendor)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (sscanf(line, "%04x", &u) == 1 && u == vid) {
|
/* the usb_device does not have modalias, compose one */
|
||||||
char *t;
|
if (dsubsys && streq(dsubsys, "usb")) {
|
||||||
|
const char *v, *p;
|
||||||
t = line+4;
|
int vn, pn;
|
||||||
t += strspn(t, WHITESPACE);
|
|
||||||
|
|
||||||
if (!(*vendor = strdup(t))) {
|
|
||||||
fprintf(stderr, "Out of memory.\n");
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
found_vendor = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
v = udev_device_get_sysattr_value(d, "idVendor");
|
||||||
|
if (!v)
|
||||||
continue;
|
continue;
|
||||||
}
|
p = udev_device_get_sysattr_value(d, "idProduct");
|
||||||
|
if (!p)
|
||||||
if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) {
|
continue;
|
||||||
unsigned u;
|
vn = strtol(v, NULL, 16);
|
||||||
|
if (vn <= 0)
|
||||||
if (sscanf(line+1, "%04x", &u) == 1 && u == pid) {
|
continue;
|
||||||
char *t;
|
pn = strtol(p, NULL, 16);
|
||||||
|
if (pn <= 0)
|
||||||
t = line+5;
|
continue;
|
||||||
t += strspn(t, WHITESPACE);
|
snprintf(str, sizeof(str), "usb:v%04Xp%04X*", vn, pn);
|
||||||
|
modalias = str;
|
||||||
if (!(*product = strdup(t))) {
|
|
||||||
fprintf(stderr, "Out of memory.\n");
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!modalias)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
trie_search_f(&trie, modalias, value_cb);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
|
||||||
|
static const struct option options[] = {
|
||||||
|
{ "subsystem", required_argument, NULL, 's' },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
const char *subsys = NULL;
|
||||||
|
|
||||||
finish:
|
for (;;) {
|
||||||
free(line);
|
int option;
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
option = getopt_long(argc, argv, "s", options, NULL);
|
||||||
free(*product);
|
if (option == -1)
|
||||||
free(*vendor);
|
break;
|
||||||
|
|
||||||
*product = *vendor = NULL;
|
switch (option) {
|
||||||
|
case 's':
|
||||||
|
subsys = optarg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
trie.dev = dev;
|
||||||
|
trie.test = test;
|
||||||
|
if (hwdb_lookup(dev, subsys) < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
struct stat st;
|
||||||
|
const char sig[] = HWDB_SIG;
|
||||||
|
|
||||||
str = udev_device_get_subsystem(dev);
|
trie.f = fopen(SYSCONFDIR "/udev/hwdb.bin", "re");
|
||||||
if (str == NULL)
|
if (!trie.f)
|
||||||
goto try_parent;
|
return -errno;
|
||||||
if (strcmp(str, subsys) != 0)
|
|
||||||
goto try_parent;
|
|
||||||
|
|
||||||
if (devtype != NULL) {
|
if (fstat(fileno(trie.f), &st) < 0 || (size_t)st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
|
||||||
str = udev_device_get_devtype(dev);
|
log_error("Error reading '%s'.", SYSCONFDIR "/udev/hwdb.bin: %m");
|
||||||
if (str == NULL)
|
fclose(trie.f);
|
||||||
goto try_parent;
|
zero(trie);
|
||||||
if (strcmp(str, devtype) != 0)
|
return -EINVAL;
|
||||||
goto try_parent;
|
|
||||||
}
|
|
||||||
return dev;
|
|
||||||
try_parent:
|
|
||||||
return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trie.map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fileno(trie.f), 0);
|
||||||
|
if (trie.map == MAP_FAILED) {
|
||||||
|
log_error("Error mapping '%s'.", SYSCONFDIR "/udev/hwdb.bin: %m");
|
||||||
|
fclose(trie.f);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
trie.file_time_usec = ts_usec(&st.st_mtim);
|
||||||
|
trie.map_size = st.st_size;
|
||||||
|
|
||||||
static int builtin_db(struct udev_device *dev, bool test,
|
if (memcmp(trie.map, sig, sizeof(trie.head->signature)) != 0 || (size_t)st.st_size != le64toh(trie.head->file_size)) {
|
||||||
const char *database,
|
log_error("Unable to recognize the format of '%s'.", SYSCONFDIR "/udev/hwdb.bin");
|
||||||
const char *vendor_attr, const char *product_attr,
|
log_error("Please try 'udevadm hwdb --update' to re-create it.");
|
||||||
const char *subsys, const char *devtype)
|
munmap((void *)trie.map, st.st_size);
|
||||||
{
|
fclose(trie.f);
|
||||||
struct udev_device *parent;
|
zero(trie);
|
||||||
uint16_t vid = 0, pid = 0;
|
return EINVAL;
|
||||||
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)
|
log_debug("=== trie on-disk ===\n");
|
||||||
goto finish;
|
log_debug("tool version: %llu", (unsigned long long)le64toh(trie.head->tool_version));
|
||||||
|
log_debug("file size: %8zi bytes\n", st.st_size);
|
||||||
if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0)
|
log_debug("header size %8zu bytes\n", (size_t)le64toh(trie.head->header_size));
|
||||||
goto finish;
|
log_debug("strings %8zu bytes\n", (size_t)le64toh(trie.head->strings_len));
|
||||||
|
log_debug("nodes %8zu bytes\n", (size_t)le64toh(trie.head->nodes_len));
|
||||||
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);
|
|
||||||
return 0;
|
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");
|
if (!trie.f)
|
||||||
|
return;
|
||||||
|
munmap((void *)trie.map, trie.map_size);
|
||||||
|
fclose(trie.f);
|
||||||
|
zero(trie);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
struct stat st;
|
||||||
|
|
||||||
|
if (fstat(fileno(trie.f), &st) < 0)
|
||||||
|
return true;
|
||||||
|
if (trie.file_time_usec != ts_usec(&st.st_mtim))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct udev_builtin udev_builtin_usb_db = {
|
const struct udev_builtin udev_builtin_hwdb = {
|
||||||
.name = "usb-db",
|
.name = "hwdb",
|
||||||
.cmd = builtin_usb_db,
|
.cmd = builtin_hwdb,
|
||||||
.help = "USB vendor/product database",
|
.init = builtin_hwdb_init,
|
||||||
.run_once = true,
|
.exit = builtin_hwdb_exit,
|
||||||
};
|
.validate = builtin_hwdb_validate,
|
||||||
|
.help = "hardware database",
|
||||||
const struct udev_builtin udev_builtin_pci_db = {
|
|
||||||
.name = "pci-db",
|
|
||||||
.cmd = builtin_pci_db,
|
|
||||||
.help = "PCI vendor/product database",
|
|
||||||
.run_once = true,
|
.run_once = true,
|
||||||
};
|
};
|
||||||
|
@ -31,11 +31,10 @@ static const struct udev_builtin *builtins[] = {
|
|||||||
[UDEV_BUILTIN_BLKID] = &udev_builtin_blkid,
|
[UDEV_BUILTIN_BLKID] = &udev_builtin_blkid,
|
||||||
[UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs,
|
[UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs,
|
||||||
[UDEV_BUILTIN_FIRMWARE] = &udev_builtin_firmware,
|
[UDEV_BUILTIN_FIRMWARE] = &udev_builtin_firmware,
|
||||||
|
[UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb,
|
||||||
[UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id,
|
[UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id,
|
||||||
[UDEV_BUILTIN_KMOD] = &udev_builtin_kmod,
|
[UDEV_BUILTIN_KMOD] = &udev_builtin_kmod,
|
||||||
[UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id,
|
[UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id,
|
||||||
[UDEV_BUILTIN_PCI_DB] = &udev_builtin_pci_db,
|
|
||||||
[UDEV_BUILTIN_USB_DB] = &udev_builtin_usb_db,
|
|
||||||
[UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id,
|
[UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id,
|
||||||
#ifdef HAVE_ACL
|
#ifdef HAVE_ACL
|
||||||
[UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess,
|
[UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess,
|
||||||
@ -128,7 +127,7 @@ int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const c
|
|||||||
int argc;
|
int argc;
|
||||||
char *argv[128];
|
char *argv[128];
|
||||||
|
|
||||||
optind = 0;
|
optind = 1;
|
||||||
util_strscpy(arg, sizeof(arg), command);
|
util_strscpy(arg, sizeof(arg), command);
|
||||||
udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv);
|
udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv);
|
||||||
return builtins[cmd]->cmd(dev, argc, argv, test);
|
return builtins[cmd]->cmd(dev, argc, argv, test);
|
||||||
|
69
src/udev/udev-hwdb.h
Normal file
69
src/udev/udev-hwdb.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/***
|
||||||
|
This file is part of systemd.
|
||||||
|
|
||||||
|
Copyright 2012 Kay Sievers <kay.sievers@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 "sparse-endian.h"
|
||||||
|
|
||||||
|
#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' }
|
||||||
|
|
||||||
|
/* on-disk trie objects */
|
||||||
|
_packed_ struct trie_header_f {
|
||||||
|
uint8_t signature[8];
|
||||||
|
|
||||||
|
/* version of tool which created the file */
|
||||||
|
le64_t tool_version;
|
||||||
|
le64_t file_size;
|
||||||
|
|
||||||
|
/* size of structures to allow them to grow */
|
||||||
|
le64_t header_size;
|
||||||
|
le64_t node_size;
|
||||||
|
le64_t child_entry_size;
|
||||||
|
le64_t value_entry_size;
|
||||||
|
|
||||||
|
/* offset of the root trie node */
|
||||||
|
le64_t nodes_root_off;
|
||||||
|
|
||||||
|
/* size of the nodes and string section */
|
||||||
|
le64_t nodes_len;
|
||||||
|
le64_t strings_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
_packed_ struct trie_node_f {
|
||||||
|
/* prefix of lookup string, shared by all children */
|
||||||
|
le64_t prefix_off;
|
||||||
|
/* size of children entry array appended to the node */
|
||||||
|
uint8_t children_count;
|
||||||
|
uint8_t padding[7];
|
||||||
|
/* size of value entry array appended to the node */
|
||||||
|
le64_t values_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* array of child entries, follows directly the node record */
|
||||||
|
_packed_ struct trie_child_entry_f {
|
||||||
|
/* index of the child node */
|
||||||
|
uint8_t c;
|
||||||
|
uint8_t padding[7];
|
||||||
|
/* offset of the child node */
|
||||||
|
le64_t child_off;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* array of value entries, follows directly the node record/child array */
|
||||||
|
_packed_ struct trie_value_entry_f {
|
||||||
|
le64_t key_off;
|
||||||
|
le64_t value_off;
|
||||||
|
};
|
@ -137,11 +137,10 @@ enum udev_builtin_cmd {
|
|||||||
UDEV_BUILTIN_BLKID,
|
UDEV_BUILTIN_BLKID,
|
||||||
UDEV_BUILTIN_BTRFS,
|
UDEV_BUILTIN_BTRFS,
|
||||||
UDEV_BUILTIN_FIRMWARE,
|
UDEV_BUILTIN_FIRMWARE,
|
||||||
|
UDEV_BUILTIN_HWDB,
|
||||||
UDEV_BUILTIN_INPUT_ID,
|
UDEV_BUILTIN_INPUT_ID,
|
||||||
UDEV_BUILTIN_KMOD,
|
UDEV_BUILTIN_KMOD,
|
||||||
UDEV_BUILTIN_PATH_ID,
|
UDEV_BUILTIN_PATH_ID,
|
||||||
UDEV_BUILTIN_PCI_DB,
|
|
||||||
UDEV_BUILTIN_USB_DB,
|
|
||||||
UDEV_BUILTIN_USB_ID,
|
UDEV_BUILTIN_USB_ID,
|
||||||
#ifdef HAVE_ACL
|
#ifdef HAVE_ACL
|
||||||
UDEV_BUILTIN_UACCESS,
|
UDEV_BUILTIN_UACCESS,
|
||||||
@ -160,11 +159,10 @@ struct udev_builtin {
|
|||||||
extern const struct udev_builtin udev_builtin_blkid;
|
extern const struct udev_builtin udev_builtin_blkid;
|
||||||
extern const struct udev_builtin udev_builtin_btrfs;
|
extern const struct udev_builtin udev_builtin_btrfs;
|
||||||
extern const struct udev_builtin udev_builtin_firmware;
|
extern const struct udev_builtin udev_builtin_firmware;
|
||||||
|
extern const struct udev_builtin udev_builtin_hwdb;
|
||||||
extern const struct udev_builtin udev_builtin_input_id;
|
extern const struct udev_builtin udev_builtin_input_id;
|
||||||
extern const struct udev_builtin udev_builtin_kmod;
|
extern const struct udev_builtin udev_builtin_kmod;
|
||||||
extern const struct udev_builtin udev_builtin_path_id;
|
extern const struct udev_builtin udev_builtin_path_id;
|
||||||
extern const struct udev_builtin udev_builtin_pci_db;
|
|
||||||
extern const struct udev_builtin udev_builtin_usb_db;
|
|
||||||
extern const struct udev_builtin udev_builtin_usb_id;
|
extern const struct udev_builtin udev_builtin_usb_id;
|
||||||
extern const struct udev_builtin udev_builtin_uaccess;
|
extern const struct udev_builtin udev_builtin_uaccess;
|
||||||
int udev_builtin_init(struct udev *udev);
|
int udev_builtin_init(struct udev *udev);
|
||||||
@ -194,6 +192,7 @@ extern const struct udevadm_cmd udevadm_trigger;
|
|||||||
extern const struct udevadm_cmd udevadm_settle;
|
extern const struct udevadm_cmd udevadm_settle;
|
||||||
extern const struct udevadm_cmd udevadm_control;
|
extern const struct udevadm_cmd udevadm_control;
|
||||||
extern const struct udevadm_cmd udevadm_monitor;
|
extern const struct udevadm_cmd udevadm_monitor;
|
||||||
|
extern const struct udevadm_cmd udevadm_hwdb;
|
||||||
extern const struct udevadm_cmd udevadm_test;
|
extern const struct udevadm_cmd udevadm_test;
|
||||||
extern const struct udevadm_cmd udevadm_test_builtin;
|
extern const struct udevadm_cmd udevadm_test_builtin;
|
||||||
#endif
|
#endif
|
||||||
|
548
src/udev/udevadm-hwdb.c
Normal file
548
src/udev/udevadm-hwdb.c
Normal file
@ -0,0 +1,548 @@
|
|||||||
|
/***
|
||||||
|
This file is part of systemd.
|
||||||
|
|
||||||
|
Copyright 2012 Kay Sievers <kay.sievers@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 <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
#include "conf-files.h"
|
||||||
|
|
||||||
|
#include "udev.h"
|
||||||
|
#include "udev-hwdb.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic udev properties, key/value database based on modalias strings.
|
||||||
|
* Uses a Patricia/radix trie to index all matches for efficient lookup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char * const conf_file_dirs[] = {
|
||||||
|
SYSCONFDIR "/udev/hwdb.d",
|
||||||
|
UDEVLIBEXECDIR "/hwdb.d",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* in-memory trie objects */
|
||||||
|
struct trie {
|
||||||
|
struct trie_node *root;
|
||||||
|
struct strbuf *strings;
|
||||||
|
|
||||||
|
size_t nodes_count;
|
||||||
|
size_t children_count;
|
||||||
|
size_t values_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct trie_node {
|
||||||
|
/* prefix, common part for all children of this node */
|
||||||
|
size_t prefix_off;
|
||||||
|
|
||||||
|
/* sorted array of pointers to children nodes */
|
||||||
|
struct trie_child_entry *children;
|
||||||
|
uint8_t children_count;
|
||||||
|
|
||||||
|
/* sorted array of key/value pairs */
|
||||||
|
struct trie_value_entry *values;
|
||||||
|
size_t values_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* children array item with char (0-255) index */
|
||||||
|
struct trie_child_entry {
|
||||||
|
uint8_t c;
|
||||||
|
struct trie_node *child;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* value array item with key/value pairs */
|
||||||
|
struct trie_value_entry {
|
||||||
|
size_t key_off;
|
||||||
|
size_t value_off;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int trie_children_cmp(const void *v1, const void *v2) {
|
||||||
|
const struct trie_child_entry *n1 = v1;
|
||||||
|
const struct trie_child_entry *n2 = v2;
|
||||||
|
|
||||||
|
return n1->c - n2->c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
|
||||||
|
struct trie_child_entry *child;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* extend array, add new entry, sort for bisection */
|
||||||
|
child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry));
|
||||||
|
if (!child) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
node->children = child;
|
||||||
|
trie->children_count++;
|
||||||
|
node->children[node->children_count].c = c;
|
||||||
|
node->children[node->children_count].child = node_child;
|
||||||
|
node->children_count++;
|
||||||
|
qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
|
||||||
|
trie->nodes_count++;
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
|
||||||
|
struct trie_child_entry *child;
|
||||||
|
struct trie_child_entry search;
|
||||||
|
|
||||||
|
search.c = c;
|
||||||
|
child = bsearch(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
|
||||||
|
if (child)
|
||||||
|
return child->child;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trie_node_cleanup(struct trie_node *node) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < node->children_count; i++)
|
||||||
|
trie_node_cleanup(node->children[i].child);
|
||||||
|
free(node->children);
|
||||||
|
free(node->values);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trie_values_cmp(const void *v1, const void *v2, void *arg) {
|
||||||
|
const struct trie_value_entry *val1 = v1;
|
||||||
|
const struct trie_value_entry *val2 = v2;
|
||||||
|
struct trie *trie = arg;
|
||||||
|
|
||||||
|
return strcmp(trie->strings->buf + val1->key_off,
|
||||||
|
trie->strings->buf + val2->key_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trie_node_add_value(struct trie *trie, struct trie_node *node,
|
||||||
|
const char *key, const char *value) {
|
||||||
|
size_t k, v;
|
||||||
|
struct trie_value_entry *val;
|
||||||
|
struct trie_value_entry search;
|
||||||
|
|
||||||
|
k = strbuf_add_string(trie->strings, key, strlen(key));
|
||||||
|
v = strbuf_add_string(trie->strings, value, strlen(value));
|
||||||
|
|
||||||
|
/* replace existing earlier key with new value */
|
||||||
|
search.value_off = k;
|
||||||
|
val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
|
||||||
|
if (val) {
|
||||||
|
val->value_off = v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extend array, add new entry, sort for bisection */
|
||||||
|
val = realloc(node->values, (node->values_count + 1) * sizeof(struct trie_value_entry));
|
||||||
|
if (!val)
|
||||||
|
return -ENOMEM;
|
||||||
|
trie->values_count++;
|
||||||
|
node->values = val;
|
||||||
|
node->values[node->values_count].key_off = k;
|
||||||
|
node->values[node->values_count].value_off = v;
|
||||||
|
node->values_count++;
|
||||||
|
qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
|
||||||
|
const char *key, const char *value) {
|
||||||
|
size_t i = 0;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
size_t p;
|
||||||
|
uint8_t c;
|
||||||
|
struct trie_node *child;
|
||||||
|
|
||||||
|
for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
|
||||||
|
char *s;
|
||||||
|
ssize_t off;
|
||||||
|
|
||||||
|
if (c == search[i + p])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* split node */
|
||||||
|
child = calloc(sizeof(struct trie_node), 1);
|
||||||
|
if (!child) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move values from parent to child */
|
||||||
|
child->prefix_off = node->prefix_off + p+1;
|
||||||
|
child->children = node->children;
|
||||||
|
child->children_count = node->children_count;
|
||||||
|
child->values = node->values;
|
||||||
|
child->values_count = node->values_count;
|
||||||
|
|
||||||
|
/* update parent; use strdup() because the source gets realloc()d */
|
||||||
|
s = strndup(trie->strings->buf + node->prefix_off, p);
|
||||||
|
if (!s) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
off = strbuf_add_string(trie->strings, s, p);
|
||||||
|
free(s);
|
||||||
|
if (off < 0) {
|
||||||
|
err = off;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
node->prefix_off = off;
|
||||||
|
node->children = NULL;
|
||||||
|
node->children_count = 0;
|
||||||
|
node->values = NULL;
|
||||||
|
node->values_count = 0;
|
||||||
|
err = node_add_child(trie, node, child, c);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += p;
|
||||||
|
|
||||||
|
c = search[i];
|
||||||
|
if (c == '\0')
|
||||||
|
return trie_node_add_value(trie, node, key, value);
|
||||||
|
|
||||||
|
child = node_lookup(node, c);
|
||||||
|
if (!child) {
|
||||||
|
ssize_t off;
|
||||||
|
|
||||||
|
/* new child */
|
||||||
|
child = calloc(sizeof(struct trie_node), 1);
|
||||||
|
if (!child) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
|
||||||
|
if (off < 0) {
|
||||||
|
err = off;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
child->prefix_off = off;
|
||||||
|
err = node_add_child(trie, node, child, c);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
return trie_node_add_value(trie, child, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = child;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct trie_f {
|
||||||
|
FILE *f;
|
||||||
|
struct trie *trie;
|
||||||
|
uint64_t strings_off;
|
||||||
|
|
||||||
|
uint64_t nodes_count;
|
||||||
|
uint64_t children_count;
|
||||||
|
uint64_t values_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* calculate the storage space for the nodes, children arrays, value arrays */
|
||||||
|
static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) {
|
||||||
|
uint64_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < node->children_count; i++)
|
||||||
|
trie_store_nodes_size(trie, node->children[i].child);
|
||||||
|
|
||||||
|
trie->strings_off += sizeof(struct trie_node_f);
|
||||||
|
for (i = 0; i < node->children_count; i++)
|
||||||
|
trie->strings_off += sizeof(struct trie_child_entry_f);
|
||||||
|
for (i = 0; i < node->values_count; i++)
|
||||||
|
trie->strings_off += sizeof(struct trie_value_entry_f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
|
||||||
|
uint64_t i;
|
||||||
|
struct trie_node_f n = {
|
||||||
|
.prefix_off = htole64(trie->strings_off + node->prefix_off),
|
||||||
|
.children_count = node->children_count,
|
||||||
|
.values_count = htole64(node->values_count),
|
||||||
|
};
|
||||||
|
struct trie_child_entry_f *children;
|
||||||
|
int64_t node_off;
|
||||||
|
|
||||||
|
if (node->children_count) {
|
||||||
|
children = new0(struct trie_child_entry_f, node->children_count);
|
||||||
|
if (!children)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* post-order recursion */
|
||||||
|
for (i = 0; i < node->children_count; i++) {
|
||||||
|
int64_t child_off;
|
||||||
|
|
||||||
|
child_off = trie_store_nodes(trie, node->children[i].child);
|
||||||
|
if (child_off < 0)
|
||||||
|
return child_off;
|
||||||
|
children[i].c = node->children[i].c;
|
||||||
|
children[i].child_off = htole64(child_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write node */
|
||||||
|
node_off = ftello(trie->f);
|
||||||
|
fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
|
||||||
|
trie->nodes_count++;
|
||||||
|
|
||||||
|
/* append children array */
|
||||||
|
if (node->children_count) {
|
||||||
|
fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
|
||||||
|
trie->children_count += node->children_count;
|
||||||
|
free(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append values array */
|
||||||
|
for (i = 0; i < node->values_count; i++) {
|
||||||
|
struct trie_value_entry_f v = {
|
||||||
|
.key_off = htole64(trie->strings_off + node->values[i].key_off),
|
||||||
|
.value_off = htole64(trie->strings_off + node->values[i].value_off),
|
||||||
|
};
|
||||||
|
|
||||||
|
fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f);
|
||||||
|
trie->values_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trie_store(struct trie *trie, const char *filename) {
|
||||||
|
struct trie_f t = {
|
||||||
|
.trie = trie,
|
||||||
|
};
|
||||||
|
char *filename_tmp;
|
||||||
|
int64_t pos;
|
||||||
|
int64_t root_off;
|
||||||
|
int64_t size;
|
||||||
|
struct trie_header_f h = {
|
||||||
|
.signature = HWDB_SIG,
|
||||||
|
.tool_version = htole64(atoi(VERSION)),
|
||||||
|
.header_size = htole64(sizeof(struct trie_header_f)),
|
||||||
|
.node_size = htole64(sizeof(struct trie_node_f)),
|
||||||
|
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
|
||||||
|
.value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
|
||||||
|
};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* calculate size of header, nodes, children entries, value entries */
|
||||||
|
t.strings_off = sizeof(struct trie_header_f);
|
||||||
|
trie_store_nodes_size(&t, trie->root);
|
||||||
|
|
||||||
|
err = fopen_temporary(filename , &t.f, &filename_tmp);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
fchmod(fileno(t.f), 0444);
|
||||||
|
|
||||||
|
/* write nodes */
|
||||||
|
fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET);
|
||||||
|
root_off = trie_store_nodes(&t, trie->root);
|
||||||
|
h.nodes_root_off = htole64(root_off);
|
||||||
|
pos = ftello(t.f);
|
||||||
|
h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
|
||||||
|
|
||||||
|
/* write string buffer */
|
||||||
|
fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
|
||||||
|
h.strings_len = htole64(trie->strings->len);
|
||||||
|
|
||||||
|
/* write header */
|
||||||
|
size = ftello(t.f);
|
||||||
|
h.file_size = htole64(size);
|
||||||
|
fseeko(t.f, 0, SEEK_SET);
|
||||||
|
fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
|
||||||
|
err = ferror(t.f);
|
||||||
|
if (err)
|
||||||
|
err = -errno;
|
||||||
|
fclose(t.f);
|
||||||
|
if (err < 0 || rename(filename_tmp, filename) < 0) {
|
||||||
|
unlink(filename_tmp);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("=== trie on-disk ===\n");
|
||||||
|
log_debug("size: %8zi bytes\n", size);
|
||||||
|
log_debug("header: %8zu bytes\n", sizeof(struct trie_header_f));
|
||||||
|
log_debug("nodes: %8zu bytes (%8zi)\n", t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
|
||||||
|
log_debug("child pointers: %8zu bytes (%8zi)\n", t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
|
||||||
|
log_debug("value pointers: %8zu bytes (%8zi)\n", t.values_count * sizeof(struct trie_value_entry_f), t.values_count);
|
||||||
|
log_debug("string store: %8zu bytes\n", trie->strings->len);
|
||||||
|
log_debug("strings start: %8llu\n", (unsigned long long) t.strings_off);
|
||||||
|
out:
|
||||||
|
free(filename_tmp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int import_file(struct trie *trie, const char *filename) {
|
||||||
|
FILE *f;
|
||||||
|
char line[LINE_MAX];
|
||||||
|
char match[LINE_MAX];
|
||||||
|
|
||||||
|
f = fopen(filename, "re");
|
||||||
|
if (f == NULL)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
match[0] = '\0';
|
||||||
|
while (fgets(line, sizeof(line), f)) {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* new line, new record */
|
||||||
|
if (line[0] == '\n') {
|
||||||
|
match[0] = '\0';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove newline */
|
||||||
|
len = strlen(line);
|
||||||
|
if (len < 2)
|
||||||
|
continue;
|
||||||
|
line[len-1] = '\0';
|
||||||
|
|
||||||
|
/* start of new record */
|
||||||
|
if (match[0] == '\0') {
|
||||||
|
strcpy(match, line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* value lines */
|
||||||
|
if (line[0] == ' ') {
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
value = strchr(line, '=');
|
||||||
|
if (!value)
|
||||||
|
continue;
|
||||||
|
value[0] = '\0';
|
||||||
|
value++;
|
||||||
|
trie_insert(trie, trie->root, match, line, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help(void) {
|
||||||
|
printf("Usage: udevadm hwdb [--create] [--help]\n"
|
||||||
|
" --update update the hardware database\n"
|
||||||
|
" --help\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
|
||||||
|
static const struct option options[] = {
|
||||||
|
{ "update", no_argument, NULL, 'u' },
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
bool update = false;
|
||||||
|
struct trie *trie;
|
||||||
|
char **files, **f;
|
||||||
|
int err;
|
||||||
|
int rc = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int option;
|
||||||
|
|
||||||
|
option = getopt_long(argc, argv, "ch", options, NULL);
|
||||||
|
if (option == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case 'u':
|
||||||
|
update = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
help();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
help();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
trie = calloc(sizeof(struct trie), 1);
|
||||||
|
if (!trie) {
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* string store */
|
||||||
|
trie->strings = strbuf_new();
|
||||||
|
if (!trie->strings) {
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* index */
|
||||||
|
trie->root = calloc(sizeof(struct trie_node), 1);
|
||||||
|
if (!trie->root) {
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
trie->nodes_count++;
|
||||||
|
|
||||||
|
err = conf_files_list_strv(&files, ".hwdb", (const char **)conf_file_dirs);
|
||||||
|
if (err < 0) {
|
||||||
|
log_error("failed to enumerate hwdb files: %s\n", strerror(-err));
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
STRV_FOREACH(f, files) {
|
||||||
|
log_debug("reading file '%s'", *f);
|
||||||
|
import_file(trie, *f);
|
||||||
|
}
|
||||||
|
strv_free(files);
|
||||||
|
|
||||||
|
strbuf_complete(trie->strings);
|
||||||
|
|
||||||
|
log_debug("=== trie in-memory ===\n");
|
||||||
|
log_debug("nodes: %8zu bytes (%8zu)\n", trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
|
||||||
|
log_debug("children arrays: %8zu bytes (%8zu)\n", trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
|
||||||
|
log_debug("values arrays: %8zu bytes (%8zu)\n", trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
|
||||||
|
log_debug("strings: %8zu bytes\n", trie->strings->len);
|
||||||
|
log_debug("strings incoming: %8zu bytes (%8zu)\n", trie->strings->in_len, trie->strings->in_count);
|
||||||
|
log_debug("strings dedup'ed: %8zu bytes (%8zu)\n", trie->strings->dedup_len, trie->strings->dedup_count);
|
||||||
|
|
||||||
|
mkdir_parents(SYSCONFDIR "/udev/hwdb.bin", 0755);
|
||||||
|
err = trie_store(trie, SYSCONFDIR "/udev/hwdb.bin");
|
||||||
|
if (err < 0) {
|
||||||
|
log_error("Failure writing hardware database '%s': %s", SYSCONFDIR "/udev/hwdb.bin", strerror(-err));
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (trie->root)
|
||||||
|
trie_node_cleanup(trie->root);
|
||||||
|
strbuf_cleanup(trie->strings);
|
||||||
|
free(trie);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct udevadm_cmd udevadm_hwdb = {
|
||||||
|
.name = "hwdb",
|
||||||
|
.cmd = adm_hwdb,
|
||||||
|
.help = "maintain the hardware database index",
|
||||||
|
};
|
@ -56,6 +56,7 @@ static const struct udevadm_cmd *udevadm_cmds[] = {
|
|||||||
&udevadm_settle,
|
&udevadm_settle,
|
||||||
&udevadm_control,
|
&udevadm_control,
|
||||||
&udevadm_monitor,
|
&udevadm_monitor,
|
||||||
|
&udevadm_hwdb,
|
||||||
&udevadm_test,
|
&udevadm_test,
|
||||||
&udevadm_test_builtin,
|
&udevadm_test_builtin,
|
||||||
&udevadm_version,
|
&udevadm_version,
|
||||||
@ -133,7 +134,7 @@ int main(int argc, char *argv[])
|
|||||||
if (strcmp(udevadm_cmds[i]->name, command) == 0) {
|
if (strcmp(udevadm_cmds[i]->name, command) == 0) {
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
optind = 0;
|
optind = 1;
|
||||||
rc = run_command(udev, udevadm_cmds[i], argc, argv);
|
rc = run_command(udev, udevadm_cmds[i], argc, argv);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user