2005-08-01 22:22:46 +04:00
/*
2006-08-05 15:24:05 +04:00
* Copyright ( C ) 2004 - 2006 Kay Sievers < kay . sievers @ vrfy . org >
2005-08-01 22:22:46 +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 version 2 of the License .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
2006-08-28 02:29:11 +04:00
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
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>
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>
# include <sys/select.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"
# include "udevd.h"
2006-08-05 15:24:05 +04:00
static int uevent_netlink_sock = - 1 ;
static int udev_monitor_sock = - 1 ;
2005-08-25 02:38:25 +04:00
static volatile int udev_exit ;
2005-08-01 22:22:46 +04:00
static int init_udev_monitor_socket ( void )
{
struct sockaddr_un saddr ;
socklen_t addrlen ;
const int feature_on = 1 ;
int retval ;
memset ( & saddr , 0x00 , sizeof ( saddr ) ) ;
saddr . sun_family = AF_LOCAL ;
/* use abstract namespace for socket path */
strcpy ( & saddr . sun_path [ 1 ] , " /org/kernel/udev/monitor " ) ;
addrlen = offsetof ( struct sockaddr_un , sun_path ) + strlen ( saddr . sun_path + 1 ) + 1 ;
udev_monitor_sock = socket ( AF_LOCAL , SOCK_DGRAM , 0 ) ;
if ( udev_monitor_sock = = - 1 ) {
2005-11-07 20:44:18 +03:00
fprintf ( stderr , " error getting socket: %s \n " , strerror ( errno ) ) ;
2005-08-01 22:22:46 +04:00
return - 1 ;
}
/* the bind takes care of ensuring only one copy running */
retval = bind ( udev_monitor_sock , ( struct sockaddr * ) & saddr , addrlen ) ;
if ( retval < 0 ) {
2005-11-07 20:44:18 +03:00
fprintf ( stderr , " bind failed: %s \n " , strerror ( errno ) ) ;
2005-08-01 22:22:46 +04:00
close ( udev_monitor_sock ) ;
2005-08-22 14:01:55 +04:00
udev_monitor_sock = - 1 ;
2005-08-01 22:22:46 +04:00
return - 1 ;
}
/* enable receiving of the sender credentials */
setsockopt ( udev_monitor_sock , SOL_SOCKET , SO_PASSCRED , & feature_on , sizeof ( feature_on ) ) ;
return 0 ;
}
static int init_uevent_netlink_sock ( void )
{
struct sockaddr_nl snl ;
int retval ;
memset ( & snl , 0x00 , sizeof ( struct sockaddr_nl ) ) ;
snl . nl_family = AF_NETLINK ;
snl . nl_pid = getpid ( ) ;
2006-07-21 23:41:11 +04:00
snl . nl_groups = 1 ;
2005-08-01 22:22:46 +04:00
uevent_netlink_sock = socket ( PF_NETLINK , SOCK_DGRAM , NETLINK_KOBJECT_UEVENT ) ;
if ( uevent_netlink_sock = = - 1 ) {
2005-11-07 20:44:18 +03:00
fprintf ( stderr , " error getting socket: %s \n " , strerror ( errno ) ) ;
2005-08-01 22:22:46 +04:00
return - 1 ;
}
retval = bind ( uevent_netlink_sock , ( struct sockaddr * ) & snl ,
sizeof ( struct sockaddr_nl ) ) ;
if ( retval < 0 ) {
2005-11-07 20:44:18 +03:00
fprintf ( stderr , " bind failed: %s \n " , strerror ( errno ) ) ;
2005-08-01 22:22:46 +04:00
close ( uevent_netlink_sock ) ;
uevent_netlink_sock = - 1 ;
return - 1 ;
}
return 0 ;
}
2005-08-25 02:38:25 +04:00
static void asmlinkage sig_handler ( int signum )
{
if ( signum = = SIGINT | | signum = = SIGTERM )
udev_exit = 1 ;
}
2005-08-01 22:22:46 +04:00
int main ( int argc , char * argv [ ] )
{
2005-08-25 02:38:25 +04:00
struct sigaction act ;
2005-08-01 22:22:46 +04:00
int env = 0 ;
fd_set readfds ;
2005-08-11 22:34:24 +04:00
int i ;
2005-08-15 13:57:04 +04:00
int retval = 0 ;
2005-08-01 22:22:46 +04:00
2005-08-11 22:34:24 +04:00
for ( i = 1 ; i < argc ; i + + ) {
char * arg = argv [ i ] ;
2006-08-21 04:38:20 +04:00
if ( strcmp ( arg , " --env " ) = = 0 | | strcmp ( arg , " -e " ) = = 0 )
2005-08-11 22:34:24 +04:00
env = 1 ;
else if ( strcmp ( arg , " --help " ) = = 0 | | strcmp ( arg , " -h " ) = = 0 ) {
2006-08-21 04:38:20 +04:00
printf ( " Usage: udevmonitor [--help] [--env] \n "
2005-08-11 22:34:24 +04:00
" --env print the whole event environment \n "
" --help print this help text \n \n " ) ;
exit ( 0 ) ;
} else {
2006-08-21 04:38:20 +04:00
fprintf ( stderr , " unrecognized option '%s' \n " , arg ) ;
2005-08-11 22:34:24 +04:00
exit ( 1 ) ;
}
}
2005-08-01 22:22:46 +04:00
if ( getuid ( ) ! = 0 ) {
2006-08-21 04:38:20 +04:00
fprintf ( stderr , " root privileges required \n " ) ;
2005-08-22 14:01:55 +04:00
exit ( 2 ) ;
2005-08-01 22:22:46 +04:00
}
2005-08-25 02:38:25 +04:00
/* set signal handlers */
memset ( & act , 0x00 , sizeof ( struct sigaction ) ) ;
act . sa_handler = ( void ( * ) ( int ) ) sig_handler ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = SA_RESTART ;
sigaction ( SIGINT , & act , NULL ) ;
sigaction ( SIGTERM , & act , NULL ) ;
2005-08-15 13:57:04 +04:00
retval = init_udev_monitor_socket ( ) ;
if ( retval )
goto out ;
2006-08-05 15:24:05 +04:00
2005-08-22 14:01:55 +04:00
retval = init_uevent_netlink_sock ( ) ;
if ( retval )
goto out ;
2005-08-01 22:22:46 +04:00
2005-08-18 15:43:27 +04:00
printf ( " udevmonitor prints the received event from the kernel [UEVENT] \n "
" and the event which udev sends out after rule processing [UDEV] \n \n " ) ;
2005-08-11 22:34:24 +04:00
2005-08-25 02:38:25 +04:00
while ( ! udev_exit ) {
2006-07-25 16:45:09 +04:00
char buf [ UEVENT_BUFFER_SIZE * 2 ] ;
2005-08-01 22:22:46 +04:00
ssize_t buflen ;
2005-08-15 13:57:04 +04:00
int fdcount ;
2005-11-07 04:23:06 +03:00
struct timeval tv ;
struct timezone tz ;
char timestr [ 64 ] ;
2005-08-01 22:22:46 +04:00
buflen = 0 ;
2005-08-15 13:57:04 +04:00
FD_ZERO ( & readfds ) ;
2006-08-05 15:24:05 +04:00
if ( uevent_netlink_sock > = 0 )
2005-08-15 13:57:04 +04:00
FD_SET ( uevent_netlink_sock , & readfds ) ;
2006-08-05 15:24:05 +04:00
if ( udev_monitor_sock > = 0 )
2005-08-15 13:57:04 +04:00
FD_SET ( udev_monitor_sock , & readfds ) ;
fdcount = select ( UDEV_MAX ( uevent_netlink_sock , udev_monitor_sock ) + 1 , & readfds , NULL , NULL , NULL ) ;
if ( fdcount < 0 ) {
2005-08-01 22:22:46 +04:00
if ( errno ! = EINTR )
2005-11-07 20:44:18 +03:00
fprintf ( stderr , " error receiving uevent message: %s \n " , strerror ( errno ) ) ;
2005-08-01 22:22:46 +04:00
continue ;
}
2005-11-07 04:23:06 +03:00
if ( gettimeofday ( & tv , & tz ) = = 0 ) {
snprintf ( timestr , sizeof ( timestr ) , " %llu.%06u " ,
( unsigned long long ) tv . tv_sec , ( unsigned int ) tv . tv_usec ) ;
} else
timestr [ 0 ] = ' \0 ' ;
2006-08-05 15:24:05 +04:00
if ( ( uevent_netlink_sock > = 0 ) & & FD_ISSET ( uevent_netlink_sock , & readfds ) ) {
2005-08-01 22:22:46 +04:00
buflen = recv ( uevent_netlink_sock , & buf , sizeof ( buf ) , 0 ) ;
if ( buflen < = 0 ) {
2005-11-07 20:44:18 +03:00
fprintf ( stderr , " error receiving uevent message: %s \n " , strerror ( errno ) ) ;
2005-08-01 22:22:46 +04:00
continue ;
}
2005-11-07 04:23:06 +03:00
printf ( " UEVENT[%s] %s \n " , timestr , buf ) ;
2005-08-01 22:22:46 +04:00
}
2006-08-05 15:24:05 +04:00
if ( ( udev_monitor_sock > = 0 ) & & FD_ISSET ( udev_monitor_sock , & readfds ) ) {
2005-08-01 22:22:46 +04:00
buflen = recv ( udev_monitor_sock , & buf , sizeof ( buf ) , 0 ) ;
if ( buflen < = 0 ) {
2005-11-07 20:44:18 +03:00
fprintf ( stderr , " error receiving udev message: %s \n " , strerror ( errno ) ) ;
2005-08-01 22:22:46 +04:00
continue ;
}
2005-11-07 04:23:06 +03:00
printf ( " UDEV [%s] %s \n " , timestr , buf ) ;
2005-08-01 22:22:46 +04:00
}
if ( buflen = = 0 )
continue ;
/* print environment */
if ( env ) {
size_t bufpos ;
/* start of payload */
bufpos = strlen ( buf ) + 1 ;
while ( bufpos < ( size_t ) buflen ) {
int keylen ;
char * key ;
key = & buf [ bufpos ] ;
keylen = strlen ( key ) ;
if ( keylen = = 0 )
break ;
printf ( " %s \n " , key ) ;
bufpos + = keylen + 1 ;
}
printf ( " \n " ) ;
}
}
2005-08-15 13:57:04 +04:00
out :
2006-08-05 15:24:05 +04:00
if ( uevent_netlink_sock > = 0 )
2005-08-11 22:34:24 +04:00
close ( uevent_netlink_sock ) ;
2006-08-05 15:24:05 +04:00
if ( udev_monitor_sock > = 0 )
2005-08-11 22:34:24 +04:00
close ( udev_monitor_sock ) ;
2005-08-15 13:57:04 +04:00
2005-08-22 14:01:55 +04:00
if ( retval )
return 3 ;
return 0 ;
2005-08-01 22:22:46 +04:00
}