2005-08-10 18:54:14 +04:00
/*
* udev_utils_run . c - execute programs from udev and read its output
*
* Copyright ( C ) 2004 - 2005 Kay Sievers < kay . sievers @ vrfy . org >
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <stdlib.h>
# include <stdio.h>
# include <stddef.h>
# include <unistd.h>
# include <fcntl.h>
# include <errno.h>
# include <ctype.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <sys/wait.h>
2005-08-11 19:32:59 +04:00
# include <sys/select.h>
2005-08-10 18:54:14 +04:00
# include "udev_libc_wrapper.h"
# include "udev.h"
# include "logging.h"
# include "udev_utils.h"
# include "list.h"
int pass_env_to_socket ( const char * sockname , const char * devpath , const char * action )
{
int sock ;
struct sockaddr_un saddr ;
socklen_t addrlen ;
char buf [ 2048 ] ;
size_t bufpos = 0 ;
int i ;
2005-08-15 13:57:04 +04:00
ssize_t count ;
int retval = 0 ;
2005-08-10 18:54:14 +04:00
dbg ( " pass environment to socket '%s' " , sockname ) ;
sock = socket ( AF_LOCAL , SOCK_DGRAM , 0 ) ;
memset ( & saddr , 0x00 , sizeof ( struct sockaddr_un ) ) ;
saddr . sun_family = AF_LOCAL ;
/* only abstract namespace is supported */
strcpy ( & saddr . sun_path [ 1 ] , sockname ) ;
addrlen = offsetof ( struct sockaddr_un , sun_path ) + strlen ( saddr . sun_path + 1 ) + 1 ;
bufpos = snprintf ( buf , sizeof ( buf ) - 1 , " %s@%s " , action , devpath ) ;
bufpos + + ;
for ( i = 0 ; environ [ i ] ! = NULL & & bufpos < sizeof ( buf ) ; i + + ) {
bufpos + = strlcpy ( & buf [ bufpos ] , environ [ i ] , sizeof ( buf ) - bufpos - 1 ) ;
bufpos + + ;
}
2005-08-15 13:57:04 +04:00
count = sendto ( sock , & buf , bufpos , 0 , ( struct sockaddr * ) & saddr , addrlen ) ;
if ( count < 0 )
retval = - 1 ;
info ( " passed %zi bytes to socket '%s', " , count , sockname ) ;
2005-08-10 18:54:14 +04:00
close ( sock ) ;
return retval ;
}
2005-08-11 19:32:59 +04:00
int run_program ( const char * command , const char * subsystem ,
2005-08-15 13:57:04 +04:00
char * result , size_t ressize , size_t * reslen , int log )
2005-08-10 18:54:14 +04:00
{
int retval = 0 ;
int status ;
2005-08-11 19:32:59 +04:00
int outpipe [ 2 ] = { - 1 , - 1 } ;
int errpipe [ 2 ] = { - 1 , - 1 } ;
2005-08-10 18:54:14 +04:00
pid_t pid ;
char arg [ PATH_SIZE ] ;
char * argv [ ( sizeof ( arg ) / 2 ) + 1 ] ;
int devnull ;
int i ;
strlcpy ( arg , command , sizeof ( arg ) ) ;
i = 0 ;
if ( strchr ( arg , ' ' ) ) {
2005-08-15 13:57:04 +04:00
char * pos = arg ;
2005-08-10 18:54:14 +04:00
while ( pos ! = NULL ) {
if ( pos [ 0 ] = = ' \' ' ) {
/* don't separate if in apostrophes */
pos + + ;
argv [ i ] = strsep ( & pos , " \' " ) ;
while ( pos & & pos [ 0 ] = = ' ' )
pos + + ;
} else {
argv [ i ] = strsep ( & pos , " " ) ;
}
dbg ( " arg[%i] '%s' " , i , argv [ i ] ) ;
i + + ;
}
2005-08-11 19:32:59 +04:00
argv [ i ] = NULL ;
2005-08-15 13:57:04 +04:00
info ( " '%s' " , command ) ;
2005-08-10 18:54:14 +04:00
} else {
argv [ 0 ] = arg ;
argv [ 1 ] = ( char * ) subsystem ;
argv [ 2 ] = NULL ;
2005-08-15 13:57:04 +04:00
info ( " '%s' '%s' " , arg , argv [ 1 ] ) ;
2005-08-10 18:54:14 +04:00
}
2005-08-11 19:32:59 +04:00
/* prepare pipes from child to parent */
2005-08-15 13:57:04 +04:00
if ( result | | log ) {
2005-08-11 19:32:59 +04:00
if ( pipe ( outpipe ) ! = 0 ) {
err ( " pipe failed " ) ;
return - 1 ;
}
}
2005-08-15 13:57:04 +04:00
if ( log ) {
2005-08-11 19:32:59 +04:00
if ( pipe ( errpipe ) ! = 0 ) {
2005-08-10 18:54:14 +04:00
err ( " pipe failed " ) ;
return - 1 ;
}
}
pid = fork ( ) ;
switch ( pid ) {
case 0 :
2005-08-11 19:32:59 +04:00
/* child closes parent ends of pipes */
if ( outpipe [ 0 ] > 0 )
close ( outpipe [ 0 ] ) ;
if ( errpipe [ 0 ] > 0 )
close ( errpipe [ 0 ] ) ;
/* discard child output or connect to pipe */
2005-08-10 18:54:14 +04:00
devnull = open ( " /dev/null " , O_RDWR ) ;
2005-08-13 04:36:12 +04:00
if ( devnull > 0 ) {
dup2 ( devnull , STDIN_FILENO ) ;
if ( outpipe [ 1 ] < 0 )
dup2 ( devnull , STDOUT_FILENO ) ;
if ( errpipe [ 1 ] < 0 )
dup2 ( devnull , STDERR_FILENO ) ;
close ( devnull ) ;
} else
2005-08-11 19:32:59 +04:00
err ( " open /dev/null failed " ) ;
if ( outpipe [ 1 ] > 0 )
dup2 ( outpipe [ 1 ] , STDOUT_FILENO ) ;
if ( errpipe [ 1 ] > 0 )
dup2 ( errpipe [ 1 ] , STDERR_FILENO ) ;
2005-08-15 13:57:04 +04:00
execv ( argv [ 0 ] , argv ) ;
2005-08-11 19:32:59 +04:00
/* we should never reach this */
2005-08-10 18:54:14 +04:00
err ( " exec of program failed " ) ;
_exit ( 1 ) ;
case - 1 :
2005-08-15 13:57:04 +04:00
err ( " fork of '%s' failed " , argv [ 0 ] ) ;
2005-08-10 18:54:14 +04:00
return - 1 ;
default :
2005-08-11 19:32:59 +04:00
/* read from child if requested */
if ( outpipe [ 0 ] > 0 | | errpipe [ 0 ] > 0 ) {
2005-08-13 02:18:44 +04:00
ssize_t count ;
2005-08-11 19:32:59 +04:00
size_t respos = 0 ;
/* parent closes child ends of pipes */
if ( outpipe [ 1 ] > 0 )
close ( outpipe [ 1 ] ) ;
if ( errpipe [ 1 ] > 0 )
close ( errpipe [ 1 ] ) ;
/* read child output */
while ( outpipe [ 0 ] > 0 | | errpipe [ 0 ] > 0 ) {
int fdcount ;
fd_set readfds ;
FD_ZERO ( & readfds ) ;
if ( outpipe [ 0 ] > 0 )
FD_SET ( outpipe [ 0 ] , & readfds ) ;
if ( errpipe [ 0 ] > 0 )
FD_SET ( errpipe [ 0 ] , & readfds ) ;
fdcount = select ( UDEV_MAX ( outpipe [ 0 ] , errpipe [ 0 ] ) + 1 , & readfds , NULL , NULL , NULL ) ;
if ( fdcount < 0 ) {
if ( errno = = EINTR )
continue ;
2005-08-10 18:54:14 +04:00
retval = - 1 ;
break ;
}
2005-08-11 19:32:59 +04:00
/* get stdout */
if ( outpipe [ 0 ] > 0 & & FD_ISSET ( outpipe [ 0 ] , & readfds ) ) {
char inbuf [ 1024 ] ;
2005-08-15 13:57:04 +04:00
char * pos ;
char * line ;
2005-08-10 18:54:14 +04:00
2005-08-11 19:32:59 +04:00
count = read ( outpipe [ 0 ] , inbuf , sizeof ( inbuf ) - 1 ) ;
if ( count < = 0 ) {
close ( outpipe [ 0 ] ) ;
outpipe [ 0 ] = - 1 ;
if ( count < 0 ) {
err ( " stdin read failed with '%s' " , strerror ( errno ) ) ;
retval = - 1 ;
}
continue ;
}
inbuf [ count ] = ' \0 ' ;
2005-08-15 13:57:04 +04:00
/* store result for rule processing */
2005-08-11 19:32:59 +04:00
if ( result ) {
2005-08-15 13:57:04 +04:00
if ( respos + count < ressize ) {
memcpy ( & result [ respos ] , inbuf , count ) ;
respos + = count ;
} else {
2005-08-11 19:32:59 +04:00
err ( " ressize %ld too short " , ( long ) ressize ) ;
retval = - 1 ;
}
}
2005-08-15 13:57:04 +04:00
pos = inbuf ;
while ( ( line = strsep ( & pos , " \n " ) ) )
if ( pos | | line [ 0 ] ! = ' \0 ' )
info ( " '%s' (stdout) '%s' " , argv [ 0 ] , line ) ;
2005-08-11 19:32:59 +04:00
}
/* get stderr */
if ( errpipe [ 0 ] > 0 & & FD_ISSET ( errpipe [ 0 ] , & readfds ) ) {
char errbuf [ 1024 ] ;
2005-08-15 13:57:04 +04:00
char * pos ;
char * line ;
2005-08-11 19:32:59 +04:00
count = read ( errpipe [ 0 ] , errbuf , sizeof ( errbuf ) - 1 ) ;
if ( count < = 0 ) {
close ( errpipe [ 0 ] ) ;
errpipe [ 0 ] = - 1 ;
if ( count < 0 )
err ( " stderr read failed with '%s' " , strerror ( errno ) ) ;
continue ;
}
errbuf [ count ] = ' \0 ' ;
2005-08-15 13:57:04 +04:00
pos = errbuf ;
while ( ( line = strsep ( & pos , " \n " ) ) )
if ( pos | | line [ 0 ] ! = ' \0 ' )
info ( " '%s' (stderr) '%s' " , argv [ 0 ] , line ) ;
2005-08-10 18:54:14 +04:00
}
}
2005-08-11 19:32:59 +04:00
if ( outpipe [ 0 ] > 0 )
close ( outpipe [ 0 ] ) ;
if ( errpipe [ 0 ] > 0 )
close ( errpipe [ 0 ] ) ;
/* return the childs stdout string */
if ( result ) {
result [ respos ] = ' \0 ' ;
dbg ( " result='%s' " , result ) ;
if ( reslen )
* reslen = respos ;
}
2005-08-10 18:54:14 +04:00
}
waitpid ( pid , & status , 0 ) ;
2005-08-15 13:57:04 +04:00
if ( WIFEXITED ( status ) ) {
info ( " '%s' returned with status %i " , argv [ 0 ] , WEXITSTATUS ( status ) ) ;
if ( WEXITSTATUS ( status ) ! = 0 )
retval = - 1 ;
} else {
err ( " '%s' abnormal exit " , argv [ 0 ] ) ;
2005-08-10 18:54:14 +04:00
retval = - 1 ;
}
}
return retval ;
}