2007-07-24 15:28:18 +04:00
/*
* Collect variables across events .
*
* usage : collect [ - - add | - - remove ] < checkpoint > < id > < idlist >
*
* Adds ID < id > to the list governed by < checkpoint > .
* < id > must be part of the ID list < idlist > .
* If all IDs given by < idlist > are listed ( ie collect has been
* invoked for each ID in < idlist > ) collect returns 0 , the
* number of missing IDs otherwise .
* A negative number is returned on error .
*
* Copyright ( C ) 2007 , Hannes Reinecke < hare @ suse . de >
*
* 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 .
*
*/
# include <stdio.h>
# include <stdlib.h>
# include <stddef.h>
# include <unistd.h>
# include <signal.h>
# include <fcntl.h>
# include <errno.h>
# include <string.h>
# include <getopt.h>
# include <sys/types.h>
# include <sys/stat.h>
2008-10-17 21:29:02 +04:00
# include "../../udev/lib/libudev.h"
# include "../../udev/lib/libudev-private.h"
2007-07-24 15:28:18 +04:00
2008-07-30 03:45:23 +04:00
# define TMPFILE UDEV_PREFIX " / dev / .udev / collect"
2007-07-24 15:28:18 +04:00
# define BUFSIZE 16
# define UDEV_ALARM_TIMEOUT 180
enum collect_state {
STATE_NONE ,
STATE_OLD ,
STATE_CONFIRMED ,
} ;
struct _mate {
2008-10-17 21:29:02 +04:00
struct udev_list_node node ;
2007-07-24 15:28:18 +04:00
char * name ;
enum collect_state state ;
} ;
2008-10-17 21:29:02 +04:00
static struct udev_list_node bunch ;
2007-07-24 15:28:18 +04:00
static int debug ;
/* This can increase dynamically */
2008-06-18 02:47:33 +04:00
static size_t bufsize = BUFSIZE ;
2007-07-24 15:28:18 +04:00
2008-10-17 21:29:02 +04:00
static struct _mate * node_to_mate ( struct udev_list_node * node )
{
char * mate ;
mate = ( char * ) node ;
mate - = offsetof ( struct _mate , node ) ;
return ( struct _mate * ) mate ;
}
2007-07-24 15:28:18 +04:00
static void sig_alrm ( int signo )
{
exit ( 4 ) ;
}
static void usage ( void )
{
printf ( " usage: collect [--add|--remove] [--debug] <checkpoint> <id> <idlist> \n "
" \n "
" Adds ID <id> to the list governed by <checkpoint>. \n "
" <id> must be part of the list <idlist>. \n "
" If all IDs given by <idlist> are listed (ie collect has been \n "
" invoked for each ID in <idlist>) collect returns 0, the \n "
" number of missing IDs otherwise. \n "
" On error a negative number is returned. \n "
" \n " ) ;
}
/*
* prepare
*
* Prepares the database file
*/
static int prepare ( char * dir , char * filename )
{
struct stat statbuf ;
char buf [ 512 ] ;
int fd ;
if ( stat ( dir , & statbuf ) < 0 )
mkdir ( dir , 0700 ) ;
sprintf ( buf , " %s/%s " , dir , filename ) ;
fd = open ( buf , O_RDWR | O_CREAT , S_IRUSR | S_IWUSR ) ;
if ( fd < 0 )
fprintf ( stderr , " Cannot open %s: %s \n " , buf , strerror ( errno ) ) ;
if ( lockf ( fd , F_TLOCK , 0 ) < 0 ) {
if ( debug )
fprintf ( stderr , " Lock taken, wait for %d seconds \n " , UDEV_ALARM_TIMEOUT ) ;
if ( errno = = EAGAIN | | errno = = EACCES ) {
alarm ( UDEV_ALARM_TIMEOUT ) ;
lockf ( fd , F_LOCK , 0 ) ;
if ( debug )
fprintf ( stderr , " Acquired lock on %s \n " , buf ) ;
} else {
if ( debug )
fprintf ( stderr , " Could not get lock on %s: %s \n " , buf , strerror ( errno ) ) ;
}
}
return fd ;
}
/*
* Read checkpoint file
*
* Tricky reading this . We allocate a buffer twice as large
* as we ' re goint to read . Then we read into the upper half
* of that buffer and start parsing .
* Once we do _not_ find end - of - work terminator ( whitespace
* character ) we move the upper half to the lower half ,
* adjust the read pointer and read the next bit .
* Quite clever methinks : - )
* I should become a programmer . . .
*
* Yes , one could have used fgets ( ) for this . But then we ' d
* have to use freopen etc which I found quite tedious .
*/
static int checkout ( int fd )
{
int len ;
char * buf , * ptr , * word = NULL ;
struct _mate * him ;
restart :
len = bufsize > > 1 ;
buf = calloc ( 1 , bufsize + 1 ) ;
if ( ! buf ) {
fprintf ( stderr , " Out of memory \n " ) ;
return - 1 ;
}
memset ( buf , ' ' , bufsize ) ;
ptr = buf + len ;
while ( ( read ( fd , buf + len , len ) ) > 0 ) {
while ( ptr & & * ptr ) {
word = ptr ;
ptr = strpbrk ( word , " \n \t \r " ) ;
if ( ! ptr & & word < ( buf + len ) ) {
bufsize = bufsize < < 1 ;
if ( debug )
2008-06-18 12:12:14 +04:00
fprintf ( stderr , " ID overflow, restarting with size %zi \n " , bufsize ) ;
2007-07-24 15:28:18 +04:00
free ( buf ) ;
lseek ( fd , 0 , SEEK_SET ) ;
goto restart ;
}
if ( ptr ) {
* ptr = ' \0 ' ;
ptr + + ;
if ( ! strlen ( word ) )
continue ;
if ( debug )
fprintf ( stderr , " Found word %s \n " , word ) ;
him = malloc ( sizeof ( struct _mate ) ) ;
2008-10-21 14:10:32 +04:00
him - > name = strdup ( word ) ;
2007-07-24 15:28:18 +04:00
him - > state = STATE_OLD ;
2008-10-17 21:29:02 +04:00
udev_list_node_append ( & him - > node , & bunch ) ;
2007-07-24 15:28:18 +04:00
word = NULL ;
}
}
memcpy ( buf , buf + len , len ) ;
memset ( buf + len , ' ' , len ) ;
if ( ! ptr )
ptr = word ;
if ( ! ptr )
break ;
ptr - = len ;
}
free ( buf ) ;
return 0 ;
}
/*
* invite
*
* Adds a new ID ' us ' to the internal list ,
* marks it as confirmed .
*/
static void invite ( char * us )
{
2008-10-17 21:29:02 +04:00
struct udev_list_node * him_node ;
struct _mate * who = NULL ;
2007-07-24 15:28:18 +04:00
if ( debug )
fprintf ( stderr , " Adding ID '%s' \n " , us ) ;
2008-10-17 21:29:02 +04:00
udev_list_node_foreach ( him_node , & bunch ) {
struct _mate * him = node_to_mate ( him_node ) ;
2007-07-24 15:28:18 +04:00
if ( ! strcmp ( him - > name , us ) ) {
him - > state = STATE_CONFIRMED ;
who = him ;
}
}
if ( debug & & ! who )
fprintf ( stderr , " ID '%s' not in database \n " , us ) ;
}
/*
* reject
*
* Marks the ID ' us ' as invalid ,
* causing it to be removed when the
* list is written out .
*/
static void reject ( char * us )
{
2008-10-17 21:29:02 +04:00
struct udev_list_node * him_node ;
struct _mate * who = NULL ;
2007-07-24 15:28:18 +04:00
if ( debug )
fprintf ( stderr , " Removing ID '%s' \n " , us ) ;
2008-10-17 21:29:02 +04:00
udev_list_node_foreach ( him_node , & bunch ) {
struct _mate * him = node_to_mate ( him_node ) ;
2007-07-24 15:28:18 +04:00
if ( ! strcmp ( him - > name , us ) ) {
him - > state = STATE_NONE ;
who = him ;
}
}
if ( debug & & ! who )
fprintf ( stderr , " ID '%s' not in database \n " , us ) ;
}
/*
* kickout
*
* Remove all IDs in the internal list which are not part
* of the list passed via the commandline .
*/
static void kickout ( void )
{
2008-10-17 21:29:02 +04:00
struct udev_list_node * him_node ;
struct udev_list_node * tmp ;
udev_list_node_foreach_safe ( him_node , tmp , & bunch ) {
struct _mate * him = node_to_mate ( him_node ) ;
2007-07-24 15:28:18 +04:00
if ( him - > state = = STATE_OLD ) {
2008-10-17 21:29:02 +04:00
udev_list_node_remove ( & him - > node ) ;
2007-07-24 15:28:18 +04:00
free ( him - > name ) ;
free ( him ) ;
}
}
}
/*
* missing
*
* Counts all missing IDs in the internal list .
*/
static int missing ( int fd )
{
char * buf ;
int ret = 0 ;
2008-10-17 21:29:02 +04:00
struct udev_list_node * him_node ;
2007-07-24 15:28:18 +04:00
buf = malloc ( bufsize ) ;
if ( ! buf )
return - 1 ;
2008-10-17 21:29:02 +04:00
udev_list_node_foreach ( him_node , & bunch ) {
struct _mate * him = node_to_mate ( him_node ) ;
2007-07-24 15:28:18 +04:00
if ( him - > state = = STATE_NONE ) {
ret + + ;
} else {
2008-05-27 21:35:03 +04:00
while ( strlen ( him - > name ) + 1 > = bufsize ) {
2008-06-18 02:47:33 +04:00
char * tmpbuf ;
2008-05-27 21:35:03 +04:00
bufsize = bufsize < < 1 ;
2008-06-18 02:47:33 +04:00
tmpbuf = realloc ( buf , bufsize ) ;
if ( ! tmpbuf ) {
free ( buf ) ;
return - 1 ;
}
buf = tmpbuf ;
2008-05-27 21:35:03 +04:00
}
snprintf ( buf , strlen ( him - > name ) + 2 , " %s " , him - > name ) ;
2007-07-24 15:28:18 +04:00
write ( fd , buf , strlen ( buf ) ) ;
}
}
free ( buf ) ;
return ret ;
}
/*
* everybody
*
* Prints out the status of the internal list .
*/
static void everybody ( void )
{
2008-10-17 21:29:02 +04:00
struct udev_list_node * him_node ;
2007-07-24 15:28:18 +04:00
const char * state = " " ;
2008-10-17 21:29:02 +04:00
udev_list_node_foreach ( him_node , & bunch ) {
struct _mate * him = node_to_mate ( him_node ) ;
2007-07-24 15:28:18 +04:00
switch ( him - > state ) {
case STATE_NONE :
state = " none " ;
break ;
case STATE_OLD :
state = " old " ;
break ;
case STATE_CONFIRMED :
state = " confirmed " ;
break ;
fprintf ( stderr , " ID: %s=%s \n " , him - > name , state ) ;
}
}
}
int main ( int argc , char * * argv )
{
static const struct option options [ ] = {
2008-10-02 18:49:05 +04:00
{ " add " , no_argument , NULL , ' a ' } ,
{ " remove " , no_argument , NULL , ' r ' } ,
{ " debug " , no_argument , NULL , ' d ' } ,
{ " help " , no_argument , NULL , ' h ' } ,
2007-07-24 15:28:18 +04:00
{ }
} ;
int argi ;
char * checkpoint , * us ;
int fd ;
int i ;
int ret = 0 ;
int prune = 0 ;
while ( 1 ) {
int option ;
option = getopt_long ( argc , argv , " ardh " , options , NULL ) ;
if ( option = = - 1 )
break ;
switch ( option ) {
case ' a ' :
prune = 0 ;
break ;
case ' r ' :
prune = 1 ;
break ;
case ' d ' :
debug = 1 ;
break ;
case ' h ' :
usage ( ) ;
goto exit ;
default :
ret = 1 ;
goto exit ;
}
}
argi = optind ;
if ( argi + 2 > argc ) {
printf ( " Missing parameter(s) \n " ) ;
ret = 1 ;
goto exit ;
}
checkpoint = argv [ argi + + ] ;
us = argv [ argi + + ] ;
if ( signal ( SIGALRM , sig_alrm ) = = SIG_ERR ) {
fprintf ( stderr , " Cannot set SIGALRM: %s \n " , strerror ( errno ) ) ;
ret = 2 ;
goto exit ;
}
2008-10-17 21:29:02 +04:00
udev_list_init ( & bunch ) ;
2007-07-24 15:28:18 +04:00
if ( debug )
fprintf ( stderr , " Using checkpoint '%s' \n " , checkpoint ) ;
fd = prepare ( TMPFILE , checkpoint ) ;
if ( fd < 0 ) {
ret = 3 ;
goto out ;
}
if ( checkout ( fd ) < 0 ) {
ret = 2 ;
goto out ;
}
for ( i = argi ; i < argc ; i + + ) {
2008-10-17 21:29:02 +04:00
struct udev_list_node * him_node ;
struct _mate * who ;
2007-07-24 15:28:18 +04:00
who = NULL ;
2008-10-17 21:29:02 +04:00
udev_list_node_foreach ( him_node , & bunch ) {
struct _mate * him = node_to_mate ( him_node ) ;
2007-07-24 15:28:18 +04:00
if ( ! strcmp ( him - > name , argv [ i ] ) )
who = him ;
}
if ( ! who ) {
2008-10-17 21:29:02 +04:00
struct _mate * him ;
2007-07-24 15:28:18 +04:00
if ( debug )
fprintf ( stderr , " ID %s: not in database \n " , argv [ i ] ) ;
him = malloc ( sizeof ( struct _mate ) ) ;
him - > name = malloc ( strlen ( argv [ i ] ) + 1 ) ;
strcpy ( him - > name , argv [ i ] ) ;
him - > state = STATE_NONE ;
2008-10-17 21:29:02 +04:00
udev_list_node_append ( & him - > node , & bunch ) ;
2007-07-24 15:28:18 +04:00
} else {
if ( debug )
fprintf ( stderr , " ID %s: found in database \n " , argv [ i ] ) ;
who - > state = STATE_CONFIRMED ;
}
}
if ( prune )
reject ( us ) ;
else
invite ( us ) ;
if ( debug ) {
everybody ( ) ;
fprintf ( stderr , " Prune lists \n " ) ;
}
kickout ( ) ;
lseek ( fd , 0 , SEEK_SET ) ;
ftruncate ( fd , 0 ) ;
ret = missing ( fd ) ;
lockf ( fd , F_ULOCK , 0 ) ;
close ( fd ) ;
out :
if ( debug )
everybody ( ) ;
if ( ret > = 0 )
printf ( " COLLECT_%s=%d \n " , checkpoint , ret ) ;
exit :
return ret ;
}