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.h"
2006-01-21 01:18:23 +03:00
extern char * * environ ;
2005-08-10 18:54:14 +04:00
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 ;
2006-01-23 05:24:16 +03:00
/* abstract namespace only */
2005-08-10 18:54:14 +04:00
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 ] ;
2005-12-03 20:55:51 +03:00
char program [ PATH_SIZE ] ;
2005-08-10 18:54:14 +04:00
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 ) {
2005-11-07 20:44:18 +03:00
err ( " pipe failed: %s " , strerror ( errno ) ) ;
2005-08-11 19:32:59 +04:00
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-11-07 20:44:18 +03:00
err ( " pipe failed: %s " , strerror ( errno ) ) ;
2005-08-10 18:54:14 +04:00
return - 1 ;
}
}
2005-12-03 20:55:51 +03:00
/* allow programs in /lib/udev called without the path */
if ( strchr ( argv [ 0 ] , ' / ' ) = = NULL ) {
strlcpy ( program , " /lib/udev/ " , sizeof ( program ) ) ;
strlcat ( program , argv [ 0 ] , sizeof ( program ) ) ;
argv [ 0 ] = program ;
}
2005-08-10 18:54:14 +04:00
pid = fork ( ) ;
switch ( pid ) {
case 0 :
2005-08-11 19:32:59 +04:00
/* child closes parent ends of pipes */
2005-08-28 22:25:37 +04:00
if ( outpipe [ READ_END ] > 0 )
close ( outpipe [ READ_END ] ) ;
if ( errpipe [ READ_END ] > 0 )
close ( errpipe [ READ_END ] ) ;
2005-08-11 19:32:59 +04:00
/* 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 ) ;
2005-08-28 22:25:37 +04:00
if ( outpipe [ WRITE_END ] < 0 )
2005-08-13 04:36:12 +04:00
dup2 ( devnull , STDOUT_FILENO ) ;
2005-08-28 22:25:37 +04:00
if ( errpipe [ WRITE_END ] < 0 )
2005-08-13 04:36:12 +04:00
dup2 ( devnull , STDERR_FILENO ) ;
close ( devnull ) ;
} else
2005-11-07 20:44:18 +03:00
err ( " open /dev/null failed: %s " , strerror ( errno ) ) ;
2005-08-28 22:25:37 +04:00
if ( outpipe [ WRITE_END ] > 0 )
dup2 ( outpipe [ WRITE_END ] , STDOUT_FILENO ) ;
if ( errpipe [ WRITE_END ] > 0 )
dup2 ( errpipe [ WRITE_END ] , 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-22 13:42:58 +04:00
err ( " exec of program '%s' failed " , argv [ 0 ] ) ;
2005-08-10 18:54:14 +04:00
_exit ( 1 ) ;
case - 1 :
2005-11-07 20:44:18 +03:00
err ( " fork of '%s' failed: %s " , argv [ 0 ] , strerror ( errno ) ) ;
2005-08-10 18:54:14 +04:00
return - 1 ;
default :
2005-08-11 19:32:59 +04:00
/* read from child if requested */
2005-08-28 22:25:37 +04:00
if ( outpipe [ READ_END ] > 0 | | errpipe [ READ_END ] > 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 */
2005-08-28 22:25:37 +04:00
if ( outpipe [ WRITE_END ] > 0 )
close ( outpipe [ WRITE_END ] ) ;
if ( errpipe [ WRITE_END ] > 0 )
close ( errpipe [ WRITE_END ] ) ;
2005-08-11 19:32:59 +04:00
/* read child output */
2005-08-28 22:25:37 +04:00
while ( outpipe [ READ_END ] > 0 | | errpipe [ READ_END ] > 0 ) {
2005-08-11 19:32:59 +04:00
int fdcount ;
fd_set readfds ;
FD_ZERO ( & readfds ) ;
2005-08-28 22:25:37 +04:00
if ( outpipe [ READ_END ] > 0 )
FD_SET ( outpipe [ READ_END ] , & readfds ) ;
if ( errpipe [ READ_END ] > 0 )
FD_SET ( errpipe [ READ_END ] , & readfds ) ;
fdcount = select ( UDEV_MAX ( outpipe [ READ_END ] , errpipe [ READ_END ] ) + 1 , & readfds , NULL , NULL , NULL ) ;
2005-08-11 19:32:59 +04:00
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 */
2005-08-28 22:25:37 +04:00
if ( outpipe [ READ_END ] > 0 & & FD_ISSET ( outpipe [ READ_END ] , & readfds ) ) {
2005-08-11 19:32:59 +04:00
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-28 22:25:37 +04:00
count = read ( outpipe [ READ_END ] , inbuf , sizeof ( inbuf ) - 1 ) ;
2005-08-11 19:32:59 +04:00
if ( count < = 0 ) {
2005-08-28 22:25:37 +04:00
close ( outpipe [ READ_END ] ) ;
outpipe [ READ_END ] = - 1 ;
2005-08-11 19:32:59 +04:00
if ( count < 0 ) {
2005-11-07 20:44:18 +03:00
err ( " stdin read failed: %s " , strerror ( errno ) ) ;
2005-08-11 19:32:59 +04:00
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 */
2005-08-28 22:25:37 +04:00
if ( errpipe [ READ_END ] > 0 & & FD_ISSET ( errpipe [ READ_END ] , & readfds ) ) {
2005-08-11 19:32:59 +04:00
char errbuf [ 1024 ] ;
2005-08-15 13:57:04 +04:00
char * pos ;
char * line ;
2005-08-11 19:32:59 +04:00
2005-08-28 22:25:37 +04:00
count = read ( errpipe [ READ_END ] , errbuf , sizeof ( errbuf ) - 1 ) ;
2005-08-11 19:32:59 +04:00
if ( count < = 0 ) {
2005-08-28 22:25:37 +04:00
close ( errpipe [ READ_END ] ) ;
errpipe [ READ_END ] = - 1 ;
2005-08-11 19:32:59 +04:00
if ( count < 0 )
2005-11-07 20:44:18 +03:00
err ( " stderr read failed: %s " , strerror ( errno ) ) ;
2005-08-11 19:32:59 +04:00
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-28 22:25:37 +04:00
if ( outpipe [ READ_END ] > 0 )
close ( outpipe [ READ_END ] ) ;
if ( errpipe [ READ_END ] > 0 )
close ( errpipe [ READ_END ] ) ;
2005-08-11 19:32:59 +04:00
/* 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 ;
}