2003-07-17 12:24:51 +04:00
/*
2012-08-08 20:24:50 +04:00
* Copyright ( C ) 2003 - 2012 Kay Sievers < kay . sievers @ vrfy . org >
2008-11-13 02:52:12 +03:00
* Copyright ( C ) 2008 Alan Jenkins < alan - jenkins @ tuffmail . co . uk >
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>
2008-11-11 23:20:11 +03:00
# include <limits.h>
2003-07-17 12:24:51 +04:00
# include <stdlib.h>
2009-11-17 01:39:33 +03:00
# include <stdbool.h>
2003-07-17 12:24:51 +04:00
# include <string.h>
# include <stdio.h>
# include <fcntl.h>
# include <ctype.h>
# include <unistd.h>
# include <errno.h>
2008-03-13 18:00:25 +03:00
# include <dirent.h>
2006-08-24 11:03:15 +04:00
# include <fnmatch.h>
2009-08-31 01:58:57 +04:00
# include <time.h>
2003-07-17 12:24:51 +04:00
# include "udev.h"
2012-05-07 23:36:12 +04:00
# include "path-util.h"
2012-05-07 20:55:45 +04:00
# include "conf-files.h"
2003-07-17 12:24:51 +04:00
2012-01-23 01:44:13 +04:00
# define PREALLOC_TOKEN 2048
# define PREALLOC_STRBUF 32 * 1024
# define PREALLOC_TRIE 256
2008-10-18 16:33:37 +04:00
2008-11-01 23:05:25 +03:00
struct uid_gid {
2012-01-10 04:34:15 +04:00
unsigned int name_off ;
union {
2012-01-23 01:44:13 +04:00
uid_t uid ;
gid_t gid ;
2012-01-10 04:34:15 +04:00
} ;
2008-11-01 23:05:25 +03:00
} ;
2008-11-11 23:20:11 +03:00
struct trie_node {
2012-01-10 04:34:15 +04:00
/* this node's first child */
unsigned int child_idx ;
/* the next child of our parent node's child list */
unsigned int next_child_idx ;
/* this node's last child (shortcut for append) */
unsigned int last_child_idx ;
unsigned int value_off ;
unsigned short value_len ;
unsigned char key ;
2008-11-11 23:20:11 +03:00
} ;
2008-11-01 23:05:25 +03:00
struct udev_rules {
2012-01-10 04:34:15 +04:00
struct udev * udev ;
2012-05-07 15:21:05 +04:00
char * * dirs ;
unsigned long long * dirs_ts_usec ;
2012-01-10 04:34:15 +04:00
int resolve_names ;
/* every key in the rules file becomes a token */
struct token * tokens ;
unsigned int token_cur ;
unsigned int token_max ;
/* all key strings are copied to a single string buffer */
char * buf ;
size_t buf_cur ;
size_t buf_max ;
unsigned int buf_count ;
2012-05-07 15:21:05 +04:00
/* during rule parsing, strings are indexed and de-duplicated */
2012-01-10 04:34:15 +04:00
struct trie_node * trie_nodes ;
unsigned int trie_nodes_cur ;
unsigned int trie_nodes_max ;
/* during rule parsing, uid/gid lookup results are cached */
struct uid_gid * uids ;
unsigned int uids_cur ;
unsigned int uids_max ;
struct uid_gid * gids ;
unsigned int gids_cur ;
unsigned int gids_max ;
2008-11-01 23:05:25 +03:00
} ;
2008-11-11 23:20:11 +03:00
/* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */
2008-10-24 13:38:05 +04:00
enum operation_type {
2012-01-10 04:34:15 +04:00
OP_UNSET ,
2008-10-24 13:38:05 +04:00
2012-01-10 04:34:15 +04:00
OP_MATCH ,
OP_NOMATCH ,
OP_MATCH_MAX ,
2008-10-24 13:38:05 +04:00
2012-01-10 04:34:15 +04:00
OP_ADD ,
OP_ASSIGN ,
OP_ASSIGN_FINAL ,
2008-10-18 16:33:37 +04:00
} ;
2008-10-24 15:32:32 +04:00
enum string_glob_type {
2012-01-10 04:34:15 +04:00
GL_UNSET ,
2012-01-23 01:44:13 +04:00
GL_PLAIN , /* no special chars */
2012-01-10 04:34:15 +04:00
GL_GLOB , /* shell globs ?,*,[] */
2012-01-23 01:44:13 +04:00
GL_SPLIT , /* multi-value A|B */
GL_SPLIT_GLOB , /* multi-value with glob A*|B* */
GL_SOMETHING , /* commonly used "?*" */
2008-10-24 15:32:32 +04:00
} ;
2009-06-06 06:52:52 +04:00
enum string_subst_type {
2012-01-10 04:34:15 +04:00
SB_UNSET ,
SB_NONE ,
SB_FORMAT ,
SB_SUBSYS ,
2009-06-06 06:52:52 +04:00
} ;
2008-10-24 12:51:04 +04:00
/* tokens of a rule are sorted/handled in this order */
2008-10-23 02:13:59 +04:00
enum token_type {
2012-01-10 04:34:15 +04:00
TK_UNSET ,
TK_RULE ,
2012-01-23 01:44:13 +04:00
TK_M_ACTION , /* val */
TK_M_DEVPATH , /* val */
TK_M_KERNEL , /* val */
TK_M_DEVLINK , /* val */
TK_M_NAME , /* val */
TK_M_ENV , /* val, attr */
TK_M_TAG , /* val */
TK_M_SUBSYSTEM , /* val */
TK_M_DRIVER , /* val */
TK_M_WAITFOR , /* val */
TK_M_ATTR , /* val, attr */
2012-01-10 04:34:15 +04:00
TK_M_PARENTS_MIN ,
2012-01-23 01:44:13 +04:00
TK_M_KERNELS , /* val */
2012-01-10 04:34:15 +04:00
TK_M_SUBSYSTEMS , /* val */
2012-01-23 01:44:13 +04:00
TK_M_DRIVERS , /* val */
TK_M_ATTRS , /* val, attr */
TK_M_TAGS , /* val */
2012-01-10 04:34:15 +04:00
TK_M_PARENTS_MAX ,
2012-01-23 01:44:13 +04:00
TK_M_TEST , /* val, mode_t */
TK_M_EVENT_TIMEOUT , /* int */
TK_M_PROGRAM , /* val */
TK_M_IMPORT_FILE , /* val */
TK_M_IMPORT_PROG , /* val */
TK_M_IMPORT_BUILTIN , /* val */
TK_M_IMPORT_DB , /* val */
TK_M_IMPORT_CMDLINE , /* val */
TK_M_IMPORT_PARENT , /* val */
TK_M_RESULT , /* val */
2012-01-10 04:34:15 +04:00
TK_M_MAX ,
TK_A_STRING_ESCAPE_NONE ,
TK_A_STRING_ESCAPE_REPLACE ,
TK_A_DB_PERSIST ,
2012-01-23 01:44:13 +04:00
TK_A_INOTIFY_WATCH , /* int */
TK_A_DEVLINK_PRIO , /* int */
TK_A_OWNER , /* val */
TK_A_GROUP , /* val */
TK_A_MODE , /* val */
TK_A_OWNER_ID , /* uid_t */
TK_A_GROUP_ID , /* gid_t */
TK_A_MODE_ID , /* mode_t */
TK_A_STATIC_NODE , /* val */
TK_A_ENV , /* val, attr */
TK_A_TAG , /* val */
TK_A_NAME , /* val */
TK_A_DEVLINK , /* val */
TK_A_ATTR , /* val, attr */
2012-04-09 18:37:54 +04:00
TK_A_RUN_BUILTIN , /* val, bool */
TK_A_RUN_PROGRAM , /* val, bool */
2012-01-23 01:44:13 +04:00
TK_A_GOTO , /* size_t */
2012-01-10 04:34:15 +04:00
TK_END ,
2008-10-18 16:33:37 +04:00
} ;
2008-11-06 00:28:52 +03:00
/* we try to pack stuff in a way that we take only 12 bytes per token */
2008-11-01 23:05:25 +03:00
struct token {
2012-01-10 04:34:15 +04:00
union {
unsigned char type ; /* same in rule and key */
struct {
enum token_type type : 8 ;
bool can_set_name : 1 ;
bool has_static_node : 1 ;
unsigned int unused : 6 ;
unsigned short token_count ;
unsigned int label_off ;
unsigned short filename_off ;
unsigned short filename_line ;
} rule ;
struct {
enum token_type type : 8 ;
enum operation_type op : 8 ;
enum string_glob_type glob : 8 ;
enum string_subst_type subst : 4 ;
enum string_subst_type attrsubst : 4 ;
unsigned int value_off ;
union {
unsigned int attr_off ;
int devlink_unique ;
unsigned int rule_goto ;
mode_t mode ;
uid_t uid ;
gid_t gid ;
int devlink_prio ;
int event_timeout ;
int watch ;
enum udev_builtin_cmd builtin_cmd ;
} ;
} key ;
} ;
2008-11-01 23:05:25 +03:00
} ;
2012-01-10 04:34:15 +04:00
# define MAX_TK 64
2008-11-01 23:05:25 +03:00
struct rule_tmp {
2012-01-10 04:34:15 +04:00
struct udev_rules * rules ;
struct token rule ;
struct token token [ MAX_TK ] ;
unsigned int token_cur ;
2008-11-01 23:05:25 +03:00
} ;
2012-04-08 18:06:20 +04:00
# ifdef DEBUG
convert debug string arrays to functions
On Fri, Nov 7, 2008 at 13:07, Matthias Schwarzott <zzam@gentoo.org> wrote:
> I managed to let udev-131 segfault at startup.
>
> I configured it like this:
> CFLAGS="-Wall -ggdb" ./configure --prefix=/usr --sysconfdir=/etc --exec-prefix=
>
> Running it in gdb shows it segfaults at udev-rules.c:831
>
> (gdb) run
> Starting program: /tmp/udev-131/udev/udevd
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x0804ea06 in get_key (udev=0x9175008, line=0xafcdc8f0, key=0xafcdc5d8,
> op=0xafcdc5d0, value=0xafcdc5d4)
> at udev-rules.c:831
> 831 dbg(udev, "%s '%s'-'%s'\n", operation_str[*op], *key, *value);
If compiled without optimization, the dbg() macro dereferences variables
which are not available. Convert the string array to a function, which just
returns NULL if compiled without DEBUG.
2008-11-07 17:59:58 +03:00
static const char * operation_str ( enum operation_type type )
{
2012-01-10 04:34:15 +04:00
static const char * operation_strs [ ] = {
2012-01-23 01:44:13 +04:00
[ OP_UNSET ] = " UNSET " ,
[ OP_MATCH ] = " match " ,
[ OP_NOMATCH ] = " nomatch " ,
2012-01-10 04:34:15 +04:00
[ OP_MATCH_MAX ] = " MATCH_MAX " ,
2012-01-23 01:44:13 +04:00
[ OP_ADD ] = " add " ,
[ OP_ASSIGN ] = " assign " ,
[ OP_ASSIGN_FINAL ] = " assign-final " ,
2012-01-10 04:34:15 +04:00
} ;
return operation_strs [ type ] ;
convert debug string arrays to functions
On Fri, Nov 7, 2008 at 13:07, Matthias Schwarzott <zzam@gentoo.org> wrote:
> I managed to let udev-131 segfault at startup.
>
> I configured it like this:
> CFLAGS="-Wall -ggdb" ./configure --prefix=/usr --sysconfdir=/etc --exec-prefix=
>
> Running it in gdb shows it segfaults at udev-rules.c:831
>
> (gdb) run
> Starting program: /tmp/udev-131/udev/udevd
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x0804ea06 in get_key (udev=0x9175008, line=0xafcdc8f0, key=0xafcdc5d8,
> op=0xafcdc5d0, value=0xafcdc5d4)
> at udev-rules.c:831
> 831 dbg(udev, "%s '%s'-'%s'\n", operation_str[*op], *key, *value);
If compiled without optimization, the dbg() macro dereferences variables
which are not available. Convert the string array to a function, which just
returns NULL if compiled without DEBUG.
2008-11-07 17:59:58 +03:00
}
2008-11-01 23:05:25 +03:00
convert debug string arrays to functions
On Fri, Nov 7, 2008 at 13:07, Matthias Schwarzott <zzam@gentoo.org> wrote:
> I managed to let udev-131 segfault at startup.
>
> I configured it like this:
> CFLAGS="-Wall -ggdb" ./configure --prefix=/usr --sysconfdir=/etc --exec-prefix=
>
> Running it in gdb shows it segfaults at udev-rules.c:831
>
> (gdb) run
> Starting program: /tmp/udev-131/udev/udevd
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x0804ea06 in get_key (udev=0x9175008, line=0xafcdc8f0, key=0xafcdc5d8,
> op=0xafcdc5d0, value=0xafcdc5d4)
> at udev-rules.c:831
> 831 dbg(udev, "%s '%s'-'%s'\n", operation_str[*op], *key, *value);
If compiled without optimization, the dbg() macro dereferences variables
which are not available. Convert the string array to a function, which just
returns NULL if compiled without DEBUG.
2008-11-07 17:59:58 +03:00
static const char * string_glob_str ( enum string_glob_type type )
{
2012-01-10 04:34:15 +04:00
static const char * string_glob_strs [ ] = {
2012-01-23 01:44:13 +04:00
[ GL_UNSET ] = " UNSET " ,
[ GL_PLAIN ] = " plain " ,
[ GL_GLOB ] = " glob " ,
[ GL_SPLIT ] = " split " ,
[ GL_SPLIT_GLOB ] = " split-glob " ,
[ GL_SOMETHING ] = " split-glob " ,
2012-01-10 04:34:15 +04:00
} ;
return string_glob_strs [ type ] ;
convert debug string arrays to functions
On Fri, Nov 7, 2008 at 13:07, Matthias Schwarzott <zzam@gentoo.org> wrote:
> I managed to let udev-131 segfault at startup.
>
> I configured it like this:
> CFLAGS="-Wall -ggdb" ./configure --prefix=/usr --sysconfdir=/etc --exec-prefix=
>
> Running it in gdb shows it segfaults at udev-rules.c:831
>
> (gdb) run
> Starting program: /tmp/udev-131/udev/udevd
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x0804ea06 in get_key (udev=0x9175008, line=0xafcdc8f0, key=0xafcdc5d8,
> op=0xafcdc5d0, value=0xafcdc5d4)
> at udev-rules.c:831
> 831 dbg(udev, "%s '%s'-'%s'\n", operation_str[*op], *key, *value);
If compiled without optimization, the dbg() macro dereferences variables
which are not available. Convert the string array to a function, which just
returns NULL if compiled without DEBUG.
2008-11-07 17:59:58 +03:00
}
static const char * token_str ( enum token_type type )
{
2012-01-10 04:34:15 +04:00
static const char * token_strs [ ] = {
2012-01-23 01:44:13 +04:00
[ TK_UNSET ] = " UNSET " ,
[ TK_RULE ] = " RULE " ,
2012-01-10 04:34:15 +04:00
2012-01-23 01:44:13 +04:00
[ TK_M_ACTION ] = " M ACTION " ,
2012-01-10 04:34:15 +04:00
[ TK_M_DEVPATH ] = " M DEVPATH " ,
2012-01-23 01:44:13 +04:00
[ TK_M_KERNEL ] = " M KERNEL " ,
2012-01-10 04:34:15 +04:00
[ TK_M_DEVLINK ] = " M DEVLINK " ,
2012-01-23 01:44:13 +04:00
[ TK_M_NAME ] = " M NAME " ,
[ TK_M_ENV ] = " M ENV " ,
[ TK_M_TAG ] = " M TAG " ,
[ TK_M_SUBSYSTEM ] = " M SUBSYSTEM " ,
[ TK_M_DRIVER ] = " M DRIVER " ,
2012-01-10 04:34:15 +04:00
[ TK_M_WAITFOR ] = " M WAITFOR " ,
2012-01-23 01:44:13 +04:00
[ TK_M_ATTR ] = " M ATTR " ,
2012-01-10 04:34:15 +04:00
2012-01-23 01:44:13 +04:00
[ TK_M_PARENTS_MIN ] = " M PARENTS_MIN " ,
2012-01-10 04:34:15 +04:00
[ TK_M_KERNELS ] = " M KERNELS " ,
2012-01-23 01:44:13 +04:00
[ TK_M_SUBSYSTEMS ] = " M SUBSYSTEMS " ,
2012-01-10 04:34:15 +04:00
[ TK_M_DRIVERS ] = " M DRIVERS " ,
2012-01-23 01:44:13 +04:00
[ TK_M_ATTRS ] = " M ATTRS " ,
[ TK_M_TAGS ] = " M TAGS " ,
[ TK_M_PARENTS_MAX ] = " M PARENTS_MAX " ,
2012-01-10 04:34:15 +04:00
2012-01-23 01:44:13 +04:00
[ TK_M_TEST ] = " M TEST " ,
[ TK_M_EVENT_TIMEOUT ] = " M EVENT_TIMEOUT " ,
2012-01-10 04:34:15 +04:00
[ TK_M_PROGRAM ] = " M PROGRAM " ,
2012-01-23 01:44:13 +04:00
[ TK_M_IMPORT_FILE ] = " M IMPORT_FILE " ,
[ TK_M_IMPORT_PROG ] = " M IMPORT_PROG " ,
[ TK_M_IMPORT_BUILTIN ] = " M IMPORT_BUILTIN " ,
[ TK_M_IMPORT_DB ] = " M IMPORT_DB " ,
[ TK_M_IMPORT_CMDLINE ] = " M IMPORT_CMDLINE " ,
[ TK_M_IMPORT_PARENT ] = " M IMPORT_PARENT " ,
[ TK_M_RESULT ] = " M RESULT " ,
[ TK_M_MAX ] = " M MAX " ,
[ TK_A_STRING_ESCAPE_NONE ] = " A STRING_ESCAPE_NONE " ,
[ TK_A_STRING_ESCAPE_REPLACE ] = " A STRING_ESCAPE_REPLACE " ,
[ TK_A_DB_PERSIST ] = " A DB_PERSIST " ,
[ TK_A_INOTIFY_WATCH ] = " A INOTIFY_WATCH " ,
[ TK_A_DEVLINK_PRIO ] = " A DEVLINK_PRIO " ,
[ TK_A_OWNER ] = " A OWNER " ,
[ TK_A_GROUP ] = " A GROUP " ,
[ TK_A_MODE ] = " A MODE " ,
[ TK_A_OWNER_ID ] = " A OWNER_ID " ,
[ TK_A_GROUP_ID ] = " A GROUP_ID " ,
[ TK_A_STATIC_NODE ] = " A STATIC_NODE " ,
2012-01-10 04:34:15 +04:00
[ TK_A_MODE_ID ] = " A MODE_ID " ,
2012-01-23 01:44:13 +04:00
[ TK_A_ENV ] = " A ENV " ,
[ TK_A_TAG ] = " A ENV " ,
[ TK_A_NAME ] = " A NAME " ,
2012-01-10 04:34:15 +04:00
[ TK_A_DEVLINK ] = " A DEVLINK " ,
2012-01-23 01:44:13 +04:00
[ TK_A_ATTR ] = " A ATTR " ,
2012-04-09 18:37:54 +04:00
[ TK_A_RUN_BUILTIN ] = " A RUN_BUILTIN " ,
[ TK_A_RUN_PROGRAM ] = " A RUN_PROGRAM " ,
2012-01-23 01:44:13 +04:00
[ TK_A_GOTO ] = " A GOTO " ,
2012-01-10 04:34:15 +04:00
2012-01-23 01:44:13 +04:00
[ TK_END ] = " END " ,
2012-01-10 04:34:15 +04:00
} ;
return token_strs [ type ] ;
convert debug string arrays to functions
On Fri, Nov 7, 2008 at 13:07, Matthias Schwarzott <zzam@gentoo.org> wrote:
> I managed to let udev-131 segfault at startup.
>
> I configured it like this:
> CFLAGS="-Wall -ggdb" ./configure --prefix=/usr --sysconfdir=/etc --exec-prefix=
>
> Running it in gdb shows it segfaults at udev-rules.c:831
>
> (gdb) run
> Starting program: /tmp/udev-131/udev/udevd
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x0804ea06 in get_key (udev=0x9175008, line=0xafcdc8f0, key=0xafcdc5d8,
> op=0xafcdc5d0, value=0xafcdc5d4)
> at udev-rules.c:831
> 831 dbg(udev, "%s '%s'-'%s'\n", operation_str[*op], *key, *value);
If compiled without optimization, the dbg() macro dereferences variables
which are not available. Convert the string array to a function, which just
returns NULL if compiled without DEBUG.
2008-11-07 17:59:58 +03:00
}
2008-10-18 16:33:37 +04:00
2008-11-01 23:05:25 +03:00
static void dump_token ( struct udev_rules * rules , struct token * token )
{
2012-01-10 04:34:15 +04:00
enum token_type type = token - > type ;
enum operation_type op = token - > key . op ;
enum string_glob_type glob = token - > key . glob ;
const char * value = & rules - > buf [ token - > key . value_off ] ;
const char * attr = & rules - > buf [ token - > key . attr_off ] ;
switch ( type ) {
case TK_RULE :
{
const char * tks_ptr = ( char * ) rules - > tokens ;
const char * tk_ptr = ( char * ) token ;
unsigned int idx = ( tk_ptr - tks_ptr ) / sizeof ( struct token ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " * RULE %s:%u, token: %u, count: %u, label: '%s' \n " ,
& rules - > buf [ token - > rule . filename_off ] , token - > rule . filename_line ,
idx , token - > rule . token_count ,
& rules - > buf [ token - > rule . label_off ] ) ;
2012-01-10 04:34:15 +04:00
break ;
}
case TK_M_ACTION :
case TK_M_DEVPATH :
case TK_M_KERNEL :
case TK_M_SUBSYSTEM :
case TK_M_DRIVER :
case TK_M_WAITFOR :
case TK_M_DEVLINK :
case TK_M_NAME :
case TK_M_KERNELS :
case TK_M_SUBSYSTEMS :
case TK_M_DRIVERS :
case TK_M_TAGS :
case TK_M_PROGRAM :
case TK_M_IMPORT_FILE :
case TK_M_IMPORT_PROG :
case TK_M_IMPORT_DB :
case TK_M_IMPORT_CMDLINE :
case TK_M_IMPORT_PARENT :
case TK_M_RESULT :
case TK_A_NAME :
case TK_A_DEVLINK :
case TK_A_OWNER :
case TK_A_GROUP :
case TK_A_MODE :
2012-04-09 18:37:54 +04:00
case TK_A_RUN_BUILTIN :
case TK_A_RUN_PROGRAM :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %s '%s'(%s) \n " ,
token_str ( type ) , operation_str ( op ) , value , string_glob_str ( glob ) ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_M_IMPORT_BUILTIN :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %i '%s' \n " , token_str ( type ) , token - > key . builtin_cmd , value ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_M_ATTR :
case TK_M_ATTRS :
case TK_M_ENV :
case TK_A_ATTR :
case TK_A_ENV :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %s '%s' '%s'(%s) \n " ,
token_str ( type ) , operation_str ( op ) , attr , value , string_glob_str ( glob ) ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_M_TAG :
case TK_A_TAG :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %s '%s' \n " , token_str ( type ) , operation_str ( op ) , value ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_STRING_ESCAPE_NONE :
case TK_A_STRING_ESCAPE_REPLACE :
case TK_A_DB_PERSIST :
2012-04-08 18:06:20 +04:00
log_debug ( " %s \n " , token_str ( type ) ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_M_TEST :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %s '%s'(%s) %#o \n " ,
token_str ( type ) , operation_str ( op ) , value , string_glob_str ( glob ) , token - > key . mode ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_INOTIFY_WATCH :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %u \n " , token_str ( type ) , token - > key . watch ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_DEVLINK_PRIO :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %u \n " , token_str ( type ) , token - > key . devlink_prio ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_OWNER_ID :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %s %u \n " , token_str ( type ) , operation_str ( op ) , token - > key . uid ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_GROUP_ID :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %s %u \n " , token_str ( type ) , operation_str ( op ) , token - > key . gid ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_MODE_ID :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %s %#o \n " , token_str ( type ) , operation_str ( op ) , token - > key . mode ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_STATIC_NODE :
2012-04-08 18:06:20 +04:00
log_debug ( " %s '%s' \n " , token_str ( type ) , value ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_M_EVENT_TIMEOUT :
2012-04-08 18:06:20 +04:00
log_debug ( " %s %u \n " , token_str ( type ) , token - > key . event_timeout ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_GOTO :
2012-04-08 18:06:20 +04:00
log_debug ( " %s '%s' %u \n " , token_str ( type ) , value , token - > key . rule_goto ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_END :
2012-04-08 18:06:20 +04:00
log_debug ( " * %s \n " , token_str ( type ) ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_M_PARENTS_MIN :
case TK_M_PARENTS_MAX :
case TK_M_MAX :
case TK_UNSET :
2012-04-08 18:06:20 +04:00
log_debug ( " unknown type %u \n " , type ) ;
2012-01-10 04:34:15 +04:00
break ;
}
2008-11-01 23:05:25 +03:00
}
2008-10-24 12:51:04 +04:00
2008-11-01 23:05:25 +03:00
static void dump_rules ( struct udev_rules * rules )
{
2012-01-10 04:34:15 +04:00
unsigned int i ;
2012-04-08 18:06:20 +04:00
log_debug ( " dumping %u (%zu bytes) tokens, %u (%zu bytes) strings \n " ,
rules - > token_cur ,
rules - > token_cur * sizeof ( struct token ) ,
rules - > buf_count ,
rules - > buf_cur ) ;
2012-01-10 04:34:15 +04:00
for ( i = 0 ; i < rules - > token_cur ; i + + )
dump_token ( rules , & rules - > tokens [ i ] ) ;
2008-11-01 23:05:25 +03:00
}
# else
convert debug string arrays to functions
On Fri, Nov 7, 2008 at 13:07, Matthias Schwarzott <zzam@gentoo.org> wrote:
> I managed to let udev-131 segfault at startup.
>
> I configured it like this:
> CFLAGS="-Wall -ggdb" ./configure --prefix=/usr --sysconfdir=/etc --exec-prefix=
>
> Running it in gdb shows it segfaults at udev-rules.c:831
>
> (gdb) run
> Starting program: /tmp/udev-131/udev/udevd
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x0804ea06 in get_key (udev=0x9175008, line=0xafcdc8f0, key=0xafcdc5d8,
> op=0xafcdc5d0, value=0xafcdc5d4)
> at udev-rules.c:831
> 831 dbg(udev, "%s '%s'-'%s'\n", operation_str[*op], *key, *value);
If compiled without optimization, the dbg() macro dereferences variables
which are not available. Convert the string array to a function, which just
returns NULL if compiled without DEBUG.
2008-11-07 17:59:58 +03:00
static inline const char * operation_str ( enum operation_type type ) { return NULL ; }
static inline const char * token_str ( enum token_type type ) { return NULL ; }
2008-11-01 23:05:25 +03:00
static inline void dump_token ( struct udev_rules * rules , struct token * token ) { }
static inline void dump_rules ( struct udev_rules * rules ) { }
2012-04-08 18:06:20 +04:00
# endif /* DEBUG */
2008-10-18 16:33:37 +04:00
2008-11-11 23:20:11 +03:00
static int add_new_string ( struct udev_rules * rules , const char * str , size_t bytes )
2008-10-18 16:33:37 +04:00
{
2012-01-10 04:34:15 +04:00
int off ;
/* grow buffer if needed */
if ( rules - > buf_cur + bytes + 1 > = rules - > buf_max ) {
char * buf ;
unsigned int add ;
/* double the buffer size */
add = rules - > buf_max ;
if ( add < bytes * 8 )
add = bytes * 8 ;
buf = realloc ( rules - > buf , rules - > buf_max + add ) ;
if ( buf = = NULL )
return - 1 ;
rules - > buf = buf ;
rules - > buf_max + = add ;
}
off = rules - > buf_cur ;
memcpy ( & rules - > buf [ rules - > buf_cur ] , str , bytes ) ;
rules - > buf_cur + = bytes ;
rules - > buf_count + + ;
return off ;
2008-10-18 16:33:37 +04:00
}
2008-11-11 23:20:11 +03:00
static int add_string ( struct udev_rules * rules , const char * str )
2008-10-18 16:33:37 +04:00
{
2012-01-10 04:34:15 +04:00
unsigned int node_idx ;
struct trie_node * new_node ;
unsigned int new_node_idx ;
unsigned char key ;
unsigned short len ;
unsigned int depth ;
unsigned int off ;
struct trie_node * parent ;
/* walk trie, start from last character of str to find matching tails */
len = strlen ( str ) ;
key = str [ len - 1 ] ;
node_idx = 0 ;
for ( depth = 0 ; depth < = len ; depth + + ) {
struct trie_node * node ;
unsigned int child_idx ;
node = & rules - > trie_nodes [ node_idx ] ;
off = node - > value_off + node - > value_len - len ;
/* match against current node */
if ( depth = = len | | ( node - > value_len > = len & & memcmp ( & rules - > buf [ off ] , str , len ) = = 0 ) )
return off ;
/* lookup child node */
key = str [ len - 1 - depth ] ;
child_idx = node - > child_idx ;
while ( child_idx > 0 ) {
struct trie_node * child ;
child = & rules - > trie_nodes [ child_idx ] ;
if ( child - > key = = key )
break ;
child_idx = child - > next_child_idx ;
}
if ( child_idx = = 0 )
break ;
node_idx = child_idx ;
}
/* string not found, add it */
off = add_new_string ( rules , str , len + 1 ) ;
/* grow trie nodes if needed */
if ( rules - > trie_nodes_cur > = rules - > trie_nodes_max ) {
struct trie_node * nodes ;
unsigned int add ;
/* double the buffer size */
add = rules - > trie_nodes_max ;
if ( add < 8 )
add = 8 ;
nodes = realloc ( rules - > trie_nodes , ( rules - > trie_nodes_max + add ) * sizeof ( struct trie_node ) ) ;
if ( nodes = = NULL )
return - 1 ;
rules - > trie_nodes = nodes ;
rules - > trie_nodes_max + = add ;
}
/* get a new node */
new_node_idx = rules - > trie_nodes_cur ;
rules - > trie_nodes_cur + + ;
new_node = & rules - > trie_nodes [ new_node_idx ] ;
memset ( new_node , 0x00 , sizeof ( struct trie_node ) ) ;
new_node - > value_off = off ;
new_node - > value_len = len ;
new_node - > key = key ;
/* join the parent's child list */
parent = & rules - > trie_nodes [ node_idx ] ;
if ( parent - > child_idx = = 0 ) {
parent - > child_idx = new_node_idx ;
} else {
struct trie_node * last_child ;
last_child = & rules - > trie_nodes [ parent - > last_child_idx ] ;
last_child - > next_child_idx = new_node_idx ;
}
parent - > last_child_idx = new_node_idx ;
return off ;
2008-11-11 23:20:11 +03:00
}
static int add_token ( struct udev_rules * rules , struct token * token )
{
2012-01-10 04:34:15 +04:00
/* grow buffer if needed */
if ( rules - > token_cur + 1 > = rules - > token_max ) {
struct token * tokens ;
unsigned int add ;
/* double the buffer size */
add = rules - > token_max ;
if ( add < 8 )
add = 8 ;
tokens = realloc ( rules - > tokens , ( rules - > token_max + add ) * sizeof ( struct token ) ) ;
if ( tokens = = NULL )
return - 1 ;
rules - > tokens = tokens ;
rules - > token_max + = add ;
}
memcpy ( & rules - > tokens [ rules - > token_cur ] , token , sizeof ( struct token ) ) ;
rules - > token_cur + + ;
return 0 ;
2008-10-18 16:33:37 +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
2008-10-24 12:51:04 +04:00
static uid_t add_uid ( struct udev_rules * rules , const char * owner )
{
2012-01-10 04:34:15 +04:00
unsigned int i ;
uid_t uid ;
unsigned int off ;
/* lookup, if we know it already */
for ( i = 0 ; i < rules - > uids_cur ; i + + ) {
off = rules - > uids [ i ] . name_off ;
2012-04-16 22:27:44 +04:00
if ( streq ( & rules - > buf [ off ] , owner ) ) {
2012-01-10 04:34:15 +04:00
uid = rules - > uids [ i ] . uid ;
return uid ;
}
}
uid = util_lookup_user ( rules - > udev , owner ) ;
/* grow buffer if needed */
if ( rules - > uids_cur + 1 > = rules - > uids_max ) {
struct uid_gid * uids ;
unsigned int add ;
/* double the buffer size */
add = rules - > uids_max ;
if ( add < 1 )
add = 8 ;
uids = realloc ( rules - > uids , ( rules - > uids_max + add ) * sizeof ( struct uid_gid ) ) ;
if ( uids = = NULL )
return uid ;
rules - > uids = uids ;
rules - > uids_max + = add ;
}
rules - > uids [ rules - > uids_cur ] . uid = uid ;
off = add_string ( rules , owner ) ;
if ( off < = 0 )
return uid ;
rules - > uids [ rules - > uids_cur ] . name_off = off ;
rules - > uids_cur + + ;
return uid ;
2008-10-24 12:51:04 +04:00
}
static gid_t add_gid ( struct udev_rules * rules , const char * group )
{
2012-01-10 04:34:15 +04:00
unsigned int i ;
gid_t gid ;
unsigned int off ;
/* lookup, if we know it already */
for ( i = 0 ; i < rules - > gids_cur ; i + + ) {
off = rules - > gids [ i ] . name_off ;
2012-04-16 22:27:44 +04:00
if ( streq ( & rules - > buf [ off ] , group ) ) {
2012-01-10 04:34:15 +04:00
gid = rules - > gids [ i ] . gid ;
return gid ;
}
}
gid = util_lookup_group ( rules - > udev , group ) ;
/* grow buffer if needed */
if ( rules - > gids_cur + 1 > = rules - > gids_max ) {
struct uid_gid * gids ;
unsigned int add ;
/* double the buffer size */
add = rules - > gids_max ;
if ( add < 1 )
add = 8 ;
gids = realloc ( rules - > gids , ( rules - > gids_max + add ) * sizeof ( struct uid_gid ) ) ;
if ( gids = = NULL )
return gid ;
rules - > gids = gids ;
rules - > gids_max + = add ;
}
rules - > gids [ rules - > gids_cur ] . gid = gid ;
off = add_string ( rules , group ) ;
if ( off < = 0 )
return gid ;
rules - > gids [ rules - > gids_cur ] . name_off = off ;
rules - > gids_cur + + ;
return gid ;
2008-10-24 12:51:04 +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
{
2012-01-10 04:34:15 +04:00
char * key ;
char * val ;
size_t len ;
/* find key */
key = line ;
while ( isspace ( key [ 0 ] ) )
key + + ;
/* comment or empty line */
if ( key [ 0 ] = = ' # ' | | key [ 0 ] = = ' \0 ' )
return - 1 ;
/* split key/value */
val = strchr ( key , ' = ' ) ;
if ( val = = NULL )
return - 1 ;
val [ 0 ] = ' \0 ' ;
val + + ;
/* 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 ] ) {
2012-04-08 18:06:20 +04:00
log_debug ( " inconsistent quoting: '%s', skip \n " , line ) ;
2012-01-10 04:34:15 +04:00
return - 1 ;
}
val [ len - 1 ] = ' \0 ' ;
val + + ;
}
/* handle device, renamed by external tool, returning new path */
2012-04-16 22:27:44 +04:00
if ( streq ( key , " DEVPATH " ) ) {
2012-01-10 04:34:15 +04:00
char syspath [ UTIL_PATH_SIZE ] ;
2012-04-08 18:06:20 +04:00
log_debug ( " updating devpath from '%s' to '%s' \n " ,
udev_device_get_devpath ( dev ) , val ) ;
2012-04-16 19:21:22 +04:00
util_strscpyl ( syspath , sizeof ( syspath ) , " /sys " , val , NULL ) ;
2012-01-10 04:34:15 +04:00
udev_device_set_syspath ( dev , syspath ) ;
} else {
struct udev_list_entry * entry ;
entry = udev_device_add_property ( dev , key , val ) ;
/* store in db, skip private keys */
if ( key [ 0 ] ! = ' . ' )
udev_list_entry_set_num ( entry , true ) ;
}
return 0 ;
2005-06-25 17:35:14 +04:00
}
2008-10-23 02:13:59 +04:00
static int import_file_into_properties ( struct udev_device * dev , const char * filename )
2005-06-25 17:35:14 +04:00
{
2012-01-10 04:34:15 +04:00
FILE * f ;
char line [ UTIL_LINE_SIZE ] ;
2012-07-05 19:33:24 +04:00
f = fopen ( filename , " re " ) ;
2012-01-10 04:34:15 +04:00
if ( f = = NULL )
return - 1 ;
while ( fgets ( line , sizeof ( line ) , f ) ! = NULL )
import_property_from_string ( dev , line ) ;
fclose ( f ) ;
return 0 ;
2005-06-25 15:10:16 +04:00
}
2011-04-20 03:53:03 +04:00
static int import_program_into_properties ( struct udev_event * event , const char * program , const sigset_t * sigmask )
2005-06-25 20:58:49 +04:00
{
2012-01-10 04:34:15 +04:00
struct udev_device * dev = event - > dev ;
char * * envp ;
char result [ UTIL_LINE_SIZE ] ;
char * line ;
int err ;
envp = udev_device_get_properties_envp ( dev ) ;
err = udev_event_spawn ( event , program , envp , sigmask , result , sizeof ( result ) ) ;
if ( err < 0 )
return err ;
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-23 02:13:59 +04:00
static int import_parent_into_properties ( struct udev_device * dev , const char * filter )
2005-07-12 16:46:36 +04:00
{
2012-01-10 04:34:15 +04:00
struct udev_device * dev_parent ;
struct udev_list_entry * list_entry ;
dev_parent = udev_device_get_parent ( dev ) ;
if ( dev_parent = = NULL )
return - 1 ;
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 ) ;
if ( fnmatch ( filter , key , 0 ) = = 0 ) {
struct udev_list_entry * entry ;
entry = udev_device_add_property ( dev , key , val ) ;
/* store in db, skip private keys */
if ( key [ 0 ] ! = ' . ' )
udev_list_entry_set_num ( entry , true ) ;
}
}
return 0 ;
2007-07-14 22:59:03 +04:00
}
2012-01-10 04:34:15 +04:00
# define WAIT_LOOP_PER_SECOND 50
2008-10-23 02:13:59 +04:00
static int wait_for_file ( struct udev_device * dev , const char * file , int timeout )
2005-07-07 22:05:51 +04:00
{
2012-01-10 04:34:15 +04:00
char filepath [ UTIL_PATH_SIZE ] ;
char devicepath [ UTIL_PATH_SIZE ] ;
struct stat stats ;
int loop = timeout * WAIT_LOOP_PER_SECOND ;
/* a relative path is a device attribute */
devicepath [ 0 ] = ' \0 ' ;
if ( file [ 0 ] ! = ' / ' ) {
2012-04-16 19:21:22 +04:00
util_strscpyl ( devicepath , sizeof ( devicepath ) , udev_device_get_syspath ( dev ) , NULL ) ;
2012-01-10 04:34:15 +04:00
util_strscpyl ( filepath , sizeof ( filepath ) , devicepath , " / " , file , NULL ) ;
file = filepath ;
}
while ( - - loop ) {
const struct timespec duration = { 0 , 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND } ;
/* lookup file */
if ( stat ( file , & stats ) = = 0 ) {
2012-04-08 18:06:20 +04:00
log_debug ( " file '%s' appeared after %i loops \n " , file , ( timeout * WAIT_LOOP_PER_SECOND ) - loop - 1 ) ;
2012-01-10 04:34:15 +04:00
return 0 ;
}
/* make sure, the device did not disappear in the meantime */
if ( devicepath [ 0 ] ! = ' \0 ' & & stat ( devicepath , & stats ) ! = 0 ) {
2012-04-08 18:06:20 +04:00
log_debug ( " device disappeared while waiting for '%s' \n " , file ) ;
2012-01-10 04:34:15 +04:00
return - 2 ;
}
2012-04-08 18:06:20 +04:00
log_debug ( " wait for '%s' for %i mseconds \n " , file , 1000 / WAIT_LOOP_PER_SECOND ) ;
2012-01-10 04:34:15 +04:00
nanosleep ( & duration , NULL ) ;
}
2012-04-08 18:06:20 +04:00
log_debug ( " waiting for '%s' failed \n " , file ) ;
2012-01-10 04:34:15 +04:00
return - 1 ;
2005-07-07 22:05:51 +04:00
}
2008-03-15 01:40:06 +03:00
static int attr_subst_subdir ( char * attr , size_t len )
{
2012-01-10 04:34:15 +04:00
bool found = false ;
if ( strstr ( attr , " /*/ " ) ) {
char * pos ;
char dirname [ UTIL_PATH_SIZE ] ;
const char * tail ;
DIR * dir ;
util_strscpy ( dirname , sizeof ( dirname ) , attr ) ;
pos = strstr ( dirname , " /*/ " ) ;
if ( pos = = NULL )
return - 1 ;
pos [ 0 ] = ' \0 ' ;
tail = & pos [ 2 ] ;
dir = opendir ( dirname ) ;
if ( dir ! = NULL ) {
struct dirent * dent ;
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
struct stat stats ;
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
util_strscpyl ( attr , len , dirname , " / " , dent - > d_name , tail , NULL ) ;
if ( stat ( attr , & stats ) = = 0 ) {
found = true ;
break ;
}
}
closedir ( dir ) ;
}
}
return found ;
2008-03-15 01:40:06 +03:00
}
2008-10-24 13:38:05 +04:00
static int get_key ( struct udev * udev , char * * line , char * * key , enum operation_type * op , char * * value )
2005-07-04 21:44:25 +04:00
{
2012-01-10 04:34:15 +04:00
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 ;
for ( ; ; ) {
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 ] = = ' = ' ) {
* op = OP_MATCH ;
linepos + = 2 ;
} else if ( linepos [ 0 ] = = ' ! ' & & linepos [ 1 ] = = ' = ' ) {
* op = OP_NOMATCH ;
linepos + = 2 ;
} else if ( linepos [ 0 ] = = ' + ' & & linepos [ 1 ] = = ' = ' ) {
* op = OP_ADD ;
linepos + = 2 ;
} else if ( linepos [ 0 ] = = ' = ' ) {
* op = OP_ASSIGN ;
linepos + + ;
} else if ( linepos [ 0 ] = = ' : ' & & linepos [ 1 ] = = ' = ' ) {
* op = OP_ASSIGN_FINAL ;
linepos + = 2 ;
} 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 ;
/* terminate */
temp = strchr ( linepos , ' " ' ) ;
if ( ! temp )
return - 1 ;
temp [ 0 ] = ' \0 ' ;
temp + + ;
/* move line to next key */
* line = temp ;
return 0 ;
2008-10-23 02:13:59 +04:00
}
2006-08-19 18:06:25 +04:00
2008-10-23 02:13:59 +04:00
/* extract possible KEY{attr} */
2012-04-10 18:41:52 +04:00
static const char * get_key_attribute ( struct udev * udev , char * str )
2008-10-23 02:13:59 +04:00
{
2012-01-10 04:34:15 +04:00
char * pos ;
char * attr ;
attr = strchr ( str , ' { ' ) ;
if ( attr ! = NULL ) {
attr + + ;
pos = strchr ( attr , ' } ' ) ;
if ( pos = = NULL ) {
2012-04-08 18:06:20 +04:00
log_error ( " missing closing brace for format \n " ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
pos [ 0 ] = ' \0 ' ;
return attr ;
}
return NULL ;
2008-10-23 02:13:59 +04:00
}
2006-08-19 18:06:25 +04:00
2008-11-01 20:34:54 +03:00
static int rule_add_key ( struct rule_tmp * rule_tmp , enum token_type type ,
2012-01-10 04:34:15 +04:00
enum operation_type op ,
const char * value , const void * data )
2008-10-23 02:13:59 +04:00
{
2012-01-10 04:34:15 +04:00
struct token * token = & rule_tmp - > token [ rule_tmp - > token_cur ] ;
const char * attr = NULL ;
memset ( token , 0x00 , sizeof ( struct token ) ) ;
switch ( type ) {
case TK_M_ACTION :
case TK_M_DEVPATH :
case TK_M_KERNEL :
case TK_M_SUBSYSTEM :
case TK_M_DRIVER :
case TK_M_WAITFOR :
case TK_M_DEVLINK :
case TK_M_NAME :
case TK_M_KERNELS :
case TK_M_SUBSYSTEMS :
case TK_M_DRIVERS :
case TK_M_TAGS :
case TK_M_PROGRAM :
case TK_M_IMPORT_FILE :
case TK_M_IMPORT_PROG :
case TK_M_IMPORT_DB :
case TK_M_IMPORT_CMDLINE :
case TK_M_IMPORT_PARENT :
case TK_M_RESULT :
case TK_A_OWNER :
case TK_A_GROUP :
case TK_A_MODE :
case TK_A_NAME :
case TK_A_GOTO :
case TK_M_TAG :
case TK_A_TAG :
token - > key . value_off = add_string ( rule_tmp - > rules , value ) ;
break ;
case TK_M_IMPORT_BUILTIN :
token - > key . value_off = add_string ( rule_tmp - > rules , value ) ;
token - > key . builtin_cmd = * ( enum udev_builtin_cmd * ) data ;
break ;
case TK_M_ENV :
case TK_M_ATTR :
case TK_M_ATTRS :
case TK_A_ATTR :
case TK_A_ENV :
attr = data ;
token - > key . value_off = add_string ( rule_tmp - > rules , value ) ;
token - > key . attr_off = add_string ( rule_tmp - > rules , attr ) ;
break ;
case TK_A_DEVLINK :
token - > key . value_off = add_string ( rule_tmp - > rules , value ) ;
token - > key . devlink_unique = * ( int * ) data ;
break ;
case TK_M_TEST :
token - > key . value_off = add_string ( rule_tmp - > rules , value ) ;
if ( data ! = NULL )
token - > key . mode = * ( mode_t * ) data ;
break ;
case TK_A_STRING_ESCAPE_NONE :
case TK_A_STRING_ESCAPE_REPLACE :
case TK_A_DB_PERSIST :
break ;
2012-04-09 18:37:54 +04:00
case TK_A_RUN_BUILTIN :
case TK_A_RUN_PROGRAM :
2012-04-12 00:34:25 +04:00
token - > key . builtin_cmd = * ( enum udev_builtin_cmd * ) data ;
2012-01-10 04:34:15 +04:00
token - > key . value_off = add_string ( rule_tmp - > rules , value ) ;
break ;
case TK_A_INOTIFY_WATCH :
case TK_A_DEVLINK_PRIO :
token - > key . devlink_prio = * ( int * ) data ;
break ;
case TK_A_OWNER_ID :
token - > key . uid = * ( uid_t * ) data ;
break ;
case TK_A_GROUP_ID :
token - > key . gid = * ( gid_t * ) data ;
break ;
case TK_A_MODE_ID :
token - > key . mode = * ( mode_t * ) data ;
break ;
case TK_A_STATIC_NODE :
token - > key . value_off = add_string ( rule_tmp - > rules , value ) ;
break ;
case TK_M_EVENT_TIMEOUT :
token - > key . event_timeout = * ( int * ) data ;
break ;
case TK_RULE :
case TK_M_PARENTS_MIN :
case TK_M_PARENTS_MAX :
case TK_M_MAX :
case TK_END :
case TK_UNSET :
2012-04-08 18:06:20 +04:00
log_error ( " wrong type %u \n " , type ) ;
2012-01-10 04:34:15 +04:00
return - 1 ;
}
if ( value ! = NULL & & type < TK_M_MAX ) {
/* check if we need to split or call fnmatch() while matching rules */
enum string_glob_type glob ;
int has_split ;
int has_glob ;
has_split = ( strchr ( value , ' | ' ) ! = NULL ) ;
has_glob = ( strchr ( value , ' * ' ) ! = NULL | | strchr ( value , ' ? ' ) ! = NULL | | strchr ( value , ' [ ' ) ! = NULL ) ;
if ( has_split & & has_glob ) {
glob = GL_SPLIT_GLOB ;
} else if ( has_split ) {
glob = GL_SPLIT ;
} else if ( has_glob ) {
2012-04-16 22:27:44 +04:00
if ( streq ( value , " ?* " ) )
2012-01-10 04:34:15 +04:00
glob = GL_SOMETHING ;
else
glob = GL_GLOB ;
} else {
glob = GL_PLAIN ;
}
token - > key . glob = glob ;
}
if ( value ! = NULL & & type > TK_M_MAX ) {
/* check if assigned value has substitution chars */
if ( value [ 0 ] = = ' [ ' )
token - > key . subst = SB_SUBSYS ;
else if ( strchr ( value , ' % ' ) ! = NULL | | strchr ( value , ' $ ' ) ! = NULL )
token - > key . subst = SB_FORMAT ;
else
token - > key . subst = SB_NONE ;
}
if ( attr ! = NULL ) {
/* check if property/attribut name has substitution chars */
if ( attr [ 0 ] = = ' [ ' )
token - > key . attrsubst = SB_SUBSYS ;
else if ( strchr ( attr , ' % ' ) ! = NULL | | strchr ( attr , ' $ ' ) ! = NULL )
token - > key . attrsubst = SB_FORMAT ;
else
token - > key . attrsubst = SB_NONE ;
}
token - > key . type = type ;
token - > key . op = op ;
rule_tmp - > token_cur + + ;
2012-04-16 05:13:22 +04:00
if ( rule_tmp - > token_cur > = ELEMENTSOF ( rule_tmp - > token ) ) {
2012-04-08 18:06:20 +04:00
log_error ( " temporary rule array too small \n " ) ;
2012-01-10 04:34:15 +04:00
return - 1 ;
}
return 0 ;
2008-10-23 02:13:59 +04:00
}
2005-02-14 03:46:05 +03:00
2008-10-23 02:13:59 +04:00
static int sort_token ( struct udev_rules * rules , struct rule_tmp * rule_tmp )
{
2012-01-10 04:34:15 +04:00
unsigned int i ;
unsigned int start = 0 ;
unsigned int end = rule_tmp - > token_cur ;
for ( i = 0 ; i < rule_tmp - > token_cur ; i + + ) {
enum token_type next_val = TK_UNSET ;
unsigned int next_idx = 0 ;
unsigned int j ;
/* find smallest value */
for ( j = start ; j < end ; j + + ) {
if ( rule_tmp - > token [ j ] . type = = TK_UNSET )
continue ;
if ( next_val = = TK_UNSET | | rule_tmp - > token [ j ] . type < next_val ) {
next_val = rule_tmp - > token [ j ] . type ;
next_idx = j ;
}
}
/* add token and mark done */
if ( add_token ( rules , & rule_tmp - > token [ next_idx ] ) ! = 0 )
return - 1 ;
rule_tmp - > token [ next_idx ] . type = TK_UNSET ;
/* shrink range */
if ( next_idx = = start )
start + + ;
if ( next_idx + 1 = = end )
end - - ;
}
return 0 ;
2004-01-23 11:21:13 +03:00
}
2008-10-23 02:13:59 +04:00
static int add_rule ( struct udev_rules * rules , char * line ,
2012-01-10 04:34:15 +04:00
const char * filename , unsigned int filename_off , unsigned int lineno )
2004-01-23 11:21:13 +03:00
{
2012-01-10 04:34:15 +04:00
char * linepos ;
2012-04-10 18:41:52 +04:00
const char * attr ;
2012-01-10 04:34:15 +04:00
struct rule_tmp rule_tmp ;
memset ( & rule_tmp , 0x00 , sizeof ( struct rule_tmp ) ) ;
rule_tmp . rules = rules ;
rule_tmp . rule . type = TK_RULE ;
2012-05-07 15:21:05 +04:00
/* the offset in the rule is limited to unsigned short */
if ( filename_off < USHRT_MAX )
rule_tmp . rule . rule . filename_off = filename_off ;
2012-01-10 04:34:15 +04:00
rule_tmp . rule . rule . filename_line = lineno ;
linepos = line ;
for ( ; ; ) {
char * key ;
char * value ;
enum operation_type op ;
if ( get_key ( rules - > udev , & linepos , & key , & op , & value ) ! = 0 )
break ;
2012-04-16 22:27:44 +04:00
if ( streq ( key , " ACTION " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid ACTION operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_ACTION , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " DEVPATH " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid DEVPATH operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_DEVPATH , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " KERNEL " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid KERNEL operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_KERNEL , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " SUBSYSTEM " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid SUBSYSTEM operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
/* bus, class, subsystem events should all be the same */
2012-04-16 22:27:44 +04:00
if ( streq ( value , " subsystem " ) | |
streq ( value , " bus " ) | |
streq ( value , " class " ) ) {
if ( streq ( value , " bus " ) | | streq ( value , " class " ) )
2012-04-08 18:06:20 +04:00
log_error ( " '%s' must be specified as 'subsystem' \n "
2012-01-10 04:34:15 +04:00
" please fix it in %s:%u " , value , filename , lineno ) ;
rule_add_key ( & rule_tmp , TK_M_SUBSYSTEM , op , " subsystem|class|bus " , NULL ) ;
} else
rule_add_key ( & rule_tmp , TK_M_SUBSYSTEM , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " DRIVER " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid DRIVER operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_DRIVER , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " ATTR{ " ) ) {
2012-01-10 04:34:15 +04:00
attr = get_key_attribute ( rules - > udev , key + sizeof ( " ATTR " ) - 1 ) ;
if ( attr = = NULL ) {
2012-04-08 18:06:20 +04:00
log_error ( " error parsing ATTR attribute \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
if ( op < OP_MATCH_MAX ) {
rule_add_key ( & rule_tmp , TK_M_ATTR , op , value , attr ) ;
} else {
rule_add_key ( & rule_tmp , TK_A_ATTR , op , value , attr ) ;
}
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " KERNELS " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid KERNELS operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_KERNELS , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " SUBSYSTEMS " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid SUBSYSTEMS operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_SUBSYSTEMS , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " DRIVERS " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid DRIVERS operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_DRIVERS , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " ATTRS{ " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid ATTRS operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
attr = get_key_attribute ( rules - > udev , key + sizeof ( " ATTRS " ) - 1 ) ;
if ( attr = = NULL ) {
2012-04-08 18:06:20 +04:00
log_error ( " error parsing ATTRS attribute \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( attr , " device/ " ) )
2012-04-08 18:06:20 +04:00
log_error ( " the 'device' link may not be available in a future kernel, "
2012-01-10 04:34:15 +04:00
" please fix it in %s:%u " , filename , lineno ) ;
else if ( strstr ( attr , " ../ " ) ! = NULL )
2012-04-08 18:06:20 +04:00
log_error ( " do not reference parent sysfs directories directly, "
2012-01-10 04:34:15 +04:00
" it may break with a future kernel, please fix it in %s:%u " , filename , lineno ) ;
rule_add_key ( & rule_tmp , TK_M_ATTRS , op , value , attr ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " TAGS " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid TAGS operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_TAGS , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " ENV{ " ) ) {
2012-01-10 04:34:15 +04:00
attr = get_key_attribute ( rules - > udev , key + sizeof ( " ENV " ) - 1 ) ;
if ( attr = = NULL ) {
2012-04-08 18:06:20 +04:00
log_error ( " error parsing ENV attribute \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
if ( op < OP_MATCH_MAX ) {
if ( rule_add_key ( & rule_tmp , TK_M_ENV , op , value , attr ) ! = 0 )
goto invalid ;
} else {
static const char * blacklist [ ] = {
" ACTION " ,
" SUBSYSTEM " ,
" DEVTYPE " ,
" MAJOR " ,
" MINOR " ,
" DRIVER " ,
" IFINDEX " ,
" DEVNAME " ,
" DEVLINKS " ,
" DEVPATH " ,
" TAGS " ,
} ;
unsigned int i ;
2012-05-04 04:26:08 +04:00
for ( i = 0 ; i < ELEMENTSOF ( blacklist ) ; i + + ) {
if ( ! streq ( attr , blacklist [ i ] ) )
2012-01-10 04:34:15 +04:00
continue ;
2012-05-04 04:26:08 +04:00
log_error ( " invalid ENV attribute, '%s' can not be set %s:%u \n " , attr , filename , lineno ) ;
goto invalid ;
}
2012-01-10 04:34:15 +04:00
if ( rule_add_key ( & rule_tmp , TK_A_ENV , op , value , attr ) ! = 0 )
goto invalid ;
}
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " TAG " ) ) {
2012-01-10 04:34:15 +04:00
if ( op < OP_MATCH_MAX )
rule_add_key ( & rule_tmp , TK_M_TAG , op , value , NULL ) ;
else
rule_add_key ( & rule_tmp , TK_A_TAG , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " PROGRAM " ) ) {
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_M_PROGRAM , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " RESULT " ) ) {
2012-01-10 04:34:15 +04:00
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid RESULT operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
rule_add_key ( & rule_tmp , TK_M_RESULT , op , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " IMPORT " ) ) {
2012-01-10 04:34:15 +04:00
attr = get_key_attribute ( rules - > udev , key + sizeof ( " IMPORT " ) - 1 ) ;
if ( attr = = NULL ) {
2012-04-08 18:06:20 +04:00
log_error ( " IMPORT{} type missing, ignoring IMPORT %s:%u \n " , filename , lineno ) ;
2012-01-10 04:34:15 +04:00
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( attr , " program " ) ) {
2012-01-10 04:34:15 +04:00
/* find known built-in command */
if ( value [ 0 ] ! = ' / ' ) {
enum udev_builtin_cmd cmd ;
cmd = udev_builtin_lookup ( value ) ;
if ( cmd < UDEV_BUILTIN_MAX ) {
2012-04-08 18:06:20 +04:00
log_debug ( " IMPORT found builtin '%s', replacing %s:%u \n " ,
value , filename , lineno ) ;
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_M_IMPORT_BUILTIN , op , value , & cmd ) ;
continue ;
}
}
rule_add_key ( & rule_tmp , TK_M_IMPORT_PROG , op , value , NULL ) ;
2012-04-16 22:27:44 +04:00
} else if ( streq ( attr , " builtin " ) ) {
2012-01-10 04:34:15 +04:00
enum udev_builtin_cmd cmd = udev_builtin_lookup ( value ) ;
if ( cmd < UDEV_BUILTIN_MAX )
rule_add_key ( & rule_tmp , TK_M_IMPORT_BUILTIN , op , value , & cmd ) ;
else
2012-04-08 18:06:20 +04:00
log_error ( " IMPORT{builtin}: '%s' unknown %s:%u \n " , value , filename , lineno ) ;
2012-04-16 22:27:44 +04:00
} else if ( streq ( attr , " file " ) ) {
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_M_IMPORT_FILE , op , value , NULL ) ;
2012-04-16 22:27:44 +04:00
} else if ( streq ( attr , " db " ) ) {
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_M_IMPORT_DB , op , value , NULL ) ;
2012-04-16 22:27:44 +04:00
} else if ( streq ( attr , " cmdline " ) ) {
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_M_IMPORT_CMDLINE , op , value , NULL ) ;
2012-04-16 22:27:44 +04:00
} else if ( streq ( attr , " parent " ) ) {
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_M_IMPORT_PARENT , op , value , NULL ) ;
2012-04-09 18:37:54 +04:00
} else
log_error ( " IMPORT{} unknown type, ignoring IMPORT %s:%u \n " , filename , lineno ) ;
2012-01-10 04:34:15 +04:00
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " TEST " ) ) {
2012-01-10 04:34:15 +04:00
mode_t mode = 0 ;
if ( op > OP_MATCH_MAX ) {
2012-04-08 18:06:20 +04:00
log_error ( " invalid TEST operation \n " ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
attr = get_key_attribute ( rules - > udev , key + sizeof ( " TEST " ) - 1 ) ;
if ( attr ! = NULL ) {
mode = strtol ( attr , NULL , 8 ) ;
rule_add_key ( & rule_tmp , TK_M_TEST , op , value , & mode ) ;
} else {
rule_add_key ( & rule_tmp , TK_M_TEST , op , value , NULL ) ;
}
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " RUN " ) ) {
2012-04-09 18:37:54 +04:00
attr = get_key_attribute ( rules - > udev , key + sizeof ( " RUN " ) - 1 ) ;
if ( attr = = NULL )
attr = " program " ;
2012-04-16 22:27:44 +04:00
if ( streq ( attr , " builtin " ) ) {
2012-04-09 18:37:54 +04:00
enum udev_builtin_cmd cmd = udev_builtin_lookup ( value ) ;
if ( cmd < UDEV_BUILTIN_MAX )
rule_add_key ( & rule_tmp , TK_A_RUN_BUILTIN , op , value , & cmd ) ;
else
log_error ( " IMPORT{builtin}: '%s' unknown %s:%u \n " , value , filename , lineno ) ;
2012-04-16 22:27:44 +04:00
} else if ( streq ( attr , " program " ) ) {
2012-04-09 18:37:54 +04:00
enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX ;
rule_add_key ( & rule_tmp , TK_A_RUN_PROGRAM , op , value , & cmd ) ;
} else {
log_error ( " RUN{} unknown type, ignoring RUN %s:%u \n " , filename , lineno ) ;
}
2012-01-10 04:34:15 +04:00
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " WAIT_FOR " ) | | streq ( key , " WAIT_FOR_SYSFS " ) ) {
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_M_WAITFOR , 0 , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " LABEL " ) ) {
2012-01-10 04:34:15 +04:00
rule_tmp . rule . rule . label_off = add_string ( rules , value ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " GOTO " ) ) {
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_A_GOTO , 0 , value , NULL ) ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " NAME " ) ) {
2012-01-10 04:34:15 +04:00
if ( op < OP_MATCH_MAX ) {
rule_add_key ( & rule_tmp , TK_M_NAME , op , value , NULL ) ;
} else {
2012-04-16 22:27:44 +04:00
if ( streq ( value , " %k " ) ) {
2012-04-08 18:06:20 +04:00
log_error ( " NAME= \" %%k \" is ignored, because it breaks kernel supplied names, "
2012-01-10 04:34:15 +04:00
" please remove it from %s:%u \n " , filename , lineno ) ;
continue ;
}
if ( value [ 0 ] = = ' \0 ' ) {
2012-04-08 18:06:20 +04:00
log_debug ( " NAME= \" \" is ignored, because udev will not delete any device nodes, "
" please remove it from %s:%u \n " , filename , lineno ) ;
2012-01-10 04:34:15 +04:00
continue ;
}
rule_add_key ( & rule_tmp , TK_A_NAME , op , value , NULL ) ;
}
rule_tmp . rule . rule . can_set_name = true ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( startswith ( key , " SYMLINK " ) ) {
2012-01-10 04:34:15 +04:00
if ( op < OP_MATCH_MAX ) {
rule_add_key ( & rule_tmp , TK_M_DEVLINK , op , value , NULL ) ;
} else {
int flag = 0 ;
attr = get_key_attribute ( rules - > udev , key + sizeof ( " SYMLINK " ) - 1 ) ;
if ( attr ! = NULL & & strstr ( attr , " unique " ) ! = NULL )
flag = 1 ;
rule_add_key ( & rule_tmp , TK_A_DEVLINK , op , value , & flag ) ;
}
rule_tmp . rule . rule . can_set_name = true ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " OWNER " ) ) {
2012-01-10 04:34:15 +04:00
uid_t uid ;
char * endptr ;
uid = strtoul ( value , & endptr , 10 ) ;
if ( endptr [ 0 ] = = ' \0 ' ) {
rule_add_key ( & rule_tmp , TK_A_OWNER_ID , op , NULL , & uid ) ;
} else if ( ( rules - > resolve_names > 0 ) & & strchr ( " $% " , value [ 0 ] ) = = NULL ) {
uid = add_uid ( rules , value ) ;
rule_add_key ( & rule_tmp , TK_A_OWNER_ID , op , NULL , & uid ) ;
} else if ( rules - > resolve_names > = 0 ) {
rule_add_key ( & rule_tmp , TK_A_OWNER , op , value , NULL ) ;
}
rule_tmp . rule . rule . can_set_name = true ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " GROUP " ) ) {
2012-01-10 04:34:15 +04:00
gid_t gid ;
char * endptr ;
gid = strtoul ( value , & endptr , 10 ) ;
if ( endptr [ 0 ] = = ' \0 ' ) {
rule_add_key ( & rule_tmp , TK_A_GROUP_ID , op , NULL , & gid ) ;
} else if ( ( rules - > resolve_names > 0 ) & & strchr ( " $% " , value [ 0 ] ) = = NULL ) {
gid = add_gid ( rules , value ) ;
rule_add_key ( & rule_tmp , TK_A_GROUP_ID , op , NULL , & gid ) ;
} else if ( rules - > resolve_names > = 0 ) {
rule_add_key ( & rule_tmp , TK_A_GROUP , op , value , NULL ) ;
}
rule_tmp . rule . rule . can_set_name = true ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " MODE " ) ) {
2012-01-10 04:34:15 +04:00
mode_t mode ;
char * endptr ;
mode = strtol ( value , & endptr , 8 ) ;
if ( endptr [ 0 ] = = ' \0 ' )
rule_add_key ( & rule_tmp , TK_A_MODE_ID , op , NULL , & mode ) ;
else
rule_add_key ( & rule_tmp , TK_A_MODE , op , value , NULL ) ;
rule_tmp . rule . rule . can_set_name = true ;
continue ;
}
2012-04-16 22:27:44 +04:00
if ( streq ( key , " OPTIONS " ) ) {
2012-01-10 04:34:15 +04:00
const char * pos ;
pos = strstr ( value , " link_priority= " ) ;
if ( pos ! = NULL ) {
int prio = atoi ( & pos [ strlen ( " link_priority= " ) ] ) ;
rule_add_key ( & rule_tmp , TK_A_DEVLINK_PRIO , op , NULL , & prio ) ;
}
pos = strstr ( value , " event_timeout= " ) ;
if ( pos ! = NULL ) {
int tout = atoi ( & pos [ strlen ( " event_timeout= " ) ] ) ;
rule_add_key ( & rule_tmp , TK_M_EVENT_TIMEOUT , op , NULL , & tout ) ;
}
pos = strstr ( value , " string_escape= " ) ;
if ( pos ! = NULL ) {
pos = & pos [ strlen ( " string_escape= " ) ] ;
2012-04-16 22:27:44 +04:00
if ( startswith ( pos , " none " ) )
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_A_STRING_ESCAPE_NONE , op , NULL , NULL ) ;
2012-04-16 22:27:44 +04:00
else if ( startswith ( pos , " replace " ) )
2012-01-10 04:34:15 +04:00
rule_add_key ( & rule_tmp , TK_A_STRING_ESCAPE_REPLACE , op , NULL , NULL ) ;
}
pos = strstr ( value , " db_persist " ) ;
if ( pos ! = NULL )
rule_add_key ( & rule_tmp , TK_A_DB_PERSIST , op , NULL , NULL ) ;
pos = strstr ( value , " nowatch " ) ;
if ( pos ! = NULL ) {
const int off = 0 ;
rule_add_key ( & rule_tmp , TK_A_INOTIFY_WATCH , op , NULL , & off ) ;
} else {
pos = strstr ( value , " watch " ) ;
if ( pos ! = NULL ) {
const int on = 1 ;
rule_add_key ( & rule_tmp , TK_A_INOTIFY_WATCH , op , NULL , & on ) ;
}
}
pos = strstr ( value , " static_node= " ) ;
if ( pos ! = NULL ) {
rule_add_key ( & rule_tmp , TK_A_STATIC_NODE , op , & pos [ strlen ( " static_node= " ) ] , NULL ) ;
rule_tmp . rule . rule . has_static_node = true ;
}
continue ;
}
2012-04-08 18:06:20 +04:00
log_error ( " unknown key '%s' in %s:%u \n " , key , filename , lineno ) ;
2012-01-10 04:34:15 +04:00
goto invalid ;
}
/* add rule token */
rule_tmp . rule . rule . token_count = 1 + rule_tmp . token_cur ;
if ( add_token ( rules , & rule_tmp . rule ) ! = 0 )
goto invalid ;
/* add tokens to list, sorted by type */
if ( sort_token ( rules , & rule_tmp ) ! = 0 )
goto invalid ;
return 0 ;
2008-10-18 16:33:37 +04:00
invalid :
2012-04-08 18:06:20 +04:00
log_error ( " invalid rule '%s:%u' \n " , filename , lineno ) ;
2012-01-10 04:34:15 +04:00
return - 1 ;
2008-10-18 16:33:37 +04:00
}
2012-05-07 15:21:05 +04:00
static int parse_file ( struct udev_rules * rules , const char * filename )
2008-10-18 16:33:37 +04:00
{
2012-01-10 04:34:15 +04:00
FILE * f ;
unsigned int first_token ;
2012-05-07 15:21:05 +04:00
unsigned int filename_off ;
2012-01-10 04:34:15 +04:00
char line [ UTIL_LINE_SIZE ] ;
int line_nr = 0 ;
unsigned int i ;
2012-05-07 15:21:05 +04:00
if ( null_or_empty_path ( filename ) ) {
log_debug ( " skip empty file: %s \n " , filename ) ;
return 0 ;
}
log_debug ( " read rules file: %s \n " , filename ) ;
2012-01-10 04:34:15 +04:00
2012-07-05 19:33:24 +04:00
f = fopen ( filename , " re " ) ;
2012-01-10 04:34:15 +04:00
if ( f = = NULL )
return - 1 ;
first_token = rules - > token_cur ;
2012-05-07 15:21:05 +04:00
filename_off = add_string ( rules , filename ) ;
2012-01-10 04:34:15 +04:00
while ( fgets ( line , sizeof ( line ) , f ) ! = NULL ) {
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 ;
if ( strlen ( & line [ len - 2 ] ) < 2 )
break ;
line_nr + + ;
len = strlen ( line ) ;
}
if ( len + 1 > = sizeof ( line ) ) {
2012-04-08 18:06:20 +04:00
log_error ( " line too long '%s':%u, ignored \n " , filename , line_nr ) ;
2012-01-10 04:34:15 +04:00
continue ;
}
add_rule ( rules , key , filename , filename_off , line_nr ) ;
}
fclose ( f ) ;
/* link GOTOs to LABEL rules in this file to be able to fast-forward */
for ( i = first_token + 1 ; i < rules - > token_cur ; i + + ) {
if ( rules - > tokens [ i ] . type = = TK_A_GOTO ) {
char * label = & rules - > buf [ rules - > tokens [ i ] . key . value_off ] ;
unsigned int j ;
for ( j = i + 1 ; j < rules - > token_cur ; j + + ) {
if ( rules - > tokens [ j ] . type ! = TK_RULE )
continue ;
if ( rules - > tokens [ j ] . rule . label_off = = 0 )
continue ;
2012-04-16 22:27:44 +04:00
if ( ! streq ( label , & rules - > buf [ rules - > tokens [ j ] . rule . label_off ] ) )
2012-01-10 04:34:15 +04:00
continue ;
rules - > tokens [ i ] . key . rule_goto = j ;
break ;
}
if ( rules - > tokens [ i ] . key . rule_goto = = 0 )
2012-04-08 18:06:20 +04:00
log_error ( " GOTO '%s' has no matching label in: '%s' \n " , label , filename ) ;
2012-01-10 04:34:15 +04:00
}
}
return 0 ;
2008-10-18 16:33:37 +04:00
}
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
{
2012-01-10 04:34:15 +04:00
struct udev_rules * rules ;
struct udev_list file_list ;
struct token end_token ;
2012-05-07 15:21:05 +04:00
char * * files , * * f ;
int r ;
2012-01-10 04:34:15 +04:00
rules = calloc ( 1 , sizeof ( struct udev_rules ) ) ;
if ( rules = = NULL )
return NULL ;
rules - > udev = udev ;
rules - > resolve_names = resolve_names ;
udev_list_init ( udev , & file_list , true ) ;
/* init token array and string buffer */
rules - > tokens = malloc ( PREALLOC_TOKEN * sizeof ( struct token ) ) ;
if ( rules - > tokens = = NULL ) {
free ( rules ) ;
return NULL ;
}
rules - > token_max = PREALLOC_TOKEN ;
rules - > buf = malloc ( PREALLOC_STRBUF ) ;
if ( rules - > buf = = NULL ) {
free ( rules - > tokens ) ;
free ( rules ) ;
return NULL ;
}
rules - > buf_max = PREALLOC_STRBUF ;
/* offset 0 is always '\0' */
rules - > buf [ 0 ] = ' \0 ' ;
rules - > buf_cur = 1 ;
rules - > trie_nodes = malloc ( PREALLOC_TRIE * sizeof ( struct trie_node ) ) ;
if ( rules - > trie_nodes = = NULL ) {
free ( rules - > buf ) ;
free ( rules - > tokens ) ;
free ( rules ) ;
return NULL ;
}
rules - > trie_nodes_max = PREALLOC_TRIE ;
/* offset 0 is the trie root, with an empty string */
memset ( rules - > trie_nodes , 0x00 , sizeof ( struct trie_node ) ) ;
rules - > trie_nodes_cur = 1 ;
2012-07-16 00:10:46 +04:00
rules - > dirs = strv_new ( SYSCONFDIR " /udev/rules.d " ,
" /run/udev/rules.d " ,
UDEVLIBEXECDIR " /rules.d " ,
2012-05-07 15:21:05 +04:00
NULL ) ;
if ( ! rules - > dirs ) {
log_error ( " failed to build config directory array " ) ;
return NULL ;
}
2012-05-07 23:36:12 +04:00
if ( ! path_strv_canonicalize ( rules - > dirs ) ) {
2012-05-07 15:21:05 +04:00
log_error ( " failed to canonicalize config directories \n " ) ;
return NULL ;
}
strv_uniq ( rules - > dirs ) ;
2012-08-08 16:45:16 +04:00
rules - > dirs_ts_usec = calloc ( strv_length ( rules - > dirs ) , sizeof ( long long ) ) ;
udev_rules_check_timestamp ( rules ) ;
2012-05-07 15:21:05 +04:00
r = conf_files_list_strv ( & files , " .rules " , ( const char * * ) rules - > dirs ) ;
if ( r < 0 ) {
log_error ( " failed to enumerate rules files: %s \n " , strerror ( - r ) ) ;
return NULL ;
2012-01-10 04:34:15 +04:00
}
2012-05-07 15:21:05 +04:00
/*
* The offset value in the rules strct is limited ; add all
* rules file names to the beginning of the string buffer .
*/
STRV_FOREACH ( f , files )
add_string ( rules , * f ) ;
2012-01-10 04:34:15 +04:00
2012-05-07 15:21:05 +04:00
STRV_FOREACH ( f , files )
parse_file ( rules , * f ) ;
strv_free ( files ) ;
2012-01-10 04:34:15 +04:00
memset ( & end_token , 0x00 , sizeof ( struct token ) ) ;
end_token . type = TK_END ;
add_token ( rules , & end_token ) ;
/* shrink allocated token and string buffer */
if ( rules - > token_cur < rules - > token_max ) {
struct token * tokens ;
tokens = realloc ( rules - > tokens , rules - > token_cur * sizeof ( struct token ) ) ;
if ( tokens ! = NULL | | rules - > token_cur = = 0 ) {
rules - > tokens = tokens ;
rules - > token_max = rules - > token_cur ;
}
}
if ( rules - > buf_cur < rules - > buf_max ) {
char * buf ;
buf = realloc ( rules - > buf , rules - > buf_cur ) ;
if ( buf ! = NULL | | rules - > buf_cur = = 0 ) {
rules - > buf = buf ;
rules - > buf_max = rules - > buf_cur ;
}
}
2012-04-08 18:06:20 +04:00
log_debug ( " rules use %zu bytes tokens (%u * %zu bytes), %zu bytes buffer \n " ,
rules - > token_max * sizeof ( struct token ) , rules - > token_max , sizeof ( struct token ) , rules - > buf_max ) ;
log_debug ( " temporary index used %zu bytes (%u * %zu bytes) \n " ,
rules - > trie_nodes_cur * sizeof ( struct trie_node ) ,
rules - > trie_nodes_cur , sizeof ( struct trie_node ) ) ;
2012-01-10 04:34:15 +04:00
/* cleanup trie */
free ( rules - > trie_nodes ) ;
rules - > trie_nodes = NULL ;
rules - > trie_nodes_cur = 0 ;
rules - > trie_nodes_max = 0 ;
/* cleanup uid/gid cache */
free ( rules - > uids ) ;
rules - > uids = NULL ;
rules - > uids_cur = 0 ;
rules - > uids_max = 0 ;
free ( rules - > gids ) ;
rules - > gids = NULL ;
rules - > gids_cur = 0 ;
rules - > gids_max = 0 ;
dump_rules ( rules ) ;
return rules ;
2008-10-18 16:33:37 +04:00
}
2011-12-25 19:37:15 +04:00
struct udev_rules * udev_rules_unref ( struct udev_rules * rules )
2008-10-18 16:33:37 +04:00
{
2012-01-10 04:34:15 +04:00
if ( rules = = NULL )
return NULL ;
free ( rules - > tokens ) ;
free ( rules - > buf ) ;
free ( rules - > trie_nodes ) ;
free ( rules - > uids ) ;
free ( rules - > gids ) ;
2012-05-07 15:21:05 +04:00
strv_free ( rules - > dirs ) ;
free ( rules - > dirs_ts_usec ) ;
2012-01-10 04:34:15 +04:00
free ( rules ) ;
return NULL ;
2008-10-18 16:33:37 +04:00
}
2008-10-23 02:13:59 +04:00
2012-04-16 19:21:22 +04:00
bool udev_rules_check_timestamp ( struct udev_rules * rules )
{
unsigned int i ;
bool changed = false ;
2012-05-31 03:15:21 +04:00
if ( rules = = NULL )
goto out ;
2012-05-07 15:21:05 +04:00
for ( i = 0 ; rules - > dirs [ i ] ; i + + ) {
2012-04-16 19:21:22 +04:00
struct stat stats ;
2012-05-07 15:21:05 +04:00
if ( stat ( rules - > dirs [ i ] , & stats ) < 0 )
2012-04-16 19:21:22 +04:00
continue ;
if ( rules - > dirs_ts_usec [ i ] = = ts_usec ( & stats . st_mtim ) )
continue ;
/* first check */
if ( rules - > dirs_ts_usec [ i ] ! = 0 ) {
2012-05-07 15:21:05 +04:00
log_debug ( " reload - timestamp of '%s' changed \n " , rules - > dirs [ i ] ) ;
2012-04-16 19:21:22 +04:00
changed = true ;
}
/* update timestamp */
rules - > dirs_ts_usec [ i ] = ts_usec ( & stats . st_mtim ) ;
}
2012-05-31 03:15:21 +04:00
out :
2012-04-16 19:21:22 +04:00
return changed ;
}
2008-10-23 02:13:59 +04:00
static int match_key ( struct udev_rules * rules , struct token * token , const char * val )
{
2012-01-10 04:34:15 +04:00
char * key_value = & rules - > buf [ token - > key . value_off ] ;
char * pos ;
bool match = false ;
if ( val = = NULL )
val = " " ;
switch ( token - > key . glob ) {
case GL_PLAIN :
2012-04-16 22:27:44 +04:00
match = ( streq ( key_value , val ) ) ;
2012-01-10 04:34:15 +04:00
break ;
case GL_GLOB :
match = ( fnmatch ( key_value , val , 0 ) = = 0 ) ;
break ;
case GL_SPLIT :
{
2012-04-16 22:27:44 +04:00
const char * s ;
2012-01-10 04:34:15 +04:00
size_t len ;
2012-04-16 22:27:44 +04:00
s = & rules - > buf [ token - > key . value_off ] ;
2012-01-10 04:34:15 +04:00
len = strlen ( val ) ;
for ( ; ; ) {
const char * next ;
2012-04-16 22:27:44 +04:00
next = strchr ( s , ' | ' ) ;
2012-01-10 04:34:15 +04:00
if ( next ! = NULL ) {
2012-04-16 22:27:44 +04:00
size_t matchlen = ( size_t ) ( next - s ) ;
2012-01-10 04:34:15 +04:00
2012-04-16 22:27:44 +04:00
match = ( matchlen = = len & & strncmp ( s , val , matchlen ) = = 0 ) ;
2012-01-10 04:34:15 +04:00
if ( match )
break ;
} else {
2012-04-16 22:27:44 +04:00
match = ( streq ( s , val ) ) ;
2012-01-10 04:34:15 +04:00
break ;
}
2012-04-16 22:27:44 +04:00
s = & next [ 1 ] ;
2012-01-10 04:34:15 +04:00
}
break ;
}
case GL_SPLIT_GLOB :
{
char value [ UTIL_PATH_SIZE ] ;
util_strscpy ( value , sizeof ( value ) , & rules - > buf [ token - > key . value_off ] ) ;
key_value = value ;
while ( key_value ! = NULL ) {
pos = strchr ( key_value , ' | ' ) ;
if ( pos ! = NULL ) {
pos [ 0 ] = ' \0 ' ;
pos = & pos [ 1 ] ;
}
match = ( fnmatch ( key_value , val , 0 ) = = 0 ) ;
if ( match )
break ;
key_value = pos ;
}
break ;
}
case GL_SOMETHING :
match = ( val [ 0 ] ! = ' \0 ' ) ;
break ;
case GL_UNSET :
return - 1 ;
}
2012-04-08 18:06:20 +04:00
if ( match & & ( token - > key . op = = OP_MATCH ) )
2012-01-10 04:34:15 +04:00
return 0 ;
2012-04-08 18:06:20 +04:00
if ( ! match & & ( token - > key . op = = OP_NOMATCH ) )
2012-01-10 04:34:15 +04:00
return 0 ;
return - 1 ;
2008-10-23 02:13:59 +04:00
}
static int match_attr ( struct udev_rules * rules , struct udev_device * dev , struct udev_event * event , struct token * cur )
{
2012-01-10 04:34:15 +04:00
const char * name ;
char nbuf [ UTIL_NAME_SIZE ] ;
const char * value ;
char vbuf [ UTIL_NAME_SIZE ] ;
size_t len ;
name = & rules - > buf [ cur - > key . attr_off ] ;
switch ( cur - > key . attrsubst ) {
case SB_FORMAT :
udev_event_apply_format ( event , name , nbuf , sizeof ( nbuf ) ) ;
name = nbuf ;
/* fall through */
case SB_NONE :
value = udev_device_get_sysattr_value ( dev , name ) ;
if ( value = = NULL )
return - 1 ;
break ;
case SB_SUBSYS :
if ( util_resolve_subsys_kernel ( event - > udev , name , vbuf , sizeof ( vbuf ) , 1 ) ! = 0 )
return - 1 ;
value = vbuf ;
break ;
default :
return - 1 ;
}
/* remove trailing whitespace, if not asked to match for it */
len = strlen ( value ) ;
if ( len > 0 & & isspace ( value [ len - 1 ] ) ) {
const char * key_value ;
size_t klen ;
key_value = & rules - > buf [ cur - > key . value_off ] ;
klen = strlen ( key_value ) ;
if ( klen > 0 & & ! isspace ( key_value [ klen - 1 ] ) ) {
if ( value ! = vbuf ) {
util_strscpy ( vbuf , sizeof ( vbuf ) , value ) ;
value = vbuf ;
}
while ( len > 0 & & isspace ( vbuf [ - - len ] ) )
vbuf [ len ] = ' \0 ' ;
}
}
return match_key ( rules , cur , value ) ;
2008-10-23 02:13:59 +04:00
}
enum escape_type {
2012-01-10 04:34:15 +04:00
ESCAPE_UNSET ,
ESCAPE_NONE ,
ESCAPE_REPLACE ,
2008-10-23 02:13:59 +04:00
} ;
2011-04-20 03:53:03 +04:00
int udev_rules_apply_to_event ( struct udev_rules * rules , struct udev_event * event , const sigset_t * sigmask )
2008-10-23 02:13:59 +04:00
{
2012-01-10 04:34:15 +04:00
struct token * cur ;
struct token * rule ;
enum escape_type esc = ESCAPE_UNSET ;
bool can_set_name ;
if ( rules - > tokens = = NULL )
return - 1 ;
2012-04-16 22:27:44 +04:00
can_set_name = ( ( ! streq ( udev_device_get_action ( event - > dev ) , " remove " ) ) & &
2012-01-10 04:34:15 +04:00
( major ( udev_device_get_devnum ( event - > dev ) ) > 0 | |
udev_device_get_ifindex ( event - > dev ) > 0 ) ) ;
/* loop through token list, match, run actions or forward to next rule */
cur = & rules - > tokens [ 0 ] ;
rule = cur ;
for ( ; ; ) {
dump_token ( rules , cur ) ;
switch ( cur - > type ) {
case TK_RULE :
/* current rule */
rule = cur ;
/* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */
if ( ! can_set_name & & rule - > rule . can_set_name )
goto nomatch ;
esc = ESCAPE_UNSET ;
break ;
case TK_M_ACTION :
if ( match_key ( rules , cur , udev_device_get_action ( event - > dev ) ) ! = 0 )
goto nomatch ;
break ;
case TK_M_DEVPATH :
if ( match_key ( rules , cur , udev_device_get_devpath ( event - > dev ) ) ! = 0 )
goto nomatch ;
break ;
case TK_M_KERNEL :
if ( match_key ( rules , cur , udev_device_get_sysname ( event - > dev ) ) ! = 0 )
goto nomatch ;
break ;
case TK_M_DEVLINK : {
struct udev_list_entry * list_entry ;
bool match = false ;
udev_list_entry_foreach ( list_entry , udev_device_get_devlinks_list_entry ( event - > dev ) ) {
const char * devlink ;
2012-07-16 00:10:46 +04:00
devlink = udev_list_entry_get_name ( list_entry ) + strlen ( " /dev/ " ) ;
2012-01-10 04:34:15 +04:00
if ( match_key ( rules , cur , devlink ) = = 0 ) {
match = true ;
break ;
}
}
if ( ! match )
goto nomatch ;
break ;
}
case TK_M_NAME :
if ( match_key ( rules , cur , event - > name ) ! = 0 )
goto nomatch ;
break ;
case TK_M_ENV : {
const char * key_name = & rules - > buf [ cur - > key . attr_off ] ;
const char * value ;
value = udev_device_get_property_value ( event - > dev , key_name ) ;
2012-04-08 18:06:20 +04:00
if ( value = = NULL )
2012-01-10 04:34:15 +04:00
value = " " ;
if ( match_key ( rules , cur , value ) )
goto nomatch ;
break ;
}
case TK_M_TAG : {
struct udev_list_entry * list_entry ;
bool match = false ;
udev_list_entry_foreach ( list_entry , udev_device_get_tags_list_entry ( event - > dev ) ) {
2012-04-16 22:27:44 +04:00
if ( streq ( & rules - > buf [ cur - > key . value_off ] , udev_list_entry_get_name ( list_entry ) ) ) {
2012-01-10 04:34:15 +04:00
match = true ;
break ;
}
}
if ( ! match & & ( cur - > key . op ! = OP_NOMATCH ) )
goto nomatch ;
break ;
}
case TK_M_SUBSYSTEM :
if ( match_key ( rules , cur , udev_device_get_subsystem ( event - > dev ) ) ! = 0 )
goto nomatch ;
break ;
case TK_M_DRIVER :
if ( match_key ( rules , cur , udev_device_get_driver ( event - > dev ) ) ! = 0 )
goto nomatch ;
break ;
case TK_M_WAITFOR : {
char filename [ UTIL_PATH_SIZE ] ;
int found ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , filename , sizeof ( filename ) ) ;
found = ( wait_for_file ( event - > dev , filename , 10 ) = = 0 ) ;
if ( ! found & & ( cur - > key . op ! = OP_NOMATCH ) )
goto nomatch ;
break ;
}
case TK_M_ATTR :
if ( match_attr ( rules , event - > dev , event , cur ) ! = 0 )
goto nomatch ;
break ;
case TK_M_KERNELS :
case TK_M_SUBSYSTEMS :
case TK_M_DRIVERS :
case TK_M_ATTRS :
case TK_M_TAGS : {
struct token * next ;
/* get whole sequence of parent matches */
next = cur ;
while ( next - > type > TK_M_PARENTS_MIN & & next - > type < TK_M_PARENTS_MAX )
next + + ;
/* loop over parents */
event - > dev_parent = event - > dev ;
for ( ; ; ) {
struct token * key ;
/* loop over sequence of parent match keys */
for ( key = cur ; key < next ; key + + ) {
dump_token ( rules , key ) ;
switch ( key - > type ) {
case TK_M_KERNELS :
if ( match_key ( rules , key , udev_device_get_sysname ( event - > dev_parent ) ) ! = 0 )
goto try_parent ;
break ;
case TK_M_SUBSYSTEMS :
if ( match_key ( rules , key , udev_device_get_subsystem ( event - > dev_parent ) ) ! = 0 )
goto try_parent ;
break ;
case TK_M_DRIVERS :
if ( match_key ( rules , key , udev_device_get_driver ( event - > dev_parent ) ) ! = 0 )
goto try_parent ;
break ;
case TK_M_ATTRS :
if ( match_attr ( rules , event - > dev_parent , event , key ) ! = 0 )
goto try_parent ;
break ;
case TK_M_TAGS : {
bool match = udev_device_has_tag ( event - > dev_parent , & rules - > buf [ cur - > key . value_off ] ) ;
if ( match & & key - > key . op = = OP_NOMATCH )
goto try_parent ;
if ( ! match & & key - > key . op = = OP_MATCH )
goto try_parent ;
break ;
}
default :
goto nomatch ;
}
}
break ;
try_parent :
event - > dev_parent = udev_device_get_parent ( event - > dev_parent ) ;
if ( event - > dev_parent = = NULL )
goto nomatch ;
}
/* move behind our sequence of parent match keys */
cur = next ;
continue ;
}
case TK_M_TEST : {
char filename [ UTIL_PATH_SIZE ] ;
struct stat statbuf ;
int match ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , filename , sizeof ( filename ) ) ;
if ( util_resolve_subsys_kernel ( event - > udev , filename , filename , sizeof ( filename ) , 0 ) ! = 0 ) {
if ( filename [ 0 ] ! = ' / ' ) {
char tmp [ UTIL_PATH_SIZE ] ;
util_strscpy ( tmp , sizeof ( tmp ) , filename ) ;
util_strscpyl ( filename , sizeof ( filename ) ,
udev_device_get_syspath ( event - > dev ) , " / " , tmp , NULL ) ;
}
}
attr_subst_subdir ( filename , sizeof ( filename ) ) ;
match = ( stat ( filename , & statbuf ) = = 0 ) ;
2012-04-08 18:06:20 +04:00
if ( match & & cur - > key . mode > 0 )
2012-01-10 04:34:15 +04:00
match = ( ( statbuf . st_mode & cur - > key . mode ) > 0 ) ;
if ( match & & cur - > key . op = = OP_NOMATCH )
goto nomatch ;
if ( ! match & & cur - > key . op = = OP_MATCH )
goto nomatch ;
break ;
}
case TK_M_EVENT_TIMEOUT :
2012-04-08 18:06:20 +04:00
log_debug ( " OPTIONS event_timeout=%u \n " , cur - > key . event_timeout ) ;
2012-01-10 04:34:15 +04:00
event - > timeout_usec = cur - > key . event_timeout * 1000 * 1000 ;
break ;
case TK_M_PROGRAM : {
char program [ UTIL_PATH_SIZE ] ;
char * * envp ;
char result [ UTIL_PATH_SIZE ] ;
free ( event - > program_result ) ;
event - > program_result = NULL ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , program , sizeof ( program ) ) ;
envp = udev_device_get_properties_envp ( event - > dev ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " PROGRAM '%s' %s:%u \n " ,
program ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
if ( udev_event_spawn ( event , program , envp , sigmask , result , sizeof ( result ) ) < 0 ) {
if ( cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
} else {
int count ;
util_remove_trailing_chars ( result , ' \n ' ) ;
if ( esc = = ESCAPE_UNSET | | esc = = ESCAPE_REPLACE ) {
count = util_replace_chars ( result , UDEV_ALLOWED_CHARS_INPUT ) ;
if ( count > 0 )
2012-04-08 18:06:20 +04:00
log_debug ( " %i character(s) replaced \n " , count ) ;
2012-01-10 04:34:15 +04:00
}
event - > program_result = strdup ( result ) ;
if ( cur - > key . op = = OP_NOMATCH )
goto nomatch ;
}
break ;
}
case TK_M_IMPORT_FILE : {
char import [ UTIL_PATH_SIZE ] ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , import , sizeof ( import ) ) ;
if ( import_file_into_properties ( event - > dev , import ) ! = 0 )
if ( cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
break ;
}
case TK_M_IMPORT_PROG : {
char import [ UTIL_PATH_SIZE ] ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , import , sizeof ( import ) ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " IMPORT '%s' %s:%u \n " ,
import ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
if ( import_program_into_properties ( event , import , sigmask ) ! = 0 )
if ( cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
break ;
}
case TK_M_IMPORT_BUILTIN : {
char command [ UTIL_PATH_SIZE ] ;
if ( udev_builtin_run_once ( cur - > key . builtin_cmd ) ) {
/* check if we ran already */
if ( event - > builtin_run & ( 1 < < cur - > key . builtin_cmd ) ) {
2012-04-08 18:06:20 +04:00
log_debug ( " IMPORT builtin skip '%s' %s:%u \n " ,
udev_builtin_name ( cur - > key . builtin_cmd ) ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
/* return the result from earlier run */
if ( event - > builtin_ret & ( 1 < < cur - > key . builtin_cmd ) )
if ( cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
break ;
}
/* mark as ran */
event - > builtin_run | = ( 1 < < cur - > key . builtin_cmd ) ;
}
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , command , sizeof ( command ) ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " IMPORT builtin '%s' %s:%u \n " ,
udev_builtin_name ( cur - > key . builtin_cmd ) ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
if ( udev_builtin_run ( event - > dev , cur - > key . builtin_cmd , command , false ) ! = 0 ) {
/* remember failure */
2012-04-08 18:06:20 +04:00
log_debug ( " IMPORT builtin '%s' returned non-zero \n " ,
udev_builtin_name ( cur - > key . builtin_cmd ) ) ;
2012-01-10 04:34:15 +04:00
event - > builtin_ret | = ( 1 < < cur - > key . builtin_cmd ) ;
if ( cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
}
break ;
}
case TK_M_IMPORT_DB : {
const char * key = & rules - > buf [ cur - > key . value_off ] ;
const char * value ;
value = udev_device_get_property_value ( event - > dev_db , key ) ;
if ( value ! = NULL ) {
struct udev_list_entry * entry ;
entry = udev_device_add_property ( event - > dev , key , value ) ;
udev_list_entry_set_num ( entry , true ) ;
} else {
if ( cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
}
break ;
}
case TK_M_IMPORT_CMDLINE : {
FILE * f ;
bool imported = false ;
2012-07-05 19:33:24 +04:00
f = fopen ( " /proc/cmdline " , " re " ) ;
2012-01-10 04:34:15 +04:00
if ( f ! = NULL ) {
char cmdline [ 4096 ] ;
if ( fgets ( cmdline , sizeof ( cmdline ) , f ) ! = NULL ) {
const char * key = & rules - > buf [ cur - > key . value_off ] ;
char * pos ;
pos = strstr ( cmdline , key ) ;
if ( pos ! = NULL ) {
struct udev_list_entry * entry ;
pos + = strlen ( key ) ;
if ( pos [ 0 ] = = ' \0 ' | | isspace ( pos [ 0 ] ) ) {
/* we import simple flags as 'FLAG=1' */
entry = udev_device_add_property ( event - > dev , key , " 1 " ) ;
udev_list_entry_set_num ( entry , true ) ;
imported = true ;
} else if ( pos [ 0 ] = = ' = ' ) {
const char * value ;
pos + + ;
value = pos ;
while ( pos [ 0 ] ! = ' \0 ' & & ! isspace ( pos [ 0 ] ) )
pos + + ;
pos [ 0 ] = ' \0 ' ;
entry = udev_device_add_property ( event - > dev , key , value ) ;
udev_list_entry_set_num ( entry , true ) ;
imported = true ;
}
}
}
fclose ( f ) ;
}
if ( ! imported & & cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
break ;
}
case TK_M_IMPORT_PARENT : {
char import [ UTIL_PATH_SIZE ] ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , import , sizeof ( import ) ) ;
if ( import_parent_into_properties ( event - > dev , import ) ! = 0 )
if ( cur - > key . op ! = OP_NOMATCH )
goto nomatch ;
break ;
}
case TK_M_RESULT :
if ( match_key ( rules , cur , event - > program_result ) ! = 0 )
goto nomatch ;
break ;
case TK_A_STRING_ESCAPE_NONE :
esc = ESCAPE_NONE ;
break ;
case TK_A_STRING_ESCAPE_REPLACE :
esc = ESCAPE_REPLACE ;
break ;
case TK_A_DB_PERSIST :
udev_device_set_db_persist ( event - > dev ) ;
break ;
case TK_A_INOTIFY_WATCH :
if ( event - > inotify_watch_final )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > inotify_watch_final = true ;
event - > inotify_watch = cur - > key . watch ;
break ;
case TK_A_DEVLINK_PRIO :
udev_device_set_devlink_priority ( event - > dev , cur - > key . devlink_prio ) ;
break ;
case TK_A_OWNER : {
char owner [ UTIL_NAME_SIZE ] ;
if ( event - > owner_final )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > owner_final = true ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , owner , sizeof ( owner ) ) ;
event - > uid = util_lookup_user ( event - > udev , owner ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " OWNER %u %s:%u \n " ,
event - > uid ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
break ;
}
case TK_A_GROUP : {
char group [ UTIL_NAME_SIZE ] ;
if ( event - > group_final )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > group_final = true ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , group , sizeof ( group ) ) ;
event - > gid = util_lookup_group ( event - > udev , group ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " GROUP %u %s:%u \n " ,
event - > gid ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
break ;
}
case TK_A_MODE : {
char mode_str [ UTIL_NAME_SIZE ] ;
mode_t mode ;
char * endptr ;
if ( event - > mode_final )
break ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , mode_str , sizeof ( mode_str ) ) ;
mode = strtol ( mode_str , & endptr , 8 ) ;
if ( endptr [ 0 ] ! = ' \0 ' ) {
2012-04-08 18:06:20 +04:00
log_error ( " ignoring invalid mode '%s' \n " , mode_str ) ;
2012-01-10 04:34:15 +04:00
break ;
}
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > mode_final = true ;
event - > mode_set = true ;
event - > mode = mode ;
2012-04-08 18:06:20 +04:00
log_debug ( " MODE %#o %s:%u \n " ,
event - > mode ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
break ;
}
case TK_A_OWNER_ID :
if ( event - > owner_final )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > owner_final = true ;
event - > uid = cur - > key . uid ;
2012-04-08 18:06:20 +04:00
log_debug ( " OWNER %u %s:%u \n " ,
event - > uid ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_GROUP_ID :
if ( event - > group_final )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > group_final = true ;
event - > gid = cur - > key . gid ;
2012-04-08 18:06:20 +04:00
log_debug ( " GROUP %u %s:%u \n " ,
event - > gid ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_MODE_ID :
if ( event - > mode_final )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > mode_final = true ;
event - > mode_set = true ;
event - > mode = cur - > key . mode ;
2012-04-08 18:06:20 +04:00
log_debug ( " MODE %#o %s:%u \n " ,
event - > mode ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
break ;
case TK_A_ENV : {
const char * name = & rules - > buf [ cur - > key . attr_off ] ;
char * value = & rules - > buf [ cur - > key . value_off ] ;
if ( value [ 0 ] ! = ' \0 ' ) {
char temp_value [ UTIL_NAME_SIZE ] ;
struct udev_list_entry * entry ;
udev_event_apply_format ( event , value , temp_value , sizeof ( temp_value ) ) ;
entry = udev_device_add_property ( event - > dev , name , temp_value ) ;
/* store in db, skip private keys */
if ( name [ 0 ] ! = ' . ' )
udev_list_entry_set_num ( entry , true ) ;
} else {
udev_device_add_property ( event - > dev , name , NULL ) ;
}
break ;
}
case TK_A_TAG : {
char tag [ UTIL_PATH_SIZE ] ;
const char * p ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , tag , sizeof ( tag ) ) ;
if ( cur - > key . op = = OP_ASSIGN | | cur - > key . op = = OP_ASSIGN_FINAL )
udev_device_cleanup_tags_list ( event - > dev ) ;
for ( p = tag ; * p ! = ' \0 ' ; p + + ) {
if ( ( * p > = ' a ' & & * p < = ' z ' ) | |
( * p > = ' A ' & & * p < = ' Z ' ) | |
( * p > = ' 0 ' & & * p < = ' 9 ' ) | |
* p = = ' - ' | | * p = = ' _ ' )
continue ;
2012-04-08 18:06:20 +04:00
log_error ( " ignoring invalid tag name '%s' \n " , tag ) ;
2012-01-10 04:34:15 +04:00
break ;
}
udev_device_add_tag ( event - > dev , tag ) ;
break ;
}
case TK_A_NAME : {
const char * name = & rules - > buf [ cur - > key . value_off ] ;
2012-01-23 08:00:59 +04:00
2012-01-10 04:34:15 +04:00
char name_str [ UTIL_PATH_SIZE ] ;
int count ;
if ( event - > name_final )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > name_final = true ;
udev_event_apply_format ( event , name , name_str , sizeof ( name_str ) ) ;
if ( esc = = ESCAPE_UNSET | | esc = = ESCAPE_REPLACE ) {
count = util_replace_chars ( name_str , " / " ) ;
if ( count > 0 )
2012-04-08 18:06:20 +04:00
log_debug ( " %i character(s) replaced \n " , count ) ;
2012-01-10 04:34:15 +04:00
}
2012-04-16 19:21:22 +04:00
if ( major ( udev_device_get_devnum ( event - > dev ) ) & &
2012-07-16 00:10:46 +04:00
( ! streq ( name_str , udev_device_get_devnode ( event - > dev ) + strlen ( " /dev/ " ) ) ) ) {
2012-04-16 19:21:22 +04:00
log_error ( " NAME= \" %s \" ignored, kernel device nodes "
" can not be renamed; please fix it in %s:%u \n " , name ,
& rules - > buf [ rule - > rule . filename_off ] , rule - > rule . filename_line ) ;
break ;
2012-01-23 08:00:59 +04:00
}
2012-01-10 04:34:15 +04:00
free ( event - > name ) ;
event - > name = strdup ( name_str ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " NAME '%s' %s:%u \n " ,
event - > name ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-01-10 04:34:15 +04:00
break ;
}
case TK_A_DEVLINK : {
char temp [ UTIL_PATH_SIZE ] ;
char filename [ UTIL_PATH_SIZE ] ;
char * pos , * next ;
int count = 0 ;
if ( event - > devlink_final )
break ;
if ( major ( udev_device_get_devnum ( event - > dev ) ) = = 0 )
break ;
if ( cur - > key . op = = OP_ASSIGN_FINAL )
event - > devlink_final = true ;
if ( cur - > key . op = = OP_ASSIGN | | cur - > key . op = = OP_ASSIGN_FINAL )
udev_device_cleanup_devlinks_list ( event - > dev ) ;
/* allow multiple symlinks separated by spaces */
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , temp , sizeof ( temp ) ) ;
if ( esc = = ESCAPE_UNSET )
count = util_replace_chars ( temp , " / " ) ;
else if ( esc = = ESCAPE_REPLACE )
count = util_replace_chars ( temp , " / " ) ;
if ( count > 0 )
2012-04-08 18:06:20 +04:00
log_debug ( " %i character(s) replaced \n " , count ) ;
2012-01-10 04:34:15 +04:00
pos = temp ;
while ( isspace ( pos [ 0 ] ) )
pos + + ;
next = strchr ( pos , ' ' ) ;
while ( next ! = NULL ) {
next [ 0 ] = ' \0 ' ;
2012-04-08 18:06:20 +04:00
log_debug ( " LINK '%s' %s:%u \n " , pos ,
& rules - > buf [ rule - > rule . filename_off ] , rule - > rule . filename_line ) ;
2012-07-16 00:10:46 +04:00
util_strscpyl ( filename , sizeof ( filename ) , " /dev/ " , pos , NULL ) ;
2012-01-10 04:34:15 +04:00
udev_device_add_devlink ( event - > dev , filename , cur - > key . devlink_unique ) ;
while ( isspace ( next [ 1 ] ) )
next + + ;
pos = & next [ 1 ] ;
next = strchr ( pos , ' ' ) ;
}
if ( pos [ 0 ] ! = ' \0 ' ) {
2012-04-08 18:06:20 +04:00
log_debug ( " LINK '%s' %s:%u \n " , pos ,
& rules - > buf [ rule - > rule . filename_off ] , rule - > rule . filename_line ) ;
2012-07-16 00:10:46 +04:00
util_strscpyl ( filename , sizeof ( filename ) , " /dev/ " , pos , NULL ) ;
2012-01-10 04:34:15 +04:00
udev_device_add_devlink ( event - > dev , filename , cur - > key . devlink_unique ) ;
}
break ;
}
case TK_A_ATTR : {
const char * key_name = & rules - > buf [ cur - > key . attr_off ] ;
char attr [ UTIL_PATH_SIZE ] ;
char value [ UTIL_NAME_SIZE ] ;
FILE * f ;
if ( util_resolve_subsys_kernel ( event - > udev , key_name , attr , sizeof ( attr ) , 0 ) ! = 0 )
util_strscpyl ( attr , sizeof ( attr ) , udev_device_get_syspath ( event - > dev ) , " / " , key_name , NULL ) ;
attr_subst_subdir ( attr , sizeof ( attr ) ) ;
udev_event_apply_format ( event , & rules - > buf [ cur - > key . value_off ] , value , sizeof ( value ) ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " ATTR '%s' writing '%s' %s:%u \n " , attr , value ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-07-05 19:33:24 +04:00
f = fopen ( attr , " we " ) ;
2012-01-10 04:34:15 +04:00
if ( f ! = NULL ) {
if ( fprintf ( f , " %s " , value ) < = 0 )
2012-04-08 18:06:20 +04:00
log_error ( " error writing ATTR{%s}: %m \n " , attr ) ;
2012-01-10 04:34:15 +04:00
fclose ( f ) ;
} else {
2012-04-08 18:06:20 +04:00
log_error ( " error opening ATTR{%s} for writing: %m \n " , attr ) ;
2012-01-10 04:34:15 +04:00
}
break ;
}
2012-04-09 18:37:54 +04:00
case TK_A_RUN_BUILTIN :
case TK_A_RUN_PROGRAM : {
struct udev_list_entry * entry ;
2012-01-10 04:34:15 +04:00
if ( cur - > key . op = = OP_ASSIGN | | cur - > key . op = = OP_ASSIGN_FINAL )
udev_list_cleanup ( & event - > run_list ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " RUN '%s' %s:%u \n " ,
& rules - > buf [ cur - > key . value_off ] ,
& rules - > buf [ rule - > rule . filename_off ] ,
rule - > rule . filename_line ) ;
2012-04-09 18:37:54 +04:00
entry = udev_list_entry_add ( & event - > run_list , & rules - > buf [ cur - > key . value_off ] , NULL ) ;
udev_list_entry_set_num ( entry , cur - > key . builtin_cmd ) ;
2012-01-10 04:34:15 +04:00
break ;
}
case TK_A_GOTO :
if ( cur - > key . rule_goto = = 0 )
break ;
cur = & rules - > tokens [ cur - > key . rule_goto ] ;
continue ;
case TK_END :
return 0 ;
case TK_M_PARENTS_MIN :
case TK_M_PARENTS_MAX :
case TK_M_MAX :
case TK_UNSET :
2012-04-08 18:06:20 +04:00
log_error ( " wrong type %u \n " , cur - > type ) ;
2012-01-10 04:34:15 +04:00
goto nomatch ;
}
cur + + ;
continue ;
nomatch :
/* fast-forward to next rule */
cur = rule + rule - > rule . token_count ;
}
2008-10-23 02:13:59 +04:00
}
2010-05-20 19:09:04 +04:00
void udev_rules_apply_static_dev_perms ( struct udev_rules * rules )
{
2012-01-10 04:34:15 +04:00
struct token * cur ;
struct token * rule ;
uid_t uid = 0 ;
gid_t gid = 0 ;
mode_t mode = 0 ;
if ( rules - > tokens = = NULL )
return ;
cur = & rules - > tokens [ 0 ] ;
rule = cur ;
for ( ; ; ) {
switch ( cur - > type ) {
case TK_RULE :
/* current rule */
rule = cur ;
/* skip rules without a static_node tag */
if ( ! rule - > rule . has_static_node )
goto next ;
uid = 0 ;
gid = 0 ;
mode = 0 ;
break ;
case TK_A_OWNER_ID :
uid = cur - > key . uid ;
break ;
case TK_A_GROUP_ID :
gid = cur - > key . gid ;
break ;
case TK_A_MODE_ID :
mode = cur - > key . mode ;
break ;
case TK_A_STATIC_NODE : {
char filename [ UTIL_PATH_SIZE ] ;
struct stat stats ;
/* we assure, that the permissions tokens are sorted before the static token */
if ( mode = = 0 & & uid = = 0 & & gid = = 0 )
goto next ;
2012-07-16 00:10:46 +04:00
util_strscpyl ( filename , sizeof ( filename ) , " /dev/ " ,
2012-01-10 04:34:15 +04:00
& rules - > buf [ cur - > key . value_off ] , NULL ) ;
if ( stat ( filename , & stats ) ! = 0 )
goto next ;
if ( ! S_ISBLK ( stats . st_mode ) & & ! S_ISCHR ( stats . st_mode ) )
goto next ;
if ( mode = = 0 ) {
if ( gid > 0 )
mode = 0660 ;
else
mode = 0600 ;
}
if ( mode ! = ( stats . st_mode & 01777 ) ) {
chmod ( filename , mode ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " chmod '%s' %#o \n " , filename , mode ) ;
2012-01-10 04:34:15 +04:00
}
if ( ( uid ! = 0 & & uid ! = stats . st_uid ) | | ( gid ! = 0 & & gid ! = stats . st_gid ) ) {
chown ( filename , uid , gid ) ;
2012-04-08 18:06:20 +04:00
log_debug ( " chown '%s' %u %u \n " , filename , uid , gid ) ;
2012-01-10 04:34:15 +04:00
}
utimensat ( AT_FDCWD , filename , NULL , 0 ) ;
break ;
}
case TK_END :
return ;
}
cur + + ;
continue ;
2010-05-20 19:09:04 +04:00
next :
2012-01-10 04:34:15 +04:00
/* fast-forward to next rule */
cur = rule + rule - > rule . token_count ;
continue ;
}
2010-05-20 19:09:04 +04:00
}