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>
2005-08-11 19:32:59 +04:00
# include <syslog.h>
2008-03-13 18:00:25 +03:00
# include <dirent.h>
2006-08-24 11:03:15 +04:00
# include <fnmatch.h>
2007-07-14 22:59:03 +04:00
# include <sys/socket.h>
# include <sys/un.h>
2003-10-15 10:32:17 +04:00
# include <sys/wait.h>
2005-07-08 01:01:04 +04:00
# include <sys/stat.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
/* extract possible {attr} and move str behind it */
2008-09-06 17:45:31 +04:00
static char * get_format_attribute ( struct udev * udev , char * * str )
[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
{
char * pos ;
char * attr = NULL ;
if ( * str [ 0 ] = = ' { ' ) {
pos = strchr ( * str , ' } ' ) ;
if ( pos = = NULL ) {
2008-09-06 17:45:31 +04:00
err ( udev , " missing closing brace for format \n " ) ;
[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
return NULL ;
}
pos [ 0 ] = ' \0 ' ;
attr = * str + 1 ;
* str = pos + 1 ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " attribute='%s', str='%s' \n " , attr , * str ) ;
[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
}
return attr ;
}
/* extract possible format length and move str behind it*/
2008-09-06 17:45:31 +04:00
static int get_format_len ( struct udev * udev , char * * str )
[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
{
int num ;
char * tail ;
if ( isdigit ( * str [ 0 ] ) ) {
num = ( int ) strtoul ( * str , & tail , 10 ) ;
2004-03-02 10:17:59 +03:00
if ( num > 0 ) {
[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
* str = tail ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " format length=%i \n " , num ) ;
[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
return num ;
} else {
2008-09-06 17:45:31 +04:00
err ( udev , " format parsing error '%s' \n " , * str ) ;
[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
}
}
return - 1 ;
}
2008-10-16 19:16:58 +04:00
static int run_program ( struct udev_device * dev , const char * command ,
2008-09-06 17:45:31 +04:00
char * result , size_t ressize , size_t * reslen )
2007-07-14 22:59:03 +04:00
{
2008-10-16 19:16:58 +04:00
struct udev * udev = udev_device_get_udev ( dev ) ;
2007-07-14 22:59:03 +04:00
int status ;
2008-10-16 19:16:58 +04:00
char * * envp ;
2007-07-14 22:59:03 +04:00
int outpipe [ 2 ] = { - 1 , - 1 } ;
int errpipe [ 2 ] = { - 1 , - 1 } ;
pid_t pid ;
2008-09-10 23:50:21 +04:00
char arg [ UTIL_PATH_SIZE ] ;
char program [ UTIL_PATH_SIZE ] ;
2007-07-14 22:59:03 +04:00
char * argv [ ( sizeof ( arg ) / 2 ) + 1 ] ;
int devnull ;
int i ;
2008-10-16 19:16:58 +04:00
int err = 0 ;
2007-07-14 22:59:03 +04:00
2008-10-16 19:16:58 +04:00
/* build argv from command */
2008-09-10 20:59:42 +04:00
util_strlcpy ( arg , command , sizeof ( arg ) ) ;
2007-07-14 22:59:03 +04:00
i = 0 ;
if ( strchr ( arg , ' ' ) ! = NULL ) {
char * pos = arg ;
2008-10-16 23:35:11 +04:00
while ( pos ! = NULL & & pos [ 0 ] ! = ' \0 ' ) {
2007-07-14 22:59:03 +04:00
if ( pos [ 0 ] = = ' \' ' ) {
2008-10-16 23:35:11 +04:00
/* do not separate quotes */
2007-07-14 22:59:03 +04:00
pos + + ;
argv [ i ] = strsep ( & pos , " \' " ) ;
while ( pos ! = NULL & & pos [ 0 ] = = ' ' )
pos + + ;
} else {
argv [ i ] = strsep ( & pos , " " ) ;
}
2008-09-06 17:45:31 +04:00
dbg ( udev , " arg[%i] '%s' \n " , i , argv [ i ] ) ;
2007-07-14 22:59:03 +04:00
i + + ;
}
argv [ i ] = NULL ;
} else {
argv [ 0 ] = arg ;
argv [ 1 ] = NULL ;
}
2008-09-06 17:45:31 +04:00
info ( udev , " '%s' \n " , command ) ;
2007-07-14 22:59:03 +04:00
/* prepare pipes from child to parent */
2008-09-06 17:45:31 +04:00
if ( result ! = NULL | | udev_get_log_priority ( udev ) > = LOG_INFO ) {
2007-07-14 22:59:03 +04:00
if ( pipe ( outpipe ) ! = 0 ) {
2008-09-29 19:01:32 +04:00
err ( udev , " pipe failed: %m \n " ) ;
2007-07-14 22:59:03 +04:00
return - 1 ;
}
}
2008-09-06 17:45:31 +04:00
if ( udev_get_log_priority ( udev ) > = LOG_INFO ) {
2007-07-14 22:59:03 +04:00
if ( pipe ( errpipe ) ! = 0 ) {
2008-09-29 19:01:32 +04:00
err ( udev , " pipe failed: %m \n " ) ;
2007-07-14 22:59:03 +04:00
return - 1 ;
}
}
2008-10-16 19:16:58 +04:00
/* allow programs in /lib/udev/ to be called without the path */
2007-07-14 22:59:03 +04:00
if ( strchr ( argv [ 0 ] , ' / ' ) = = NULL ) {
2008-09-10 20:59:42 +04:00
util_strlcpy ( program , UDEV_PREFIX " /lib/udev/ " , sizeof ( program ) ) ;
util_strlcat ( program , argv [ 0 ] , sizeof ( program ) ) ;
2007-07-14 22:59:03 +04:00
argv [ 0 ] = program ;
}
2008-10-16 19:16:58 +04:00
envp = udev_device_get_properties_envp ( dev ) ;
2007-07-14 22:59:03 +04:00
pid = fork ( ) ;
switch ( pid ) {
case 0 :
/* child closes parent ends of pipes */
if ( outpipe [ READ_END ] > 0 )
close ( outpipe [ READ_END ] ) ;
if ( errpipe [ READ_END ] > 0 )
close ( errpipe [ READ_END ] ) ;
/* discard child output or connect to pipe */
devnull = open ( " /dev/null " , O_RDWR ) ;
if ( devnull > 0 ) {
dup2 ( devnull , STDIN_FILENO ) ;
if ( outpipe [ WRITE_END ] < 0 )
dup2 ( devnull , STDOUT_FILENO ) ;
if ( errpipe [ WRITE_END ] < 0 )
dup2 ( devnull , STDERR_FILENO ) ;
close ( devnull ) ;
} else
2008-09-29 19:01:32 +04:00
err ( udev , " open /dev/null failed: %m \n " ) ;
2007-07-14 22:59:03 +04:00
if ( outpipe [ WRITE_END ] > 0 ) {
dup2 ( outpipe [ WRITE_END ] , STDOUT_FILENO ) ;
close ( outpipe [ WRITE_END ] ) ;
}
if ( errpipe [ WRITE_END ] > 0 ) {
dup2 ( errpipe [ WRITE_END ] , STDERR_FILENO ) ;
close ( errpipe [ WRITE_END ] ) ;
}
2008-10-16 19:16:58 +04:00
execve ( argv [ 0 ] , argv , envp ) ;
2007-07-14 22:59:03 +04:00
if ( errno = = ENOENT | | errno = = ENOTDIR ) {
/* may be on a filesytem which is not mounted right now */
2008-09-06 17:45:31 +04:00
info ( udev , " program '%s' not found \n " , argv [ 0 ] ) ;
2007-07-14 22:59:03 +04:00
} else {
/* other problems */
2008-09-06 17:45:31 +04:00
err ( udev , " exec of program '%s' failed \n " , argv [ 0 ] ) ;
2007-07-14 22:59:03 +04:00
}
_exit ( 1 ) ;
case - 1 :
2008-09-29 19:01:32 +04:00
err ( udev , " fork of '%s' failed: %m \n " , argv [ 0 ] ) ;
2007-07-14 22:59:03 +04:00
return - 1 ;
default :
/* read from child if requested */
if ( outpipe [ READ_END ] > 0 | | errpipe [ READ_END ] > 0 ) {
ssize_t count ;
size_t respos = 0 ;
/* parent closes child ends of pipes */
if ( outpipe [ WRITE_END ] > 0 )
close ( outpipe [ WRITE_END ] ) ;
if ( errpipe [ WRITE_END ] > 0 )
close ( errpipe [ WRITE_END ] ) ;
/* read child output */
while ( outpipe [ READ_END ] > 0 | | errpipe [ READ_END ] > 0 ) {
int fdcount ;
fd_set readfds ;
FD_ZERO ( & readfds ) ;
if ( outpipe [ READ_END ] > 0 )
FD_SET ( outpipe [ READ_END ] , & readfds ) ;
if ( errpipe [ READ_END ] > 0 )
FD_SET ( errpipe [ READ_END ] , & readfds ) ;
fdcount = select ( UDEV_MAX ( outpipe [ READ_END ] , errpipe [ READ_END ] ) + 1 , & readfds , NULL , NULL , NULL ) ;
if ( fdcount < 0 ) {
if ( errno = = EINTR )
continue ;
2008-10-16 19:16:58 +04:00
err = - 1 ;
2007-07-14 22:59:03 +04:00
break ;
}
/* get stdout */
if ( outpipe [ READ_END ] > 0 & & FD_ISSET ( outpipe [ READ_END ] , & readfds ) ) {
char inbuf [ 1024 ] ;
char * pos ;
char * line ;
count = read ( outpipe [ READ_END ] , inbuf , sizeof ( inbuf ) - 1 ) ;
if ( count < = 0 ) {
close ( outpipe [ READ_END ] ) ;
outpipe [ READ_END ] = - 1 ;
if ( count < 0 ) {
2008-09-29 19:01:32 +04:00
err ( udev , " stdin read failed: %m \n " ) ;
2008-10-16 19:16:58 +04:00
err = - 1 ;
2007-07-14 22:59:03 +04:00
}
continue ;
}
inbuf [ count ] = ' \0 ' ;
/* store result for rule processing */
if ( result ) {
if ( respos + count < ressize ) {
memcpy ( & result [ respos ] , inbuf , count ) ;
respos + = count ;
} else {
2008-09-06 17:45:31 +04:00
err ( udev , " ressize %ld too short \n " , ( long ) ressize ) ;
2008-10-16 19:16:58 +04:00
err = - 1 ;
2007-07-14 22:59:03 +04:00
}
}
pos = inbuf ;
while ( ( line = strsep ( & pos , " \n " ) ) )
if ( pos | | line [ 0 ] ! = ' \0 ' )
2008-09-06 17:45:31 +04:00
info ( udev , " '%s' (stdout) '%s' \n " , argv [ 0 ] , line ) ;
2007-07-14 22:59:03 +04:00
}
/* get stderr */
if ( errpipe [ READ_END ] > 0 & & FD_ISSET ( errpipe [ READ_END ] , & readfds ) ) {
char errbuf [ 1024 ] ;
char * pos ;
char * line ;
count = read ( errpipe [ READ_END ] , errbuf , sizeof ( errbuf ) - 1 ) ;
if ( count < = 0 ) {
close ( errpipe [ READ_END ] ) ;
errpipe [ READ_END ] = - 1 ;
if ( count < 0 )
2008-09-29 19:01:32 +04:00
err ( udev , " stderr read failed: %m \n " ) ;
2007-07-14 22:59:03 +04:00
continue ;
}
errbuf [ count ] = ' \0 ' ;
pos = errbuf ;
while ( ( line = strsep ( & pos , " \n " ) ) )
if ( pos | | line [ 0 ] ! = ' \0 ' )
2008-09-06 17:45:31 +04:00
info ( udev , " '%s' (stderr) '%s' \n " , argv [ 0 ] , line ) ;
2007-07-14 22:59:03 +04:00
}
}
if ( outpipe [ READ_END ] > 0 )
close ( outpipe [ READ_END ] ) ;
if ( errpipe [ READ_END ] > 0 )
close ( errpipe [ READ_END ] ) ;
/* return the childs stdout string */
if ( result ) {
result [ respos ] = ' \0 ' ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " result='%s' \n " , result ) ;
2007-07-14 22:59:03 +04:00
if ( reslen )
* reslen = respos ;
}
}
waitpid ( pid , & status , 0 ) ;
if ( WIFEXITED ( status ) ) {
2008-09-06 17:45:31 +04:00
info ( udev , " '%s' returned with status %i \n " , argv [ 0 ] , WEXITSTATUS ( status ) ) ;
2007-07-14 22:59:03 +04:00
if ( WEXITSTATUS ( status ) ! = 0 )
2008-10-16 19:16:58 +04:00
err = - 1 ;
2007-07-14 22:59:03 +04:00
} else {
2008-09-06 17:45:31 +04:00
err ( udev , " '%s' abnormal exit \n " , argv [ 0 ] ) ;
2008-10-16 19:16:58 +04:00
err = - 1 ;
2007-07-14 22:59:03 +04:00
}
}
2008-10-16 19:16:58 +04:00
return err ;
2007-07-14 22:59:03 +04: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
{
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-17 00:41:52 +04:00
if ( run_program ( dev , program , 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
}
2008-10-16 19:16:58 +04:00
int udev_rules_run ( struct udev_event * event )
2007-07-14 22:44:19 +04:00
{
2008-10-16 19:16:58 +04:00
struct udev_list_entry * list_entry ;
int err = 0 ;
dbg ( event - > udev , " executing run list \n " ) ;
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & event - > run_list ) ) {
const char * cmd = udev_list_entry_get_name ( list_entry ) ;
if ( strncmp ( cmd , " socket: " , strlen ( " socket: " ) ) = = 0 ) {
struct udev_monitor * monitor ;
2007-07-14 22:44:19 +04:00
2008-10-16 19:16:58 +04:00
monitor = udev_monitor_new_from_socket ( event - > udev , & cmd [ strlen ( " socket: " ) ] ) ;
if ( monitor = = NULL )
continue ;
udev_monitor_send_device ( monitor , event - > dev ) ;
udev_monitor_unref ( monitor ) ;
2007-07-14 22:44:19 +04:00
} else {
2008-09-10 23:50:21 +04:00
char program [ UTIL_PATH_SIZE ] ;
2007-07-14 22:44:19 +04:00
2008-10-16 19:16:58 +04:00
util_strlcpy ( program , cmd , sizeof ( program ) ) ;
udev_rules_apply_format ( event , program , sizeof ( program ) ) ;
if ( run_program ( event - > dev , program , NULL , 0 , NULL ) ! = 0 ) {
if ( ! udev_list_entry_get_flag ( list_entry ) )
err = - 1 ;
}
2007-07-14 22:44:19 +04:00
}
}
2008-10-16 19:16:58 +04:00
return err ;
2007-07-14 22:44:19 +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 ;
}
2007-06-03 14:29:46 +04:00
/* handle "[$SUBSYSTEM/$KERNEL]<attribute>" lookup */
2008-10-16 19:16:58 +04:00
static int split_subsys_sysname ( struct udev * udev , char * attrstr , char * * subsys , char * * sysname , char * * attr )
2007-06-03 14:29:46 +04:00
{
2008-09-01 12:21:05 +04:00
char * pos ;
2007-06-03 14:29:46 +04:00
if ( attrstr [ 0 ] ! = ' [ ' )
2008-10-16 19:16:58 +04:00
return - 1 ;
* subsys = & attrstr [ 1 ] ;
pos = strchr ( * subsys , ' ] ' ) ;
if ( pos = = NULL )
return - 1 ;
pos [ 0 ] = ' \0 ' ;
pos = & pos [ 1 ] ;
2007-06-03 14:29:46 +04:00
2008-10-16 19:16:58 +04:00
if ( pos [ 0 ] = = ' / ' )
pos = & pos [ 1 ] ;
if ( pos [ 0 ] ! = ' \0 ' )
* attr = pos ;
else
* attr = NULL ;
2007-06-03 14:29:46 +04:00
2008-10-16 19:16:58 +04:00
pos = strchr ( * subsys , ' / ' ) ;
2008-09-01 12:21:05 +04:00
if ( pos = = NULL )
2008-10-16 19:16:58 +04:00
return - 1 ;
2008-09-01 12:21:05 +04:00
pos [ 0 ] = ' \0 ' ;
2008-10-16 19:16:58 +04:00
* sysname = & pos [ 1 ] ;
return 0 ;
2007-06-03 14:29:46 +04:00
}
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 ;
}
2008-10-16 19:16:58 +04:00
void udev_rules_apply_format ( struct udev_event * event , char * string , size_t maxsize )
2003-11-24 09:25:13 +03:00
{
2008-10-16 19:16:58 +04:00
struct udev_device * dev = event - > dev ;
2008-09-10 23:50:21 +04:00
char temp [ UTIL_PATH_SIZE ] ;
char temp2 [ UTIL_PATH_SIZE ] ;
2008-10-16 19:16:58 +04:00
char * head , * tail , * cpos , * attr , * rest ;
2004-02-28 17:53:25 +03:00
int len ;
2004-02-17 12:29:03 +03:00
int i ;
2005-08-28 17:55:58 +04:00
int count ;
2005-06-20 02:29:38 +04:00
enum subst_type {
SUBST_UNKNOWN ,
SUBST_DEVPATH ,
2006-08-19 18:06:25 +04:00
SUBST_KERNEL ,
2005-06-20 02:29:38 +04:00
SUBST_KERNEL_NUMBER ,
2006-01-16 08:12:49 +03:00
SUBST_ID ,
2007-07-25 17:24:58 +04:00
SUBST_DRIVER ,
2005-06-20 02:29:38 +04:00
SUBST_MAJOR ,
SUBST_MINOR ,
SUBST_RESULT ,
2006-08-19 18:06:25 +04:00
SUBST_ATTR ,
2005-06-20 02:29:38 +04:00
SUBST_PARENT ,
SUBST_TEMP_NODE ,
2007-08-29 16:04:55 +04:00
SUBST_NAME ,
2008-04-21 19:43:12 +04:00
SUBST_LINKS ,
2005-06-20 02:29:38 +04:00
SUBST_ROOT ,
2007-06-03 00:06:01 +04:00
SUBST_SYS ,
2005-06-25 15:10:16 +04:00
SUBST_ENV ,
2005-06-20 02:29:38 +04:00
} ;
static const struct subst_map {
char * name ;
char fmt ;
enum subst_type type ;
} map [ ] = {
2006-08-19 18:06:25 +04:00
{ . name = " devpath " , . fmt = ' p ' , . type = SUBST_DEVPATH } ,
{ . name = " number " , . fmt = ' n ' , . type = SUBST_KERNEL_NUMBER } ,
{ . name = " kernel " , . fmt = ' k ' , . type = SUBST_KERNEL } ,
{ . name = " id " , . fmt = ' b ' , . type = SUBST_ID } ,
2007-07-25 17:24:58 +04:00
{ . name = " driver " , . fmt = ' d ' , . type = SUBST_DRIVER } ,
2006-08-19 18:06:25 +04:00
{ . name = " major " , . fmt = ' M ' , . type = SUBST_MAJOR } ,
{ . name = " minor " , . fmt = ' m ' , . type = SUBST_MINOR } ,
{ . name = " result " , . fmt = ' c ' , . type = SUBST_RESULT } ,
{ . name = " attr " , . fmt = ' s ' , . type = SUBST_ATTR } ,
{ . name = " sysfs " , . fmt = ' s ' , . type = SUBST_ATTR } ,
{ . name = " parent " , . fmt = ' P ' , . type = SUBST_PARENT } ,
{ . name = " tempnode " , . fmt = ' N ' , . type = SUBST_TEMP_NODE } ,
2007-08-29 16:04:55 +04:00
{ . name = " name " , . fmt = ' D ' , . type = SUBST_NAME } ,
2008-04-21 19:43:12 +04:00
{ . name = " links " , . fmt = ' L ' , . type = SUBST_LINKS } ,
2006-08-19 18:06:25 +04:00
{ . name = " root " , . fmt = ' r ' , . type = SUBST_ROOT } ,
2007-06-03 00:06:01 +04:00
{ . name = " sys " , . fmt = ' S ' , . type = SUBST_SYS } ,
2006-08-19 18:06:25 +04:00
{ . name = " env " , . fmt = ' E ' , . type = SUBST_ENV } ,
2005-08-13 02:18:44 +04:00
{ NULL , ' \0 ' , 0 }
2005-06-20 02:29:38 +04:00
} ;
enum subst_type type ;
const struct subst_map * subst ;
head = string ;
2003-11-24 09:25:13 +03:00
while ( 1 ) {
2005-06-20 02:29:38 +04:00
len = - 1 ;
while ( head [ 0 ] ! = ' \0 ' ) {
if ( head [ 0 ] = = ' $ ' ) {
/* substitute named variable */
if ( head [ 1 ] = = ' \0 ' )
break ;
if ( head [ 1 ] = = ' $ ' ) {
2008-09-10 20:59:42 +04:00
util_strlcpy ( temp , head + 2 , sizeof ( temp ) ) ;
util_strlcpy ( head + 1 , temp , maxsize ) ;
2005-06-20 02:29:38 +04:00
head + + ;
continue ;
}
head [ 0 ] = ' \0 ' ;
for ( subst = map ; subst - > name ; subst + + ) {
if ( strncasecmp ( & head [ 1 ] , subst - > name , strlen ( subst - > name ) ) = = 0 ) {
type = subst - > type ;
tail = head + strlen ( subst - > name ) + 1 ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " will substitute format name '%s' \n " , subst - > name ) ;
2005-06-20 02:29:38 +04:00
goto found ;
}
}
2007-02-01 22:18:52 +03:00
head [ 0 ] = ' $ ' ;
2008-10-16 19:16:58 +04:00
err ( event - > udev , " unknown format variable '%s' \n " , head ) ;
2006-04-28 19:52:09 +04:00
} else if ( head [ 0 ] = = ' % ' ) {
2005-06-20 02:29:38 +04:00
/* substitute format char */
if ( head [ 1 ] = = ' \0 ' )
break ;
if ( head [ 1 ] = = ' % ' ) {
2008-09-10 20:59:42 +04:00
util_strlcpy ( temp , head + 2 , sizeof ( temp ) ) ;
util_strlcpy ( head + 1 , temp , maxsize ) ;
2005-06-20 02:29:38 +04:00
head + + ;
continue ;
}
head [ 0 ] = ' \0 ' ;
tail = head + 1 ;
2008-10-16 19:16:58 +04:00
len = get_format_len ( event - > udev , & tail ) ;
2005-06-20 02:29:38 +04:00
for ( subst = map ; subst - > name ; subst + + ) {
if ( tail [ 0 ] = = subst - > fmt ) {
type = subst - > type ;
tail + + ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " will substitute format char '%c' \n " , subst - > fmt ) ;
2005-06-20 02:29:38 +04:00
goto found ;
}
}
2007-02-01 22:18:52 +03:00
head [ 0 ] = ' % ' ;
2008-10-16 19:16:58 +04:00
err ( event - > udev , " unknown format char '%c' \n " , tail [ 0 ] ) ;
2005-06-20 02:29:38 +04:00
}
head + + ;
}
break ;
found :
2008-10-16 19:16:58 +04:00
attr = get_format_attribute ( event - > udev , & tail ) ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( temp , tail , sizeof ( temp ) ) ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " format=%i, string='%s', tail='%s' \n " , type , string , tail ) ;
[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
2005-06-20 02:29:38 +04:00
switch ( type ) {
case SUBST_DEVPATH :
2008-10-16 19:16:58 +04:00
util_strlcat ( string , udev_device_get_devpath ( dev ) , maxsize ) ;
dbg ( event - > udev , " substitute devpath '%s' \n " , udev_device_get_devpath ( dev ) ) ;
[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
break ;
2006-08-19 18:06:25 +04:00
case SUBST_KERNEL :
2008-10-16 19:16:58 +04:00
util_strlcat ( string , udev_device_get_sysname ( dev ) , maxsize ) ;
dbg ( event - > udev , " substitute kernel name '%s' \n " , udev_device_get_sysname ( dev ) ) ;
[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
break ;
2005-06-20 02:29:38 +04:00
case SUBST_KERNEL_NUMBER :
2008-10-16 19:16:58 +04:00
if ( udev_device_get_sysnum ( dev ) = = NULL )
break ;
util_strlcat ( string , udev_device_get_sysnum ( dev ) , maxsize ) ;
dbg ( event - > udev , " substitute kernel number '%s' \n " , udev_device_get_sysnum ( dev ) ) ;
[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
break ;
2006-01-16 08:12:49 +03:00
case SUBST_ID :
2008-10-16 19:16:58 +04:00
if ( event - > dev_parent ! = NULL ) {
util_strlcat ( string , udev_device_get_sysname ( event - > dev_parent ) , maxsize ) ;
dbg ( event - > udev , " substitute id '%s' \n " , udev_device_get_sysname ( event - > dev_parent ) ) ;
2006-01-16 08:12:49 +03:00
}
break ;
2007-07-25 17:24:58 +04:00
case SUBST_DRIVER :
2008-10-16 19:16:58 +04:00
if ( event - > dev_parent ! = NULL ) {
const char * driver = udev_device_get_driver ( event - > dev_parent ) ;
if ( driver = = NULL )
break ;
util_strlcat ( string , driver , maxsize ) ;
dbg ( event - > udev , " substitute driver '%s' \n " , driver ) ;
2007-07-25 17:24:58 +04:00
}
break ;
2005-06-20 02:29:38 +04:00
case SUBST_MAJOR :
2008-10-16 19:16:58 +04:00
sprintf ( temp2 , " %d " , major ( udev_device_get_devnum ( dev ) ) ) ;
2008-09-10 20:59:42 +04:00
util_strlcat ( string , temp2 , maxsize ) ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " substitute major number '%s' \n " , temp2 ) ;
[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
break ;
2005-06-20 02:29:38 +04:00
case SUBST_MINOR :
2008-10-16 19:16:58 +04:00
sprintf ( temp2 , " %d " , minor ( udev_device_get_devnum ( dev ) ) ) ;
2008-09-10 20:59:42 +04:00
util_strlcat ( string , temp2 , maxsize ) ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " substitute minor number '%s' \n " , temp2 ) ;
2005-06-20 02:29:38 +04:00
break ;
case SUBST_RESULT :
2008-10-16 19:16:58 +04:00
if ( event - > program_result [ 0 ] = = ' \0 ' )
2003-11-24 09:25:13 +03:00
break ;
2004-02-17 12:29:03 +03:00
/* get part part of the result string */
2004-02-28 17:53:25 +03:00
i = 0 ;
2004-02-17 12:29:03 +03:00
if ( attr ! = NULL )
2004-03-11 12:37:18 +03:00
i = strtoul ( attr , & rest , 10 ) ;
2004-02-17 12:29:03 +03:00
if ( i > 0 ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " request part #%d of result string \n " , i ) ;
cpos = event - > program_result ;
2005-03-05 08:50:09 +03:00
while ( - - i ) {
while ( cpos [ 0 ] ! = ' \0 ' & & ! isspace ( cpos [ 0 ] ) )
cpos + + ;
while ( isspace ( cpos [ 0 ] ) )
cpos + + ;
[PATCH] get part of callout return string
Try this patch if you like, to get special parts of the callout output.
This beast works now:
CALLOUT, BUS="scsi", PROGRAM="/bin/echo -n node link1 link2", ID="node *", NAME="%1c", SYMLINK="%2c %3c"
The callout returned string is separated by spaces and is
addressed by the "len" value of the 'c' format char.
Since we support symlinks, this my be useful for other uses of callout too.
introduce 'len number' for format chars
the first use is 'c'-the callout return to select a part of the output string like:
CALLOUT, BUS="scsi", PROGRAM="/bin/echo -n node link1 link2", ID="node *", NAME="%1c", SYMLINK="%2c %3c"
(note: first part is requested by len=1, len=0 will return the whole string)
add a test to udev-test.pl
2003-12-16 09:54:38 +03:00
}
2004-03-04 05:16:35 +03:00
if ( i > 0 ) {
2008-10-16 19:16:58 +04:00
err ( event - > udev , " requested part of result string not found \n " ) ;
2004-03-04 05:16:35 +03:00
break ;
[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-09-10 20:59:42 +04:00
util_strlcpy ( temp2 , cpos , sizeof ( temp2 ) ) ;
2005-03-05 08:50:09 +03:00
/* %{2+}c copies the whole string from the second part on */
if ( rest [ 0 ] ! = ' + ' ) {
cpos = strchr ( temp2 , ' ' ) ;
if ( cpos )
cpos [ 0 ] = ' \0 ' ;
}
2008-09-10 20:59:42 +04:00
util_strlcat ( string , temp2 , maxsize ) ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " substitute part of result string '%s' \n " , temp2 ) ;
[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
} else {
2008-10-16 19:16:58 +04:00
util_strlcat ( string , event - > program_result , maxsize ) ;
dbg ( event - > udev , " substitute result string '%s' \n " , event - > program_result ) ;
2003-11-24 09:25:13 +03:00
}
break ;
2006-08-19 18:06:25 +04:00
case SUBST_ATTR :
2006-09-05 04:18:06 +04:00
if ( attr = = NULL )
2008-10-16 19:16:58 +04:00
err ( event - > udev , " missing file parameter for attr \n " ) ;
2006-09-05 04:18:06 +04:00
else {
2008-10-16 19:16:58 +04:00
char * subsys ;
char * sysname ;
2007-06-03 14:29:46 +04:00
char * attrib ;
2008-10-16 19:16:58 +04:00
char value [ UTIL_NAME_SIZE ] = " " ;
2006-09-05 04:18:06 +04:00
size_t size ;
2008-10-16 19:16:58 +04:00
if ( split_subsys_sysname ( event - > udev , attr , & subsys , & sysname , & attrib ) = = 0 ) {
struct udev_device * d ;
const char * val ;
if ( attrib = = NULL )
2007-06-03 14:29:46 +04:00
break ;
2008-10-16 19:16:58 +04:00
d = udev_device_new_from_subsystem_sysname ( event - > udev , subsys , sysname ) ;
if ( d = = NULL )
break ;
val = udev_device_get_attr_value ( d , attrib ) ;
if ( val ! = NULL )
util_strlcpy ( value , val , sizeof ( value ) ) ;
udev_device_unref ( d ) ;
2007-06-03 14:29:46 +04:00
}
/* try the current device, other matches may have selected */
2008-10-16 19:16:58 +04:00
if ( value [ 0 ] = = ' \0 ' & & event - > dev_parent ! = NULL & & event - > dev_parent ! = event - > dev ) {
const char * val ;
val = udev_device_get_attr_value ( event - > dev_parent , attr ) ;
if ( val ! = NULL )
util_strlcpy ( value , val , sizeof ( value ) ) ;
}
2006-09-05 04:18:06 +04:00
/* look at all devices along the chain of parents */
2008-10-16 19:16:58 +04:00
if ( value [ 0 ] = = ' \0 ' ) {
struct udev_device * dev_parent = dev ;
const char * val ;
2006-09-05 04:18:06 +04:00
do {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " looking at '%s' \n " , udev_device_get_syspath ( dev_parent ) ) ;
val = udev_device_get_attr_value ( dev_parent , attr ) ;
if ( val ! = NULL ) {
util_strlcpy ( value , val , sizeof ( value ) ) ;
2006-09-05 04:18:06 +04:00
break ;
2008-10-16 19:16:58 +04:00
}
dev_parent = udev_device_get_parent ( dev_parent ) ;
2006-09-05 04:18:06 +04:00
} while ( dev_parent ! = NULL ) ;
}
2006-01-09 23:18:00 +03:00
2008-10-16 19:16:58 +04:00
if ( value [ 0 ] = = ' \0 ' )
2006-09-05 04:18:06 +04:00
break ;
2006-01-09 23:18:00 +03:00
2007-05-17 22:01:54 +04:00
/* strip trailing whitespace, and replace unwanted characters */
2008-10-16 19:16:58 +04:00
size = strlen ( value ) ;
while ( size > 0 & & isspace ( value [ - - size ] ) )
value [ size ] = ' \0 ' ;
count = util_replace_chars ( value , ALLOWED_CHARS_INPUT ) ;
2006-09-05 04:18:06 +04:00
if ( count > 0 )
2008-10-16 19:16:58 +04:00
info ( event - > udev , " %i character(s) replaced \n " , count ) ;
util_strlcat ( string , value , maxsize ) ;
dbg ( event - > udev , " substitute sysfs value '%s' \n " , value ) ;
2005-02-21 16:01:23 +03:00
}
[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
break ;
2005-06-20 02:29:38 +04:00
case SUBST_PARENT :
2006-01-09 23:18:00 +03:00
{
2008-10-16 19:16:58 +04:00
struct udev_device * dev_parent ;
const char * devnode ;
dev_parent = udev_device_get_parent ( event - > dev ) ;
if ( dev_parent = = NULL )
break ;
devnode = udev_device_get_devnode ( dev_parent ) ;
if ( devnode ! = NULL ) {
size_t devlen = strlen ( udev_get_dev_path ( event - > udev ) ) + 1 ;
util_strlcat ( string , & devnode [ devlen ] , maxsize ) ;
dbg ( event - > udev , " found parent '%s', got node name '%s' \n " ,
udev_device_get_syspath ( dev_parent ) , & devnode [ devlen ] ) ;
2006-01-09 23:18:00 +03:00
}
2005-02-10 11:03:55 +03:00
}
break ;
2005-06-20 02:29:38 +04:00
case SUBST_TEMP_NODE :
2008-10-16 19:16:58 +04:00
if ( event - > tmp_node [ 0 ] = = ' \0 ' & & major ( udev_device_get_devnum ( dev ) ) > 0 ) {
dbg ( event - > udev , " create temporary device node for callout \n " ) ;
snprintf ( event - > tmp_node , sizeof ( event - > tmp_node ) , " %s/.tmp-%u-%u " ,
udev_get_dev_path ( event - > udev ) ,
major ( udev_device_get_devnum ( dev ) ) , minor ( udev_device_get_devnum ( dev ) ) ) ;
udev_node_mknod ( dev , event - > tmp_node , makedev ( 0 , 0 ) , 0600 , 0 , 0 ) ;
2005-02-09 06:37:32 +03:00
}
2008-10-16 19:16:58 +04:00
util_strlcat ( string , event - > tmp_node , maxsize ) ;
dbg ( event - > udev , " substitute temporary device node name '%s' \n " , event - > tmp_node ) ;
2005-02-09 06:37:32 +03:00
break ;
2007-08-29 16:04:55 +04:00
case SUBST_NAME :
2008-10-16 19:16:58 +04:00
if ( event - > name ! = NULL ) {
util_strlcat ( string , event - > name , maxsize ) ;
dbg ( event - > udev , " substitute name '%s' \n " , event - > name ) ;
2008-04-21 19:43:12 +04:00
} else {
2008-10-16 19:16:58 +04:00
util_strlcat ( string , udev_device_get_sysname ( dev ) , maxsize ) ;
dbg ( event - > udev , " substitute sysname '%s' \n " , udev_device_get_sysname ( dev ) ) ;
2008-04-21 19:43:12 +04:00
}
break ;
case SUBST_LINKS :
2008-10-16 19:16:58 +04:00
{
struct udev_list_entry * list_entry ;
2008-04-21 19:43:12 +04:00
2008-10-16 19:16:58 +04:00
list_entry = udev_device_get_properties_list_entry ( dev ) ;
util_strlcpy ( string , udev_list_entry_get_name ( list_entry ) , maxsize ) ;
udev_list_entry_foreach ( list_entry , udev_list_entry_get_next ( list_entry ) ) {
util_strlcat ( string , " " , maxsize ) ;
util_strlcat ( string , udev_list_entry_get_name ( list_entry ) , maxsize ) ;
2008-04-21 19:43:12 +04:00
}
}
2007-08-29 16:04:55 +04:00
break ;
2005-06-20 02:29:38 +04:00
case SUBST_ROOT :
2008-10-16 19:16:58 +04:00
util_strlcat ( string , udev_get_dev_path ( event - > udev ) , maxsize ) ;
dbg ( event - > udev , " substitute udev_root '%s' \n " , udev_get_dev_path ( event - > udev ) ) ;
2005-02-10 11:03:55 +03:00
break ;
2007-06-03 00:06:01 +04:00
case SUBST_SYS :
2008-10-16 19:16:58 +04:00
util_strlcat ( string , udev_get_sys_path ( event - > udev ) , maxsize ) ;
dbg ( event - > udev , " substitute sys_path '%s' \n " , udev_get_sys_path ( event - > udev ) ) ;
2007-06-03 00:06:01 +04:00
break ;
2005-06-25 15:10:16 +04:00
case SUBST_ENV :
if ( attr = = NULL ) {
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " missing attribute \n " ) ;
2005-06-25 15:10:16 +04:00
break ;
2008-10-16 19:16:58 +04:00
} else {
struct udev_list_entry * list_entry ;
const char * value ;
list_entry = udev_device_get_properties_list_entry ( event - > dev ) ;
list_entry = udev_list_entry_get_by_name ( list_entry , attr ) ;
if ( list_entry = = NULL )
break ;
value = udev_list_entry_get_value ( list_entry ) ;
dbg ( event - > udev , " substitute env '%s=%s' \n " , attr , value ) ;
util_strlcat ( string , value , maxsize ) ;
2005-06-25 15:10:16 +04:00
break ;
2005-07-12 16:46:36 +04:00
}
[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
default :
2008-10-16 19:16:58 +04:00
err ( event - > udev , " unknown substitution type=%i \n " , type ) ;
[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
break ;
}
2005-06-20 02:29:38 +04:00
/* possibly truncate to format-char specified length */
2007-02-01 22:18:52 +03:00
if ( len > = 0 & & len < ( int ) strlen ( head ) ) {
2005-06-20 02:29:38 +04:00
head [ len ] = ' \0 ' ;
2008-10-16 19:16:58 +04:00
dbg ( event - > udev , " truncate to %i chars, subtitution string becomes '%s' \n " , len , head ) ;
2005-06-20 02:29:38 +04:00
}
2008-09-10 20:59:42 +04:00
util_strlcat ( string , temp , maxsize ) ;
2003-11-24 09:25:13 +03:00
}
}
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 ] ;
2008-10-16 19:16:58 +04:00
char * subsys ;
char * sysname ;
char * attrib ;
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-16 19:16:58 +04:00
udev_rules_apply_format ( event , filename , sizeof ( filename ) ) ;
if ( split_subsys_sysname ( event - > udev , filename , & subsys , & sysname , & attrib ) = = 0 ) {
struct udev_device * d ;
d = udev_device_new_from_subsystem_sysname ( event - > udev , subsys , sysname ) ;
if ( d ! = NULL ) {
util_strlcpy ( filename , udev_device_get_syspath ( d ) , sizeof ( filename ) ) ;
if ( attrib ! = NULL ) {
util_strlcat ( filename , " / " , sizeof ( filename ) ) ;
util_strlcat ( filename , attrib , sizeof ( filename ) ) ;
}
udev_device_unref ( d ) ;
2007-06-03 14:29:46 +04:00
}
2007-08-16 14:45:31 +04:00
} else if ( filename [ 0 ] ! = ' / ' ) {
2008-09-10 23:50:21 +04:00
char tmp [ UTIL_PATH_SIZE ] ;
2007-08-16 14:45:31 +04:00
2008-10-16 19:16:58 +04:00
util_strlcpy ( tmp , udev_device_get_syspath ( dev ) , sizeof ( tmp ) ) ;
2008-09-10 20:59:42 +04:00
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-16 19:16:58 +04:00
udev_rules_apply_format ( event , filename , sizeof ( filename ) ) ;
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 * subsys ;
char * sysname ;
2007-06-03 14:29:46 +04:00
char * attrib ;
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 ) ) ;
if ( split_subsys_sysname ( event - > udev , attr , & subsys , & sysname , & attrib ) = = 0 ) {
struct udev_device * d ;
const char * val ;
if ( attrib = = NULL )
2007-06-03 14:29:46 +04:00
goto nomatch ;
2008-10-16 19:16:58 +04:00
d = udev_device_new_from_subsystem_sysname ( event - > udev , subsys , sysname ) ;
if ( d = = NULL )
goto nomatch ;
val = udev_device_get_attr_value ( d , attrib ) ;
if ( val ! = NULL )
util_strlcpy ( value , val , sizeof ( value ) ) ;
udev_device_unref ( d ) ;
2007-06-03 14:29:46 +04:00
}
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 ] ;
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-16 19:16:58 +04:00
udev_rules_apply_format ( event , program , sizeof ( program ) ) ;
if ( run_program ( event - > dev , program , result , sizeof ( result ) , NULL ) ! = 0 ) {
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-16 19:16:58 +04:00
udev_rules_apply_format ( event , import , sizeof ( import ) ) ;
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-16 19:16:58 +04:00
udev_rules_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 * subsys ;
char * sysname ;
2007-06-11 01:53:40 +04:00
char * attrib ;
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 ) ) ;
if ( split_subsys_sysname ( event - > udev , attr , & subsys , & sysname , & attrib ) = = 0 ) {
struct udev_device * d ;
2007-06-11 01:53:40 +04:00
2008-10-16 19:16:58 +04:00
d = udev_device_new_from_subsystem_sysname ( event - > udev , subsys , sysname ) ;
if ( d ! = NULL ) {
util_strlcpy ( attr , udev_device_get_syspath ( d ) , sizeof ( attr ) ) ;
if ( attrib ! = NULL ) {
util_strlcat ( attr , " / " , sizeof ( attr ) ) ;
util_strlcat ( attr , attrib , sizeof ( attr ) ) ;
}
udev_device_unref ( d ) ;
}
} else {
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-16 19:16:58 +04:00
udev_rules_apply_format ( event , value , sizeof ( value ) ) ;
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-16 19:16:58 +04:00
udev_rules_apply_format ( event , buf , sizeof ( buf ) ) ;
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 ) ) ;
udev_rules_apply_format ( event , event - > owner , sizeof ( event - > owner ) ) ;
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 ) ) ;
udev_rules_apply_format ( event , event - > group , sizeof ( event - > group ) ) ;
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-16 19:16:58 +04:00
udev_rules_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 ) ) ;
udev_rules_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 " ) ;
udev_list_cleanup ( 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 " ) ;
unlink_secure ( event - > udev , event - > tmp_node ) ;
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 " ) ;
udev_list_cleanup ( 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 ] ;
uid_t uid = lookup_user ( rules - > udev , value ) ;
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 ] ;
gid_t gid = lookup_group ( rules - > udev , value ) ;
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 ) {
create_path ( udev , filename ) ;
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
}