2003-12-03 05:38:30 +03:00
/*
2004-01-27 06:21:58 +03:00
* Copyright ( C ) 2003 , 2004 Greg Kroah - Hartman < greg @ kroah . com >
2006-08-28 02:29:11 +04:00
* Copyright ( C ) 2003 - 2006 Kay Sievers < kay . sievers @ vrfy . org >
2003-12-03 05:38:30 +03: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 version 2 of the License .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
2006-08-28 02:29:11 +04:00
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
2003-12-03 05:38:30 +03:00
*
*/
# include <stddef.h>
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <ctype.h>
# include <unistd.h>
2004-02-24 06:31:14 +03:00
# include <sys/stat.h>
2003-12-03 05:38:30 +03:00
# include <errno.h>
# include "udev.h"
2005-03-13 00:36:32 +03:00
# include "udev_rules.h"
2003-12-03 05:38:30 +03:00
2004-03-11 09:35:37 +03:00
2005-07-05 17:24:41 +04:00
void udev_rules_iter_init ( struct udev_rules * rules )
2005-06-24 20:05:32 +04:00
{
2005-07-05 17:24:41 +04:00
dbg ( " bufsize=%zi " , rules - > bufsize ) ;
rules - > current = 0 ;
2005-06-24 20:05:32 +04:00
}
2005-07-05 17:24:41 +04:00
struct udev_rule * udev_rules_iter_next ( struct udev_rules * rules )
2005-06-24 20:05:32 +04:00
{
static struct udev_rule * rule ;
2005-07-05 17:24:41 +04:00
if ( ! rules )
return NULL ;
2005-06-24 20:05:32 +04:00
2005-07-05 17:24:41 +04:00
dbg ( " current=%zi " , rules - > current ) ;
2005-07-16 09:46:31 +04:00
if ( rules - > current > = rules - > bufsize ) {
dbg ( " no more rules " ) ;
2005-07-05 17:24:41 +04:00
return NULL ;
2005-07-16 09:46:31 +04:00
}
2003-12-03 05:38:30 +03:00
2005-07-05 17:24:41 +04:00
/* get next rule */
rule = ( struct udev_rule * ) ( rules - > buf + rules - > current ) ;
rules - > current + = sizeof ( struct udev_rule ) + rule - > bufsize ;
return rule ;
2003-12-03 05:38:30 +03:00
}
2003-12-04 05:33:58 +03:00
2005-07-16 09:46:31 +04:00
struct udev_rule * udev_rules_iter_label ( struct udev_rules * rules , const char * label )
{
static struct udev_rule * rule ;
next :
dbg ( " current=%zi " , rules - > current ) ;
if ( rules - > current > = rules - > bufsize ) {
dbg ( " no more rules " ) ;
return NULL ;
}
rule = ( struct udev_rule * ) ( rules - > buf + rules - > current ) ;
if ( strcmp ( & rule - > buf [ rule - > label . val_off ] , label ) ! = 0 ) {
dbg ( " moving forward, looking for label '%s' " , label ) ;
rules - > current + = sizeof ( struct udev_rule ) + rule - > bufsize ;
goto next ;
}
dbg ( " found label '%s' " , label ) ;
return rule ;
}
2005-03-13 07:46:31 +03:00
static int get_key ( char * * line , char * * key , enum key_operation * operation , char * * value )
{
char * linepos ;
char * temp ;
linepos = * line ;
2005-08-08 04:21:55 +04:00
if ( linepos = = NULL & & linepos [ 0 ] = = ' \0 ' )
2005-03-13 07:46:31 +03:00
return - 1 ;
/* skip whitespace */
while ( isspace ( linepos [ 0 ] ) | | linepos [ 0 ] = = ' , ' )
linepos + + ;
/* get the key */
2005-08-08 04:21:55 +04:00
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
2005-03-13 07:46:31 +03:00
* key = linepos ;
2005-08-08 04:21:55 +04:00
2005-03-13 07:46:31 +03:00
while ( 1 ) {
linepos + + ;
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
if ( isspace ( linepos [ 0 ] ) )
break ;
if ( linepos [ 0 ] = = ' = ' )
break ;
2007-02-25 03:04:23 +03:00
if ( ( linepos [ 0 ] = = ' + ' ) | | ( linepos [ 0 ] = = ' ! ' ) | | ( linepos [ 0 ] = = ' : ' ) )
if ( linepos [ 1 ] = = ' = ' )
break ;
2005-03-13 07:46:31 +03:00
}
/* remember end of key */
temp = linepos ;
/* skip whitespace after key */
while ( isspace ( linepos [ 0 ] ) )
linepos + + ;
2005-08-08 04:21:55 +04:00
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
2005-03-13 07:46:31 +03:00
/* get operation type */
if ( linepos [ 0 ] = = ' = ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_MATCH ;
linepos + = 2 ;
dbg ( " operator=match " ) ;
} else if ( linepos [ 0 ] = = ' ! ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_NOMATCH ;
linepos + = 2 ;
dbg ( " operator=nomatch " ) ;
} else if ( linepos [ 0 ] = = ' + ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_ADD ;
linepos + = 2 ;
dbg ( " operator=add " ) ;
} else if ( linepos [ 0 ] = = ' = ' ) {
* operation = KEY_OP_ASSIGN ;
linepos + + ;
dbg ( " operator=assign " ) ;
2005-06-05 06:57:03 +04:00
} else if ( linepos [ 0 ] = = ' : ' & & linepos [ 1 ] = = ' = ' ) {
* operation = KEY_OP_ASSIGN_FINAL ;
linepos + = 2 ;
dbg ( " operator=assign_final " ) ;
2005-03-13 07:46:31 +03:00
} else
return - 1 ;
/* terminate key */
temp [ 0 ] = ' \0 ' ;
dbg ( " key='%s' " , * key ) ;
/* skip whitespace after operator */
while ( isspace ( linepos [ 0 ] ) )
linepos + + ;
2005-08-08 04:21:55 +04:00
if ( linepos [ 0 ] = = ' \0 ' )
return - 1 ;
2005-03-13 07:46:31 +03:00
/* get the value*/
if ( linepos [ 0 ] = = ' " ' )
linepos + + ;
else
return - 1 ;
* value = linepos ;
temp = strchr ( linepos , ' " ' ) ;
if ( ! temp )
return - 1 ;
temp [ 0 ] = ' \0 ' ;
temp + + ;
dbg ( " value='%s' " , * value ) ;
/* move line to next key */
* line = temp ;
return 0 ;
}
2004-11-13 14:50:44 +03:00
/* extract possible KEY{attr} */
2004-02-17 08:39:40 +03:00
static char * get_key_attribute ( char * str )
{
char * pos ;
char * attr ;
attr = strchr ( str , ' { ' ) ;
if ( attr ! = NULL ) {
attr + + ;
pos = strchr ( attr , ' } ' ) ;
if ( pos = = NULL ) {
2005-03-27 03:11:03 +04:00
err ( " missing closing brace for format " ) ;
2004-02-17 08:39:40 +03:00
return NULL ;
}
pos [ 0 ] = ' \0 ' ;
dbg ( " attribute='%s' " , attr ) ;
return attr ;
}
return NULL ;
}
2005-07-05 17:24:41 +04:00
static int add_rule_key ( struct udev_rule * rule , struct key * key ,
enum key_operation operation , const char * value )
2003-12-03 05:38:30 +03:00
{
2005-07-05 17:24:41 +04:00
size_t val_len = strnlen ( value , PATH_SIZE ) ;
key - > operation = operation ;
key - > val_off = rule - > bufsize ;
strlcpy ( rule - > buf + rule - > bufsize , value , val_len + 1 ) ;
rule - > bufsize + = val_len + 1 ;
return 0 ;
}
static int add_rule_key_pair ( struct udev_rule * rule , struct key_pairs * pairs ,
enum key_operation operation , const char * key , const char * value )
{
size_t key_len = strnlen ( key , PATH_SIZE ) ;
if ( pairs - > count > = PAIRS_MAX ) {
2006-08-20 20:16:27 +04:00
err ( " skip, too many keys of the same type in a single rule " ) ;
2005-07-05 17:24:41 +04:00
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 ;
strlcpy ( rule - > buf + rule - > bufsize , key , key_len + 1 ) ;
rule - > bufsize + = key_len + 1 ;
pairs - > count + + ;
return 0 ;
}
2006-04-24 21:25:55 +04:00
static int add_to_rules ( struct udev_rules * rules , char * line , const char * filename , unsigned int lineno )
2005-07-05 17:24:41 +04:00
{
2007-06-22 03:27:02 +04:00
char buf [ sizeof ( struct udev_rule ) + LINE_SIZE ] ;
2005-07-05 17:24:41 +04:00
struct udev_rule * rule ;
size_t rule_size ;
int valid ;
2005-03-13 07:46:31 +03:00
char * linepos ;
2004-02-17 08:39:40 +03:00
char * attr ;
2005-07-17 17:40:29 +04:00
size_t padding ;
2006-09-19 00:14:18 +04:00
int physdev = 0 ;
2005-07-05 17:24:41 +04:00
int retval ;
2003-12-03 05:38:30 +03:00
2007-06-22 03:27:02 +04:00
memset ( buf , 0x00 , sizeof ( buf ) ) ;
rule = ( struct udev_rule * ) buf ;
2005-07-05 17:24:41 +04:00
linepos = line ;
valid = 0 ;
2003-12-03 05:38:30 +03:00
2007-06-22 03:27:02 +04:00
/* get all the keys */
2005-07-05 17:24:41 +04:00
while ( 1 ) {
char * key ;
char * value ;
enum key_operation operation = KEY_OP_UNSET ;
2004-12-20 09:38:33 +03:00
2005-07-05 17:24:41 +04:00
retval = get_key ( & linepos , & key , & operation , & value ) ;
if ( retval )
break ;
2003-12-03 05:38:30 +03:00
2006-08-19 18:06:25 +04:00
if ( strcasecmp ( key , " ACTION " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( " invalid ACTION operation " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > action , operation , value ) ;
2005-07-16 09:46:31 +04:00
valid = 1 ;
continue ;
}
2006-08-19 18:06:25 +04:00
if ( strcasecmp ( key , " DEVPATH " ) = = 0 ) {
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( " invalid DEVPATH operation " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > devpath , operation , value ) ;
2005-07-16 09:46:31 +04:00
valid = 1 ;
continue ;
}
2005-07-05 17:24:41 +04:00
if ( strcasecmp ( key , " KERNEL " ) = = 0 ) {
2006-04-24 21:25:55 +04:00
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( " invalid KERNEL operation " ) ;
goto invalid ;
}
2006-08-19 18:06:25 +04:00
add_rule_key ( rule , & rule - > kernel , operation , value ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
2004-09-11 07:54:04 +04:00
continue ;
}
2003-12-18 05:24:05 +03:00
2005-07-05 17:24:41 +04:00
if ( strcasecmp ( key , " SUBSYSTEM " ) = = 0 ) {
2006-04-24 21:25:55 +04:00
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( " invalid SUBSYSTEM operation " ) ;
goto invalid ;
}
2007-06-02 12:02:32 +04:00
/* 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 ( " '%s' must be specified as 'subsystem' "
" 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 ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
2004-09-15 04:45:48 +04:00
continue ;
2005-07-05 17:24:41 +04:00
}
2004-09-11 07:54:04 +04:00
2006-08-19 18:06:25 +04:00
if ( strcasecmp ( key , " DRIVER " ) = = 0 ) {
2007-02-25 01:43:04 +03:00
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( " invalid DRIVER operation " ) ;
goto invalid ;
}
2007-02-01 22:23:41 +03:00
add_rule_key ( rule , & rule - > driver , operation , value ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
2003-12-03 05:38:30 +03:00
continue ;
2004-12-20 09:38:33 +03:00
}
2003-12-03 05:38:30 +03:00
2006-08-20 21:08:37 +04:00
if ( strncasecmp ( key , " ATTR{ " , sizeof ( " ATTR{ " ) - 1 ) = = 0 ) {
2006-08-19 18:06:25 +04:00
attr = get_key_attribute ( key + sizeof ( " ATTR " ) - 1 ) ;
if ( attr = = NULL ) {
2006-08-20 20:15:29 +04:00
err ( " error parsing ATTR attribute " ) ;
goto invalid ;
2006-08-19 18:06:25 +04:00
}
2006-08-20 20:15:29 +04:00
if ( add_rule_key_pair ( rule , & rule - > attr , operation , attr , value ) ! = 0 )
goto invalid ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2004-09-11 07:54:04 +04:00
2006-08-19 18:06:25 +04:00
if ( strcasecmp ( key , " KERNELS " ) = = 0 | |
strcasecmp ( key , " ID " ) = = 0 ) {
2006-04-24 21:25:55 +04:00
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
2006-08-19 18:06:25 +04:00
err ( " invalid KERNELS operation " ) ;
2006-04-24 21:25:55 +04:00
goto invalid ;
}
2006-08-19 18:06:25 +04:00
add_rule_key ( rule , & rule - > kernels , operation , value ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2005-03-13 07:46:31 +03:00
2006-08-20 20:20:55 +04:00
if ( strcasecmp ( key , " SUBSYSTEMS " ) = = 0 | |
2006-08-19 18:06:25 +04:00
strcasecmp ( key , " BUS " ) = = 0 ) {
2006-04-24 21:25:55 +04:00
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
2006-08-19 18:06:25 +04:00
err ( " invalid SUBSYSTEMS operation " ) ;
2006-04-24 21:25:55 +04:00
goto invalid ;
}
2006-08-19 18:06:25 +04:00
add_rule_key ( rule , & rule - > subsystems , operation , value ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2003-12-07 20:12:07 +03:00
2006-08-19 18:06:25 +04:00
if ( strcasecmp ( key , " DRIVERS " ) = = 0 ) {
2006-05-01 03:45:26 +04:00
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
2006-08-19 18:06:25 +04:00
err ( " invalid DRIVERS operation " ) ;
2006-05-01 03:45:26 +04:00
goto invalid ;
}
2006-08-19 18:06:25 +04:00
add_rule_key ( rule , & rule - > drivers , operation , value ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2005-02-21 16:01:23 +03:00
2006-08-20 21:08:37 +04:00
if ( strncasecmp ( key , " ATTRS{ " , sizeof ( " ATTRS{ " ) - 1 ) = = 0 | |
strncasecmp ( key , " SYSFS{ " , sizeof ( " SYSFS{ " ) - 1 ) = = 0 ) {
2007-02-01 22:23:41 +03:00
if ( operation ! = KEY_OP_MATCH & &
operation ! = KEY_OP_NOMATCH ) {
err ( " invalid ATTRS operation " ) ;
goto invalid ;
}
2006-08-19 18:06:25 +04:00
attr = get_key_attribute ( key + sizeof ( " ATTRS " ) - 1 ) ;
if ( attr = = NULL ) {
2006-08-20 20:15:29 +04:00
err ( " error parsing ATTRS attribute " ) ;
goto invalid ;
2006-08-19 18:06:25 +04:00
}
2006-09-18 04:01:29 +04:00
if ( strncmp ( attr , " device/ " , 7 ) = = 0 )
err ( " the 'device' link is deprecated and will be removed from a future kernel, "
" please fix it in %s:%u " , filename , lineno ) ;
2007-06-01 18:37:43 +04:00
else if ( strstr ( attr , " ../ " ) ! = NULL )
2006-09-18 04:01:29 +04:00
err ( " do not reference parent sysfs directories directly, that may break with a future kernel, "
" please fix it in %s:%u " , filename , lineno ) ;
2006-08-20 20:15:29 +04:00
if ( add_rule_key_pair ( rule , & rule - > attrs , operation , attr , value ) ! = 0 )
goto invalid ;
2005-07-07 22:05:51 +04:00
valid = 1 ;
continue ;
}
2006-08-20 21:08:37 +04:00
if ( strncasecmp ( key , " ENV{ " , sizeof ( " ENV{ " ) - 1 ) = = 0 ) {
2005-07-05 17:24:41 +04:00
attr = get_key_attribute ( key + sizeof ( " ENV " ) - 1 ) ;
if ( attr = = NULL ) {
err ( " error parsing ENV attribute " ) ;
2006-08-20 20:15:29 +04:00
goto invalid ;
2005-02-21 16:01:23 +03:00
}
2006-09-18 04:01:29 +04:00
if ( strncmp ( attr , " PHYSDEV " , 7 ) = = 0 )
2006-09-19 00:14:18 +04:00
physdev = 1 ;
2006-08-20 20:15:29 +04:00
if ( add_rule_key_pair ( rule , & rule - > env , operation , attr , value ) ! = 0 )
goto invalid ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2005-02-21 16:01:23 +03:00
2006-08-19 18:06:25 +04:00
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 ( " invalid RESULT operation " ) ;
goto invalid ;
}
add_rule_key ( rule , & rule - > result , operation , value ) ;
valid = 1 ;
continue ;
}
2005-07-05 17:24:41 +04:00
if ( strncasecmp ( key , " IMPORT " , sizeof ( " IMPORT " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( key + sizeof ( " IMPORT " ) - 1 ) ;
2007-07-14 22:44:19 +04:00
if ( attr ! = NULL & & strstr ( attr , " program " ) ) {
2005-07-05 17:24:41 +04:00
dbg ( " IMPORT will be executed " ) ;
2005-07-12 16:46:36 +04:00
rule - > import_type = IMPORT_PROGRAM ;
2007-07-14 22:44:19 +04:00
} else if ( attr ! = NULL & & strstr ( attr , " file " ) ) {
2005-07-05 17:24:41 +04:00
dbg ( " IMPORT will be included as file " ) ;
2005-07-12 16:46:36 +04:00
rule - > import_type = IMPORT_FILE ;
2007-07-14 22:44:19 +04:00
} else if ( attr ! = NULL & & strstr ( attr , " parent " ) ) {
2005-07-12 16:46:36 +04:00
dbg ( " IMPORT will include the parent values " ) ;
rule - > import_type = IMPORT_PARENT ;
2005-07-05 17:24:41 +04:00
} else {
/* figure it out if it is executable */
char file [ PATH_SIZE ] ;
char * pos ;
2007-07-15 21:10:06 +04:00
struct stat statbuf ;
2005-07-05 17:24:41 +04:00
strlcpy ( file , value , sizeof ( file ) ) ;
pos = strchr ( file , ' ' ) ;
if ( pos )
pos [ 0 ] = ' \0 ' ;
2006-01-26 05:59:13 +03:00
/* allow programs in /lib/udev called without the path */
if ( strchr ( file , ' / ' ) = = NULL ) {
strlcpy ( file , " /lib/udev/ " , sizeof ( file ) ) ;
strlcat ( file , value , sizeof ( file ) ) ;
pos = strchr ( file , ' ' ) ;
if ( pos )
pos [ 0 ] = ' \0 ' ;
}
2005-07-05 17:24:41 +04:00
dbg ( " IMPORT auto mode for '%s' " , file ) ;
2007-07-15 21:10:06 +04:00
if ( ! lstat ( file , & statbuf ) & & ( statbuf . st_mode & S_IXUSR ) ) {
2005-07-12 16:46:36 +04:00
dbg ( " IMPORT is executable, will be executed (autotype) " ) ;
rule - > import_type = IMPORT_PROGRAM ;
} else {
dbg ( " IMPORT is not executable, will be included as file (autotype) " ) ;
rule - > import_type = IMPORT_FILE ;
2005-07-05 17:24:41 +04:00
}
2005-06-20 02:29:38 +04:00
}
2005-07-05 17:24:41 +04:00
add_rule_key ( rule , & rule - > import , operation , value ) ;
valid = 1 ;
continue ;
}
2005-06-20 02:29:38 +04:00
2007-06-03 02:01:46 +04:00
if ( strncasecmp ( key , " TEST " , sizeof ( " TEST " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( 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 ;
}
2007-07-14 22:43:01 +04:00
if ( strncasecmp ( key , " RUN " , sizeof ( " RUN " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( key + sizeof ( " RUN " ) - 1 ) ;
2007-07-14 22:44:19 +04:00
if ( attr ! = NULL ) {
if ( strstr ( attr , " ignore_error " ) )
rule - > run_ignore_error = 1 ;
}
2006-08-19 18:06:25 +04:00
add_rule_key ( rule , & rule - > run , operation , value ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2003-12-17 10:36:19 +03:00
2006-08-19 18:06:25 +04:00
if ( strcasecmp ( key , " WAIT_FOR_SYSFS " ) = = 0 ) {
add_rule_key ( rule , & rule - > wait_for_sysfs , operation , value ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2003-12-17 10:36:19 +03:00
2006-08-19 18:06:25 +04:00
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 ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2005-03-13 10:15:10 +03:00
2005-07-05 17:24:41 +04:00
if ( strncasecmp ( key , " NAME " , sizeof ( " NAME " ) - 1 ) = = 0 ) {
attr = get_key_attribute ( key + sizeof ( " NAME " ) - 1 ) ;
if ( attr ! = NULL ) {
if ( strstr ( attr , " all_partitions " ) ! = NULL ) {
dbg ( " creation of partition nodes requested " ) ;
rule - > partitions = DEFAULT_PARTITIONS_COUNT ;
2003-12-23 09:31:35 +03:00
}
2005-07-05 17:24:41 +04:00
if ( strstr ( attr , " ignore_remove " ) ! = NULL ) {
dbg ( " remove event should be ignored " ) ;
rule - > ignore_remove = 1 ;
2005-03-13 13:40:32 +03:00
}
}
2005-11-07 18:00:41 +03:00
if ( value [ 0 ] = = ' \0 ' )
2006-03-21 18:17:28 +03:00
dbg ( " name empty, node creation supressed " ) ;
2005-11-07 18:00:41 +03:00
add_rule_key ( rule , & rule - > name , operation , value ) ;
2005-07-05 17:24:41 +04:00
continue ;
}
2005-03-13 13:40:32 +03:00
2005-07-05 17:24:41 +04:00
if ( strcasecmp ( key , " SYMLINK " ) = = 0 ) {
2007-07-16 15:29:28 +04:00
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 ) ;
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2005-03-13 13:40:32 +03:00
2005-07-05 17:24:41 +04:00
if ( strcasecmp ( key , " OWNER " ) = = 0 ) {
valid = 1 ;
2005-07-06 04:01:16 +04:00
if ( rules - > resolve_names & & ( ! strchr ( value , ' $ ' ) & & ! strchr ( value , ' % ' ) ) ) {
2005-07-05 17:24:41 +04:00
char * endptr ;
strtoul ( value , & endptr , 10 ) ;
if ( endptr [ 0 ] ! = ' \0 ' ) {
char owner [ 32 ] ;
uid_t uid = lookup_user ( value ) ;
dbg ( " replacing username='%s' by id=%i " , value , uid ) ;
2005-10-27 22:32:07 +04:00
sprintf ( owner , " %u " , ( unsigned int ) uid ) ;
2005-07-05 17:24:41 +04:00
add_rule_key ( rule , & rule - > owner , operation , owner ) ;
2005-03-13 10:15:10 +03:00
continue ;
2003-12-23 09:31:35 +03:00
}
2003-12-17 10:36:19 +03:00
}
2005-07-05 17:24:41 +04:00
add_rule_key ( rule , & rule - > owner , operation , value ) ;
continue ;
}
2005-06-20 02:29:38 +04:00
2005-07-05 17:24:41 +04:00
if ( strcasecmp ( key , " GROUP " ) = = 0 ) {
valid = 1 ;
2005-07-06 04:01:16 +04:00
if ( rules - > resolve_names & & ( ! strchr ( value , ' $ ' ) & & ! strchr ( value , ' % ' ) ) ) {
2005-07-05 17:24:41 +04:00
char * endptr ;
strtoul ( value , & endptr , 10 ) ;
if ( endptr [ 0 ] ! = ' \0 ' ) {
char group [ 32 ] ;
gid_t gid = lookup_group ( value ) ;
dbg ( " replacing groupname='%s' by id=%i " , value , gid ) ;
2005-10-27 22:32:07 +04:00
sprintf ( group , " %u " , ( unsigned int ) gid ) ;
2005-07-08 00:40:09 +04:00
add_rule_key ( rule , & rule - > group , operation , group ) ;
2005-07-05 17:24:41 +04:00
continue ;
2005-06-25 20:58:49 +04:00
}
2005-06-25 15:10:16 +04:00
}
2005-07-05 17:24:41 +04:00
add_rule_key ( rule , & rule - > group , operation , value ) ;
continue ;
}
2004-11-13 07:21:12 +03:00
2005-07-05 17:24:41 +04:00
if ( strcasecmp ( key , " MODE " ) = = 0 ) {
rule - > mode = strtol ( value , NULL , 8 ) ;
rule - > mode_operation = operation ;
valid = 1 ;
continue ;
}
[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
2005-07-05 17:24:41 +04:00
if ( strcasecmp ( key , " OPTIONS " ) = = 0 ) {
2007-03-16 17:16:08 +03:00
const char * pos ;
2005-07-05 17:24:41 +04:00
if ( strstr ( value , " last_rule " ) ! = NULL ) {
dbg ( " last rule to be applied " ) ;
rule - > last_rule = 1 ;
2003-12-17 10:36:19 +03:00
}
2005-07-05 17:24:41 +04:00
if ( strstr ( value , " ignore_device " ) ! = NULL ) {
dbg ( " device should be ignored " ) ;
rule - > ignore_device = 1 ;
2003-12-17 10:36:19 +03:00
}
2005-07-05 17:24:41 +04:00
if ( strstr ( value , " ignore_remove " ) ! = NULL ) {
dbg ( " remove event should be ignored " ) ;
rule - > ignore_remove = 1 ;
2004-03-11 09:35:37 +03:00
}
2007-03-16 17:16:08 +03:00
pos = strstr ( value , " link_priority= " ) ;
if ( pos ! = NULL ) {
rule - > link_priority = atoi ( & pos [ strlen ( " link_priority= " ) ] ) ;
2007-07-15 21:10:06 +04:00
dbg ( " link priority=%i " , rule - > link_priority ) ;
2007-03-16 17:16:08 +03:00
}
2007-06-22 03:27:02 +04:00
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 ;
}
2005-07-05 17:24:41 +04:00
if ( strstr ( value , " all_partitions " ) ! = NULL ) {
dbg ( " creation of partition nodes requested " ) ;
rule - > partitions = DEFAULT_PARTITIONS_COUNT ;
2004-03-11 09:35:37 +03:00
}
2005-07-05 17:24:41 +04:00
valid = 1 ;
continue ;
}
2004-03-11 09:35:37 +03:00
2006-08-13 06:23:16 +04:00
err ( " unknown key '%s' in %s:%u " , key , filename , lineno ) ;
2005-07-05 17:24:41 +04:00
}
2005-04-02 19:45:35 +04:00
2006-09-19 00:14:18 +04:00
if ( physdev & & rule - > wait_for_sysfs . operation = = KEY_OP_UNSET )
2006-09-20 22:22:57 +04:00
err ( " PHYSDEV* values are deprecated and will be removed from a future kernel, "
2006-09-19 00:14:18 +04:00
" please fix it in %s:%u " , filename , lineno ) ;
2005-07-05 17:24:41 +04:00
/* skip line if not any valid key was found */
2006-04-24 21:25:55 +04:00
if ( ! valid )
goto invalid ;
2004-03-11 09:35:37 +03:00
2005-07-05 17:24:41 +04:00
/* grow buffer and add rule */
rule_size = sizeof ( struct udev_rule ) + rule - > bufsize ;
2005-07-17 17:40:29 +04:00
padding = ( sizeof ( size_t ) - rule_size % sizeof ( size_t ) ) % sizeof ( size_t ) ;
dbg ( " add %zi padding bytes " , padding ) ;
rule_size + = padding ;
rule - > bufsize + = padding ;
2005-07-05 17:24:41 +04:00
rules - > buf = realloc ( rules - > buf , rules - > bufsize + rule_size ) ;
if ( ! rules - > buf ) {
err ( " realloc failed " ) ;
goto exit ;
}
2005-07-16 09:46:31 +04:00
dbg ( " adding rule to offset %zi " , rules - > bufsize ) ;
2005-07-05 17:24:41 +04:00
memcpy ( rules - > buf + rules - > bufsize , rule , rule_size ) ;
rules - > bufsize + = rule_size ;
exit :
return 0 ;
2006-04-24 21:25:55 +04:00
invalid :
err ( " invalid rule '%s:%u' " , filename , lineno ) ;
return - 1 ;
2005-07-05 17:24:41 +04:00
}
2005-02-14 08:03:06 +03:00
2005-07-05 17:24:41 +04:00
static int parse_file ( struct udev_rules * rules , const char * filename )
{
char line [ LINE_SIZE ] ;
char * bufline ;
2006-04-24 21:25:55 +04:00
unsigned int lineno ;
2005-07-05 17:24:41 +04:00
char * buf ;
size_t bufsize ;
size_t cur ;
size_t count ;
int retval = 0 ;
if ( file_map ( filename , & buf , & bufsize ) ! = 0 ) {
2005-11-07 20:44:18 +03:00
err ( " can't open '%s' as rules file: %s " , filename , strerror ( errno ) ) ;
2005-07-05 17:24:41 +04:00
return - 1 ;
}
2006-12-08 11:48:53 +03:00
info ( " reading '%s' as rules file " , filename ) ;
2003-12-17 10:36:19 +03:00
2005-07-05 17:24:41 +04:00
/* loop through the whole file */
cur = 0 ;
lineno = 0 ;
while ( cur < bufsize ) {
unsigned int i , j ;
count = buf_get_line ( buf , bufsize , cur ) ;
bufline = & buf [ cur ] ;
cur + = count + 1 ;
lineno + + ;
/* eat the whitespace */
while ( ( count > 0 ) & & isspace ( bufline [ 0 ] ) ) {
bufline + + ;
count - - ;
2003-12-03 05:38:30 +03:00
}
2005-07-05 17:24:41 +04:00
if ( count = = 0 )
continue ;
2003-12-03 05:38:30 +03:00
2005-07-05 17:24:41 +04:00
/* see if this is a comment */
if ( bufline [ 0 ] = = COMMENT_CHARACTER )
2003-12-17 10:36:19 +03:00
continue ;
2005-07-05 17:24:41 +04:00
2006-08-13 06:23:16 +04:00
if ( count > = sizeof ( line ) ) {
err ( " line too long, rule skipped '%s:%u' " , filename , lineno ) ;
continue ;
}
/* skip backslash and newline from multiline rules */
2005-07-05 17:24:41 +04:00
for ( i = j = 0 ; i < count ; i + + ) {
if ( bufline [ i ] = = ' \\ ' & & bufline [ i + 1 ] = = ' \n ' )
continue ;
line [ j + + ] = bufline [ i ] ;
2003-12-03 05:38:30 +03:00
}
2005-07-05 17:24:41 +04:00
line [ j ] = ' \0 ' ;
dbg ( " read '%s' " , line ) ;
2006-04-24 21:25:55 +04:00
add_to_rules ( rules , line , filename , lineno ) ;
2003-12-03 05:38:30 +03:00
}
2004-03-23 09:22:20 +03:00
file_unmap ( buf , bufsize ) ;
2003-12-03 05:38:30 +03:00
return retval ;
2003-12-17 10:36:19 +03:00
}
2003-12-03 05:38:30 +03:00
2005-11-05 22:00:31 +03:00
int udev_rules_init ( struct udev_rules * rules , int resolve_names )
2005-06-24 20:05:32 +04:00
{
2007-07-15 21:10:06 +04:00
struct stat statbuf ;
char filename [ PATH_MAX ] ;
LIST_HEAD ( name_list ) ;
LIST_HEAD ( dyn_list ) ;
struct name_entry * name_loop , * name_tmp ;
struct name_entry * dyn_loop , * dyn_tmp ;
int retval = 0 ;
2003-12-03 05:38:30 +03:00
2005-07-05 17:24:41 +04:00
memset ( rules , 0x00 , sizeof ( struct udev_rules ) ) ;
rules - > resolve_names = resolve_names ;
2007-07-15 21:10:06 +04:00
/* read main config from single file or all files in a directory */
if ( stat ( udev_rules_dir , & statbuf ) ! = 0 )
2004-03-23 09:22:20 +03:00
return - 1 ;
2007-07-15 21:10:06 +04:00
if ( ( statbuf . st_mode & S_IFMT ) ! = S_IFDIR ) {
2006-12-08 11:48:53 +03:00
dbg ( " parse single rules file '%s' " , udev_rules_dir ) ;
2007-07-15 21:10:06 +04:00
name_list_add ( & name_list , udev_rules_dir , 1 ) ;
2005-06-24 20:05:32 +04:00
} else {
2006-12-08 11:48:53 +03:00
dbg ( " parse rules directory '%s' " , udev_rules_dir ) ;
retval = add_matching_files ( & name_list , udev_rules_dir , RULESFILE_SUFFIX ) ;
2007-07-15 21:10:06 +04:00
}
/* read dynamic rules directory */
strlcpy ( filename , udev_root , sizeof ( filename ) ) ;
strlcat ( filename , " / " RULES_DYN_DIR , sizeof ( filename ) ) ;
if ( stat ( filename , & statbuf ) ! = 0 ) {
create_path ( filename ) ;
mkdir ( filename , 0755 ) ;
}
add_matching_files ( & dyn_list , filename , RULESFILE_SUFFIX ) ;
/* sort dynamic rules files by basename into list of files */
list_for_each_entry_safe ( dyn_loop , dyn_tmp , & dyn_list , node ) {
const char * dyn_base = strrchr ( dyn_loop - > name , ' / ' ) ;
if ( dyn_base = = NULL )
continue ;
2005-03-17 11:59:32 +03:00
list_for_each_entry_safe ( name_loop , name_tmp , & name_list , node ) {
2007-07-15 21:10:06 +04:00
const char * name_base = strrchr ( name_loop - > name , ' / ' ) ;
if ( name_base = = NULL )
continue ;
if ( strcmp ( name_base , dyn_base ) > 0 ) {
list_move_tail ( & dyn_loop - > node , & name_loop - > node ) ;
break ;
}
2005-03-17 11:59:32 +03:00
}
}
2004-02-24 06:31:14 +03:00
2007-07-15 21:10:06 +04:00
/* parse list of files */
list_for_each_entry_safe ( name_loop , name_tmp , & name_list , node ) {
if ( stat ( name_loop - > name , & statbuf ) = = 0 ) {
if ( statbuf . st_size )
parse_file ( rules , name_loop - > name ) ;
else
dbg ( " empty rules file '%s' " , name_loop - > name ) ;
} else
err ( " could not read '%s': %s " , name_loop - > name , strerror ( errno ) ) ;
list_del ( & name_loop - > node ) ;
free ( name_loop ) ;
}
2004-12-18 13:34:17 +03:00
return retval ;
2004-02-24 06:31:14 +03:00
}
2005-02-09 10:43:18 +03:00
2006-01-09 23:18:00 +03:00
void udev_rules_cleanup ( struct udev_rules * rules )
2005-02-09 10:43:18 +03:00
{
2005-08-08 07:07:36 +04:00
if ( rules - > buf ) {
2005-11-05 22:00:31 +03:00
free ( rules - > buf ) ;
2005-08-08 07:07:36 +04:00
rules - > buf = NULL ;
}
2005-02-09 10:43:18 +03:00
}