Dynamically get the udevadm hwdb files with a path variable

Introduce UDEV_HWDB_PATH, containing a colon-separated path with hwdb
files in it. The whole path is searched, in addition to the system
locations, when searching for hwdb files.

Due to how conf_files_list_strv is implemented, as a thin wrapper
around the internal function, it is easy to also implement a variant
accepting a search path in the same way. Since the internal function
expects an allocated array of allocated strings, the path scanning is
done in 2 steps, to first get the array length, and then duplicate the
items. It is not possible to use strtok(_r) here because it would work
for only 1 pass.

* src/shared/conf-files.c (conf_files_list_follow_path): New function.
(conf_files_list_strv_path): Use it here.
* src/shared/conf-files.h: Export the signature for the _path variant.
* src/udev/udevadm-hwdb.c (list_conf_file_path): New function
replacing conf_file_dirs.
(help): Document the search path for hwdb files.
(adm_hwdb): Use list_conf_file_path and conf_files_list_strv_path.
* man/udevadm.xml: Mention UDEV_HWDB_PATH.
* man/udev.xml: Same.
* man/udevadm.8: Regenerate.
* man/udev.7: Same.
This commit is contained in:
Vivien Kraus 2023-09-26 18:17:34 +02:00
parent 0d86dd9a2a
commit 65e17b2f2f
7 changed files with 117 additions and 21 deletions

View File

@ -34,7 +34,7 @@ All device information udev processes is stored in the udev database and sent ou
.SH "RULES FILES"
.PP
The udev rules are read from the files located in the system rules directory
/lib/udev/rules\&.d (additionally /usr/lib/udev/rules\&.d when built with --enable-split-usr), the volatile runtime directory
/lib/udev/rules\&.d, the volatile runtime directory
/run/udev/rules\&.d
and the local administration directory
/etc/udev/rules\&.d\&. All rules files are collectively sorted and processed in lexical order, regardless of the directories in which they live\&. However, files with identical filenames replace each other\&. Files in
@ -42,7 +42,7 @@ and the local administration directory
have the highest priority, files in
/run
take precedence over files with the same name in
/lib (or /usr/lib)\&. This can be used to override a system\-supplied rules file with a local file if needed; a symlink in
/lib\&. This can be used to override a system\-supplied rules file with a local file if needed; a symlink in
/etc
with the same name as a rules file in
/lib, pointing to
@ -535,13 +535,17 @@ character itself\&.
The hwdb files are read from the files located in the system hwdb directory
/usr/lib/udev/hwdb\&.d, the volatile runtime directory
/run/udev/hwdb\&.d
and the local administration directory
/etc/udev/hwdb\&.d\&. All hwdb files are collectively sorted and processed in lexical order, regardless of the directories in which they live\&. However, files with identical filenames replace each other\&. Files in
the local administration directory
/etc/udev/hwdb\&.d, and any other directory in the
\fBUDEV_HWDB_PATH\fR
search path variable\&. All hwdb files are collectively sorted and processed in lexical order, regardless of the directories in which they live\&. However, files with identical filenames replace each other\&. Files in
/etc
have the highest priority, files in
/run
take precedence over files with the same name in
/usr/lib\&. This can be used to override a system\-supplied hwdb file with a local file if needed; a symlink in
/usr/lib, and the content of
\fBUDEV_HWDB_PATH\fR
comes last\&. This order can be used to override a system\-supplied hwdb file with a local file if needed; a symlink in
/etc
with the same name as a hwdb file in
/usr/lib, pointing to

View File

@ -736,12 +736,15 @@
<para>The hwdb files are read from the files located in the
system hwdb directory <filename>/usr/lib/udev/hwdb.d</filename>,
the volatile runtime directory <filename>/run/udev/hwdb.d</filename>
and the local administration directory <filename>/etc/udev/hwdb.d</filename>.
the local administration directory <filename>/etc/udev/hwdb.d</filename>,
and any other directory in the <envar>UDEV_HWDB_PATH</envar> search path variable.
All hwdb files are collectively sorted and processed in lexical order,
regardless of the directories in which they live. However, files with
identical filenames replace each other. Files in <filename>/etc</filename>
have the highest priority, files in <filename>/run</filename> take precedence
over files with the same name in <filename>/usr/lib</filename>. This can be
have the highest priority, files in <filename>/run</filename>
take precedence over files with the same name in
<filename>/usr/lib</filename>, and the content of
<envar>UDEV_HWDB_PATH</envar> comes last. This order can be
used to override a system-supplied hwdb file with a local file if needed;
a symlink in <filename>/etc</filename> with the same name as a hwdb file in
<filename>/usr/lib</filename>, pointing to <filename>/dev/null</filename>,

View File

@ -356,7 +356,11 @@ Maintain the hardware database index in
.PP
\fB\-u\fR, \fB\-\-update\fR
.RS 4
Compile the hardware database information located in /usr/lib/udev/hwdb\&.d/, /etc/udev/hwdb\&.d/ and store it in
Compile the hardware database information located in
/etc/udev/hwdb\&.d/,
/usr/lib/udev/hwdb\&.d/, and under the
\fBUDEV_HWDB_PATH\fR
path environment variable, and store it in
/etc/udev/hwdb\&.bin\&. This should be done after any update to the source files; it will not be called automatically\&. The running udev daemon will detect a new database on its own and does not need to be notified about it\&.
.RE
.PP

View File

@ -521,11 +521,16 @@
<term><option>-u</option></term>
<term><option>--update</option></term>
<listitem>
<para>Compile the hardware database information located in /usr/lib/udev/hwdb.d/,
/etc/udev/hwdb.d/ and store it in <filename>/etc/udev/hwdb.bin</filename>. This should be done after
any update to the source files; it will not be called automatically. The running
udev daemon will detect a new database on its own and does not need to be
notified about it.</para>
<para>Compile the hardware database information located in
<filename>/etc/udev/hwdb.d/</filename>,
<filename>/usr/lib/udev/hwdb.d/</filename>, and under the
<envar>UDEV_HWDB_PATH</envar> path environment variable,
and store it in
<filename>/etc/udev/hwdb.bin</filename>. This should be
done after any update to the source files; it will not be
called automatically. The running udev daemon will detect
a new database on its own and does not need to be notified
about it.</para>
</listitem>
</varlistentry>
<varlistentry>

View File

@ -143,3 +143,64 @@ int conf_files_list_strv(char ***strv, const char *suffix, const char *root, con
return conf_files_list_strv_internal(strv, suffix, root, copy);
}
/* Copy every non-empty path element from path_variable to
destination, and add a final NULL pointer. If destination is NULL,
then only count the number of non-empty path elements. Return
-ENOMEM if the copy failed, or the number of elements. */
static ssize_t conf_files_list_follow_path (const char *path_variable, char **destination) {
/* This function is not using strtok_r, because two passes are
required: the first one to count the number of elements,
the second one to actually copy them once an array of the
correct size has been allocated. */
size_t n = 0;
if (path_variable != NULL) {
while (path_variable[0] == ':') {
path_variable++;
}
while (path_variable[0] != '\0') {
const char *end = strchr(path_variable, ':');
if (end == NULL) {
end = path_variable + strlen(path_variable);
}
if (destination != NULL) {
destination[n] = strndup(path_variable, end - path_variable);
if (destination[n] == NULL) {
return -ENOMEM;
}
}
n++;
path_variable = end;
while (path_variable[0] == ':') {
path_variable++;
}
}
}
if (destination != NULL) {
destination[n] = NULL;
}
return n;
}
/* path is a colon-separated list of directories. */
int conf_files_list_strv_path(char ***strv, const char *suffix, const char *root, const char* path) {
_cleanup_strv_free_ char **copy = NULL;
assert(strv);
assert(suffix);
ssize_t path_length = conf_files_list_follow_path(path, NULL);
if (path_length < 0)
return -ENOMEM;
copy = new0(char *, path_length + 1);
if (!copy)
return -ENOMEM;
ssize_t error = conf_files_list_follow_path(path, copy);
if (error < 0)
return -ENOMEM;
assert (error == path_length);
return conf_files_list_strv_internal(strv, suffix, root, copy);
}

View File

@ -23,3 +23,4 @@
#include "macro.h"
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs);
int conf_files_list_strv_path(char ***strv, const char *suffix, const char *root, const char* path);

View File

@ -35,11 +35,17 @@
* Uses a Patricia/radix trie to index all matches for efficient lookup.
*/
static const char * const conf_file_dirs[] = {
UDEV_HWDB_DIR,
UDEV_LIBEXEC_DIR "/hwdb.d",
NULL
};
static char *list_conf_file_path (void) {
static const char *main_hwdb_dir = UDEV_HWDB_DIR;
static const char *libexec_dir = UDEV_LIBEXEC_DIR "/hwdb.d";
const char *path_variable = getenv ("UDEV_HWDB_PATH");
/* UDEV_HWDB_PATH comes last, so that it cannot override
system settings. */
/* System settings can be overriden by putting the files in
/etc. */
/* path_variable may be NULL, strjoin works either way. */
return strjoin(main_hwdb_dir, ":", libexec_dir, ":", path_variable, NULL);
}
/* in-memory trie objects */
struct trie {
@ -567,7 +573,12 @@ static void help(void) {
" --usr generate in " UDEV_LIBEXEC_DIR " instead of /etc/udev\n"
" -t,--test=MODALIAS query database and print result\n"
" -r,--root=PATH alternative root path in the filesystem\n"
" -h,--help\n\n");
" -h,--help\n"
"\n"
"The HWDB is searched in "
UDEV_HWDB_DIR ", " UDEV_LIBEXEC_DIR "/hwdb.d, "
"and the UDEV_HWDB_PATH search path.\n"
"\n");
}
static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
@ -644,7 +655,14 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
}
trie->nodes_count++;
err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs);
char *conf_file_path = list_conf_file_path ();
if (conf_file_path == NULL) {
rc = EXIT_FAILURE;
goto out;
}
err = conf_files_list_strv_path(&files, ".hwdb", root,
conf_file_path);
free (conf_file_path);
if (err < 0) {
log_error_errno(err, "failed to enumerate hwdb files: %m");
rc = EXIT_FAILURE;