2005-08-01 22:22:46 +04:00
/*
2010-03-10 17:00:00 +03:00
* Copyright ( C ) 2004 - 2010 Kay Sievers < kay . sievers @ vrfy . org >
2005-08-01 22:22:46 +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 .
2005-08-01 22:22:46 +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 .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-08-01 22:22:46 +04:00
*/
# include <unistd.h>
# include <stdio.h>
# include <stdlib.h>
2006-05-01 22:36:21 +04:00
# include <stddef.h>
2005-08-01 22:22:46 +04:00
# include <string.h>
# include <fcntl.h>
# include <errno.h>
2005-08-25 02:38:25 +04:00
# include <signal.h>
2007-03-10 17:12:42 +03:00
# include <getopt.h>
2005-11-07 04:23:06 +03:00
# include <sys/time.h>
2005-08-01 22:22:46 +04:00
# include <sys/socket.h>
# include <sys/un.h>
2011-04-15 13:58:17 +04:00
# include <sys/epoll.h>
2005-11-12 07:09:34 +03:00
# include <linux/types.h>
2005-08-01 22:22:46 +04:00
# include <linux/netlink.h>
# include "udev.h"
2011-04-15 13:58:17 +04:00
static bool udev_exit ;
2005-08-01 22:22:46 +04:00
2009-05-05 04:39:16 +04:00
static void sig_handler ( int signum )
2005-08-25 02:38:25 +04:00
{
if ( signum = = SIGINT | | signum = = SIGTERM )
2011-04-15 13:58:17 +04:00
udev_exit = true ;
2005-08-25 02:38:25 +04:00
}
2009-06-07 05:07:33 +04:00
static void print_device ( struct udev_device * device , const char * source , int prop )
2008-09-28 03:34:55 +04:00
{
struct timeval tv ;
struct timezone tz ;
gettimeofday ( & tv , & tz ) ;
printf ( " %-6s[%llu.%06u] %-8s %s (%s) \n " ,
source ,
( unsigned long long ) tv . tv_sec , ( unsigned int ) tv . tv_usec ,
udev_device_get_action ( device ) ,
udev_device_get_devpath ( device ) ,
udev_device_get_subsystem ( device ) ) ;
2009-06-07 05:07:33 +04:00
if ( prop ) {
2008-09-28 03:34:55 +04:00
struct udev_list_entry * list_entry ;
udev_list_entry_foreach ( list_entry , udev_device_get_properties_list_entry ( device ) )
printf ( " %s=%s \n " ,
udev_list_entry_get_name ( list_entry ) ,
udev_list_entry_get_value ( list_entry ) ) ;
printf ( " \n " ) ;
}
}
2008-09-06 17:45:31 +04:00
int udevadm_monitor ( struct udev * udev , int argc , char * argv [ ] )
2005-08-01 22:22:46 +04:00
{
2005-08-25 02:38:25 +04:00
struct sigaction act ;
2009-10-31 13:53:41 +03:00
sigset_t mask ;
2007-03-10 17:12:42 +03:00
int option ;
2011-04-15 13:58:17 +04:00
bool prop = false ;
bool print_kernel = false ;
bool print_udev = false ;
2009-04-22 07:00:15 +04:00
struct udev_list_node subsystem_match_list ;
2010-04-22 20:12:36 +04:00
struct udev_list_node tag_match_list ;
2008-09-09 16:07:08 +04:00
struct udev_monitor * udev_monitor = NULL ;
2008-09-09 19:41:17 +04:00
struct udev_monitor * kernel_monitor = NULL ;
2011-04-15 13:58:17 +04:00
int fd_ep = - 1 ;
int fd_kernel = - 1 , fd_udev = - 1 ;
struct epoll_event ep_kernel , ep_udev ;
2008-09-09 16:07:08 +04:00
int rc = 0 ;
2005-08-01 22:22:46 +04:00
2007-03-10 17:12:42 +03:00
static const struct option options [ ] = {
2009-06-07 05:07:33 +04:00
{ " property " , no_argument , NULL , ' p ' } ,
2008-10-02 18:49:05 +04:00
{ " environment " , no_argument , NULL , ' e ' } ,
{ " kernel " , no_argument , NULL , ' k ' } ,
{ " udev " , no_argument , NULL , ' u ' } ,
2009-04-22 07:00:15 +04:00
{ " subsystem-match " , required_argument , NULL , ' s ' } ,
2010-04-22 20:12:36 +04:00
{ " tag-match " , required_argument , NULL , ' t ' } ,
2008-10-02 18:49:05 +04:00
{ " help " , no_argument , NULL , ' h ' } ,
2007-03-10 17:12:42 +03:00
{ }
} ;
2009-04-22 07:00:15 +04:00
udev_list_init ( & subsystem_match_list ) ;
2010-04-22 20:12:36 +04:00
udev_list_init ( & tag_match_list ) ;
2011-04-15 13:58:17 +04:00
2010-05-27 17:11:00 +04:00
for ( ; ; ) {
2010-04-22 20:12:36 +04:00
option = getopt_long ( argc , argv , " pekus:t:h " , options , NULL ) ;
2007-03-10 17:12:42 +03:00
if ( option = = - 1 )
break ;
switch ( option ) {
2009-06-07 05:07:33 +04:00
case ' p ' :
2007-03-10 17:12:42 +03:00
case ' e ' :
2011-04-15 13:58:17 +04:00
prop = true ;
2007-03-10 17:12:42 +03:00
break ;
case ' k ' :
2011-04-15 13:58:17 +04:00
print_kernel = true ;
2007-03-10 17:12:42 +03:00
break ;
case ' u ' :
2011-04-15 13:58:17 +04:00
print_udev = true ;
2007-03-10 17:12:42 +03:00
break ;
2009-04-22 07:00:15 +04:00
case ' s ' :
2009-04-23 06:07:15 +04:00
{
char subsys [ UTIL_NAME_SIZE ] ;
char * devtype ;
2009-05-20 19:57:52 +04:00
util_strscpy ( subsys , sizeof ( subsys ) , optarg ) ;
2010-03-10 17:00:00 +03:00
devtype = strchr ( subsys , ' / ' ) ;
2009-04-23 06:07:15 +04:00
if ( devtype ! = NULL ) {
devtype [ 0 ] = ' \0 ' ;
devtype + + ;
}
2011-04-24 02:22:23 +04:00
udev_list_entry_add ( udev , & subsystem_match_list , subsys , devtype , 0 ) ;
2009-04-23 06:07:15 +04:00
break ;
}
2010-04-22 20:12:36 +04:00
case ' t ' :
2011-04-24 02:22:23 +04:00
udev_list_entry_add ( udev , & tag_match_list , optarg , NULL , 0 ) ;
2010-04-22 20:12:36 +04:00
break ;
2007-03-10 17:12:42 +03:00
case ' h ' :
2009-06-07 05:07:33 +04:00
printf ( " Usage: udevadm monitor [--property] [--kernel] [--udev] [--help] \n "
2010-03-10 17:00:00 +03:00
" --property print the event properties \n "
" --kernel print kernel uevents \n "
" --udev print udev events \n "
" --subsystem-match=<subsystem[/devtype]> filter events by subsystem \n "
2010-04-22 20:12:36 +04:00
" --tag-match=<tag> filter events by tag \n "
2009-03-07 04:32:40 +03:00
" --help \n \n " ) ;
2007-03-10 17:12:42 +03:00
goto out ;
2005-08-11 22:34:24 +04:00
}
}
2008-09-06 17:45:31 +04:00
if ( ! print_kernel & & ! print_udev ) {
2011-04-15 13:58:17 +04:00
print_kernel = true ;
print_udev = true ;
2007-03-10 17:12:42 +03:00
}
2005-08-25 02:38:25 +04:00
/* set signal handlers */
memset ( & act , 0x00 , sizeof ( struct sigaction ) ) ;
2009-05-05 04:39:16 +04:00
act . sa_handler = sig_handler ;
2005-08-25 02:38:25 +04:00
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = SA_RESTART ;
sigaction ( SIGINT , & act , NULL ) ;
sigaction ( SIGTERM , & act , NULL ) ;
2009-10-31 13:53:41 +03:00
sigemptyset ( & mask ) ;
sigaddset ( & mask , SIGINT ) ;
sigaddset ( & mask , SIGTERM ) ;
sigprocmask ( SIG_UNBLOCK , & mask , NULL ) ;
2005-08-25 02:38:25 +04:00
2011-04-15 13:58:17 +04:00
fd_ep = epoll_create1 ( EPOLL_CLOEXEC ) ;
if ( fd_ep < 0 ) {
err ( udev , " error creating epoll fd: %m \n " ) ;
goto out ;
}
2008-09-06 17:45:31 +04:00
printf ( " monitor will print the received events for: \n " ) ;
if ( print_udev ) {
2009-04-22 07:00:15 +04:00
struct udev_list_entry * entry ;
2009-03-29 22:08:52 +04:00
udev_monitor = udev_monitor_new_from_netlink ( udev , " udev " ) ;
2008-09-09 16:07:08 +04:00
if ( udev_monitor = = NULL ) {
2009-04-22 06:11:18 +04:00
fprintf ( stderr , " error: unable to create netlink socket \n " ) ;
2008-09-09 16:07:08 +04:00
rc = 1 ;
goto out ;
}
2010-02-17 18:27:36 +03:00
udev_monitor_set_receive_buffer_size ( udev_monitor , 128 * 1024 * 1024 ) ;
2011-04-15 13:58:17 +04:00
fd_udev = udev_monitor_get_fd ( udev_monitor ) ;
2009-04-22 07:00:15 +04:00
udev_list_entry_foreach ( entry , udev_list_get_entry ( & subsystem_match_list ) ) {
const char * subsys = udev_list_entry_get_name ( entry ) ;
2009-04-23 06:07:15 +04:00
const char * devtype = udev_list_entry_get_value ( entry ) ;
2009-04-22 07:00:15 +04:00
2009-04-23 06:07:15 +04:00
if ( udev_monitor_filter_add_match_subsystem_devtype ( udev_monitor , subsys , devtype ) < 0 )
2009-04-22 07:00:15 +04:00
fprintf ( stderr , " error: unable to apply subsystem filter '%s' \n " , subsys ) ;
}
2010-04-22 20:12:36 +04:00
udev_list_entry_foreach ( entry , udev_list_get_entry ( & tag_match_list ) ) {
const char * tag = udev_list_entry_get_name ( entry ) ;
if ( udev_monitor_filter_add_match_tag ( udev_monitor , tag ) < 0 )
fprintf ( stderr , " error: unable to apply tag filter '%s' \n " , tag ) ;
}
2008-09-09 16:07:08 +04:00
if ( udev_monitor_enable_receiving ( udev_monitor ) < 0 ) {
2009-04-22 06:11:18 +04:00
fprintf ( stderr , " error: unable to subscribe to udev events \n " ) ;
2008-09-09 16:07:08 +04:00
rc = 2 ;
2007-03-10 17:12:42 +03:00
goto out ;
2008-09-09 16:07:08 +04:00
}
2011-04-15 13:58:17 +04:00
memset ( & ep_udev , 0 , sizeof ( struct epoll_event ) ) ;
ep_udev . events = EPOLLIN ;
ep_udev . data . fd = fd_udev ;
if ( epoll_ctl ( fd_ep , EPOLL_CTL_ADD , fd_udev , & ep_udev ) < 0 ) {
err ( udev , " fail to add fd to epoll: %m \n " ) ;
goto out ;
}
2009-03-18 20:47:23 +03:00
printf ( " UDEV - the event which udev sends out after rule processing \n " ) ;
2007-03-10 17:12:42 +03:00
}
2011-04-15 13:58:17 +04:00
2008-09-06 17:45:31 +04:00
if ( print_kernel ) {
2009-04-23 06:07:15 +04:00
struct udev_list_entry * entry ;
2009-03-29 22:08:52 +04:00
kernel_monitor = udev_monitor_new_from_netlink ( udev , " kernel " ) ;
2008-09-09 19:41:17 +04:00
if ( kernel_monitor = = NULL ) {
2009-04-22 06:11:18 +04:00
fprintf ( stderr , " error: unable to create netlink socket \n " ) ;
2008-09-09 16:07:08 +04:00
rc = 3 ;
2007-03-10 17:12:42 +03:00
goto out ;
2008-09-09 16:07:08 +04:00
}
2010-02-18 13:03:28 +03:00
udev_monitor_set_receive_buffer_size ( kernel_monitor , 128 * 1024 * 1024 ) ;
2011-04-15 13:58:17 +04:00
fd_kernel = udev_monitor_get_fd ( kernel_monitor ) ;
2009-04-23 06:07:15 +04:00
udev_list_entry_foreach ( entry , udev_list_get_entry ( & subsystem_match_list ) ) {
const char * subsys = udev_list_entry_get_name ( entry ) ;
if ( udev_monitor_filter_add_match_subsystem_devtype ( kernel_monitor , subsys , NULL ) < 0 )
fprintf ( stderr , " error: unable to apply subsystem filter '%s' \n " , subsys ) ;
}
2008-09-09 19:41:17 +04:00
if ( udev_monitor_enable_receiving ( kernel_monitor ) < 0 ) {
2009-04-22 06:11:18 +04:00
fprintf ( stderr , " error: unable to subscribe to kernel events \n " ) ;
2008-09-09 19:41:17 +04:00
rc = 4 ;
goto out ;
}
2011-04-15 13:58:17 +04:00
memset ( & ep_kernel , 0 , sizeof ( struct epoll_event ) ) ;
ep_kernel . events = EPOLLIN ;
ep_kernel . data . fd = fd_kernel ;
if ( epoll_ctl ( fd_ep , EPOLL_CTL_ADD , fd_kernel , & ep_kernel ) < 0 ) {
err ( udev , " fail to add fd to epoll: %m \n " ) ;
goto out ;
}
2009-03-29 22:08:52 +04:00
printf ( " KERNEL - the kernel uevent \n " ) ;
2007-03-10 17:12:42 +03:00
}
printf ( " \n " ) ;
2005-08-11 22:34:24 +04:00
2005-08-25 02:38:25 +04:00
while ( ! udev_exit ) {
2005-08-15 13:57:04 +04:00
int fdcount ;
2011-04-15 13:58:17 +04:00
struct epoll_event ev [ 4 ] ;
int i ;
2005-08-01 22:22:46 +04:00
2011-04-15 13:58:17 +04:00
fdcount = epoll_wait ( fd_ep , ev , ARRAY_SIZE ( ev ) , - 1 ) ;
2005-08-15 13:57:04 +04:00
if ( fdcount < 0 ) {
2005-08-01 22:22:46 +04:00
if ( errno ! = EINTR )
2008-09-29 19:01:32 +04:00
fprintf ( stderr , " error receiving uevent message: %m \n " ) ;
2005-08-01 22:22:46 +04:00
continue ;
}
2011-04-15 13:58:17 +04:00
for ( i = 0 ; i < fdcount ; i + + ) {
if ( ev [ i ] . data . fd = = fd_kernel & & ev [ i ] . events & EPOLLIN ) {
struct udev_device * device ;
2008-09-28 03:34:55 +04:00
2011-04-15 13:58:17 +04:00
device = udev_monitor_receive_device ( kernel_monitor ) ;
if ( device = = NULL )
continue ;
print_device ( device , " KERNEL " , prop ) ;
udev_device_unref ( device ) ;
2011-04-18 00:10:18 +04:00
} else if ( ev [ i ] . data . fd = = fd_udev & & ev [ i ] . events & EPOLLIN ) {
2011-04-15 13:58:17 +04:00
struct udev_device * device ;
2008-09-28 03:34:55 +04:00
2011-04-15 13:58:17 +04:00
device = udev_monitor_receive_device ( udev_monitor ) ;
if ( device = = NULL )
continue ;
print_device ( device , " UDEV " , prop ) ;
udev_device_unref ( device ) ;
}
2005-08-01 22:22:46 +04:00
}
}
2005-08-15 13:57:04 +04:00
out :
2011-04-15 13:58:17 +04:00
if ( fd_ep > = 0 )
close ( fd_ep ) ;
2008-09-09 16:07:08 +04:00
udev_monitor_unref ( udev_monitor ) ;
2008-09-09 19:41:17 +04:00
udev_monitor_unref ( kernel_monitor ) ;
2009-04-22 07:00:15 +04:00
udev_list_cleanup_entries ( udev , & subsystem_match_list ) ;
2010-04-22 20:12:36 +04:00
udev_list_cleanup_entries ( udev , & tag_match_list ) ;
2008-09-09 16:07:08 +04:00
return rc ;
2005-08-01 22:22:46 +04:00
}