2012-10-22 20:23:08 +04:00
/***
This file is part of systemd .
2012-11-12 22:36:23 +04:00
Copyright 2012 Kay Sievers < kay @ vrfy . org >
2012-10-22 20:23:08 +04:00
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>
2013-07-10 16:59:24 +04:00
# include <ctype.h>
2012-10-22 20:23:08 +04:00
# include "util.h"
# include "strbuf.h"
# include "conf-files.h"
# include "udev.h"
2012-10-27 22:59:01 +04:00
# include "libudev-hwdb-def.h"
2012-10-22 20:23:08 +04:00
/*
* 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 [ ] = {
2012-10-27 22:59:01 +04:00
" /etc/udev/hwdb.d " ,
2012-10-22 20:23:08 +04:00
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 ;
/* extend array, add new entry, sort for bisection */
child = realloc ( node - > children , ( node - > children_count + 1 ) * sizeof ( struct trie_child_entry ) ) ;
2013-04-06 01:39:46 +04:00
if ( ! child )
return - ENOMEM ;
2012-10-22 20:23:08 +04:00
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 + + ;
2013-04-06 01:39:46 +04:00
return 0 ;
2012-10-22 20:23:08 +04:00
}
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 ) {
2012-10-25 23:39:01 +04:00
ssize_t k , v ;
2012-10-22 20:23:08 +04:00
struct trie_value_entry * val ;
k = strbuf_add_string ( trie - > strings , key , strlen ( key ) ) ;
2012-10-25 23:39:01 +04:00
if ( k < 0 )
return k ;
2012-10-22 20:23:08 +04:00
v = strbuf_add_string ( trie - > strings , value , strlen ( value ) ) ;
2012-10-25 23:39:01 +04:00
if ( v < 0 )
return v ;
if ( node - > values_count ) {
struct trie_value_entry search = {
. key_off = k ,
. value_off = v ,
} ;
val = xbsearch_r ( & search , node - > values , node - > values_count , sizeof ( struct trie_value_entry ) , trie_values_cmp , trie ) ;
if ( val ) {
/* replace existing earlier key with new value */
val - > value_off = v ;
return 0 ;
}
2012-10-22 20:23:08 +04:00
}
/* 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 ;
2013-04-06 01:39:46 +04:00
struct trie_node * child ;
2012-10-22 20:23:08 +04:00
for ( p = 0 ; ( c = trie - > strings - > buf [ node - > prefix_off + p ] ) ; p + + ) {
2013-04-18 11:11:22 +04:00
_cleanup_free_ char * s = NULL ;
2012-10-22 20:23:08 +04:00
ssize_t off ;
2013-04-18 11:11:22 +04:00
_cleanup_free_ struct trie_node * new_child = NULL ;
2012-10-22 20:23:08 +04:00
if ( c = = search [ i + p ] )
continue ;
/* split node */
2013-04-06 01:39:46 +04:00
new_child = calloc ( sizeof ( struct trie_node ) , 1 ) ;
if ( ! new_child )
return - ENOMEM ;
2012-10-22 20:23:08 +04:00
/* move values from parent to child */
2013-04-06 01:39:46 +04:00
new_child - > prefix_off = node - > prefix_off + p + 1 ;
new_child - > children = node - > children ;
new_child - > children_count = node - > children_count ;
new_child - > values = node - > values ;
new_child - > values_count = node - > values_count ;
2012-10-22 20:23:08 +04:00
/* update parent; use strdup() because the source gets realloc()d */
s = strndup ( trie - > strings - > buf + node - > prefix_off , p ) ;
2013-04-06 01:39:46 +04:00
if ( ! s )
return - ENOMEM ;
2012-10-22 20:23:08 +04:00
off = strbuf_add_string ( trie - > strings , s , p ) ;
2013-04-06 01:39:46 +04:00
if ( off < 0 )
return off ;
2012-10-22 20:23:08 +04:00
node - > prefix_off = off ;
node - > children = NULL ;
node - > children_count = 0 ;
node - > values = NULL ;
node - > values_count = 0 ;
2013-04-06 01:39:46 +04:00
err = node_add_child ( trie , node , new_child , c ) ;
2012-10-22 20:23:08 +04:00
if ( err )
2013-04-06 01:39:46 +04:00
return err ;
new_child = NULL ; /* avoid cleanup */
2012-10-22 20:23:08 +04:00
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 ) ;
2013-04-06 01:39:46 +04:00
if ( ! child )
return - ENOMEM ;
2012-10-22 20:23:08 +04:00
off = strbuf_add_string ( trie - > strings , search + i + 1 , strlen ( search + i + 1 ) ) ;
if ( off < 0 ) {
2013-04-06 01:39:46 +04:00
free ( child ) ;
return off ;
2012-10-22 20:23:08 +04:00
}
2013-04-06 01:39:46 +04:00
2012-10-22 20:23:08 +04:00
child - > prefix_off = off ;
err = node_add_child ( trie , node , child , c ) ;
2013-04-06 01:39:46 +04:00
if ( err ) {
free ( child ) ;
return err ;
}
2012-10-22 20:23:08 +04:00
return trie_node_add_value ( trie , child , key , value ) ;
}
node = child ;
i + + ;
}
}
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 ) ,
} ;
2012-10-27 22:59:01 +04:00
struct trie_child_entry_f * children = NULL ;
2012-10-22 20:23:08 +04:00
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 ) ;
2013-07-19 03:36:55 +04:00
if ( child_off < 0 ) {
free ( children ) ;
2012-10-22 20:23:08 +04:00
return child_off ;
2013-07-19 03:36:55 +04:00
}
2012-10-22 20:23:08 +04:00
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 ;
}
2013-12-24 19:39:37 +04:00
log_debug ( " === trie on-disk === " ) ;
log_debug ( " size: %8llu bytes " , ( unsigned long long ) size ) ;
log_debug ( " header: %8zu bytes " , sizeof ( struct trie_header_f ) ) ;
log_debug ( " nodes: %8llu bytes (%8llu) " ,
2012-10-27 22:59:01 +04:00
( unsigned long long ) t . nodes_count * sizeof ( struct trie_node_f ) , ( unsigned long long ) t . nodes_count ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " child pointers: %8llu bytes (%8llu) " ,
2012-10-27 22:59:01 +04:00
( unsigned long long ) t . children_count * sizeof ( struct trie_child_entry_f ) , ( unsigned long long ) t . children_count ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " value pointers: %8llu bytes (%8llu) " ,
2012-10-27 22:59:01 +04:00
( unsigned long long ) t . values_count * sizeof ( struct trie_value_entry_f ) , ( unsigned long long ) t . values_count ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " string store: %8llu bytes " , ( unsigned long long ) trie - > strings - > len ) ;
log_debug ( " strings start: %8llu " , ( unsigned long long ) t . strings_off ) ;
2012-10-22 20:23:08 +04:00
out :
free ( filename_tmp ) ;
return err ;
}
2013-07-10 16:59:24 +04:00
static int insert_data ( struct trie * trie , struct udev_list * match_list ,
char * line , const char * filename ) {
char * value ;
struct udev_list_entry * entry ;
value = strchr ( line , ' = ' ) ;
if ( ! value ) {
2013-12-24 19:39:37 +04:00
log_error ( " Error, key/value pair expected but got '%s' in '%s': " , line , filename ) ;
2013-07-10 16:59:24 +04:00
return - EINVAL ;
}
value [ 0 ] = ' \0 ' ;
value + + ;
if ( line [ 0 ] = = ' \0 ' | | value [ 0 ] = = ' \0 ' ) {
2013-12-24 19:39:37 +04:00
log_error ( " Error, empty key or value '%s' in '%s': " , line , filename ) ;
2013-07-10 16:59:24 +04:00
return - EINVAL ;
}
udev_list_entry_foreach ( entry , udev_list_get_entry ( match_list ) )
trie_insert ( trie , trie - > root , udev_list_entry_get_name ( entry ) , line , value ) ;
return 0 ;
}
static int import_file ( struct udev * udev , struct trie * trie , const char * filename ) {
enum {
HW_MATCH ,
HW_DATA ,
HW_NONE ,
} state = HW_NONE ;
2012-10-22 20:23:08 +04:00
FILE * f ;
char line [ LINE_MAX ] ;
2013-07-10 16:59:24 +04:00
struct udev_list match_list ;
udev_list_init ( udev , & match_list , false ) ;
2012-10-22 20:23:08 +04:00
f = fopen ( filename , " re " ) ;
if ( f = = NULL )
return - errno ;
while ( fgets ( line , sizeof ( line ) , f ) ) {
size_t len ;
2013-07-10 16:59:24 +04:00
char * pos ;
2012-10-22 20:23:08 +04:00
2013-07-10 16:59:24 +04:00
/* comment line */
2012-10-22 20:23:08 +04:00
if ( line [ 0 ] = = ' # ' )
continue ;
2013-07-10 16:59:24 +04:00
/* strip trailing comment */
pos = strchr ( line , ' # ' ) ;
if ( pos )
pos [ 0 ] = ' \0 ' ;
2012-10-22 20:23:08 +04:00
2013-07-10 16:59:24 +04:00
/* strip trailing whitespace */
2012-10-22 20:23:08 +04:00
len = strlen ( line ) ;
2013-07-10 16:59:24 +04:00
while ( len > 0 & & isspace ( line [ len - 1 ] ) )
len - - ;
line [ len ] = ' \0 ' ;
switch ( state ) {
case HW_NONE :
if ( len = = 0 )
break ;
if ( line [ 0 ] = = ' ' ) {
2013-12-24 19:39:37 +04:00
log_error ( " Error, MATCH expected but got '%s' in '%s': " , line , filename ) ;
2013-07-10 16:59:24 +04:00
break ;
}
2012-10-22 20:23:08 +04:00
2013-07-10 16:59:24 +04:00
/* start of record, first match */
state = HW_MATCH ;
udev_list_entry_add ( & match_list , line , NULL ) ;
break ;
2012-10-22 20:23:08 +04:00
2013-07-10 16:59:24 +04:00
case HW_MATCH :
if ( len = = 0 ) {
2013-12-24 19:39:37 +04:00
log_error ( " Error, DATA expected but got empty line in '%s': " , filename ) ;
2013-07-10 16:59:24 +04:00
state = HW_NONE ;
udev_list_cleanup ( & match_list ) ;
break ;
}
2012-10-22 20:23:08 +04:00
2013-07-10 16:59:24 +04:00
/* another match */
if ( line [ 0 ] ! = ' ' ) {
udev_list_entry_add ( & match_list , line , NULL ) ;
break ;
}
/* first data */
state = HW_DATA ;
insert_data ( trie , & match_list , line , filename ) ;
break ;
case HW_DATA :
/* end of record */
if ( len = = 0 ) {
state = HW_NONE ;
udev_list_cleanup ( & match_list ) ;
break ;
}
if ( line [ 0 ] ! = ' ' ) {
2013-12-24 19:39:37 +04:00
log_error ( " Error, DATA expected but got '%s' in '%s': " , line , filename ) ;
2013-07-10 16:59:24 +04:00
state = HW_NONE ;
udev_list_cleanup ( & match_list ) ;
break ;
}
2013-07-01 21:39:50 +04:00
2013-07-10 16:59:24 +04:00
insert_data ( trie , & match_list , line , filename ) ;
break ;
} ;
2012-10-22 20:23:08 +04:00
}
2013-07-10 16:59:24 +04:00
2012-10-22 20:23:08 +04:00
fclose ( f ) ;
2013-07-10 16:59:24 +04:00
udev_list_cleanup ( & match_list ) ;
2012-10-22 20:23:08 +04:00
return 0 ;
}
static void help ( void ) {
2012-11-21 05:47:48 +04:00
printf ( " Usage: udevadm hwdb OPTIONS \n "
2013-12-18 06:48:14 +04:00
" -u,--update update the hardware database \n "
" -t,--test=MODALIAS query database and print result \n "
" -r,--root=PATH alternative root path in the filesystem \n "
" -h,--help \n \n " ) ;
2012-10-22 20:23:08 +04:00
}
static int adm_hwdb ( struct udev * udev , int argc , char * argv [ ] ) {
static const struct option options [ ] = {
2013-12-18 06:48:14 +04:00
{ " update " , no_argument , NULL , ' u ' } ,
{ " test " , required_argument , NULL , ' t ' } ,
{ " root " , required_argument , NULL , ' r ' } ,
{ " help " , no_argument , NULL , ' h ' } ,
2012-10-22 20:23:08 +04:00
{ }
} ;
2012-10-28 07:41:15 +04:00
const char * test = NULL ;
2013-02-08 15:00:05 +04:00
const char * root = " " ;
2012-10-22 20:23:08 +04:00
bool update = false ;
2012-10-28 07:41:15 +04:00
struct trie * trie = NULL ;
2013-12-18 06:48:14 +04:00
int err , c ;
2012-10-22 20:23:08 +04:00
int rc = EXIT_SUCCESS ;
2013-12-18 06:48:14 +04:00
while ( ( c = getopt_long ( argc , argv , " ut:r:h " , options , NULL ) ) > = 0 )
switch ( c ) {
2012-10-22 20:23:08 +04:00
case ' u ' :
update = true ;
break ;
2012-10-28 07:41:15 +04:00
case ' t ' :
test = optarg ;
break ;
2013-02-08 15:00:05 +04:00
case ' r ' :
root = optarg ;
break ;
2012-10-22 20:23:08 +04:00
case ' h ' :
help ( ) ;
return EXIT_SUCCESS ;
2013-12-18 06:48:14 +04:00
case ' ? ' :
return EXIT_FAILURE ;
default :
assert_not_reached ( " Unknown option " ) ;
2012-10-22 20:23:08 +04:00
}
2012-10-28 07:41:15 +04:00
if ( ! update & & ! test ) {
2013-12-18 06:48:14 +04:00
log_error ( " Either --update or --test must be used " ) ;
return EXIT_FAILURE ;
2012-10-22 20:23:08 +04:00
}
2012-10-28 07:41:15 +04:00
if ( update ) {
char * * files , * * f ;
2013-02-08 15:00:05 +04:00
_cleanup_free_ char * hwdb_bin = NULL ;
2012-10-22 20:23:08 +04:00
2012-10-28 07:41:15 +04:00
trie = calloc ( sizeof ( struct trie ) , 1 ) ;
if ( ! trie ) {
rc = EXIT_FAILURE ;
goto out ;
}
2012-10-22 20:23:08 +04:00
2012-10-28 07:41:15 +04:00
/* string store */
trie - > strings = strbuf_new ( ) ;
if ( ! trie - > strings ) {
rc = EXIT_FAILURE ;
goto out ;
}
2012-10-22 20:23:08 +04:00
2012-10-28 07:41:15 +04:00
/* index */
trie - > root = calloc ( sizeof ( struct trie_node ) , 1 ) ;
if ( ! trie - > root ) {
rc = EXIT_FAILURE ;
goto out ;
}
trie - > nodes_count + + ;
2013-03-29 04:17:24 +04:00
err = conf_files_list_strv ( & files , " .hwdb " , root , conf_file_dirs ) ;
2012-10-28 07:41:15 +04:00
if ( err < 0 ) {
2013-12-24 19:39:37 +04:00
log_error ( " failed to enumerate hwdb files: %s " , strerror ( - err ) ) ;
2012-10-28 07:41:15 +04:00
rc = EXIT_FAILURE ;
goto out ;
}
STRV_FOREACH ( f , files ) {
log_debug ( " reading file '%s' " , * f ) ;
2013-07-10 16:59:24 +04:00
import_file ( udev , trie , * f ) ;
2012-10-28 07:41:15 +04:00
}
strv_free ( files ) ;
strbuf_complete ( trie - > strings ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " === trie in-memory === " ) ;
log_debug ( " nodes: %8zu bytes (%8zu) " ,
2012-10-31 04:55:51 +04:00
trie - > nodes_count * sizeof ( struct trie_node ) , trie - > nodes_count ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " children arrays: %8zu bytes (%8zu) " ,
2012-10-31 04:55:51 +04:00
trie - > children_count * sizeof ( struct trie_child_entry ) , trie - > children_count ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " values arrays: %8zu bytes (%8zu) " ,
2012-10-31 04:55:51 +04:00
trie - > values_count * sizeof ( struct trie_value_entry ) , trie - > values_count ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " strings: %8zu bytes " ,
2012-10-31 04:55:51 +04:00
trie - > strings - > len ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " strings incoming: %8zu bytes (%8zu) " ,
2012-10-31 04:55:51 +04:00
trie - > strings - > in_len , trie - > strings - > in_count ) ;
2013-12-24 19:39:37 +04:00
log_debug ( " strings dedup'ed: %8zu bytes (%8zu) " ,
2012-10-31 04:55:51 +04:00
trie - > strings - > dedup_len , trie - > strings - > dedup_count ) ;
2012-10-28 07:41:15 +04:00
2013-02-08 15:00:05 +04:00
if ( asprintf ( & hwdb_bin , " %s/etc/udev/hwdb.bin " , root ) < 0 ) {
rc = EXIT_FAILURE ;
goto out ;
}
mkdir_parents ( hwdb_bin , 0755 ) ;
err = trie_store ( trie , hwdb_bin ) ;
2012-10-28 07:41:15 +04:00
if ( err < 0 ) {
2013-02-08 15:00:05 +04:00
log_error ( " Failure writing database %s: %s " , hwdb_bin , strerror ( - err ) ) ;
2012-10-28 07:41:15 +04:00
rc = EXIT_FAILURE ;
}
2012-10-22 20:23:08 +04:00
}
2012-10-28 07:41:15 +04:00
if ( test ) {
struct udev_hwdb * hwdb = udev_hwdb_new ( udev ) ;
if ( hwdb ) {
struct udev_list_entry * entry ;
udev_list_entry_foreach ( entry , udev_hwdb_get_properties_list_entry ( hwdb , test , 0 ) )
printf ( " %s=%s \n " , udev_list_entry_get_name ( entry ) , udev_list_entry_get_value ( entry ) ) ;
2013-10-12 03:34:17 +04:00
udev_hwdb_unref ( hwdb ) ;
2012-10-28 07:41:15 +04:00
}
2012-10-22 20:23:08 +04:00
}
out :
2012-10-28 07:41:15 +04:00
if ( trie ) {
if ( trie - > root )
trie_node_cleanup ( trie - > root ) ;
strbuf_cleanup ( trie - > strings ) ;
free ( trie ) ;
}
2012-10-22 20:23:08 +04:00
return rc ;
}
const struct udevadm_cmd udevadm_hwdb = {
. name = " hwdb " ,
. cmd = adm_hwdb ,
. help = " maintain the hardware database index " ,
} ;