2003-07-17 12:24:51 +04:00
/*
* Copyright ( C ) 2003 Greg Kroah - Hartman < greg @ kroah . com >
2008-09-10 04:40:42 +04:00
* Copyright ( C ) 2003 - 2008 Kay Sievers < kay . sievers @ vrfy . org >
2003-07-17 12:24:51 +04:00
*
2008-09-10 04:40:42 +04:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
2006-04-28 19:52:09 +04:00
*
2008-09-10 04:40:42 +04:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2003-07-17 12:24:51 +04:00
*
2008-09-10 04:40:42 +04:00
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2003-07-17 12:24:51 +04:00
*/
# include <stddef.h>
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <fcntl.h>
# include <ctype.h>
# include <unistd.h>
# include <errno.h>
2008-03-13 18:00:25 +03:00
# include <dirent.h>
2006-08-24 11:03:15 +04:00
# include <fnmatch.h>
2003-07-17 12:24:51 +04:00
# include "udev.h"
2008-10-18 17:02:01 +04:00
struct udev_rules {
struct udev * udev ;
char * buf ;
size_t bufsize ;
int resolve_names ;
} ;
struct udev_rules_iter {
struct udev_rules * rules ;
size_t current ;
} ;
2008-10-18 16:33:37 +04:00
enum key_operation {
KEY_OP_UNSET ,
KEY_OP_MATCH ,
KEY_OP_NOMATCH ,
KEY_OP_ADD ,
KEY_OP_ASSIGN ,
KEY_OP_ASSIGN_FINAL ,
} ;
struct key {
enum key_operation operation ;
size_t val_off ;
} ;
struct key_pair {
struct key key ;
size_t key_name_off ;
} ;
2008-10-18 17:02:01 +04:00
# define PAIRS_MAX 5
2008-10-18 16:33:37 +04:00
struct key_pairs {
int count ;
struct key_pair keys [ PAIRS_MAX ] ;
} ;
enum import_type {
IMPORT_UNSET ,
IMPORT_PROGRAM ,
IMPORT_FILE ,
IMPORT_PARENT ,
} ;
enum escape_type {
ESCAPE_UNSET ,
ESCAPE_NONE ,
ESCAPE_REPLACE ,
} ;
struct udev_rule {
struct key action ;
struct key devpath ;
struct key kernel ;
struct key subsystem ;
struct key driver ;
struct key_pairs attr ;
struct key kernels ;
struct key subsystems ;
struct key drivers ;
struct key_pairs attrs ;
struct key_pairs env ;
struct key program ;
struct key result ;
struct key import ;
enum import_type import_type ;
struct key test ;
mode_t test_mode_mask ;
struct key run ;
struct key wait_for ;
struct key label ;
struct key goto_label ;
size_t goto_rule_off ;
struct key name ;
struct key symlink ;
struct key symlink_match ;
struct key owner ;
struct key group ;
struct key mode ;
enum escape_type string_escape ;
unsigned int link_priority ;
int event_timeout ;
unsigned int partitions ;
unsigned int last_rule : 1 ,
run_ignore_error : 1 ,
ignore_device : 1 ,
ignore_remove : 1 ;
size_t bufsize ;
char buf [ ] ;
} ;
static void udev_rules_iter_init ( struct udev_rules_iter * iter , struct udev_rules * rules )
{
dbg ( rules - > udev , " bufsize=%zi \n " , rules - > bufsize ) ;
iter - > rules = rules ;
iter - > current = 0 ;
}
static struct udev_rule * udev_rules_iter_next ( struct udev_rules_iter * iter )
{
struct udev_rules * rules ;
struct udev_rule * rule ;
rules = iter - > rules ;
if ( ! rules )
return NULL ;
dbg ( rules - > udev , " current=%zi \n " , iter - > current ) ;
if ( iter - > current > = rules - > bufsize ) {
dbg ( rules - > udev , " no more rules \n " ) ;
return NULL ;
}
/* get next rule */
rule = ( struct udev_rule * ) ( rules - > buf + iter - > current ) ;
iter - > current + = sizeof ( struct udev_rule ) + rule - > bufsize ;
return rule ;
}
static struct udev_rule * udev_rules_iter_goto ( struct udev_rules_iter * iter , size_t rule_off )
{
struct udev_rules * rules = iter - > rules ;
struct udev_rule * rule ;
dbg ( rules - > udev , " current=%zi \n " , iter - > current ) ;
iter - > current = rule_off ;
rule = ( struct udev_rule * ) ( rules - > buf + iter - > current ) ;
return rule ;
}
static size_t find_label ( const struct udev_rules_iter * iter , const char * label )
{
struct udev_rule * rule ;
struct udev_rules * rules = iter - > rules ;
size_t current = iter - > current ;
next :
dbg ( rules - > udev , " current=%zi \n " , current ) ;
if ( current > = rules - > bufsize ) {
dbg ( rules - > udev , " LABEL='%s' not found \n " , label ) ;
return 0 ;
}
rule = ( struct udev_rule * ) ( rules - > buf + current ) ;
if ( strcmp ( & rule - > buf [ rule - > label . val_off ] , label ) ! = 0 ) {
dbg ( rules - > udev , " moving forward, looking for label '%s' \n " , label ) ;
current + = sizeof ( struct udev_rule ) + rule - > bufsize ;
goto next ;
}
dbg ( rules - > udev , " found label '%s' \n " , label ) ;
return current ;
}
[PATCH] Adding '%s' format specifier to NAME and SYMLINK
On Thu, Feb 12, 2004 at 05:34:57PM -0800, Greg KH wrote:
> On Tue, Feb 10, 2004 at 09:14:20AM +0100, Hannes Reinecke wrote:
> > Hi all,
> >
> > this patch makes the format for NAME and SYMLINK a bit more flexible:
> > I've added a new format specifier '%s{<SYSFS_var>}', which allows for
> > the value of any sysfs entry found for this device to be inserted.
> > Example (for our S/390 fcp adapter):
> >
> > BUS="ccw", SYSFS_devtype="1732/03", NAME="%k" \
> > SYMLINK="zfcp-%s{hba_id}-%s{wwpn}:%s{fcp_lun}"
> >
> > I know this could also be done with an external program, but having this
> > incorporated into udev makes life easier, especially if run from
> > initramfs. Plus it makes the rules easier to follow, as the result is
> > directly visible and need not to be looked up in some external program.
> >
> > Comments etc. welcome.
>
> Oops, sorry I missed this for the 017 release. I'll look at it tomorrow
> and get back to you. At first glance it looks like a good thing.
>
> Oh, you forgot to update the documentation, that's important to do if
> you want this change to make it in :)
I took a part of the code and made a version that uses already implemented
attribute finding logic.
The parsing of the format length '%3x' and the '%x{attribute}' is a fuction now,
maybe there are more possible users in the future.
I've also added the test to udev-test.pl.
2004-02-17 08:36:34 +03:00
2008-10-17 00:41:52 +04:00
static int import_property_from_string ( struct udev_device * dev , char * line )
2005-06-25 15:10:16 +04:00
{
2008-10-17 00:41:52 +04:00
struct udev * udev = udev_device_get_udev ( dev ) ;
char * key ;
char * val ;
size_t len ;
2005-06-25 15:10:16 +04:00
2008-10-17 00:41:52 +04:00
/* find key */
key = line ;
while ( isspace ( key [ 0 ] ) )
key + + ;
2005-06-25 15:10:16 +04:00
2008-10-17 00:41:52 +04:00
/* comment or empty line */
if ( key [ 0 ] = = ' # ' | | key [ 0 ] = = ' \0 ' )
return - 1 ;
2007-09-11 19:17:29 +04:00
2008-10-17 00:41:52 +04:00
/* split key/value */
val = strchr ( key , ' = ' ) ;
if ( val = = NULL )
return - 1 ;
val [ 0 ] = ' \0 ' ;
val + + ;
2008-10-16 19:16:58 +04:00
2008-10-17 00:41:52 +04:00
/* find value */
while ( isspace ( val [ 0 ] ) )
val + + ;
/* terminate key */
len = strlen ( key ) ;
if ( len = = 0 )
return - 1 ;
while ( isspace ( key [ len - 1 ] ) )
len - - ;
key [ len ] = ' \0 ' ;
/* terminate value */
len = strlen ( val ) ;
if ( len = = 0 )
return - 1 ;
while ( isspace ( val [ len - 1 ] ) )
len - - ;
val [ len ] = ' \0 ' ;
if ( len = = 0 )
return - 1 ;
/* unquote */
if ( val [ 0 ] = = ' " ' | | val [ 0 ] = = ' \' ' ) {
if ( val [ len - 1 ] ! = val [ 0 ] ) {
info ( udev , " inconsistent quoting: '%s', skip \n " , line ) ;
return - 1 ;
2005-06-25 15:10:16 +04:00
}
2008-10-17 00:41:52 +04:00
val [ len - 1 ] = ' \0 ' ;
val + + ;
}
info ( udev , " adding '%s'='%s' \n " , key , val ) ;
/* handle device, renamed by external tool, returning new path */
if ( strcmp ( key , " DEVPATH " ) = = 0 ) {
char syspath [ UTIL_PATH_SIZE ] ;
info ( udev , " updating devpath from '%s' to '%s' \n " ,
udev_device_get_devpath ( dev ) , val ) ;
util_strlcpy ( syspath , udev_get_sys_path ( udev ) , sizeof ( syspath ) ) ;
util_strlcat ( syspath , val , sizeof ( syspath ) ) ;
udev_device_set_syspath ( dev , syspath ) ;
} else {
struct udev_list_entry * entry ;
entry = udev_device_add_property ( dev , key , val ) ;
/* store in db */
udev_list_entry_set_flag ( entry , 1 ) ;
2005-06-25 15:10:16 +04:00
}
2005-06-25 17:35:14 +04:00
return 0 ;
}
2008-10-17 00:41:52 +04:00
static int import_file_into_env ( struct udev_device * dev , const char * filename )
2005-06-25 17:35:14 +04:00
{
2008-10-17 00:41:52 +04:00
FILE * f ;
char line [ UTIL_LINE_SIZE ] ;
2005-06-25 17:35:14 +04:00
2008-10-17 00:41:52 +04:00
f = fopen ( filename , " r " ) ;
if ( f = = NULL )
2005-06-25 17:35:14 +04:00
return - 1 ;
2008-10-17 02:40:03 +04:00
while ( fgets ( line , sizeof ( line ) , f ) ! = NULL )
2008-10-17 00:41:52 +04:00
import_property_from_string ( dev , line ) ;
fclose ( f ) ;
2005-06-25 17:35:14 +04:00
return 0 ;
2005-06-25 15:10:16 +04:00
}
2008-10-17 00:41:52 +04:00
static int import_program_into_env ( struct udev_device * dev , const char * program )
2005-06-25 20:58:49 +04:00
{
2008-10-18 17:25:05 +04:00
struct udev * udev = udev_device_get_udev ( dev ) ;
char * * envp ;
2007-05-16 21:51:13 +04:00
char result [ 2048 ] ;
2005-06-25 20:58:49 +04:00
size_t reslen ;
2008-10-17 00:41:52 +04:00
char * line ;
2005-06-25 20:58:49 +04:00
2008-10-18 17:25:05 +04:00
envp = udev_device_get_properties_envp ( dev ) ;
2008-10-18 22:12:55 +04:00
if ( util_run_program ( udev , program , envp , result , sizeof ( result ) , & reslen ) ! = 0 )
2005-06-25 20:58:49 +04:00
return - 1 ;
2008-10-17 00:41:52 +04:00
line = result ;
while ( line ! = NULL ) {
char * pos ;
pos = strchr ( line , ' \n ' ) ;
if ( pos ! = NULL ) {
pos [ 0 ] = ' \0 ' ;
pos = & pos [ 1 ] ;
}
import_property_from_string ( dev , line ) ;
line = pos ;
}
return 0 ;
2005-06-25 20:58:49 +04:00
}
2008-10-17 00:41:52 +04:00
static int import_parent_into_env ( struct udev_device * dev , const char * filter )
2005-07-12 16:46:36 +04:00
{
2008-10-17 00:41:52 +04:00
struct udev * udev = udev_device_get_udev ( dev ) ;
2008-10-16 19:16:58 +04:00
struct udev_device * dev_parent ;
struct udev_list_entry * list_entry ;
2005-07-12 16:46:36 +04:00
2008-10-17 00:41:52 +04:00
dev_parent = udev_device_get_parent ( dev ) ;
2008-10-16 19:16:58 +04:00
if ( dev_parent = = NULL )
return - 1 ;
2005-07-12 16:46:36 +04:00
2008-10-17 00:41:52 +04:00
dbg ( udev , " found parent '%s', get the node name \n " , udev_device_get_syspath ( dev_parent ) ) ;
2008-10-16 19:16:58 +04:00
udev_list_entry_foreach ( list_entry , udev_device_get_properties_list_entry ( dev_parent ) ) {
const char * key = udev_list_entry_get_name ( list_entry ) ;
const char * val = udev_list_entry_get_value ( list_entry ) ;
2005-07-12 16:46:36 +04:00
2008-10-16 19:16:58 +04:00
if ( fnmatch ( filter , key , 0 ) = = 0 ) {
struct udev_list_entry * entry ;
2007-07-14 22:59:03 +04:00
2008-10-17 00:41:52 +04:00
dbg ( udev , " import key '%s=%s' \n " , key , val ) ;
entry = udev_device_add_property ( dev , key , val ) ;
2008-10-16 19:16:58 +04:00
/* store in db */
udev_list_entry_set_flag ( entry , 1 ) ;
}
2007-07-14 22:59:03 +04:00
}
2008-10-16 19:16:58 +04:00
return 0 ;
2007-07-14 22:59:03 +04:00
}
2005-11-12 06:17:48 +03:00
# define WAIT_LOOP_PER_SECOND 50
2008-10-16 19:16:58 +04:00
static int wait_for_file ( struct udev_event * event , const char * file , int timeout )
2005-07-07 22:05:51 +04:00
{
2008-09-10 23:50:21 +04:00
char filepath [ UTIL_PATH_SIZE ] ;
char devicepath [ UTIL_PATH_SIZE ] = " " ;
2005-07-07 22:05:51 +04:00
struct stat stats ;
int loop = timeout * WAIT_LOOP_PER_SECOND ;
2008-05-20 17:02:17 +04:00
/* a relative path is a device attribute */
if ( file [ 0 ] ! = ' / ' ) {
2008-10-16 19:16:58 +04:00
util_strlcpy ( devicepath , udev_get_sys_path ( event - > udev ) , sizeof ( devicepath ) ) ;
util_strlcat ( devicepath , udev_device_get_devpath ( event - > dev ) , sizeof ( devicepath ) ) ;
2005-07-07 22:05:51 +04:00
2008-09-10 20:59:42 +04:00
util_strlcpy ( filepath , devicepath , sizeof ( filepath ) ) ;
util_strlcat ( filepath , " / " , sizeof ( filepath ) ) ;
util_strlcat ( filepath , file , sizeof ( filepath ) ) ;
2008-05-20 17:02:17 +04:00
file = filepath ;
}
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " will wait %i sec for '%s' \n " , timeout , file ) ;
2005-07-07 22:05:51 +04:00
while ( - - loop ) {
2006-02-15 22:43:28 +03:00
/* lookup file */
2008-05-20 17:02:17 +04:00
if ( stat ( file , & stats ) = = 0 ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " file '%s' appeared after %i loops \n " , file , ( timeout * WAIT_LOOP_PER_SECOND ) - loop - 1 ) ;
2005-07-07 22:05:51 +04:00
return 0 ;
}
2007-03-19 11:56:53 +03:00
/* make sure, the device did not disappear in the meantime */
2008-05-20 17:02:17 +04:00
if ( devicepath [ 0 ] ! = ' \0 ' & & stat ( devicepath , & stats ) ! = 0 ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " device disappeared while waiting for '%s' \n " , file ) ;
2006-02-15 22:43:28 +03:00
return - 2 ;
}
2008-10-16 19:16:58 +04:00
info ( event - > udev , " wait for '%s' for %i mseconds \n " , file , 1000 / WAIT_LOOP_PER_SECOND ) ;
2005-07-07 22:05:51 +04:00
usleep ( 1000 * 1000 / WAIT_LOOP_PER_SECOND ) ;
}
2008-10-16 19:16:58 +04:00
info ( event - > udev , " waiting for '%s' failed \n " , file ) ;
2005-07-07 22:05:51 +04:00
return - 1 ;
}
2008-03-15 01:40:06 +03:00
static int attr_subst_subdir ( char * attr , size_t len )
{
char * pos ;
int found = 0 ;
pos = strstr ( attr , " /*/ " ) ;
if ( pos ! = NULL ) {
2008-09-10 23:50:21 +04:00
char str [ UTIL_PATH_SIZE ] ;
2008-03-15 01:40:06 +03:00
DIR * dir ;
pos [ 1 ] = ' \0 ' ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( str , & pos [ 2 ] , sizeof ( str ) ) ;
2008-03-15 01:40:06 +03:00
dir = opendir ( attr ) ;
if ( dir ! = NULL ) {
struct dirent * dent ;
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
struct stat stats ;
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
2008-09-10 20:59:42 +04:00
util_strlcat ( attr , dent - > d_name , len ) ;
util_strlcat ( attr , str , len ) ;
2008-03-15 01:40:06 +03:00
if ( stat ( attr , & stats ) = = 0 ) {
found = 1 ;
break ;
}
pos [ 1 ] = ' \0 ' ;
}
closedir ( dir ) ;
}
if ( ! found )
2008-09-10 20:59:42 +04:00
util_strlcat ( attr , str , len ) ;
2008-03-15 01:40:06 +03:00
}
return found ;
}
2005-07-05 17:24:41 +04:00
static char * key_val ( struct udev_rule * rule , struct key * key )
{
return rule - > buf + key - > val_off ;
}
static char * key_pair_name ( struct udev_rule * rule , struct key_pair * pair )
{
return rule - > buf + pair - > key_name_off ;
}
2008-09-06 17:45:31 +04:00
static int match_key ( struct udev * udev , const char * key_name , struct udev_rule * rule , struct key * key , const char * val )
2005-07-04 21:44:25 +04:00
{
2008-09-10 23:50:21 +04:00
char value [ UTIL_PATH_SIZE ] ;
2005-07-05 17:24:41 +04:00
char * key_value ;
2005-07-12 14:52:56 +04:00
char * pos ;
2007-03-31 04:46:32 +04:00
int match = 0 ;
2005-07-04 21:44:25 +04:00
2006-04-24 21:25:55 +04:00
if ( key - > operation ! = KEY_OP_MATCH & &
key - > operation ! = KEY_OP_NOMATCH )
2005-07-04 21:44:25 +04:00
return 0 ;
2008-10-16 19:16:58 +04:00
if ( val = = NULL )
val = " " ;
2007-03-31 04:46:32 +04:00
/* look for a matching string, parts are separated by '|' */
2008-09-10 20:59:42 +04:00
util_strlcpy ( value , rule - > buf + key - > val_off , sizeof ( value ) ) ;
2005-07-12 14:52:56 +04:00
key_value = value ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " key %s value='%s' \n " , key_name , key_value ) ;
2008-10-16 19:16:58 +04:00
while ( key_value ! = NULL ) {
2005-07-12 14:52:56 +04:00
pos = strchr ( key_value , ' | ' ) ;
2008-10-16 19:16:58 +04:00
if ( pos ! = NULL ) {
2005-07-12 14:52:56 +04:00
pos [ 0 ] = ' \0 ' ;
2008-10-16 19:16:58 +04:00
pos = & pos [ 1 ] ;
2005-07-12 14:52:56 +04:00
}
2007-03-31 04:46:32 +04:00
2008-09-06 17:45:31 +04:00
dbg ( udev , " match %s '%s' <-> '%s' \n " , key_name , key_value , val ) ;
2006-08-24 11:03:15 +04:00
match = ( fnmatch ( key_value , val , 0 ) = = 0 ) ;
2007-03-31 04:46:32 +04:00
if ( match )
break ;
2005-07-12 14:52:56 +04:00
key_value = pos ;
2005-07-04 21:44:25 +04:00
}
2007-03-31 04:46:32 +04:00
if ( match & & ( key - > operation = = KEY_OP_MATCH ) ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " %s is true (matching value) \n " , key_name ) ;
2007-03-31 04:46:32 +04:00
return 0 ;
}
if ( ! match & & ( key - > operation = = KEY_OP_NOMATCH ) ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " %s is true (non-matching value) \n " , key_name ) ;
2007-03-31 04:46:32 +04:00
return 0 ;
}
2005-07-04 21:44:25 +04:00
return - 1 ;
}
2005-08-29 05:48:17 +04:00
/* match a single rule against a given device and possibly its parent devices */
2008-10-16 19:16:58 +04:00
static int match_rule ( struct udev_event * event , struct udev_rule * rule )
2003-12-20 12:05:13 +03:00
{
2008-10-16 19:16:58 +04:00
struct udev_device * dev = event - > dev ;
2005-08-16 06:25:20 +04:00
int i ;
2005-03-28 04:12:39 +04:00
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " ACTION " , rule , & rule - > action , udev_device_get_action ( dev ) ) )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2005-04-02 19:45:35 +04:00
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " KERNEL " , rule , & rule - > kernel , udev_device_get_sysname ( dev ) ) )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2003-11-13 16:52:08 +03:00
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " SUBSYSTEM " , rule , & rule - > subsystem , udev_device_get_subsystem ( dev ) ) )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2004-11-12 08:52:55 +03:00
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " DEVPATH " , rule , & rule - > devpath , udev_device_get_devpath ( dev ) ) )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2005-06-20 02:29:38 +04:00
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " DRIVER " , rule , & rule - > driver , udev_device_get_driver ( dev ) ) )
2006-08-19 18:06:25 +04:00
goto nomatch ;
2007-07-25 17:29:14 +04:00
/* match NAME against a value assigned by an earlier rule */
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " NAME " , rule , & rule - > name , event - > name ) )
2006-04-24 21:25:55 +04:00
goto nomatch ;
2007-07-16 15:29:28 +04:00
/* match against current list of symlinks */
if ( rule - > symlink_match . operation = = KEY_OP_MATCH | |
rule - > symlink_match . operation = = KEY_OP_NOMATCH ) {
2008-10-16 19:16:58 +04:00
size_t devlen = strlen ( udev_get_dev_path ( event - > udev ) ) + 1 ;
struct udev_list_entry * list_entry ;
2007-07-16 15:29:28 +04:00
int match = 0 ;
2008-10-16 19:16:58 +04:00
udev_list_entry_foreach ( list_entry , udev_device_get_devlinks_list_entry ( dev ) ) {
const char * devlink ;
devlink = & udev_list_entry_get_name ( list_entry ) [ devlen ] ;
if ( match_key ( event - > udev , " SYMLINK " , rule , & rule - > symlink_match , devlink ) = = 0 ) {
2007-07-16 15:29:28 +04:00
match = 1 ;
break ;
}
}
if ( ! match )
goto nomatch ;
}
2005-08-16 06:25:20 +04:00
for ( i = 0 ; i < rule - > env . count ; i + + ) {
struct key_pair * pair = & rule - > env . keys [ i ] ;
2005-03-13 13:40:32 +03:00
2005-08-16 06:25:20 +04:00
/* we only check for matches, assignments will be handled later */
2006-04-24 21:25:55 +04:00
if ( pair - > key . operation = = KEY_OP_MATCH | |
pair - > key . operation = = KEY_OP_NOMATCH ) {
2008-10-16 19:16:58 +04:00
struct udev_list_entry * list_entry ;
2005-07-05 17:24:41 +04:00
const char * key_name = key_pair_name ( rule , pair ) ;
2008-10-16 19:16:58 +04:00
const char * value ;
2005-03-13 13:40:32 +03:00
2008-10-16 19:16:58 +04:00
list_entry = udev_device_get_properties_list_entry ( event - > dev ) ;
list_entry = udev_list_entry_get_by_name ( list_entry , key_name ) ;
value = udev_list_entry_get_value ( list_entry ) ;
if ( value = = NULL ) {
dbg ( event - > udev , " ENV{%s} is not set, treat as empty \n " , key_name ) ;
2005-07-16 07:50:34 +04:00
value = " " ;
2005-03-13 13:40:32 +03:00
}
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " ENV " , rule , & pair - > key , value ) )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2005-03-13 13:40:32 +03:00
}
}
2007-08-30 00:19:28 +04:00
if ( rule - > test . operation = = KEY_OP_MATCH | |
rule - > test . operation = = KEY_OP_NOMATCH ) {
2008-09-10 23:50:21 +04:00
char filename [ UTIL_PATH_SIZE ] ;
2007-06-03 02:01:46 +04:00
struct stat statbuf ;
int match ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( filename , key_val ( rule , & rule - > test ) , sizeof ( filename ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , filename , sizeof ( filename ) ) ;
if ( udev_event_apply_subsys_kernel ( event , NULL , filename , sizeof ( filename ) , 0 ) ! = 0 )
if ( filename [ 0 ] ! = ' / ' ) {
char tmp [ UTIL_PATH_SIZE ] ;
util_strlcpy ( tmp , udev_device_get_syspath ( dev ) , sizeof ( tmp ) ) ;
util_strlcat ( tmp , " / " , sizeof ( tmp ) ) ;
util_strlcat ( tmp , filename , sizeof ( tmp ) ) ;
util_strlcpy ( filename , tmp , sizeof ( filename ) ) ;
2007-06-03 14:29:46 +04:00
}
2008-03-15 01:40:06 +03:00
attr_subst_subdir ( filename , sizeof ( filename ) ) ;
2007-06-03 02:01:46 +04:00
match = ( stat ( filename , & statbuf ) = = 0 ) ;
2008-10-16 19:16:58 +04:00
info ( event - > udev , " '%s' %s " , filename , match ? " exists \n " : " does not exist \n " ) ;
2007-06-03 02:01:46 +04:00
if ( match & & rule - > test_mode_mask > 0 ) {
match = ( ( statbuf . st_mode & rule - > test_mode_mask ) > 0 ) ;
2008-10-16 19:16:58 +04:00
info ( event - > udev , " '%s' has mode=%#o and %s %#o \n " , filename , statbuf . st_mode ,
2007-06-03 02:01:46 +04:00
match ? " matches " : " does not match " ,
rule - > test_mode_mask ) ;
}
if ( match & & rule - > test . operation = = KEY_OP_NOMATCH )
goto nomatch ;
if ( ! match & & rule - > test . operation = = KEY_OP_MATCH )
goto nomatch ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " TEST key is true \n " ) ;
2007-06-03 02:01:46 +04:00
}
2008-05-20 17:02:17 +04:00
if ( rule - > wait_for . operation ! = KEY_OP_UNSET ) {
2008-09-10 23:50:21 +04:00
char filename [ UTIL_PATH_SIZE ] ;
2006-01-31 18:24:22 +03:00
int found ;
2005-07-07 22:05:51 +04:00
2008-09-10 20:59:42 +04:00
util_strlcpy ( filename , key_val ( rule , & rule - > wait_for ) , sizeof ( filename ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , filename , sizeof ( filename ) ) ;
2008-10-16 19:16:58 +04:00
found = ( wait_for_file ( event , filename , 10 ) = = 0 ) ;
2008-05-20 17:02:17 +04:00
if ( ! found & & ( rule - > wait_for . operation ! = KEY_OP_NOMATCH ) )
2006-01-31 18:24:22 +03:00
goto nomatch ;
2005-07-07 22:05:51 +04:00
}
2006-08-20 20:16:27 +04:00
/* check for matching sysfs attribute pairs */
for ( i = 0 ; i < rule - > attr . count ; i + + ) {
struct key_pair * pair = & rule - > attr . keys [ i ] ;
if ( pair - > key . operation = = KEY_OP_MATCH | |
pair - > key . operation = = KEY_OP_NOMATCH ) {
2008-10-16 19:16:58 +04:00
char attr [ UTIL_PATH_SIZE ] ;
2006-08-19 18:06:25 +04:00
const char * key_name = key_pair_name ( rule , pair ) ;
const char * key_value = key_val ( rule , & pair - > key ) ;
2008-10-16 19:16:58 +04:00
char value [ UTIL_NAME_SIZE ] = " " ;
2006-08-19 18:06:25 +04:00
size_t len ;
2008-10-16 19:16:58 +04:00
util_strlcpy ( attr , key_name , sizeof ( attr ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_subsys_kernel ( event , attr , value , sizeof ( value ) , 1 ) ;
2008-10-16 19:16:58 +04:00
if ( value [ 0 ] = = ' \0 ' ) {
const char * val ;
val = udev_device_get_attr_value ( dev , key_name ) ;
if ( val ! = NULL )
util_strlcpy ( value , val , sizeof ( value ) ) ;
}
if ( value [ 0 ] = = ' \0 ' )
2006-08-19 18:06:25 +04:00
goto nomatch ;
/* strip trailing whitespace of value, if not asked to match for it */
len = strlen ( key_value ) ;
if ( len > 0 & & ! isspace ( key_value [ len - 1 ] ) ) {
2008-10-16 19:16:58 +04:00
len = strlen ( value ) ;
while ( len > 0 & & isspace ( value [ - - len ] ) )
value [ len ] = ' \0 ' ;
dbg ( event - > udev , " removed trailing whitespace from '%s' \n " , value ) ;
2006-08-19 18:06:25 +04:00
}
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " ATTR " , rule , & pair - > key , value ) )
2006-08-19 18:06:25 +04:00
goto nomatch ;
}
}
2006-01-16 08:12:49 +03:00
/* walk up the chain of parent devices and find a match */
2008-10-16 19:16:58 +04:00
event - > dev_parent = dev ;
2005-02-11 20:33:40 +03:00
while ( 1 ) {
2006-08-19 18:06:25 +04:00
/* check for matching kernel device name */
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " KERNELS " , rule ,
& rule - > kernels , udev_device_get_sysname ( event - > dev_parent ) ) )
2006-04-24 21:25:55 +04:00
goto try_parent ;
2004-11-13 07:21:12 +03:00
2006-08-19 18:06:25 +04:00
/* check for matching subsystem value */
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " SUBSYSTEMS " , rule ,
& rule - > subsystems , udev_device_get_subsystem ( event - > dev_parent ) ) )
2006-04-24 21:25:55 +04:00
goto try_parent ;
2005-02-11 07:21:03 +03:00
2006-08-19 18:06:25 +04:00
/* check for matching driver */
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " DRIVERS " , rule ,
& rule - > drivers , udev_device_get_driver ( event - > dev_parent ) ) )
2006-04-24 21:25:55 +04:00
goto try_parent ;
2003-11-13 16:52:08 +03:00
2006-09-05 04:18:06 +04:00
/* check for matching sysfs attribute pairs */
2006-08-20 20:16:27 +04:00
for ( i = 0 ; i < rule - > attrs . count ; i + + ) {
struct key_pair * pair = & rule - > attrs . keys [ i ] ;
if ( pair - > key . operation = = KEY_OP_MATCH | |
pair - > key . operation = = KEY_OP_NOMATCH ) {
2005-07-05 17:24:41 +04:00
const char * key_name = key_pair_name ( rule , pair ) ;
const char * key_value = key_val ( rule , & pair - > key ) ;
2008-10-16 19:16:58 +04:00
const char * val ;
char value [ UTIL_NAME_SIZE ] ;
2005-03-28 14:37:54 +04:00
size_t len ;
2005-03-13 13:40:32 +03:00
2008-10-16 19:16:58 +04:00
val = udev_device_get_attr_value ( event - > dev_parent , key_name ) ;
if ( val = = NULL )
val = udev_device_get_attr_value ( dev , key_name ) ;
if ( val = = NULL )
2005-03-28 14:20:05 +04:00
goto try_parent ;
2008-10-16 19:16:58 +04:00
util_strlcpy ( value , val , sizeof ( value ) ) ;
2005-03-28 14:20:05 +04:00
/* strip trailing whitespace of value, if not asked to match for it */
2005-07-05 17:24:41 +04:00
len = strlen ( key_value ) ;
2006-01-09 23:18:00 +03:00
if ( len > 0 & & ! isspace ( key_value [ len - 1 ] ) ) {
2008-10-16 19:16:58 +04:00
len = strlen ( value ) ;
while ( len > 0 & & isspace ( value [ - - len ] ) )
value [ len ] = ' \0 ' ;
dbg ( event - > udev , " removed trailing whitespace from '%s' \n " , value ) ;
2005-03-28 14:20:05 +04:00
}
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " ATTRS " , rule , & pair - > key , value ) )
2005-07-04 21:44:25 +04:00
goto try_parent ;
[PATCH] udev - drop all methods :)
> Hi,
> as promised yesterday, here is a patch that drops the explicit methods
> given in the udev config and implement only one type of rule.
>
> A rule now consists only of a number of keys to match. All known keys
> are valid in any combination. The former configs should work with a few
> changes:
>
> o the "<METHOD>, " at the beginning of the line should be removed
>
> o the result of the externel program is matched with RESULT= instead if ID=
> the PROGRAM= key is only valid if the program exits with zero
> (just exit with nozero in a script if the rule should not match)
>
> o rules are processed in order they appear in the file, no priority
>
> o if NAME="" is given, udev is instructed to ignore this device,
> no node will be created
>
>
> EXAMPLE:
>
> # combined BUS, SYSFS and KERNEL
> BUS="usb", KERNEL="video*", SYSFS_model="Creative Labs WebCam*", NAME="test/webcam%n"
>
> # exec script only for the first ide drive (hda), all other will be skipped
> BUS="ide", KERNEL="hda*", PROGRAM="/home/kay/src/udev.kay/extras/ide-devfs.sh %k %b %n", RESULT="hd*", NAME="%1c", SYMLINK="%2c %3c"
>
>
> The udev-test.pl and test.block works fine here.
> Please adapt your config and give it a try.
>
Here is a slightly better version of the patch.
After a conversation with Patrick, we are now able to execute the PROGRAM
and also match in all following rules with the RESULT value from this exec.
EXAMPLE:
We have 7 rules with RESULT and 2 with PROGRAM.
Only the 5th rule matches with the callout result from the exec in the 4th rule.
RULES:
PROGRAM="/bin/echo abc", RESULT="no_match", NAME="web-no-2"
KERNEL="video*", RESULT="123", NAME="web-no-3"
KERNEL="video*", RESULT="123", NAME="web-no-4"
PROGRAM="/bin/echo 123", RESULT="no_match", NAME="web-no-5"
KERNEL="video*", RESULT="123", NAME="web-yes"
RESULT:
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check PROGRAM
Jan 11 23:36:52 pim udev[26050]: execute_program: executing '/bin/echo abc'
Jan 11 23:36:52 pim udev[26050]: execute_program: result is 'abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: PROGRAM returned successful
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='no_match', udev->program_result='abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for KERNEL dev->kernel='video*' class_dev->name='video0'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: KERNEL matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='123', udev->program_result='abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for KERNEL dev->kernel='video*' class_dev->name='video0'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: KERNEL matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='123', udev->program_result='abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check PROGRAM
Jan 11 23:36:52 pim udev[26050]: execute_program: executing '/bin/echo 123'
Jan 11 23:36:52 pim udev[26050]: execute_program: result is '123'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: PROGRAM returned successful
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='no_match', udev->program_result='123'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for KERNEL dev->kernel='video*' class_dev->name='video0'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: KERNEL matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='123', udev->program_result='123'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: found matching rule, 'video*' becomes ''
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: name, 'web-yes' is going to have owner='', group='', mode = 0600
2004-01-13 08:39:05 +03:00
}
}
2006-01-09 23:18:00 +03:00
/* found matching device */
2005-02-14 03:46:05 +03:00
break ;
2004-02-12 09:28:51 +03:00
try_parent :
2006-01-16 08:12:49 +03:00
/* move to parent device */
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " try parent sysfs device \n " ) ;
event - > dev_parent = udev_device_get_parent ( event - > dev_parent ) ;
if ( event - > dev_parent = = NULL )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " looking at dev_parent->devpath='%s' \n " ,
udev_device_get_syspath ( event - > dev_parent ) ) ;
2004-01-23 11:21:13 +03:00
}
2005-02-11 07:21:03 +03:00
2005-02-14 03:46:05 +03:00
/* execute external program */
2005-07-05 17:24:41 +04:00
if ( rule - > program . operation ! = KEY_OP_UNSET ) {
2008-09-10 23:50:21 +04:00
char program [ UTIL_PATH_SIZE ] ;
2008-10-18 17:25:05 +04:00
char * * envp ;
2008-09-10 23:50:21 +04:00
char result [ UTIL_PATH_SIZE ] ;
2005-02-14 03:46:05 +03:00
2008-09-10 20:59:42 +04:00
util_strlcpy ( program , key_val ( rule , & rule - > program ) , sizeof ( program ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , program , sizeof ( program ) ) ;
2008-10-18 17:25:05 +04:00
envp = udev_device_get_properties_envp ( dev ) ;
2008-10-18 22:12:55 +04:00
if ( util_run_program ( event - > udev , program , envp , result , sizeof ( result ) , NULL ) ! = 0 ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " PROGRAM is false \n " ) ;
event - > program_result [ 0 ] = ' \0 ' ;
2005-07-05 17:24:41 +04:00
if ( rule - > program . operation ! = KEY_OP_NOMATCH )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2005-03-13 07:46:31 +03:00
} else {
2005-08-28 17:55:58 +04:00
int count ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " PROGRAM matches \n " ) ;
2008-09-10 20:39:23 +04:00
util_remove_trailing_chars ( result , ' \n ' ) ;
2007-06-22 03:27:02 +04:00
if ( rule - > string_escape = = ESCAPE_UNSET | |
rule - > string_escape = = ESCAPE_REPLACE ) {
2008-09-10 20:39:23 +04:00
count = util_replace_chars ( result , ALLOWED_CHARS_INPUT ) ;
2007-06-22 03:27:02 +04:00
if ( count > 0 )
2008-10-16 19:16:58 +04:00
info ( event - > udev , " %i character(s) replaced \n " , count ) ;
2007-06-22 03:27:02 +04:00
}
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " result is '%s' \n " , result ) ;
util_strlcpy ( event - > program_result , result , sizeof ( event - > program_result ) ) ;
dbg ( event - > udev , " PROGRAM returned successful \n " ) ;
2005-07-05 17:24:41 +04:00
if ( rule - > program . operation = = KEY_OP_NOMATCH )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2005-02-14 03:46:05 +03:00
}
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " PROGRAM key is true \n " ) ;
2005-02-14 03:46:05 +03:00
}
/* check for matching result of external program */
2008-10-16 19:16:58 +04:00
if ( match_key ( event - > udev , " RESULT " , rule , & rule - > result , event - > program_result ) )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2005-02-14 03:46:05 +03:00
2008-10-18 16:33:37 +04:00
/* import variables returned from program or or file into properties */
2005-08-16 06:25:20 +04:00
if ( rule - > import . operation ! = KEY_OP_UNSET ) {
2008-09-10 23:50:21 +04:00
char import [ UTIL_PATH_SIZE ] ;
2005-08-16 06:25:20 +04:00
int rc = - 1 ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( import , key_val ( rule , & rule - > import ) , sizeof ( import ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , import , sizeof ( import ) ) ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " check for IMPORT import='%s' \n " , import ) ;
2005-08-16 06:25:20 +04:00
if ( rule - > import_type = = IMPORT_PROGRAM ) {
2008-10-17 00:41:52 +04:00
rc = import_program_into_env ( event - > dev , import ) ;
2005-08-16 06:25:20 +04:00
} else if ( rule - > import_type = = IMPORT_FILE ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " import file import='%s' \n " , import ) ;
2008-10-17 00:41:52 +04:00
rc = import_file_into_env ( event - > dev , import ) ;
2006-01-09 23:18:00 +03:00
} else if ( rule - > import_type = = IMPORT_PARENT ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " import parent import='%s' \n " , import ) ;
2008-10-17 00:41:52 +04:00
rc = import_parent_into_env ( event - > dev , import ) ;
2005-08-16 06:25:20 +04:00
}
2006-01-09 23:18:00 +03:00
if ( rc ! = 0 ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " IMPORT failed \n " ) ;
2005-08-16 06:25:20 +04:00
if ( rule - > import . operation ! = KEY_OP_NOMATCH )
2006-01-16 08:12:49 +03:00
goto nomatch ;
2005-08-16 06:25:20 +04:00
} else
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " IMPORT '%s' imported \n " , key_val ( rule , & rule - > import ) ) ;
dbg ( event - > udev , " IMPORT key is true \n " ) ;
2005-08-16 06:25:20 +04:00
}
/* rule matches, if we have ENV assignments export it */
for ( i = 0 ; i < rule - > env . count ; i + + ) {
struct key_pair * pair = & rule - > env . keys [ i ] ;
if ( pair - > key . operation = = KEY_OP_ASSIGN ) {
2008-09-10 23:50:21 +04:00
char temp_value [ UTIL_NAME_SIZE ] ;
2005-08-16 06:25:20 +04:00
const char * key_name = key_pair_name ( rule , pair ) ;
const char * value = key_val ( rule , & pair - > key ) ;
2006-09-05 02:50:25 +04:00
/* make sure we don't write to the same string we possibly read from */
2008-09-10 20:59:42 +04:00
util_strlcpy ( temp_value , value , sizeof ( temp_value ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , temp_value , sizeof ( temp_value ) ) ;
2006-09-05 02:50:25 +04:00
2008-10-16 19:16:58 +04:00
if ( temp_value [ 0 ] ! = ' \0 ' ) {
struct udev_list_entry * entry ;
2005-08-16 06:25:20 +04:00
2008-10-16 19:16:58 +04:00
info ( event - > udev , " set ENV '%s=%s' \n " , key_name , temp_value ) ;
entry = udev_device_add_property ( dev , key_name , temp_value ) ;
/* store in db */
udev_list_entry_set_flag ( entry , 1 ) ;
2007-04-25 03:52:00 +04:00
}
2005-08-16 06:25:20 +04:00
}
}
2007-05-16 21:51:13 +04:00
/* if we have ATTR assignments, write value to sysfs file */
2006-08-20 20:16:27 +04:00
for ( i = 0 ; i < rule - > attr . count ; i + + ) {
struct key_pair * pair = & rule - > attr . keys [ i ] ;
if ( pair - > key . operation = = KEY_OP_ASSIGN ) {
const char * key_name = key_pair_name ( rule , pair ) ;
2008-10-16 19:16:58 +04:00
char attr [ UTIL_PATH_SIZE ] ;
2008-09-10 23:50:21 +04:00
char value [ UTIL_NAME_SIZE ] ;
2006-08-20 20:16:27 +04:00
FILE * f ;
2008-10-16 19:16:58 +04:00
util_strlcpy ( attr , key_name , sizeof ( attr ) ) ;
2008-10-18 21:19:56 +04:00
if ( udev_event_apply_subsys_kernel ( event , key_name , attr , sizeof ( attr ) , 0 ) ! = 0 ) {
2008-10-16 19:16:58 +04:00
util_strlcpy ( attr , udev_device_get_syspath ( dev ) , sizeof ( attr ) ) ;
2008-09-10 20:59:42 +04:00
util_strlcat ( attr , " / " , sizeof ( attr ) ) ;
util_strlcat ( attr , key_name , sizeof ( attr ) ) ;
2007-06-11 01:53:40 +04:00
}
2008-03-15 01:40:06 +03:00
attr_subst_subdir ( attr , sizeof ( attr ) ) ;
2008-03-13 18:00:25 +03:00
2008-09-10 20:59:42 +04:00
util_strlcpy ( value , key_val ( rule , & pair - > key ) , sizeof ( value ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , value , sizeof ( value ) ) ;
2008-10-16 19:16:58 +04:00
info ( event - > udev , " writing '%s' to sysfs file '%s' \n " , value , attr ) ;
2006-08-20 20:16:27 +04:00
f = fopen ( attr , " w " ) ;
if ( f ! = NULL ) {
2008-10-16 19:16:58 +04:00
if ( ! event - > test )
2007-03-23 19:18:03 +03:00
if ( fprintf ( f , " %s " , value ) < = 0 )
2008-10-16 19:16:58 +04:00
err ( event - > udev , " error writing ATTR{%s}: %m \n " , attr ) ;
2006-08-20 20:16:27 +04:00
fclose ( f ) ;
} else
2008-10-16 19:16:58 +04:00
err ( event - > udev , " error opening ATTR{%s} for writing: %m \n " , attr ) ;
2006-08-20 20:16:27 +04:00
}
}
2005-02-14 03:46:05 +03:00
return 0 ;
2006-01-16 08:12:49 +03:00
nomatch :
2005-02-11 07:21:03 +03:00
return - 1 ;
2004-01-23 11:21:13 +03:00
}
2008-10-16 19:16:58 +04:00
int udev_rules_get_name ( struct udev_rules * rules , struct udev_event * event )
2004-01-23 11:21:13 +03:00
{
2008-10-16 19:16:58 +04:00
struct udev_device * dev = event - > dev ;
2008-09-09 02:09:49 +04:00
struct udev_rules_iter iter ;
2005-03-13 00:36:32 +03:00
struct udev_rule * rule ;
2005-07-08 00:32:48 +04:00
int name_set = 0 ;
2004-01-23 11:21:13 +03:00
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " device: '%s' \n " , udev_device_get_syspath ( dev ) ) ;
2004-01-23 11:21:13 +03:00
/* look for a matching rule to apply */
2008-09-09 02:09:49 +04:00
udev_rules_iter_init ( & iter , rules ) ;
2005-06-24 20:05:32 +04:00
while ( 1 ) {
2008-09-09 02:09:49 +04:00
rule = udev_rules_iter_next ( & iter ) ;
2005-06-24 20:05:32 +04:00
if ( rule = = NULL )
break ;
2006-04-24 21:25:55 +04:00
if ( name_set & &
( rule - > name . operation = = KEY_OP_ASSIGN | |
rule - > name . operation = = KEY_OP_ASSIGN_FINAL | |
rule - > name . operation = = KEY_OP_ADD ) ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " node name already set, rule ignored \n " ) ;
2005-06-20 02:29:38 +04:00
continue ;
}
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " process rule \n " ) ;
if ( match_rule ( event , rule ) = = 0 ) {
2005-02-14 08:03:06 +03:00
/* apply options */
2005-03-13 00:36:32 +03:00
if ( rule - > ignore_device ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " rule applied, '%s' is ignored \n " , udev_device_get_sysname ( dev ) ) ;
event - > ignore_device = 1 ;
2005-04-02 19:45:35 +04:00
return 0 ;
2005-02-14 08:03:06 +03:00
}
2005-03-13 00:36:32 +03:00
if ( rule - > ignore_remove ) {
2008-10-16 19:16:58 +04:00
udev_device_set_ignore_remove ( dev , 1 ) ;
dbg ( event - > udev , " remove event should be ignored \n " ) ;
2005-02-14 08:03:06 +03:00
}
2007-04-06 21:50:19 +04:00
if ( rule - > link_priority ! = 0 ) {
2008-10-16 19:16:58 +04:00
udev_device_set_devlink_priority ( dev , rule - > link_priority ) ;
info ( event - > udev , " devlink_priority=%i \n " , rule - > link_priority ) ;
2007-03-16 17:16:08 +03:00
}
2008-04-21 21:00:54 +04:00
if ( rule - > event_timeout > = 0 ) {
2008-10-16 19:16:58 +04:00
udev_device_set_event_timeout ( dev , rule - > event_timeout ) ;
info ( event - > udev , " event_timeout=%i \n " , rule - > event_timeout ) ;
2008-04-21 21:00:54 +04:00
}
2008-10-16 19:16:58 +04:00
/* apply all_partitions option only at a disk device */
if ( rule - > partitions > 0 & &
strcmp ( udev_device_get_subsystem ( dev ) , " block " ) = = 0 & &
udev_device_get_sysnum ( dev ) = = NULL ) {
udev_device_set_num_fake_partitions ( dev , rule - > partitions ) ;
dbg ( event - > udev , " creation of partition nodes requested \n " ) ;
2005-02-14 08:03:06 +03:00
}
2005-02-06 02:13:18 +03:00
/* apply permissions */
2008-10-16 19:16:58 +04:00
if ( ! event - > mode_final & & rule - > mode . operation ! = KEY_OP_UNSET ) {
2008-04-18 23:07:29 +04:00
if ( rule - > mode . operation = = KEY_OP_ASSIGN_FINAL )
2008-10-16 19:16:58 +04:00
event - > mode_final = 1 ;
2008-05-19 11:05:20 +04:00
char buf [ 20 ] ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( buf , key_val ( rule , & rule - > mode ) , sizeof ( buf ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , buf , sizeof ( buf ) ) ;
2008-10-16 19:16:58 +04:00
event - > mode = strtol ( buf , NULL , 8 ) ;
dbg ( event - > udev , " applied mode=%#o to '%s' \n " ,
event - > mode , udev_device_get_sysname ( dev ) ) ;
2005-02-06 02:13:18 +03:00
}
2008-10-16 19:16:58 +04:00
if ( ! event - > owner_final & & rule - > owner . operation ! = KEY_OP_UNSET ) {
2005-07-05 17:24:41 +04:00
if ( rule - > owner . operation = = KEY_OP_ASSIGN_FINAL )
2008-10-16 19:16:58 +04:00
event - > owner_final = 1 ;
util_strlcpy ( event - > owner , key_val ( rule , & rule - > owner ) , sizeof ( event - > owner ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , event - > owner , sizeof ( event - > owner ) ) ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " applied owner='%s' to '%s' \n " ,
event - > owner , udev_device_get_sysname ( dev ) ) ;
2005-02-06 02:13:18 +03:00
}
2008-10-16 19:16:58 +04:00
if ( ! event - > group_final & & rule - > group . operation ! = KEY_OP_UNSET ) {
2005-07-05 17:24:41 +04:00
if ( rule - > group . operation = = KEY_OP_ASSIGN_FINAL )
2008-10-16 19:16:58 +04:00
event - > group_final = 1 ;
util_strlcpy ( event - > group , key_val ( rule , & rule - > group ) , sizeof ( event - > group ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , event - > group , sizeof ( event - > group ) ) ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " applied group='%s' to '%s' \n " ,
event - > group , udev_device_get_sysname ( dev ) ) ;
2005-02-06 02:13:18 +03:00
}
2005-02-21 16:48:12 +03:00
/* collect symlinks */
2008-10-16 19:16:58 +04:00
if ( ! event - > devlink_final & &
2007-07-16 15:29:28 +04:00
( rule - > symlink . operation = = KEY_OP_ASSIGN | |
rule - > symlink . operation = = KEY_OP_ASSIGN_FINAL | |
rule - > symlink . operation = = KEY_OP_ADD ) ) {
2008-09-10 23:50:21 +04:00
char temp [ UTIL_PATH_SIZE ] ;
2008-10-16 19:16:58 +04:00
char filename [ UTIL_PATH_SIZE ] ;
2005-03-05 07:35:31 +03:00
char * pos , * next ;
2008-10-07 02:41:00 +04:00
int count = 0 ;
2004-03-25 04:34:00 +03:00
2005-07-05 17:24:41 +04:00
if ( rule - > symlink . operation = = KEY_OP_ASSIGN_FINAL )
2008-10-16 19:16:58 +04:00
event - > devlink_final = 1 ;
2007-07-16 15:29:28 +04:00
if ( rule - > symlink . operation = = KEY_OP_ASSIGN | |
rule - > symlink . operation = = KEY_OP_ASSIGN_FINAL ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " reset symlink list \n " ) ;
udev_device_cleanup_devlinks_list ( dev ) ;
2005-06-05 07:13:33 +04:00
}
2007-05-17 22:01:54 +04:00
/* allow multiple symlinks separated by spaces */
2008-09-10 20:59:42 +04:00
util_strlcpy ( temp , key_val ( rule , & rule - > symlink ) , sizeof ( temp ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , temp , sizeof ( temp ) ) ;
2008-10-07 02:41:00 +04:00
if ( rule - > string_escape = = ESCAPE_UNSET )
2008-09-10 20:39:23 +04:00
count = util_replace_chars ( temp , ALLOWED_CHARS_FILE " " ) ;
2008-10-07 02:41:00 +04:00
else if ( rule - > string_escape = = ESCAPE_REPLACE )
count = util_replace_chars ( temp , ALLOWED_CHARS_FILE ) ;
if ( count > 0 )
2008-10-16 19:16:58 +04:00
info ( event - > udev , " %i character(s) replaced \n " , count ) ;
dbg ( event - > udev , " rule applied, added symlink(s) '%s' \n " , temp ) ;
2005-07-05 17:24:41 +04:00
pos = temp ;
2005-07-08 01:43:13 +04:00
while ( isspace ( pos [ 0 ] ) )
pos + + ;
next = strchr ( pos , ' ' ) ;
2005-07-05 17:24:41 +04:00
while ( next ) {
next [ 0 ] = ' \0 ' ;
2008-10-16 19:16:58 +04:00
info ( event - > udev , " add symlink '%s' \n " , pos ) ;
util_strlcpy ( filename , udev_get_dev_path ( event - > udev ) , sizeof ( filename ) ) ;
util_strlcat ( filename , " / " , sizeof ( filename ) ) ;
util_strlcat ( filename , pos , sizeof ( filename ) ) ;
udev_device_add_devlink ( dev , filename ) ;
2005-07-08 01:43:13 +04:00
while ( isspace ( next [ 1 ] ) )
next + + ;
2005-07-05 17:24:41 +04:00
pos = & next [ 1 ] ;
next = strchr ( pos , ' ' ) ;
2005-03-05 07:35:31 +03:00
}
2005-07-08 01:43:13 +04:00
if ( pos [ 0 ] ! = ' \0 ' ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " add symlink '%s' \n " , pos ) ;
util_strlcpy ( filename , udev_get_dev_path ( event - > udev ) , sizeof ( filename ) ) ;
util_strlcat ( filename , " / " , sizeof ( filename ) ) ;
util_strlcat ( filename , pos , sizeof ( filename ) ) ;
udev_device_add_devlink ( dev , filename ) ;
2005-07-08 01:43:13 +04:00
}
2004-03-02 09:23:39 +03:00
}
2005-04-02 19:45:35 +04:00
/* set name, later rules with name set will be ignored */
2006-04-24 21:25:55 +04:00
if ( rule - > name . operation = = KEY_OP_ASSIGN | |
rule - > name . operation = = KEY_OP_ASSIGN_FINAL | |
rule - > name . operation = = KEY_OP_ADD ) {
2005-08-28 17:55:58 +04:00
int count ;
2006-01-27 03:40:26 +03:00
2005-07-08 00:32:48 +04:00
name_set = 1 ;
2008-10-16 19:16:58 +04:00
util_strlcpy ( event - > name , key_val ( rule , & rule - > name ) , sizeof ( event - > name ) ) ;
2008-10-18 21:19:56 +04:00
udev_event_apply_format ( event , event - > name , sizeof ( event - > name ) ) ;
2007-06-22 03:27:02 +04:00
if ( rule - > string_escape = = ESCAPE_UNSET | |
rule - > string_escape = = ESCAPE_REPLACE ) {
2008-10-16 19:16:58 +04:00
count = util_replace_chars ( event - > name , ALLOWED_CHARS_FILE ) ;
2007-06-22 03:27:02 +04:00
if ( count > 0 )
2008-10-16 19:16:58 +04:00
info ( event - > udev , " %i character(s) replaced \n " , count ) ;
2007-06-22 03:27:02 +04:00
}
2005-07-05 17:24:41 +04:00
2008-10-16 19:16:58 +04:00
info ( event - > udev , " rule applied, '%s' becomes '%s' \n " ,
udev_device_get_sysname ( dev ) , event - > name ) ;
if ( strcmp ( udev_device_get_subsystem ( dev ) , " net " ) ! = 0 )
dbg ( event - > udev , " '%s' owner='%s', group='%s', mode=%#o partitions=%i \n " ,
event - > name , event - > owner , event - > group , event - > mode ,
udev_device_get_num_fake_partitions ( dev ) ) ;
2005-04-02 19:45:35 +04:00
}
2004-11-29 15:44:01 +03:00
2008-10-16 19:16:58 +04:00
if ( ! event - > run_final & & rule - > run . operation ! = KEY_OP_UNSET ) {
struct udev_list_entry * list_entry ;
2007-07-14 22:43:01 +04:00
2005-07-05 17:24:41 +04:00
if ( rule - > run . operation = = KEY_OP_ASSIGN_FINAL )
2008-10-16 19:16:58 +04:00
event - > run_final = 1 ;
2005-07-05 17:24:41 +04:00
if ( rule - > run . operation = = KEY_OP_ASSIGN | | rule - > run . operation = = KEY_OP_ASSIGN_FINAL ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " reset run list \n " ) ;
2008-10-18 21:27:38 +04:00
udev_list_cleanup_entries ( event - > udev , & event - > run_list ) ;
2005-06-05 07:13:33 +04:00
}
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " add run '%s' \n " , key_val ( rule , & rule - > run ) ) ;
list_entry = udev_list_entry_add ( event - > udev , & event - > run_list ,
key_val ( rule , & rule - > run ) , NULL , 1 , 0 ) ;
if ( rule - > run_ignore_error & & list_entry ! = NULL )
udev_list_entry_set_flag ( list_entry , 1 ) ;
2004-03-02 09:23:39 +03:00
}
2005-03-13 00:55:08 +03:00
if ( rule - > last_rule ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " last rule to be applied \n " ) ;
2005-03-13 00:55:08 +03:00
break ;
}
2005-07-16 09:46:31 +04:00
if ( rule - > goto_label . operation ! = KEY_OP_UNSET ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " moving forward to label '%s' \n " , key_val ( rule , & rule - > goto_label ) ) ;
2008-09-28 15:02:44 +04:00
udev_rules_iter_goto ( & iter , rule - > goto_rule_off ) ;
2005-07-16 09:46:31 +04:00
}
2004-01-23 11:21:13 +03:00
}
[PATCH] udev - drop all methods :)
> Hi,
> as promised yesterday, here is a patch that drops the explicit methods
> given in the udev config and implement only one type of rule.
>
> A rule now consists only of a number of keys to match. All known keys
> are valid in any combination. The former configs should work with a few
> changes:
>
> o the "<METHOD>, " at the beginning of the line should be removed
>
> o the result of the externel program is matched with RESULT= instead if ID=
> the PROGRAM= key is only valid if the program exits with zero
> (just exit with nozero in a script if the rule should not match)
>
> o rules are processed in order they appear in the file, no priority
>
> o if NAME="" is given, udev is instructed to ignore this device,
> no node will be created
>
>
> EXAMPLE:
>
> # combined BUS, SYSFS and KERNEL
> BUS="usb", KERNEL="video*", SYSFS_model="Creative Labs WebCam*", NAME="test/webcam%n"
>
> # exec script only for the first ide drive (hda), all other will be skipped
> BUS="ide", KERNEL="hda*", PROGRAM="/home/kay/src/udev.kay/extras/ide-devfs.sh %k %b %n", RESULT="hd*", NAME="%1c", SYMLINK="%2c %3c"
>
>
> The udev-test.pl and test.block works fine here.
> Please adapt your config and give it a try.
>
Here is a slightly better version of the patch.
After a conversation with Patrick, we are now able to execute the PROGRAM
and also match in all following rules with the RESULT value from this exec.
EXAMPLE:
We have 7 rules with RESULT and 2 with PROGRAM.
Only the 5th rule matches with the callout result from the exec in the 4th rule.
RULES:
PROGRAM="/bin/echo abc", RESULT="no_match", NAME="web-no-2"
KERNEL="video*", RESULT="123", NAME="web-no-3"
KERNEL="video*", RESULT="123", NAME="web-no-4"
PROGRAM="/bin/echo 123", RESULT="no_match", NAME="web-no-5"
KERNEL="video*", RESULT="123", NAME="web-yes"
RESULT:
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check PROGRAM
Jan 11 23:36:52 pim udev[26050]: execute_program: executing '/bin/echo abc'
Jan 11 23:36:52 pim udev[26050]: execute_program: result is 'abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: PROGRAM returned successful
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='no_match', udev->program_result='abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for KERNEL dev->kernel='video*' class_dev->name='video0'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: KERNEL matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='123', udev->program_result='abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for KERNEL dev->kernel='video*' class_dev->name='video0'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: KERNEL matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='123', udev->program_result='abc'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check PROGRAM
Jan 11 23:36:52 pim udev[26050]: execute_program: executing '/bin/echo 123'
Jan 11 23:36:52 pim udev[26050]: execute_program: result is '123'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: PROGRAM returned successful
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='no_match', udev->program_result='123'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT is not matching
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: process rule
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for KERNEL dev->kernel='video*' class_dev->name='video0'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: KERNEL matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: check for RESULT dev->result='123', udev->program_result='123'
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: RESULT matches
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: found matching rule, 'video*' becomes ''
Jan 11 23:36:52 pim udev[26050]: namedev_name_device: name, 'web-yes' is going to have owner='', group='', mode = 0600
2004-01-13 08:39:05 +03:00
}
2003-10-20 08:56:21 +04:00
2007-07-25 17:29:14 +04:00
if ( ! name_set ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " no node name set, will use kernel name '%s' \n " ,
udev_device_get_sysname ( dev ) ) ;
util_strlcpy ( event - > name , udev_device_get_sysname ( dev ) , sizeof ( event - > name ) ) ;
2007-07-25 17:29:14 +04:00
}
2003-11-13 04:38:14 +03:00
2008-10-16 19:16:58 +04:00
if ( event - > tmp_node [ 0 ] ! = ' \0 ' ) {
dbg ( event - > udev , " removing temporary device node \n " ) ;
2008-10-18 22:12:55 +04:00
util_unlink_secure ( event - > udev , event - > tmp_node ) ;
2008-10-16 19:16:58 +04:00
event - > tmp_node [ 0 ] = ' \0 ' ;
2005-02-09 06:37:32 +03:00
}
2003-11-12 19:26:08 +03:00
return 0 ;
2003-07-19 09:48:28 +04:00
}
2005-04-02 19:45:35 +04:00
2008-10-16 19:16:58 +04:00
int udev_rules_get_run ( struct udev_rules * rules , struct udev_event * event )
2005-04-02 19:45:35 +04:00
{
2008-10-16 19:16:58 +04:00
struct udev_device * dev = event - > dev ;
2008-09-09 02:09:49 +04:00
struct udev_rules_iter iter ;
2005-04-02 19:45:35 +04:00
struct udev_rule * rule ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " sysname: '%s' \n " , udev_device_get_sysname ( dev ) ) ;
2005-07-20 20:12:56 +04:00
2005-04-02 19:45:35 +04:00
/* look for a matching rule to apply */
2008-09-09 02:09:49 +04:00
udev_rules_iter_init ( & iter , rules ) ;
2005-06-24 20:05:32 +04:00
while ( 1 ) {
2008-09-09 02:09:49 +04:00
rule = udev_rules_iter_next ( & iter ) ;
2005-06-24 20:05:32 +04:00
if ( rule = = NULL )
break ;
2005-04-02 19:45:35 +04:00
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " process rule \n " ) ;
2007-08-29 16:11:37 +04:00
if ( rule - > name . operation = = KEY_OP_ASSIGN | |
rule - > name . operation = = KEY_OP_ASSIGN_FINAL | |
rule - > name . operation = = KEY_OP_ADD | |
rule - > symlink . operation = = KEY_OP_ASSIGN | |
rule - > symlink . operation = = KEY_OP_ASSIGN_FINAL | |
rule - > symlink . operation = = KEY_OP_ADD | |
2008-04-18 23:07:29 +04:00
rule - > mode . operation ! = KEY_OP_UNSET | |
2007-08-29 16:11:37 +04:00
rule - > owner . operation ! = KEY_OP_UNSET | | rule - > group . operation ! = KEY_OP_UNSET ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " skip rule that names a device \n " ) ;
2005-04-02 19:45:35 +04:00
continue ;
}
2008-10-16 19:16:58 +04:00
if ( match_rule ( event , rule ) = = 0 ) {
2005-06-05 17:55:29 +04:00
if ( rule - > ignore_device ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " rule applied, '%s' is ignored \n " , udev_device_get_sysname ( dev ) ) ;
event - > ignore_device = 1 ;
2005-06-05 17:55:29 +04:00
return 0 ;
2005-04-02 19:45:35 +04:00
}
2007-08-29 16:11:37 +04:00
if ( rule - > ignore_remove ) {
2008-10-16 19:16:58 +04:00
udev_device_set_ignore_remove ( dev , 1 ) ;
dbg ( event - > udev , " remove event should be ignored \n " ) ;
2007-08-29 16:11:37 +04:00
}
2005-04-02 19:45:35 +04:00
2008-10-16 19:16:58 +04:00
if ( ! event - > run_final & & rule - > run . operation ! = KEY_OP_UNSET ) {
struct udev_list_entry * list_entry ;
2007-07-14 22:43:01 +04:00
2007-06-03 02:01:46 +04:00
if ( rule - > run . operation = = KEY_OP_ASSIGN | |
rule - > run . operation = = KEY_OP_ASSIGN_FINAL ) {
2008-10-16 19:16:58 +04:00
info ( event - > udev , " reset run list \n " ) ;
2008-10-18 21:27:38 +04:00
udev_list_cleanup_entries ( event - > udev , & event - > run_list ) ;
2005-04-02 19:45:35 +04:00
}
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " add run '%s' \n " , key_val ( rule , & rule - > run ) ) ;
list_entry = udev_list_entry_add ( event - > udev , & event - > run_list ,
key_val ( rule , & rule - > run ) , NULL , 1 , 0 ) ;
if ( rule - > run_ignore_error & & list_entry ! = NULL )
udev_list_entry_set_flag ( list_entry , 1 ) ;
2005-07-05 17:24:41 +04:00
if ( rule - > run . operation = = KEY_OP_ASSIGN_FINAL )
2005-06-05 17:55:29 +04:00
break ;
2005-04-02 19:45:35 +04:00
}
2005-06-05 17:55:29 +04:00
if ( rule - > last_rule ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " last rule to be applied \n " ) ;
2005-06-05 17:55:29 +04:00
break ;
}
2005-07-20 20:12:56 +04:00
if ( rule - > goto_label . operation ! = KEY_OP_UNSET ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " moving forward to label '%s' \n " , key_val ( rule , & rule - > goto_label ) ) ;
2008-09-28 15:02:44 +04:00
udev_rules_iter_goto ( & iter , rule - > goto_rule_off ) ;
2005-07-20 20:12:56 +04:00
}
2005-04-02 19:45:35 +04:00
}
}
return 0 ;
}
2008-10-18 16:33:37 +04:00
static int get_key ( struct udev_rules * rules , char * * line , char * * key , enum key_operation * operation , char * * value )
{
char * linepos ;
char * temp ;
linepos = * line ;
if ( linepos = = NULL & & linepos [ 0 ] = = ' \0 ' )
return - 1 ;
/* skip whitespace */
while ( isspace ( linepos [ 0 ] ) | | linepos [ 0 ] = = ' , ' )
linepos + + ;
/* get the key */
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
* key = linepos ;
while ( 1 ) {
linepos + + ;
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
if ( isspace ( linepos [ 0 ] ) )
break ;
if ( linepos [ 0 ] = = ' = ' )
break ;
if ( ( linepos [ 0 ] = = ' + ' ) | | ( linepos [ 0 ] = = ' ! ' ) | | ( linepos [ 0 ] = = ' : ' ) )
if ( linepos [ 1 ] = = ' = ' )
break ;
}
/* remember end of key */
temp = linepos ;
/* skip whitespace after key */
while ( isspace ( linepos [ 0 ] ) )
linepos + + ;
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
/* get operation type */
if ( linepos [ 0 ] = = ' = ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_MATCH ;
linepos + = 2 ;
dbg ( rules - > udev , " match: \n " ) ;
} else if ( linepos [ 0 ] = = ' ! ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_NOMATCH ;
linepos + = 2 ;
dbg ( rules - > udev , " nomatch: \n " ) ;
} else if ( linepos [ 0 ] = = ' + ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_ADD ;
linepos + = 2 ;
dbg ( rules - > udev , " add: \n " ) ;
} else if ( linepos [ 0 ] = = ' = ' ) {
* operation = KEY_OP_ASSIGN ;
linepos + + ;
dbg ( rules - > udev , " assign: \n " ) ;
} else if ( linepos [ 0 ] = = ' : ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_ASSIGN_FINAL ;
linepos + = 2 ;
dbg ( rules - > udev , " assign_final: \n " ) ;
} else
return - 1 ;
/* terminate key */
temp [ 0 ] = ' \0 ' ;
/* skip whitespace after operator */
while ( isspace ( linepos [ 0 ] ) )
linepos + + ;
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
/* get the value*/
if ( linepos [ 0 ] = = ' " ' )
linepos + + ;
else
return - 1 ;
* value = linepos ;
temp = strchr ( linepos , ' " ' ) ;
if ( ! temp )
return - 1 ;
temp [ 0 ] = ' \0 ' ;
temp + + ;
dbg ( rules - > udev , " '%s'-'%s' \n " , * key , * value ) ;
/* move line to next key */
* line = temp ;
return 0 ;
}
/* extract possible KEY{attr} */
static char * get_key_attribute ( struct udev_rules * rules , char * str )
{
char * pos ;
char * attr ;
attr = strchr ( str , ' { ' ) ;
if ( attr ! = NULL ) {
attr + + ;
pos = strchr ( attr , ' } ' ) ;
if ( pos = = NULL ) {
err ( rules - > udev , " missing closing brace for format \n " ) ;
return NULL ;
}
pos [ 0 ] = ' \0 ' ;
dbg ( rules - > udev , " attribute='%s' \n " , attr ) ;
return attr ;
}
return NULL ;
}
static int add_rule_key ( struct udev_rule * rule , struct key * key ,
enum key_operation operation , const char * value )
{
size_t val_len = strnlen ( value , UTIL_PATH_SIZE ) ;
key - > operation = operation ;
key - > val_off = rule - > bufsize ;
util_strlcpy ( rule - > buf + rule - > bufsize , value , val_len + 1 ) ;
rule - > bufsize + = val_len + 1 ;
return 0 ;
}
static int add_rule_key_pair ( struct udev_rules * rules , struct udev_rule * rule , struct key_pairs * pairs ,
enum key_operation operation , const char * key , const char * value )
{
size_t key_len = strnlen ( key , UTIL_PATH_SIZE ) ;
if ( pairs - > count > = PAIRS_MAX ) {
err ( rules - > udev , " skip, too many keys of the same type in a single rule \n " ) ;
return - 1 ;
}
add_rule_key ( rule , & pairs - > keys [ pairs - > count ] . key , operation , value ) ;
/* add the key-name of the pair */
pairs - > keys [ pairs - > count ] . key_name_off = rule - > bufsize ;
util_strlcpy ( rule - > buf + rule - > bufsize , key , key_len + 1 ) ;
rule - > bufsize + = key_len + 1 ;
pairs - > count + + ;
return 0 ;
}
static int add_to_rules ( struct udev_rules * rules , char * line , const char * filename , unsigned int lineno )
{
char buf [ sizeof ( struct udev_rule ) + UTIL_LINE_SIZE ] ;
struct udev_rule * rule ;
size_t rule_size ;
int valid ;
char * linepos ;
char * attr ;
size_t padding ;
int physdev = 0 ;
memset ( buf , 0x00 , sizeof ( buf ) ) ;
rule = ( struct udev_rule * ) buf ;
rule - > event_timeout = - 1 ;
linepos = line ;
valid = 0 ;
/* get all the keys */
while ( 1 ) {
char * key ;
char * value ;
enum key_operation operation = KEY_OP_UNSET ;
2008-10-18 17:02:01 +04:00
if ( get_key ( rules , & linepos , & key , & operation , & value ) ! = 0 )
2008-10-18 16:33:37 +04:00
break ;
if ( strcasecmp ( key , " ACTION " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid ACTION operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > action , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " DEVPATH " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid DEVPATH operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > devpath , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " KERNEL " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid KERNEL operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > kernel , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " SUBSYSTEM " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid SUBSYSTEM operation \n " ) ;
goto invalid ;
}
/* bus, class, subsystem events should all be the same */
if ( strcmp ( value , " subsystem " ) = = 0 | |
strcmp ( value , " bus " ) = = 0 | |
strcmp ( value , " class " ) = = 0 ) {
if ( strcmp ( value , " bus " ) = = 0 | | strcmp ( value , " class " ) = = 0 )
err ( rules - > udev , " '%s' must be specified as 'subsystem' \n "
" please fix it in %s:%u " , value , filename , lineno ) ;
add_rule_key ( rule , & rule - > subsystem , operation , " subsystem|class|bus " ) ;
} else
add_rule_key ( rule , & rule - > subsystem , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " DRIVER " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid DRIVER operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > driver , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strncasecmp ( key , " ATTR{ " , sizeof ( " ATTR{ " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( rules , key + sizeof ( " ATTR " ) - 1 ) ;
if ( attr = = NULL ) {
err ( rules - > udev , " error parsing ATTR attribute \n " ) ;
goto invalid ;
}
if ( add_rule_key_pair ( rules , rule , & rule - > attr , operation , attr , value ) ! = 0 )
goto invalid ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " KERNELS " ) = = 0 | |
strcasecmp ( key , " ID " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid KERNELS operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > kernels , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " SUBSYSTEMS " ) = = 0 | |
strcasecmp ( key , " BUS " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid SUBSYSTEMS operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > subsystems , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " DRIVERS " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid DRIVERS operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > drivers , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strncasecmp ( key , " ATTRS{ " , sizeof ( " ATTRS{ " ) - 1 ) = = 0 | |
strncasecmp ( key , " SYSFS{ " , sizeof ( " SYSFS{ " ) - 1 ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid ATTRS operation \n " ) ;
goto invalid ;
}
attr = get_key_attribute ( rules , key + sizeof ( " ATTRS " ) - 1 ) ;
if ( attr = = NULL ) {
err ( rules - > udev , " error parsing ATTRS attribute \n " ) ;
goto invalid ;
}
if ( strncmp ( attr , " device/ " , 7 ) = = 0 )
err ( rules - > udev , " the 'device' link may not be available in a future kernel, "
" please fix it in %s:%u " , filename , lineno ) ;
else if ( strstr ( attr , " ../ " ) ! = NULL )
err ( rules - > udev , " do not reference parent sysfs directories directly, "
" it may break with a future kernel, please fix it in %s:%u " , filename , lineno ) ;
if ( add_rule_key_pair ( rules , rule , & rule - > attrs , operation , attr , value ) ! = 0 )
goto invalid ;
valid = 1 ;
continue ;
}
if ( strncasecmp ( key , " ENV{ " , sizeof ( " ENV{ " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( rules , key + sizeof ( " ENV " ) - 1 ) ;
if ( attr = = NULL ) {
err ( rules - > udev , " error parsing ENV attribute \n " ) ;
goto invalid ;
}
if ( strncmp ( attr , " PHYSDEV " , 7 ) = = 0 )
physdev = 1 ;
if ( add_rule_key_pair ( rules , rule , & rule - > env , operation , attr , value ) ! = 0 )
goto invalid ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " PROGRAM " ) = = 0 ) {
add_rule_key ( rule , & rule - > program , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " RESULT " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid RESULT operation \n " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > result , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strncasecmp ( key , " IMPORT " , sizeof ( " IMPORT " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( rules , key + sizeof ( " IMPORT " ) - 1 ) ;
if ( attr ! = NULL & & strstr ( attr , " program " ) ) {
dbg ( rules - > udev , " IMPORT will be executed \n " ) ;
rule - > import_type = IMPORT_PROGRAM ;
} else if ( attr ! = NULL & & strstr ( attr , " file " ) ) {
dbg ( rules - > udev , " IMPORT will be included as file \n " ) ;
rule - > import_type = IMPORT_FILE ;
} else if ( attr ! = NULL & & strstr ( attr , " parent " ) ) {
dbg ( rules - > udev , " IMPORT will include the parent values \n " ) ;
rule - > import_type = IMPORT_PARENT ;
} else {
/* figure it out if it is executable */
char file [ UTIL_PATH_SIZE ] ;
char * pos ;
struct stat statbuf ;
util_strlcpy ( file , value , sizeof ( file ) ) ;
pos = strchr ( file , ' ' ) ;
if ( pos )
pos [ 0 ] = ' \0 ' ;
/* allow programs in /lib/udev called without the path */
if ( strchr ( file , ' / ' ) = = NULL ) {
util_strlcpy ( file , UDEV_PREFIX " /lib/udev/ " , sizeof ( file ) ) ;
util_strlcat ( file , value , sizeof ( file ) ) ;
pos = strchr ( file , ' ' ) ;
if ( pos )
pos [ 0 ] = ' \0 ' ;
}
dbg ( rules - > udev , " IMPORT auto mode for '%s' \n " , file ) ;
if ( ! lstat ( file , & statbuf ) & & ( statbuf . st_mode & S_IXUSR ) ) {
dbg ( rules - > udev , " IMPORT is executable, will be executed (autotype) \n " ) ;
rule - > import_type = IMPORT_PROGRAM ;
} else {
dbg ( rules - > udev , " IMPORT is not executable, will be included as file (autotype) \n " ) ;
rule - > import_type = IMPORT_FILE ;
}
}
add_rule_key ( rule , & rule - > import , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strncasecmp ( key , " TEST " , sizeof ( " TEST " ) - 1 ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( rules - > udev , " invalid TEST operation \n " ) ;
goto invalid ;
}
attr = get_key_attribute ( rules , key + sizeof ( " TEST " ) - 1 ) ;
if ( attr ! = NULL )
rule - > test_mode_mask = strtol ( attr , NULL , 8 ) ;
add_rule_key ( rule , & rule - > test , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strncasecmp ( key , " RUN " , sizeof ( " RUN " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( rules , key + sizeof ( " RUN " ) - 1 ) ;
if ( attr ! = NULL ) {
if ( strstr ( attr , " ignore_error " ) )
rule - > run_ignore_error = 1 ;
}
add_rule_key ( rule , & rule - > run , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " WAIT_FOR " ) = = 0 | | strcasecmp ( key , " WAIT_FOR_SYSFS " ) = = 0 ) {
add_rule_key ( rule , & rule - > wait_for , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " LABEL " ) = = 0 ) {
add_rule_key ( rule , & rule - > label , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " GOTO " ) = = 0 ) {
add_rule_key ( rule , & rule - > goto_label , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strncasecmp ( key , " NAME " , sizeof ( " NAME " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( rules , key + sizeof ( " NAME " ) - 1 ) ;
if ( attr ! = NULL ) {
if ( strstr ( attr , " all_partitions " ) ! = NULL ) {
dbg ( rules - > udev , " creation of partition nodes requested \n " ) ;
rule - > partitions = DEFAULT_FAKE_PARTITIONS_COUNT ;
}
if ( strstr ( attr , " ignore_remove " ) ! = NULL ) {
dbg ( rules - > udev , " remove event should be ignored \n " ) ;
rule - > ignore_remove = 1 ;
}
}
if ( value [ 0 ] = = ' \0 ' )
dbg ( rules - > udev , " name empty, node creation supressed \n " ) ;
add_rule_key ( rule , & rule - > name , operation , value ) ;
continue ;
}
if ( strcasecmp ( key , " SYMLINK " ) = = 0 ) {
if ( operation = = KEY_OP_MATCH | |
operation = = KEY_OP_NOMATCH )
add_rule_key ( rule , & rule - > symlink_match , operation , value ) ;
else
add_rule_key ( rule , & rule - > symlink , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " OWNER " ) = = 0 ) {
valid = 1 ;
if ( rules - > resolve_names & & ( ! strchr ( value , ' $ ' ) & & ! strchr ( value , ' % ' ) ) ) {
char * endptr ;
strtoul ( value , & endptr , 10 ) ;
if ( endptr [ 0 ] ! = ' \0 ' ) {
char owner [ 32 ] ;
2008-10-18 22:12:55 +04:00
uid_t uid = util_lookup_user ( rules - > udev , value ) ;
2008-10-18 16:33:37 +04:00
dbg ( rules - > udev , " replacing username='%s' by id=%i \n " , value , uid ) ;
sprintf ( owner , " %u " , ( unsigned int ) uid ) ;
add_rule_key ( rule , & rule - > owner , operation , owner ) ;
continue ;
}
}
add_rule_key ( rule , & rule - > owner , operation , value ) ;
continue ;
}
if ( strcasecmp ( key , " GROUP " ) = = 0 ) {
valid = 1 ;
if ( rules - > resolve_names & & ( ! strchr ( value , ' $ ' ) & & ! strchr ( value , ' % ' ) ) ) {
char * endptr ;
strtoul ( value , & endptr , 10 ) ;
if ( endptr [ 0 ] ! = ' \0 ' ) {
char group [ 32 ] ;
2008-10-18 22:12:55 +04:00
gid_t gid = util_lookup_group ( rules - > udev , value ) ;
2008-10-18 16:33:37 +04:00
dbg ( rules - > udev , " replacing groupname='%s' by id=%i \n " , value , gid ) ;
sprintf ( group , " %u " , ( unsigned int ) gid ) ;
add_rule_key ( rule , & rule - > group , operation , group ) ;
continue ;
}
}
add_rule_key ( rule , & rule - > group , operation , value ) ;
continue ;
}
if ( strcasecmp ( key , " MODE " ) = = 0 ) {
add_rule_key ( rule , & rule - > mode , operation , value ) ;
valid = 1 ;
continue ;
}
if ( strcasecmp ( key , " OPTIONS " ) = = 0 ) {
const char * pos ;
if ( strstr ( value , " last_rule " ) ! = NULL ) {
dbg ( rules - > udev , " last rule to be applied \n " ) ;
rule - > last_rule = 1 ;
}
if ( strstr ( value , " ignore_device " ) ! = NULL ) {
dbg ( rules - > udev , " device should be ignored \n " ) ;
rule - > ignore_device = 1 ;
}
if ( strstr ( value , " ignore_remove " ) ! = NULL ) {
dbg ( rules - > udev , " remove event should be ignored \n " ) ;
rule - > ignore_remove = 1 ;
}
pos = strstr ( value , " link_priority= " ) ;
if ( pos ! = NULL ) {
rule - > link_priority = atoi ( & pos [ strlen ( " link_priority= " ) ] ) ;
dbg ( rules - > udev , " link priority=%i \n " , rule - > link_priority ) ;
}
pos = strstr ( value , " event_timeout= " ) ;
if ( pos ! = NULL ) {
rule - > event_timeout = atoi ( & pos [ strlen ( " event_timeout= " ) ] ) ;
dbg ( rules - > udev , " event timout=%i \n " , rule - > event_timeout ) ;
}
pos = strstr ( value , " string_escape= " ) ;
if ( pos ! = NULL ) {
pos = & pos [ strlen ( " string_escape= " ) ] ;
if ( strncmp ( pos , " none " , strlen ( " none " ) ) = = 0 )
rule - > string_escape = ESCAPE_NONE ;
else if ( strncmp ( pos , " replace " , strlen ( " replace " ) ) = = 0 )
rule - > string_escape = ESCAPE_REPLACE ;
}
if ( strstr ( value , " all_partitions " ) ! = NULL ) {
dbg ( rules - > udev , " creation of partition nodes requested \n " ) ;
rule - > partitions = DEFAULT_FAKE_PARTITIONS_COUNT ;
}
valid = 1 ;
continue ;
}
err ( rules - > udev , " unknown key '%s' in %s:%u \n " , key , filename , lineno ) ;
}
if ( physdev & & rule - > wait_for . operation = = KEY_OP_UNSET )
err ( rules - > udev , " PHYSDEV* values are deprecated and will be removed from a future kernel, \n "
" please fix it in %s:%u " , filename , lineno ) ;
/* skip line if not any valid key was found */
if ( ! valid )
goto invalid ;
/* grow buffer and add rule */
rule_size = sizeof ( struct udev_rule ) + rule - > bufsize ;
padding = ( sizeof ( size_t ) - rule_size % sizeof ( size_t ) ) % sizeof ( size_t ) ;
dbg ( rules - > udev , " add %zi padding bytes \n " , padding ) ;
rule_size + = padding ;
rule - > bufsize + = padding ;
rules - > buf = realloc ( rules - > buf , rules - > bufsize + rule_size ) ;
if ( ! rules - > buf ) {
err ( rules - > udev , " realloc failed \n " ) ;
goto exit ;
}
dbg ( rules - > udev , " adding rule to offset %zi \n " , rules - > bufsize ) ;
memcpy ( rules - > buf + rules - > bufsize , rule , rule_size ) ;
rules - > bufsize + = rule_size ;
exit :
return 0 ;
invalid :
err ( rules - > udev , " invalid rule '%s:%u' \n " , filename , lineno ) ;
return - 1 ;
}
static int parse_file ( struct udev_rules * rules , const char * filename )
{
FILE * f ;
char line [ UTIL_LINE_SIZE ] ;
size_t start ;
struct udev_rule * rule ;
struct udev_rules_iter iter ;
start = rules - > bufsize ;
info ( rules - > udev , " reading '%s' as rules file \n " , filename ) ;
f = fopen ( filename , " r " ) ;
if ( f = = NULL )
return - 1 ;
while ( fgets ( line , sizeof ( line ) , f ) ! = NULL ) {
int line_nr = 0 ;
char * key ;
size_t len ;
/* skip whitespace */
line_nr + + ;
key = line ;
while ( isspace ( key [ 0 ] ) )
key + + ;
/* comment */
if ( key [ 0 ] = = ' # ' )
continue ;
len = strlen ( line ) ;
if ( len < 3 )
continue ;
/* continue reading if backslash+newline is found */
while ( line [ len - 2 ] = = ' \\ ' ) {
if ( fgets ( & line [ len - 2 ] , ( sizeof ( line ) - len ) + 2 , f ) = = NULL )
break ;
line_nr + + ;
len = strlen ( line ) ;
}
if ( len + 1 > = sizeof ( line ) ) {
err ( rules - > udev , " line too long '%s':%u, ignored \n " , filename , line_nr ) ;
continue ;
}
add_to_rules ( rules , key , filename , line_nr ) ;
}
fclose ( f ) ;
/* compute all goto targets within this file */
udev_rules_iter_init ( & iter , rules ) ;
udev_rules_iter_goto ( & iter , start ) ;
while ( ( rule = udev_rules_iter_next ( & iter ) ) ) {
if ( rule - > goto_label . operation ! = KEY_OP_UNSET ) {
char * goto_label = & rule - > buf [ rule - > goto_label . val_off ] ;
dbg ( rules - > udev , " resolving goto label '%s' \n " , goto_label ) ;
rule - > goto_rule_off = find_label ( & iter , goto_label ) ;
if ( rule - > goto_rule_off = = 0 ) {
err ( rules - > udev , " ignore goto to nonexistent label '%s' in '%s' \n " ,
goto_label , filename ) ;
rule - > goto_rule_off = iter . current ;
}
}
}
return 0 ;
}
static int add_matching_files ( struct udev * udev , struct udev_list_node * file_list , const char * dirname , const char * suffix )
{
struct dirent * ent ;
DIR * dir ;
char filename [ UTIL_PATH_SIZE ] ;
dbg ( udev , " open directory '%s' \n " , dirname ) ;
dir = opendir ( dirname ) ;
if ( dir = = NULL ) {
err ( udev , " unable to open '%s': %m \n " , dirname ) ;
return - 1 ;
}
while ( 1 ) {
ent = readdir ( dir ) ;
if ( ent = = NULL | | ent - > d_name [ 0 ] = = ' \0 ' )
break ;
if ( ( ent - > d_name [ 0 ] = = ' . ' ) | | ( ent - > d_name [ 0 ] = = ' # ' ) )
continue ;
/* look for file matching with specified suffix */
if ( suffix ! = NULL ) {
const char * ext ;
ext = strrchr ( ent - > d_name , ' . ' ) ;
if ( ext = = NULL )
continue ;
if ( strcmp ( ext , suffix ) ! = 0 )
continue ;
}
dbg ( udev , " put file '%s/%s' into list \n " , dirname , ent - > d_name ) ;
snprintf ( filename , sizeof ( filename ) , " %s/%s " , dirname , ent - > d_name ) ;
filename [ sizeof ( filename ) - 1 ] = ' \0 ' ;
udev_list_entry_add ( udev , file_list , filename , NULL , 1 , 1 ) ;
}
closedir ( dir ) ;
return 0 ;
}
2008-10-18 17:02:01 +04:00
struct udev_rules * udev_rules_new ( struct udev * udev , int resolve_names )
2008-10-18 16:33:37 +04:00
{
2008-10-18 17:02:01 +04:00
struct udev_rules * rules ;
2008-10-18 16:33:37 +04:00
struct stat statbuf ;
char filename [ PATH_MAX ] ;
struct udev_list_node file_list ;
struct udev_list_entry * file_loop , * file_tmp ;
2008-10-18 17:02:01 +04:00
rules = malloc ( sizeof ( struct udev_rules ) ) ;
if ( rules = = NULL )
return rules ;
2008-10-18 16:33:37 +04:00
memset ( rules , 0x00 , sizeof ( struct udev_rules ) ) ;
rules - > udev = udev ;
rules - > resolve_names = resolve_names ;
udev_list_init ( & file_list ) ;
if ( udev_get_rules_path ( udev ) ! = NULL ) {
/* custom rules location for testing */
add_matching_files ( udev , & file_list , udev_get_rules_path ( udev ) , " .rules " ) ;
} else {
struct udev_list_node sort_list ;
struct udev_list_entry * sort_loop , * sort_tmp ;
/* read user/custom rules */
add_matching_files ( udev , & file_list , SYSCONFDIR " /udev/rules.d " , " .rules " ) ;
/* read dynamic/temporary rules */
util_strlcpy ( filename , udev_get_dev_path ( udev ) , sizeof ( filename ) ) ;
util_strlcat ( filename , " /.udev/rules.d " , sizeof ( filename ) ) ;
if ( stat ( filename , & statbuf ) ! = 0 ) {
2008-10-18 22:12:55 +04:00
util_create_path ( udev , filename ) ;
2008-10-18 16:33:37 +04:00
udev_selinux_setfscreatecon ( udev , filename , S_IFDIR | 0755 ) ;
mkdir ( filename , 0755 ) ;
udev_selinux_resetfscreatecon ( udev ) ;
}
udev_list_init ( & sort_list ) ;
add_matching_files ( udev , & sort_list , filename , " .rules " ) ;
/* read default rules */
add_matching_files ( udev , & sort_list , UDEV_PREFIX " /lib/udev/rules.d " , " .rules " ) ;
/* sort all rules files by basename into list of files */
udev_list_entry_foreach_safe ( sort_loop , sort_tmp , udev_list_get_entry ( & sort_list ) ) {
const char * sort_name = udev_list_entry_get_name ( sort_loop ) ;
const char * sort_base = strrchr ( sort_name , ' / ' ) ;
if ( sort_base = = NULL )
continue ;
udev_list_entry_foreach_safe ( file_loop , file_tmp , udev_list_get_entry ( & file_list ) ) {
const char * file_name = udev_list_entry_get_name ( file_loop ) ;
const char * file_base = strrchr ( file_name , ' / ' ) ;
if ( file_base = = NULL )
continue ;
if ( strcmp ( file_base , sort_base ) = = 0 ) {
info ( udev , " rule file basename '%s' already added, ignoring '%s' \n " ,
file_name , sort_name ) ;
udev_list_entry_remove ( sort_loop ) ;
sort_loop = NULL ;
break ;
}
if ( strcmp ( file_base , sort_base ) > 0 )
break ;
}
if ( sort_loop ! = NULL )
udev_list_entry_move_before ( sort_loop , file_loop ) ;
}
}
/* parse list of files */
udev_list_entry_foreach_safe ( file_loop , file_tmp , udev_list_get_entry ( & file_list ) ) {
const char * file_name = udev_list_entry_get_name ( file_loop ) ;
if ( stat ( file_name , & statbuf ) = = 0 & & statbuf . st_size > 0 )
parse_file ( rules , file_name ) ;
else
info ( udev , " can not read '%s' \n " , file_name ) ;
udev_list_entry_remove ( file_loop ) ;
}
2008-10-18 17:02:01 +04:00
return rules ;
2008-10-18 16:33:37 +04:00
}
2008-10-18 17:02:01 +04:00
void udev_rules_unref ( struct udev_rules * rules )
2008-10-18 16:33:37 +04:00
{
2008-10-18 17:02:01 +04:00
if ( rules = = NULL )
return ;
2008-10-18 16:33:37 +04:00
if ( rules - > buf ) {
free ( rules - > buf ) ;
rules - > buf = NULL ;
}
2008-10-18 17:02:01 +04:00
free ( rules ) ;
2008-10-18 16:33:37 +04:00
}