2008-10-01 11:34:07 +04:00
/*
* libudev - interface to udev device information
*
* Copyright ( C ) 2008 Kay Sievers < kay . sievers @ vrfy . org >
2009-05-22 00:22:37 +04:00
* Copyright ( C ) 2009 Alan Jenkins < alan - jenkins @ tuffmail . co . uk >
2008-10-01 11:34:07 +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-10-01 11:34:07 +04:00
*/
# include <stdio.h>
# include <stdlib.h>
# include <stddef.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <dirent.h>
# include <fcntl.h>
2009-05-22 00:22:37 +04:00
# include <limits.h>
2008-10-01 11:34:07 +04:00
# include <sys/stat.h>
# include "libudev.h"
# include "libudev-private.h"
2009-06-15 19:09:43 +04:00
/**
* SECTION : libudev - queue
* @ short_description : access to currently active events
*
2010-05-31 13:18:52 +04:00
* The udev daemon processes events asynchronously . All events which do not have
* interdependencies run in parallel . This exports the current state of the
* event processing queue , and the current event sequence numbers from the kernel
2009-06-15 19:09:43 +04:00
* and the udev daemon .
*/
/**
* udev_queue :
*
* Opaque object representing the current event queue in the udev daemon .
*/
2008-10-01 11:34:07 +04:00
struct udev_queue {
struct udev * udev ;
int refcount ;
2008-10-10 00:24:43 +04:00
struct udev_list_node queue_list ;
struct udev_list_node failed_list ;
2008-10-01 11:34:07 +04:00
} ;
2009-06-15 22:28:28 +04:00
/**
* udev_queue_new :
* @ udev : udev library context
*
* The initial refcount is 1 , and needs to be decremented to
* release the resources of the udev queue context .
*
* Returns : the udev queue context , or # NULL on error .
* */
2008-10-01 11:34:07 +04:00
struct udev_queue * udev_queue_new ( struct udev * udev )
{
struct udev_queue * udev_queue ;
if ( udev = = NULL )
return NULL ;
2008-10-21 14:10:32 +04:00
udev_queue = calloc ( 1 , sizeof ( struct udev_queue ) ) ;
2008-10-01 11:34:07 +04:00
if ( udev_queue = = NULL )
return NULL ;
udev_queue - > refcount = 1 ;
udev_queue - > udev = udev ;
2008-10-10 00:24:43 +04:00
udev_list_init ( & udev_queue - > queue_list ) ;
udev_list_init ( & udev_queue - > failed_list ) ;
2008-10-01 11:34:07 +04:00
return udev_queue ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_ref :
* @ udev_queue : udev queue context
*
* Take a reference of a udev queue context .
*
* Returns : the same udev queue context .
* */
2008-10-01 11:34:07 +04:00
struct udev_queue * udev_queue_ref ( struct udev_queue * udev_queue )
{
if ( udev_queue = = NULL )
return NULL ;
udev_queue - > refcount + + ;
return udev_queue ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_unref :
* @ udev_queue : udev queue context
*
* Drop a reference of a udev queue context . If the refcount reaches zero ,
* the resources of the queue context will be released .
* */
2008-10-01 11:34:07 +04:00
void udev_queue_unref ( struct udev_queue * udev_queue )
{
if ( udev_queue = = NULL )
return ;
udev_queue - > refcount - - ;
if ( udev_queue - > refcount > 0 )
return ;
2008-10-18 21:27:38 +04:00
udev_list_cleanup_entries ( udev_queue - > udev , & udev_queue - > queue_list ) ;
udev_list_cleanup_entries ( udev_queue - > udev , & udev_queue - > failed_list ) ;
2008-10-01 11:34:07 +04:00
free ( udev_queue ) ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_udev :
* @ udev_queue : udev queue context
*
* Retrieve the udev library context the queue context was created with .
*
* Returns : the udev library context .
* */
2008-10-01 11:34:07 +04:00
struct udev * udev_queue_get_udev ( struct udev_queue * udev_queue )
{
if ( udev_queue = = NULL )
return NULL ;
return udev_queue - > udev ;
}
2009-05-22 00:22:37 +04:00
unsigned long long int udev_get_kernel_seqnum ( struct udev * udev )
2008-10-01 11:34:07 +04:00
{
char filename [ UTIL_PATH_SIZE ] ;
unsigned long long int seqnum ;
int fd ;
char buf [ 32 ] ;
ssize_t len ;
2009-05-22 00:22:37 +04:00
util_strscpyl ( filename , sizeof ( filename ) , udev_get_sys_path ( udev ) , " /kernel/uevent_seqnum " , NULL ) ;
2010-05-14 16:52:18 +04:00
fd = open ( filename , O_RDONLY | O_CLOEXEC ) ;
2008-10-01 11:34:07 +04:00
if ( fd < 0 )
return 0 ;
len = read ( fd , buf , sizeof ( buf ) ) ;
close ( fd ) ;
if ( len < = 2 )
return 0 ;
buf [ len - 1 ] = ' \0 ' ;
seqnum = strtoull ( buf , NULL , 10 ) ;
return seqnum ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_kernel_seqnum :
* @ udev_queue : udev queue context
*
* Returns : the current kernel event sequence number .
* */
2009-05-22 00:22:37 +04:00
unsigned long long int udev_queue_get_kernel_seqnum ( struct udev_queue * udev_queue )
2008-10-01 11:34:07 +04:00
{
unsigned long long int seqnum ;
if ( udev_queue = = NULL )
return - EINVAL ;
2009-05-22 00:22:37 +04:00
seqnum = udev_get_kernel_seqnum ( udev_queue - > udev ) ;
2008-11-01 22:16:24 +03:00
dbg ( udev_queue - > udev , " seqnum=%llu \n " , seqnum ) ;
2008-10-01 11:34:07 +04:00
return seqnum ;
}
2009-05-22 00:22:37 +04:00
int udev_queue_read_seqnum ( FILE * queue_file , unsigned long long int * seqnum )
{
if ( fread ( seqnum , sizeof ( unsigned long long int ) , 1 , queue_file ) ! = 1 )
return - 1 ;
return 0 ;
}
ssize_t udev_queue_skip_devpath ( FILE * queue_file )
{
unsigned short int len ;
if ( fread ( & len , sizeof ( unsigned short int ) , 1 , queue_file ) = = 1 ) {
char devpath [ len ] ;
/* use fread to skip, fseek might drop buffered data */
if ( fread ( devpath , 1 , len , queue_file ) = = len )
return len ;
}
return - 1 ;
}
ssize_t udev_queue_read_devpath ( FILE * queue_file , char * devpath , size_t size )
{
unsigned short int read_bytes = 0 ;
unsigned short int len ;
if ( fread ( & len , sizeof ( unsigned short int ) , 1 , queue_file ) ! = 1 )
return - 1 ;
read_bytes = ( len < size - 1 ) ? len : size - 1 ;
if ( fread ( devpath , 1 , read_bytes , queue_file ) ! = read_bytes )
return - 1 ;
devpath [ read_bytes ] = ' \0 ' ;
/* if devpath was too long, skip unread characters */
if ( read_bytes ! = len ) {
unsigned short int skip_bytes = len - read_bytes ;
char buf [ skip_bytes ] ;
if ( fread ( buf , 1 , skip_bytes , queue_file ) ! = skip_bytes )
return - 1 ;
}
return read_bytes ;
}
static FILE * open_queue_file ( struct udev_queue * udev_queue , unsigned long long int * seqnum_start )
2008-11-06 21:41:58 +03:00
{
char filename [ UTIL_PATH_SIZE ] ;
2009-05-22 00:22:37 +04:00
FILE * queue_file ;
2008-11-06 21:41:58 +03:00
2009-05-22 00:22:37 +04:00
util_strscpyl ( filename , sizeof ( filename ) , udev_get_dev_path ( udev_queue - > udev ) , " /.udev/queue.bin " , NULL ) ;
2010-05-14 16:52:18 +04:00
queue_file = fopen ( filename , " re " ) ;
2009-05-22 00:22:37 +04:00
if ( queue_file = = NULL )
return NULL ;
if ( udev_queue_read_seqnum ( queue_file , seqnum_start ) < 0 ) {
err ( udev_queue - > udev , " corrupt queue file \n " ) ;
fclose ( queue_file ) ;
return NULL ;
}
return queue_file ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_udev_seqnum :
* @ udev_queue : udev queue context
*
* Returns : the last known udev event sequence number .
* */
2009-05-22 00:22:37 +04:00
unsigned long long int udev_queue_get_udev_seqnum ( struct udev_queue * udev_queue )
{
unsigned long long int seqnum_udev ;
FILE * queue_file ;
queue_file = open_queue_file ( udev_queue , & seqnum_udev ) ;
if ( queue_file = = NULL )
2008-11-06 21:41:58 +03:00
return 0 ;
2009-05-22 00:22:37 +04:00
2010-05-27 17:11:00 +04:00
for ( ; ; ) {
2009-05-22 00:22:37 +04:00
unsigned long long int seqnum ;
ssize_t devpath_len ;
if ( udev_queue_read_seqnum ( queue_file , & seqnum ) < 0 )
break ;
devpath_len = udev_queue_skip_devpath ( queue_file ) ;
if ( devpath_len < 0 )
break ;
if ( devpath_len > 0 )
seqnum_udev = seqnum ;
}
fclose ( queue_file ) ;
return seqnum_udev ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_udev_is_active :
* @ udev_queue : udev queue context
*
* Returns : a flag indicating if udev is active .
* */
2009-05-22 00:22:37 +04:00
int udev_queue_get_udev_is_active ( struct udev_queue * udev_queue )
{
unsigned long long int seqnum_start ;
FILE * queue_file ;
queue_file = open_queue_file ( udev_queue , & seqnum_start ) ;
if ( queue_file = = NULL )
return 0 ;
fclose ( queue_file ) ;
return 1 ;
2008-11-06 21:41:58 +03:00
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_queue_is_empty :
* @ udev_queue : udev queue context
*
* Returns : a flag indicating if udev is currently handling events .
* */
2008-10-01 11:34:07 +04:00
int udev_queue_get_queue_is_empty ( struct udev_queue * udev_queue )
{
unsigned long long int seqnum_kernel ;
2009-05-22 00:22:37 +04:00
unsigned long long int seqnum_udev = 0 ;
int queued = 0 ;
int is_empty = 0 ;
FILE * queue_file ;
2008-10-01 11:34:07 +04:00
if ( udev_queue = = NULL )
return - EINVAL ;
2009-05-22 00:22:37 +04:00
queue_file = open_queue_file ( udev_queue , & seqnum_udev ) ;
if ( queue_file = = NULL )
return 1 ;
2010-05-25 14:17:43 +04:00
for ( ; ; ) {
2009-05-22 00:22:37 +04:00
unsigned long long int seqnum ;
ssize_t devpath_len ;
if ( udev_queue_read_seqnum ( queue_file , & seqnum ) < 0 )
break ;
devpath_len = udev_queue_skip_devpath ( queue_file ) ;
if ( devpath_len < 0 )
break ;
if ( devpath_len > 0 ) {
queued + + ;
seqnum_udev = seqnum ;
} else {
queued - - ;
}
}
if ( queued > 0 ) {
2008-11-01 22:16:24 +03:00
dbg ( udev_queue - > udev , " queue is not empty \n " ) ;
2009-05-22 00:22:37 +04:00
goto out ;
2008-10-01 11:34:07 +04:00
}
2009-05-22 00:22:37 +04:00
2008-10-01 11:34:07 +04:00
seqnum_kernel = udev_queue_get_kernel_seqnum ( udev_queue ) ;
2009-05-22 00:22:37 +04:00
if ( seqnum_udev < seqnum_kernel ) {
dbg ( udev_queue - > udev , " queue is empty but kernel events still pending [%llu]<->[%llu] \n " ,
seqnum_kernel , seqnum_udev ) ;
goto out ;
2008-10-01 11:34:07 +04:00
}
2009-05-22 00:22:37 +04:00
dbg ( udev_queue - > udev , " queue is empty \n " ) ;
is_empty = 1 ;
out :
fclose ( queue_file ) ;
return is_empty ;
2008-10-01 11:34:07 +04:00
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_seqnum_sequence_is_finished :
* @ udev_queue : udev queue context
* @ start : first event sequence number
* @ end : last event sequence number
*
* Returns : if any of the sequence numbers in the given range is currently active .
* */
2009-05-22 00:22:37 +04:00
int udev_queue_get_seqnum_sequence_is_finished ( struct udev_queue * udev_queue ,
unsigned long long int start , unsigned long long int end )
2008-10-01 11:34:07 +04:00
{
2010-05-25 14:17:43 +04:00
unsigned long long int seqnum ;
2009-05-22 00:22:37 +04:00
ssize_t devpath_len ;
int unfinished ;
FILE * queue_file ;
2008-10-01 11:34:07 +04:00
if ( udev_queue = = NULL )
return - EINVAL ;
2009-05-22 00:22:37 +04:00
queue_file = open_queue_file ( udev_queue , & seqnum ) ;
if ( queue_file = = NULL )
return 1 ;
if ( start < seqnum )
start = seqnum ;
2009-09-06 19:28:26 +04:00
if ( start > end ) {
fclose ( queue_file ) ;
2009-05-22 00:22:37 +04:00
return 1 ;
2009-09-06 19:28:26 +04:00
}
if ( end - start > INT_MAX - 1 ) {
fclose ( queue_file ) ;
2009-05-22 00:22:37 +04:00
return - EOVERFLOW ;
2009-09-06 19:28:26 +04:00
}
2009-05-22 00:22:37 +04:00
2010-05-25 14:17:43 +04:00
/*
* we might start with 0 , and handle the initial seqnum
* only when we find an entry in the queue file
* */
unfinished = end - start ;
do {
2009-05-22 00:22:37 +04:00
if ( udev_queue_read_seqnum ( queue_file , & seqnum ) < 0 )
break ;
devpath_len = udev_queue_skip_devpath ( queue_file ) ;
if ( devpath_len < 0 )
break ;
2010-05-25 14:17:43 +04:00
/*
* we might start with an empty or re - build queue file , where
* the initial seqnum is not recorded as finished
*/
if ( start = = seqnum & & devpath_len > 0 )
unfinished + + ;
2009-05-22 00:22:37 +04:00
if ( devpath_len = = 0 ) {
if ( seqnum > = start & & seqnum < = end )
unfinished - - ;
}
2010-05-25 14:17:43 +04:00
} while ( unfinished > 0 ) ;
2009-05-22 00:22:37 +04:00
fclose ( queue_file ) ;
return ( unfinished = = 0 ) ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_seqnum_is_finished :
* @ udev_queue : udev queue context
* @ seqnum : sequence number
*
* Returns : a flag indicating if the given sequence number is handled .
* */
2009-05-22 00:22:37 +04:00
int udev_queue_get_seqnum_is_finished ( struct udev_queue * udev_queue , unsigned long long int seqnum )
{
if ( ! udev_queue_get_seqnum_sequence_is_finished ( udev_queue , seqnum , seqnum ) )
2008-10-01 11:34:07 +04:00
return 0 ;
2009-05-22 00:22:37 +04:00
2008-11-01 22:16:24 +03:00
dbg ( udev_queue - > udev , " seqnum: %llu finished \n " , seqnum ) ;
2008-10-01 11:34:07 +04:00
return 1 ;
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_queued_list_entry :
* @ udev_queue : udev queue context
*
* Returns : the first entry of the list of queued events .
* */
2008-10-01 11:34:07 +04:00
struct udev_list_entry * udev_queue_get_queued_list_entry ( struct udev_queue * udev_queue )
{
2009-05-22 00:22:37 +04:00
unsigned long long int seqnum ;
FILE * queue_file ;
2008-10-01 11:34:07 +04:00
if ( udev_queue = = NULL )
return NULL ;
2008-10-18 21:27:38 +04:00
udev_list_cleanup_entries ( udev_queue - > udev , & udev_queue - > queue_list ) ;
2009-05-22 00:22:37 +04:00
queue_file = open_queue_file ( udev_queue , & seqnum ) ;
if ( queue_file = = NULL )
2008-10-01 11:34:07 +04:00
return NULL ;
2009-05-22 00:22:37 +04:00
2010-05-27 17:11:00 +04:00
for ( ; ; ) {
2008-10-01 11:34:07 +04:00
char syspath [ UTIL_PATH_SIZE ] ;
2009-05-20 19:57:52 +04:00
char * s ;
size_t l ;
2008-10-01 11:34:07 +04:00
ssize_t len ;
2009-05-22 00:22:37 +04:00
char seqnum_str [ 32 ] ;
struct udev_list_entry * list_entry ;
if ( udev_queue_read_seqnum ( queue_file , & seqnum ) < 0 )
break ;
snprintf ( seqnum_str , sizeof ( seqnum_str ) , " %llu " , seqnum ) ;
2008-10-01 11:34:07 +04:00
2009-05-20 19:57:52 +04:00
s = syspath ;
l = util_strpcpyl ( & s , sizeof ( syspath ) , udev_get_sys_path ( udev_queue - > udev ) , NULL ) ;
2009-05-22 00:22:37 +04:00
len = udev_queue_read_devpath ( queue_file , s , l ) ;
if ( len < 0 )
break ;
if ( len > 0 ) {
udev_list_entry_add ( udev_queue - > udev , & udev_queue - > queue_list , syspath , seqnum_str , 0 , 0 ) ;
} else {
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_queue - > queue_list ) ) {
if ( strcmp ( seqnum_str , udev_list_entry_get_value ( list_entry ) ) = = 0 ) {
udev_list_entry_delete ( list_entry ) ;
break ;
}
}
}
2008-10-01 11:34:07 +04:00
}
2009-05-22 00:22:37 +04:00
fclose ( queue_file ) ;
2008-10-10 00:24:43 +04:00
return udev_list_get_entry ( & udev_queue - > queue_list ) ;
2008-10-01 11:34:07 +04:00
}
2009-06-15 22:28:28 +04:00
/**
* udev_queue_get_failed_list_entry :
* @ udev_queue : udev queue context
*
* Returns : the first entry of the list of recorded failed events .
* */
2008-10-01 11:34:07 +04:00
struct udev_list_entry * udev_queue_get_failed_list_entry ( struct udev_queue * udev_queue )
{
char path [ UTIL_PATH_SIZE ] ;
DIR * dir ;
struct dirent * dent ;
if ( udev_queue = = NULL )
return NULL ;
2008-10-18 21:27:38 +04:00
udev_list_cleanup_entries ( udev_queue - > udev , & udev_queue - > failed_list ) ;
2009-05-20 19:57:52 +04:00
util_strscpyl ( path , sizeof ( path ) , udev_get_dev_path ( udev_queue - > udev ) , " /.udev/failed " , NULL ) ;
2008-10-01 11:34:07 +04:00
dir = opendir ( path ) ;
if ( dir = = NULL )
return NULL ;
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
char filename [ UTIL_PATH_SIZE ] ;
char syspath [ UTIL_PATH_SIZE ] ;
2009-05-20 19:57:52 +04:00
char * s ;
size_t l ;
2008-10-01 11:34:07 +04:00
ssize_t len ;
2009-05-20 19:57:52 +04:00
struct stat statbuf ;
2008-10-01 11:34:07 +04:00
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
2009-05-20 19:57:52 +04:00
s = syspath ;
l = util_strpcpyl ( & s , sizeof ( syspath ) , udev_get_sys_path ( udev_queue - > udev ) , NULL ) ;
2009-05-20 20:58:52 +04:00
len = readlinkat ( dirfd ( dir ) , dent - > d_name , s , l ) ;
2010-04-21 16:44:33 +04:00
if ( len < = 0 | | ( size_t ) len = = l )
2008-10-01 11:34:07 +04:00
continue ;
2009-05-20 19:57:52 +04:00
s [ len ] = ' \0 ' ;
2008-11-01 22:16:24 +03:00
dbg ( udev_queue - > udev , " found '%s' [%s] \n " , syspath , dent - > d_name ) ;
2009-05-20 19:57:52 +04:00
util_strscpyl ( filename , sizeof ( filename ) , syspath , " /uevent " , NULL ) ;
2008-10-01 11:34:07 +04:00
if ( stat ( filename , & statbuf ) ! = 0 )
continue ;
2008-10-10 00:24:43 +04:00
udev_list_entry_add ( udev_queue - > udev , & udev_queue - > failed_list , syspath , NULL , 0 , 0 ) ;
2008-10-01 11:34:07 +04:00
}
closedir ( dir ) ;
2008-10-10 00:24:43 +04:00
return udev_list_get_entry ( & udev_queue - > failed_list ) ;
2008-10-01 11:34:07 +04:00
}