2008-08-29 01:05:01 +04:00
/*
* libudev - interface to udev device information
*
2010-04-22 20:12:36 +04:00
* Copyright ( C ) 2008 - 2010 Kay Sievers < kay . sievers @ vrfy . org >
2008-08-29 01:05:01 +04:00
*
2009-03-26 21:29:36 +03:00
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
2008-08-29 01:05:01 +04:00
*/
# include <stdio.h>
# include <stdlib.h>
# include <stddef.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <dirent.h>
2009-04-22 05:50:11 +04:00
# include <sys/poll.h>
2008-08-29 01:05:01 +04:00
# include <sys/stat.h>
# include <sys/socket.h>
# include <sys/un.h>
2009-04-22 05:50:11 +04:00
# include <arpa/inet.h>
2008-09-09 19:38:10 +04:00
# include <linux/netlink.h>
2009-04-22 05:50:11 +04:00
# include <linux/filter.h>
2008-08-29 01:05:01 +04:00
# include "libudev.h"
# include "libudev-private.h"
2012-04-13 17:08:55 +04:00
# include "socket-util.h"
2008-08-29 01:05:01 +04:00
2009-06-15 19:09:43 +04:00
/**
* SECTION : libudev - monitor
* @ short_description : device event source
*
* Connects to a device event source .
*/
/**
* udev_monitor :
*
2011-06-28 02:15:39 +04:00
* Opaque object handling an event source .
2009-06-15 19:09:43 +04:00
*/
2008-08-29 01:05:01 +04:00
struct udev_monitor {
2012-01-10 04:34:15 +04:00
struct udev * udev ;
int refcount ;
int sock ;
2012-04-13 17:08:55 +04:00
union sockaddr_union snl ;
union sockaddr_union snl_trusted_sender ;
union sockaddr_union snl_destination ;
2012-01-10 04:34:15 +04:00
socklen_t addrlen ;
struct udev_list filter_subsystem_list ;
struct udev_list filter_tag_list ;
bool bound ;
2008-08-29 01:05:01 +04:00
} ;
2009-03-29 22:08:52 +04:00
enum udev_monitor_netlink_group {
2012-01-10 04:34:15 +04:00
UDEV_MONITOR_NONE ,
UDEV_MONITOR_KERNEL ,
UDEV_MONITOR_UDEV ,
2009-03-29 22:08:52 +04:00
} ;
2012-01-10 04:34:15 +04:00
# define UDEV_MONITOR_MAGIC 0xfeedcafe
2009-04-22 05:50:11 +04:00
struct udev_monitor_netlink_header {
2012-01-10 04:34:15 +04:00
/* "libudev" prefix to distinguish libudev and kernel messages */
char prefix [ 8 ] ;
/*
* magic to protect against daemon < - > library message format mismatch
* used in the kernel from socket filter rules ; needs to be stored in network order
*/
unsigned int magic ;
/* total length of header structure known to the sender */
unsigned int header_size ;
/* properties string buffer */
unsigned int properties_off ;
unsigned int properties_len ;
/*
* hashes of primary device properties strings , to let libudev subscribers
* use in - kernel socket filters ; values need to be stored in network order
*/
unsigned int filter_subsystem_hash ;
unsigned int filter_devtype_hash ;
unsigned int filter_tag_bloom_hi ;
unsigned int filter_tag_bloom_lo ;
2009-04-22 05:50:11 +04:00
} ;
static struct udev_monitor * udev_monitor_new ( struct udev * udev )
{
2012-01-10 04:34:15 +04:00
struct udev_monitor * udev_monitor ;
udev_monitor = calloc ( 1 , sizeof ( struct udev_monitor ) ) ;
if ( udev_monitor = = NULL )
return NULL ;
udev_monitor - > refcount = 1 ;
udev_monitor - > udev = udev ;
udev_list_init ( udev , & udev_monitor - > filter_subsystem_list , false ) ;
udev_list_init ( udev , & udev_monitor - > filter_tag_list , true ) ;
return udev_monitor ;
2009-04-22 05:50:11 +04:00
}
2008-08-30 18:16:37 +04:00
/**
* udev_monitor_new_from_socket :
* @ udev : udev library context
* @ socket_path : unix socket path
*
2012-04-09 22:45:45 +04:00
* This function is removed from libudev and will not do anything .
2011-06-28 02:15:39 +04:00
*
2012-04-09 22:45:45 +04:00
* Returns : # NULL
2008-08-30 18:16:37 +04:00
* */
2012-04-04 15:30:09 +04:00
_public_ struct udev_monitor * udev_monitor_new_from_socket ( struct udev * udev , const char * socket_path )
2008-08-29 01:05:01 +04:00
{
2012-04-10 15:33:43 +04:00
errno = ENOSYS ;
2012-04-09 22:45:45 +04:00
return NULL ;
2008-09-08 19:59:00 +04:00
}
2008-08-29 01:05:01 +04:00
2011-04-15 01:46:44 +04:00
struct udev_monitor * udev_monitor_new_from_netlink_fd ( struct udev * udev , const char * name , int fd )
2008-09-09 19:38:10 +04:00
{
2012-01-10 04:34:15 +04:00
struct udev_monitor * udev_monitor ;
unsigned int group ;
if ( udev = = NULL )
return NULL ;
if ( name = = NULL )
group = UDEV_MONITOR_NONE ;
else if ( strcmp ( name , " udev " ) = = 0 )
group = UDEV_MONITOR_UDEV ;
else if ( strcmp ( name , " kernel " ) = = 0 )
group = UDEV_MONITOR_KERNEL ;
else
return NULL ;
udev_monitor = udev_monitor_new ( udev ) ;
if ( udev_monitor = = NULL )
return NULL ;
if ( fd < 0 ) {
udev_monitor - > sock = socket ( PF_NETLINK , SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK , NETLINK_KOBJECT_UEVENT ) ;
if ( udev_monitor - > sock = = - 1 ) {
err ( udev , " error getting socket: %m \n " ) ;
free ( udev_monitor ) ;
return NULL ;
}
} else {
udev_monitor - > bound = true ;
udev_monitor - > sock = fd ;
}
2012-04-13 17:08:55 +04:00
udev_monitor - > snl . nl . nl_family = AF_NETLINK ;
udev_monitor - > snl . nl . nl_groups = group ;
2012-01-10 04:34:15 +04:00
/* default destination for sending */
2012-04-13 17:08:55 +04:00
udev_monitor - > snl_destination . nl . nl_family = AF_NETLINK ;
udev_monitor - > snl_destination . nl . nl_groups = UDEV_MONITOR_UDEV ;
2012-01-10 04:34:15 +04:00
return udev_monitor ;
2008-09-09 19:38:10 +04:00
}
2011-04-15 01:46:44 +04:00
/**
* udev_monitor_new_from_netlink :
* @ udev : udev library context
* @ name : name of event source
*
* Create new udev monitor and connect to a specified event
* source . Valid sources identifiers are " udev " and " kernel " .
*
* Applications should usually not connect directly to the
* " kernel " events , because the devices might not be useable
* at that time , before udev has configured them , and created
2011-06-28 02:15:39 +04:00
* device nodes . Accessing devices at the same time as udev ,
* might result in unpredictable behavior . The " udev " events
* are sent out after udev has finished its event processing ,
* all rules have been processed , and needed device nodes are
* created .
2011-04-15 01:46:44 +04:00
*
* The initial refcount is 1 , and needs to be decremented to
* release the resources of the udev monitor .
*
* Returns : a new udev monitor , or # NULL , in case of an error
* */
2012-04-04 15:30:09 +04:00
_public_ struct udev_monitor * udev_monitor_new_from_netlink ( struct udev * udev , const char * name )
2011-04-15 01:46:44 +04:00
{
2012-01-10 04:34:15 +04:00
return udev_monitor_new_from_netlink_fd ( udev , name , - 1 ) ;
2011-04-15 01:46:44 +04:00
}
2009-04-22 05:50:11 +04:00
static inline void bpf_stmt ( struct sock_filter * inss , unsigned int * i ,
2012-01-10 04:34:15 +04:00
unsigned short code , unsigned int data )
2009-04-22 05:50:11 +04:00
{
2012-01-10 04:34:15 +04:00
struct sock_filter * ins = & inss [ * i ] ;
2009-04-22 05:50:11 +04:00
2012-01-10 04:34:15 +04:00
ins - > code = code ;
ins - > k = data ;
( * i ) + + ;
2009-04-22 05:50:11 +04:00
}
static inline void bpf_jmp ( struct sock_filter * inss , unsigned int * i ,
2012-01-10 04:34:15 +04:00
unsigned short code , unsigned int data ,
unsigned short jt , unsigned short jf )
2009-04-22 05:50:11 +04:00
{
2012-01-10 04:34:15 +04:00
struct sock_filter * ins = & inss [ * i ] ;
2009-04-22 05:50:11 +04:00
2012-01-10 04:34:15 +04:00
ins - > code = code ;
ins - > jt = jt ;
ins - > jf = jf ;
ins - > k = data ;
( * i ) + + ;
2009-04-22 05:50:11 +04:00
}
2009-06-15 19:09:43 +04:00
/**
* udev_monitor_filter_update :
* @ udev_monitor : monitor
*
2011-06-28 02:15:39 +04:00
* Update the installed socket filter . This is only needed ,
* if the filter was removed or changed .
2009-06-15 19:09:43 +04:00
*
* Returns : 0 on success , otherwise a negative error value .
*/
2012-04-04 15:30:09 +04:00
_public_ int udev_monitor_filter_update ( struct udev_monitor * udev_monitor )
2009-04-22 05:50:11 +04:00
{
2012-01-10 04:34:15 +04:00
struct sock_filter ins [ 512 ] ;
struct sock_fprog filter ;
unsigned int i ;
struct udev_list_entry * list_entry ;
int err ;
if ( udev_list_get_entry ( & udev_monitor - > filter_subsystem_list ) = = NULL & &
udev_list_get_entry ( & udev_monitor - > filter_tag_list ) = = NULL )
return 0 ;
memset ( ins , 0x00 , sizeof ( ins ) ) ;
i = 0 ;
/* load magic in A */
bpf_stmt ( ins , & i , BPF_LD | BPF_W | BPF_ABS , offsetof ( struct udev_monitor_netlink_header , magic ) ) ;
/* jump if magic matches */
bpf_jmp ( ins , & i , BPF_JMP | BPF_JEQ | BPF_K , UDEV_MONITOR_MAGIC , 1 , 0 ) ;
/* wrong magic, pass packet */
bpf_stmt ( ins , & i , BPF_RET | BPF_K , 0xffffffff ) ;
if ( udev_list_get_entry ( & udev_monitor - > filter_tag_list ) ! = NULL ) {
int tag_matches ;
/* count tag matches, to calculate end of tag match block */
tag_matches = 0 ;
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_monitor - > filter_tag_list ) )
tag_matches + + ;
/* add all tags matches */
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_monitor - > filter_tag_list ) ) {
uint64_t tag_bloom_bits = util_string_bloom64 ( udev_list_entry_get_name ( list_entry ) ) ;
uint32_t tag_bloom_hi = tag_bloom_bits > > 32 ;
uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff ;
/* load device bloom bits in A */
bpf_stmt ( ins , & i , BPF_LD | BPF_W | BPF_ABS , offsetof ( struct udev_monitor_netlink_header , filter_tag_bloom_hi ) ) ;
/* clear bits (tag bits & bloom bits) */
bpf_stmt ( ins , & i , BPF_ALU | BPF_AND | BPF_K , tag_bloom_hi ) ;
/* jump to next tag if it does not match */
bpf_jmp ( ins , & i , BPF_JMP | BPF_JEQ | BPF_K , tag_bloom_hi , 0 , 3 ) ;
/* load device bloom bits in A */
bpf_stmt ( ins , & i , BPF_LD | BPF_W | BPF_ABS , offsetof ( struct udev_monitor_netlink_header , filter_tag_bloom_lo ) ) ;
/* clear bits (tag bits & bloom bits) */
bpf_stmt ( ins , & i , BPF_ALU | BPF_AND | BPF_K , tag_bloom_lo ) ;
/* jump behind end of tag match block if tag matches */
tag_matches - - ;
bpf_jmp ( ins , & i , BPF_JMP | BPF_JEQ | BPF_K , tag_bloom_lo , 1 + ( tag_matches * 6 ) , 0 ) ;
}
/* nothing matched, drop packet */
bpf_stmt ( ins , & i , BPF_RET | BPF_K , 0 ) ;
}
/* add all subsystem matches */
if ( udev_list_get_entry ( & udev_monitor - > filter_subsystem_list ) ! = NULL ) {
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_monitor - > filter_subsystem_list ) ) {
unsigned int hash = util_string_hash32 ( udev_list_entry_get_name ( list_entry ) ) ;
/* load device subsystem value in A */
bpf_stmt ( ins , & i , BPF_LD | BPF_W | BPF_ABS , offsetof ( struct udev_monitor_netlink_header , filter_subsystem_hash ) ) ;
if ( udev_list_entry_get_value ( list_entry ) = = NULL ) {
/* jump if subsystem does not match */
bpf_jmp ( ins , & i , BPF_JMP | BPF_JEQ | BPF_K , hash , 0 , 1 ) ;
} else {
/* jump if subsystem does not match */
bpf_jmp ( ins , & i , BPF_JMP | BPF_JEQ | BPF_K , hash , 0 , 3 ) ;
/* load device devtype value in A */
bpf_stmt ( ins , & i , BPF_LD | BPF_W | BPF_ABS , offsetof ( struct udev_monitor_netlink_header , filter_devtype_hash ) ) ;
/* jump if value does not match */
hash = util_string_hash32 ( udev_list_entry_get_value ( list_entry ) ) ;
bpf_jmp ( ins , & i , BPF_JMP | BPF_JEQ | BPF_K , hash , 0 , 1 ) ;
}
/* matched, pass packet */
bpf_stmt ( ins , & i , BPF_RET | BPF_K , 0xffffffff ) ;
if ( i + 1 > = ARRAY_SIZE ( ins ) )
return - 1 ;
}
/* nothing matched, drop packet */
bpf_stmt ( ins , & i , BPF_RET | BPF_K , 0 ) ;
}
/* matched, pass packet */
bpf_stmt ( ins , & i , BPF_RET | BPF_K , 0xffffffff ) ;
/* install filter */
memset ( & filter , 0x00 , sizeof ( filter ) ) ;
filter . len = i ;
filter . filter = ins ;
err = setsockopt ( udev_monitor - > sock , SOL_SOCKET , SO_ATTACH_FILTER , & filter , sizeof ( filter ) ) ;
return err ;
2009-04-22 05:50:11 +04:00
}
2009-06-04 03:44:04 +04:00
int udev_monitor_allow_unicast_sender ( struct udev_monitor * udev_monitor , struct udev_monitor * sender )
{
2012-04-13 17:08:55 +04:00
udev_monitor - > snl_trusted_sender . nl . nl_pid = sender - > snl . nl . nl_pid ;
2012-01-10 04:34:15 +04:00
return 0 ;
2009-06-04 03:44:04 +04:00
}
2009-06-15 19:09:43 +04:00
/**
* udev_monitor_enable_receiving :
* @ udev_monitor : the monitor which should receive events
*
* Binds the @ udev_monitor socket to the event source .
*
* Returns : 0 on success , otherwise a negative error value .
*/
2012-04-04 15:30:09 +04:00
_public_ int udev_monitor_enable_receiving ( struct udev_monitor * udev_monitor )
2008-09-08 19:59:00 +04:00
{
2012-01-10 04:34:15 +04:00
int err = 0 ;
const int on = 1 ;
2012-04-13 17:08:55 +04:00
if ( udev_monitor - > snl . nl . nl_family = = 0 )
2012-01-10 04:34:15 +04:00
return - EINVAL ;
2012-04-09 22:45:45 +04:00
udev_monitor_filter_update ( udev_monitor ) ;
if ( ! udev_monitor - > bound ) {
err = bind ( udev_monitor - > sock ,
2012-04-13 17:08:55 +04:00
& udev_monitor - > snl . sa , sizeof ( struct sockaddr_nl ) ) ;
2012-04-09 22:45:45 +04:00
if ( err = = 0 )
udev_monitor - > bound = true ;
2012-01-10 04:34:15 +04:00
}
2012-04-09 22:45:45 +04:00
if ( err > = 0 ) {
2012-04-13 17:08:55 +04:00
union sockaddr_union snl ;
2012-04-09 22:45:45 +04:00
socklen_t addrlen ;
/*
* get the address the kernel has assigned us
* it is usually , but not necessarily the pid
*/
addrlen = sizeof ( struct sockaddr_nl ) ;
2012-04-13 17:08:55 +04:00
err = getsockname ( udev_monitor - > sock , & snl . sa , & addrlen ) ;
2012-04-09 22:45:45 +04:00
if ( err = = 0 )
2012-04-13 17:08:55 +04:00
udev_monitor - > snl . nl . nl_pid = snl . nl . nl_pid ;
2012-04-09 22:45:45 +04:00
} else {
2012-01-10 04:34:15 +04:00
err ( udev_monitor - > udev , " bind failed: %m \n " ) ;
return err ;
}
/* enable receiving of sender credentials */
setsockopt ( udev_monitor - > sock , SOL_SOCKET , SO_PASSCRED , & on , sizeof ( on ) ) ;
return 0 ;
2008-08-29 01:05:01 +04:00
}
2010-05-08 01:04:42 +04:00
/**
* udev_monitor_set_receive_buffer_size :
* @ udev_monitor : the monitor which should receive events
* @ size : the size in bytes
*
* Set the size of the kernel socket buffer . This call needs the
* appropriate privileges to succeed .
*
* Returns : 0 on success , otherwise - 1 on error .
*/
2012-04-04 15:30:09 +04:00
_public_ int udev_monitor_set_receive_buffer_size ( struct udev_monitor * udev_monitor , int size )
2008-10-17 18:49:27 +04:00
{
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return - 1 ;
return setsockopt ( udev_monitor - > sock , SOL_SOCKET , SO_RCVBUFFORCE , & size , sizeof ( size ) ) ;
2008-10-17 18:49:27 +04:00
}
2009-06-04 03:44:04 +04:00
int udev_monitor_disconnect ( struct udev_monitor * udev_monitor )
{
2012-01-10 04:34:15 +04:00
int err ;
2009-06-04 03:44:04 +04:00
2012-01-10 04:34:15 +04:00
err = close ( udev_monitor - > sock ) ;
udev_monitor - > sock = - 1 ;
return err ;
2009-06-04 03:44:04 +04:00
}
2008-08-30 18:16:37 +04:00
/**
* udev_monitor_ref :
* @ udev_monitor : udev monitor
*
* Take a reference of a udev monitor .
*
* Returns : the passed udev monitor
* */
2012-04-04 15:30:09 +04:00
_public_ struct udev_monitor * udev_monitor_ref ( struct udev_monitor * udev_monitor )
2008-08-29 01:05:01 +04:00
{
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return NULL ;
udev_monitor - > refcount + + ;
return udev_monitor ;
2008-08-29 01:05:01 +04:00
}
2008-08-30 18:16:37 +04:00
/**
* udev_monitor_unref :
* @ udev_monitor : udev monitor
*
2009-04-16 03:49:16 +04:00
* Drop a reference of a udev monitor . If the refcount reaches zero ,
2008-10-26 17:48:48 +03:00
* the bound socket will be closed , and the resources of the monitor
2008-08-30 18:16:37 +04:00
* will be released .
*
* */
2012-04-04 15:30:09 +04:00
_public_ void udev_monitor_unref ( struct udev_monitor * udev_monitor )
2008-08-29 01:05:01 +04:00
{
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return ;
udev_monitor - > refcount - - ;
if ( udev_monitor - > refcount > 0 )
return ;
if ( udev_monitor - > sock > = 0 )
close ( udev_monitor - > sock ) ;
udev_list_cleanup ( & udev_monitor - > filter_subsystem_list ) ;
udev_list_cleanup ( & udev_monitor - > filter_tag_list ) ;
free ( udev_monitor ) ;
2008-08-29 01:05:01 +04:00
}
2008-08-30 18:16:37 +04:00
/**
* udev_monitor_get_udev :
* @ udev_monitor : udev monitor
*
2008-09-01 18:41:41 +04:00
* Retrieve the udev library context the monitor was created with .
2008-08-30 18:16:37 +04:00
*
* Returns : the udev library context
* */
2012-04-04 15:30:09 +04:00
_public_ struct udev * udev_monitor_get_udev ( struct udev_monitor * udev_monitor )
2008-08-29 01:05:01 +04:00
{
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return NULL ;
return udev_monitor - > udev ;
2008-08-29 01:05:01 +04:00
}
2008-08-30 18:16:37 +04:00
/**
* udev_monitor_get_fd :
* @ udev_monitor : udev monitor
*
* Retrieve the socket file descriptor associated with the monitor .
*
* Returns : the socket file descriptor
* */
2012-04-04 15:30:09 +04:00
_public_ int udev_monitor_get_fd ( struct udev_monitor * udev_monitor )
2008-08-29 01:05:01 +04:00
{
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return - 1 ;
return udev_monitor - > sock ;
2008-08-29 01:05:01 +04:00
}
2009-04-22 05:50:11 +04:00
static int passes_filter ( struct udev_monitor * udev_monitor , struct udev_device * udev_device )
{
2012-01-10 04:34:15 +04:00
struct udev_list_entry * list_entry ;
if ( udev_list_get_entry ( & udev_monitor - > filter_subsystem_list ) = = NULL )
goto tag ;
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_monitor - > filter_subsystem_list ) ) {
const char * subsys = udev_list_entry_get_name ( list_entry ) ;
const char * dsubsys = udev_device_get_subsystem ( udev_device ) ;
const char * devtype ;
const char * ddevtype ;
if ( strcmp ( dsubsys , subsys ) ! = 0 )
continue ;
devtype = udev_list_entry_get_value ( list_entry ) ;
if ( devtype = = NULL )
goto tag ;
ddevtype = udev_device_get_devtype ( udev_device ) ;
if ( ddevtype = = NULL )
continue ;
if ( strcmp ( ddevtype , devtype ) = = 0 )
goto tag ;
}
return 0 ;
2010-04-22 20:12:36 +04:00
tag :
2012-01-10 04:34:15 +04:00
if ( udev_list_get_entry ( & udev_monitor - > filter_tag_list ) = = NULL )
return 1 ;
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_monitor - > filter_tag_list ) ) {
const char * tag = udev_list_entry_get_name ( list_entry ) ;
if ( udev_device_has_tag ( udev_device , tag ) )
return 1 ;
}
return 0 ;
2009-04-22 05:50:11 +04:00
}
2008-08-30 18:16:37 +04:00
/**
2008-09-08 19:59:00 +04:00
* udev_monitor_receive_device :
2008-08-30 18:16:37 +04:00
* @ udev_monitor : udev monitor
*
2008-09-08 19:59:00 +04:00
* Receive data from the udev monitor socket , allocate a new udev
2008-09-01 18:41:41 +04:00
* device , fill in the received data , and return the device .
2008-08-30 18:16:37 +04:00
*
2011-06-28 02:15:39 +04:00
* Only socket connections with uid = 0 are accepted .
2008-08-30 18:16:37 +04:00
*
* The initial refcount is 1 , and needs to be decremented to
2008-10-26 17:48:48 +03:00
* release the resources of the udev device .
2008-08-30 18:16:37 +04:00
*
* Returns : a new udev device , or # NULL , in case of an error
* */
2012-04-04 15:30:09 +04:00
_public_ struct udev_device * udev_monitor_receive_device ( struct udev_monitor * udev_monitor )
2008-08-29 01:05:01 +04:00
{
2012-01-10 04:34:15 +04:00
struct udev_device * udev_device ;
struct msghdr smsg ;
struct iovec iov ;
char cred_msg [ CMSG_SPACE ( sizeof ( struct ucred ) ) ] ;
struct cmsghdr * cmsg ;
2012-04-13 17:08:55 +04:00
union sockaddr_union snl ;
2012-01-10 04:34:15 +04:00
struct ucred * cred ;
char buf [ 8192 ] ;
ssize_t buflen ;
ssize_t bufpos ;
struct udev_monitor_netlink_header * nlh ;
2008-08-29 01:05:01 +04:00
2009-04-22 05:50:11 +04:00
retry :
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return NULL ;
iov . iov_base = & buf ;
iov . iov_len = sizeof ( buf ) ;
memset ( & smsg , 0x00 , sizeof ( struct msghdr ) ) ;
smsg . msg_iov = & iov ;
smsg . msg_iovlen = 1 ;
smsg . msg_control = cred_msg ;
smsg . msg_controllen = sizeof ( cred_msg ) ;
2012-04-13 17:08:55 +04:00
if ( udev_monitor - > snl . nl . nl_family ! = 0 ) {
2012-01-10 04:34:15 +04:00
smsg . msg_name = & snl ;
smsg . msg_namelen = sizeof ( snl ) ;
}
buflen = recvmsg ( udev_monitor - > sock , & smsg , 0 ) ;
if ( buflen < 0 ) {
if ( errno ! = EINTR )
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " unable to receive message \n " ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
if ( buflen < 32 | | ( size_t ) buflen > = sizeof ( buf ) ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " invalid message length \n " ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
2012-04-13 17:08:55 +04:00
if ( udev_monitor - > snl . nl . nl_family ! = 0 ) {
if ( snl . nl . nl_groups = = 0 ) {
2012-01-10 04:34:15 +04:00
/* unicast message, check if we trust the sender */
2012-04-13 17:08:55 +04:00
if ( udev_monitor - > snl_trusted_sender . nl . nl_pid = = 0 | |
snl . nl . nl_pid ! = udev_monitor - > snl_trusted_sender . nl . nl_pid ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " unicast netlink message ignored \n " ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
2012-04-13 17:08:55 +04:00
} else if ( snl . nl . nl_groups = = UDEV_MONITOR_KERNEL ) {
if ( snl . nl . nl_pid > 0 ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " multicast kernel netlink message from pid %d ignored \n " ,
2012-04-13 17:08:55 +04:00
snl . nl . nl_pid ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
}
}
cmsg = CMSG_FIRSTHDR ( & smsg ) ;
if ( cmsg = = NULL | | cmsg - > cmsg_type ! = SCM_CREDENTIALS ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " no sender credentials received, message ignored \n " ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
cred = ( struct ucred * ) CMSG_DATA ( cmsg ) ;
if ( cred - > uid ! = 0 ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " sender uid=%d, message ignored \n " , cred - > uid ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
if ( memcmp ( buf , " libudev " , 8 ) = = 0 ) {
/* udev message needs proper version magic */
nlh = ( struct udev_monitor_netlink_header * ) buf ;
if ( nlh - > magic ! = htonl ( UDEV_MONITOR_MAGIC ) ) {
err ( udev_monitor - > udev , " unrecognized message signature (%x != %x) \n " ,
nlh - > magic , htonl ( UDEV_MONITOR_MAGIC ) ) ;
return NULL ;
}
if ( nlh - > properties_off + 32 > buflen )
return NULL ;
bufpos = nlh - > properties_off ;
} else {
/* kernel message with header */
bufpos = strlen ( buf ) + 1 ;
if ( ( size_t ) bufpos < sizeof ( " a@/d " ) | | bufpos > = buflen ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " invalid message length \n " ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
/* check message header */
if ( strstr ( buf , " @/ " ) = = NULL ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " unrecognized message header \n " ) ;
2012-01-10 04:34:15 +04:00
return NULL ;
}
}
udev_device = udev_device_new ( udev_monitor - > udev ) ;
if ( udev_device = = NULL )
return NULL ;
udev_device_set_info_loaded ( udev_device ) ;
while ( bufpos < buflen ) {
char * key ;
size_t keylen ;
key = & buf [ bufpos ] ;
keylen = strlen ( key ) ;
if ( keylen = = 0 )
break ;
bufpos + = keylen + 1 ;
udev_device_add_property_from_string_parse ( udev_device , key ) ;
}
if ( udev_device_add_property_from_string_parse_finish ( udev_device ) < 0 ) {
2012-04-08 18:06:20 +04:00
dbg ( udev_monitor - > udev , " missing values, invalid device \n " ) ;
2012-01-10 04:34:15 +04:00
udev_device_unref ( udev_device ) ;
return NULL ;
}
/* skip device, if it does not pass the current filter */
if ( ! passes_filter ( udev_monitor , udev_device ) ) {
struct pollfd pfd [ 1 ] ;
int rc ;
udev_device_unref ( udev_device ) ;
/* if something is queued, get next device */
pfd [ 0 ] . fd = udev_monitor - > sock ;
pfd [ 0 ] . events = POLLIN ;
rc = poll ( pfd , 1 , 0 ) ;
if ( rc > 0 )
goto retry ;
return NULL ;
}
return udev_device ;
2008-08-29 01:05:01 +04:00
}
2008-10-10 01:38:30 +04:00
2009-06-04 03:44:04 +04:00
int udev_monitor_send_device ( struct udev_monitor * udev_monitor ,
2012-01-10 04:34:15 +04:00
struct udev_monitor * destination , struct udev_device * udev_device )
2008-10-10 01:38:30 +04:00
{
2012-01-10 04:34:15 +04:00
const char * buf ;
ssize_t blen ;
ssize_t count ;
2012-04-09 22:45:45 +04:00
struct msghdr smsg ;
struct iovec iov [ 2 ] ;
const char * val ;
struct udev_monitor_netlink_header nlh ;
struct udev_list_entry * list_entry ;
uint64_t tag_bloom_bits ;
2012-04-13 17:08:55 +04:00
if ( udev_monitor - > snl . nl . nl_family = = 0 )
2012-04-09 22:45:45 +04:00
return - EINVAL ;
2012-01-10 04:34:15 +04:00
blen = udev_device_get_properties_monitor_buf ( udev_device , & buf ) ;
if ( blen < 32 )
return - EINVAL ;
2012-04-09 22:45:45 +04:00
/* add versioned header */
memset ( & nlh , 0x00 , sizeof ( struct udev_monitor_netlink_header ) ) ;
memcpy ( nlh . prefix , " libudev " , 8 ) ;
nlh . magic = htonl ( UDEV_MONITOR_MAGIC ) ;
nlh . header_size = sizeof ( struct udev_monitor_netlink_header ) ;
val = udev_device_get_subsystem ( udev_device ) ;
nlh . filter_subsystem_hash = htonl ( util_string_hash32 ( val ) ) ;
val = udev_device_get_devtype ( udev_device ) ;
if ( val ! = NULL )
nlh . filter_devtype_hash = htonl ( util_string_hash32 ( val ) ) ;
iov [ 0 ] . iov_base = & nlh ;
iov [ 0 ] . iov_len = sizeof ( struct udev_monitor_netlink_header ) ;
/* add tag bloom filter */
tag_bloom_bits = 0 ;
udev_list_entry_foreach ( list_entry , udev_device_get_tags_list_entry ( udev_device ) )
tag_bloom_bits | = util_string_bloom64 ( udev_list_entry_get_name ( list_entry ) ) ;
if ( tag_bloom_bits > 0 ) {
nlh . filter_tag_bloom_hi = htonl ( tag_bloom_bits > > 32 ) ;
nlh . filter_tag_bloom_lo = htonl ( tag_bloom_bits & 0xffffffff ) ;
2012-01-10 04:34:15 +04:00
}
2012-04-09 22:45:45 +04:00
/* add properties list */
nlh . properties_off = iov [ 0 ] . iov_len ;
nlh . properties_len = blen ;
iov [ 1 ] . iov_base = ( char * ) buf ;
iov [ 1 ] . iov_len = blen ;
2012-01-10 04:34:15 +04:00
2012-04-09 22:45:45 +04:00
memset ( & smsg , 0x00 , sizeof ( struct msghdr ) ) ;
smsg . msg_iov = iov ;
smsg . msg_iovlen = 2 ;
/*
* Use custom address for target , or the default one .
*
* If we send to a multicast group , we will get
* ECONNREFUSED , which is expected .
*/
if ( destination ! = NULL )
smsg . msg_name = & destination - > snl ;
else
smsg . msg_name = & udev_monitor - > snl_destination ;
smsg . msg_namelen = sizeof ( struct sockaddr_nl ) ;
count = sendmsg ( udev_monitor - > sock , & smsg , 0 ) ;
dbg ( udev_monitor - > udev , " passed %zi bytes to netlink monitor %p \n " , count , udev_monitor ) ;
return count ;
2008-10-10 01:38:30 +04:00
}
2009-04-22 05:50:11 +04:00
2009-06-15 19:09:43 +04:00
/**
* udev_monitor_filter_add_match_subsystem_devtype :
* @ udev_monitor : the monitor
* @ subsystem : the subsystem value to match the incoming devices against
2009-08-08 17:29:38 +04:00
* @ devtype : the devtype value to match the incoming devices against
2009-06-15 19:09:43 +04:00
*
2011-06-28 02:15:39 +04:00
* This filter is efficiently executed inside the kernel , and libudev subscribers
2010-04-22 20:12:36 +04:00
* will usually not be woken up for devices which do not match .
*
2009-06-15 19:09:43 +04:00
* The filter must be installed before the monitor is switched to listening mode .
*
* Returns : 0 on success , otherwise a negative error value .
*/
2012-04-04 15:30:09 +04:00
_public_ int udev_monitor_filter_add_match_subsystem_devtype ( struct udev_monitor * udev_monitor , const char * subsystem , const char * devtype )
2009-04-22 05:50:11 +04:00
{
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return - EINVAL ;
if ( subsystem = = NULL )
return - EINVAL ;
if ( udev_list_entry_add ( & udev_monitor - > filter_subsystem_list , subsystem , devtype ) = = NULL )
return - ENOMEM ;
return 0 ;
2009-04-22 05:50:11 +04:00
}
2009-04-23 06:07:51 +04:00
2010-04-22 20:12:36 +04:00
/**
* udev_monitor_filter_add_match_tag :
* @ udev_monitor : the monitor
* @ tag : the name of a tag
*
2011-06-28 02:15:39 +04:00
* This filter is efficiently executed inside the kernel , and libudev subscribers
2010-04-22 20:12:36 +04:00
* will usually not be woken up for devices which do not match .
*
* The filter must be installed before the monitor is switched to listening mode .
*
* Returns : 0 on success , otherwise a negative error value .
*/
2012-04-04 15:30:09 +04:00
_public_ int udev_monitor_filter_add_match_tag ( struct udev_monitor * udev_monitor , const char * tag )
2010-04-22 20:12:36 +04:00
{
2012-01-10 04:34:15 +04:00
if ( udev_monitor = = NULL )
return - EINVAL ;
if ( tag = = NULL )
return - EINVAL ;
if ( udev_list_entry_add ( & udev_monitor - > filter_tag_list , tag , NULL ) = = NULL )
return - ENOMEM ;
return 0 ;
2010-04-22 20:12:36 +04:00
}
2009-06-15 19:09:43 +04:00
/**
* udev_monitor_filter_remove :
* @ udev_monitor : monitor
*
* Remove all filters from monitor .
*
* Returns : 0 on success , otherwise a negative error value .
*/
2012-04-04 15:30:09 +04:00
_public_ int udev_monitor_filter_remove ( struct udev_monitor * udev_monitor )
2009-04-23 06:07:51 +04:00
{
2012-01-10 04:34:15 +04:00
static struct sock_fprog filter = { 0 , NULL } ;
2009-04-23 06:07:51 +04:00
2012-01-10 04:34:15 +04:00
udev_list_cleanup ( & udev_monitor - > filter_subsystem_list ) ;
return setsockopt ( udev_monitor - > sock , SOL_SOCKET , SO_ATTACH_FILTER , & filter , sizeof ( filter ) ) ;
2009-04-23 06:07:51 +04:00
}