2004-03-23 09:22:20 +03:00
/*
2008-09-10 04:40:42 +04:00
* Copyright ( C ) 2004 - 2008 Kay Sievers < kay . sievers @ vrfy . org >
2004-03-23 09:22:20 +03:00
*
2008-09-10 04:40:42 +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 , either version 2 of the License , or
* ( at your option ) any later version .
2004-03-23 09:22:20 +03:00
*
2008-09-10 04:40:42 +04:00
* 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 , see < http : //www.gnu.org/licenses/>.
2004-03-23 09:22:20 +03:00
*/
# include <stdlib.h>
# include <stdio.h>
2004-03-27 12:21:46 +03:00
# include <stddef.h>
2004-03-23 09:22:20 +03:00
# include <unistd.h>
2008-07-30 03:45:23 +04:00
# include <string.h>
2004-03-23 09:22:20 +03:00
# include <fcntl.h>
2004-03-27 12:21:46 +03:00
# include <errno.h>
2004-12-20 09:38:33 +03:00
# include <ctype.h>
2006-08-20 20:25:57 +04:00
# include <pwd.h>
# include <grp.h>
2008-10-18 17:25:05 +04:00
# include <sys/wait.h>
2004-03-23 09:22:20 +03:00
# include "udev.h"
2008-10-16 22:23:56 +04:00
int create_path ( struct udev * udev , const char * path )
2004-03-27 12:21:46 +03:00
{
2008-10-16 22:23:56 +04:00
char p [ UTIL_PATH_SIZE ] ;
char * pos ;
struct stat stats ;
int ret ;
util_strlcpy ( p , path , sizeof ( p ) ) ;
pos = strrchr ( p , ' / ' ) ;
if ( pos = = p | | pos = = NULL )
return 0 ;
while ( pos [ - 1 ] = = ' / ' )
pos - - ;
pos [ 0 ] = ' \0 ' ;
dbg ( udev , " stat '%s' \n " , p ) ;
if ( stat ( p , & stats ) = = 0 & & ( stats . st_mode & S_IFMT ) = = S_IFDIR )
return 0 ;
if ( create_path ( udev , p ) ! = 0 )
return - 1 ;
dbg ( udev , " mkdir '%s' \n " , p ) ;
udev_selinux_setfscreatecon ( udev , p , S_IFDIR | 0755 ) ;
ret = mkdir ( p , 0755 ) ;
udev_selinux_resetfscreatecon ( udev ) ;
if ( ret = = 0 )
return 0 ;
if ( errno = = EEXIST )
if ( stat ( p , & stats ) = = 0 & & ( stats . st_mode & S_IFMT ) = = S_IFDIR )
return 0 ;
return - 1 ;
}
2004-03-27 12:21:46 +03:00
2008-10-16 22:23:56 +04:00
int delete_path ( struct udev * udev , const char * path )
{
char p [ UTIL_PATH_SIZE ] ;
char * pos ;
int retval ;
strcpy ( p , path ) ;
pos = strrchr ( p , ' / ' ) ;
if ( pos = = p | | pos = = NULL )
return 0 ;
while ( 1 ) {
* pos = ' \0 ' ;
pos = strrchr ( p , ' / ' ) ;
/* don't remove the last one */
if ( ( pos = = p ) | | ( pos = = NULL ) )
break ;
/* remove if empty */
retval = rmdir ( p ) ;
if ( errno = = ENOENT )
retval = 0 ;
if ( retval ) {
if ( errno = = ENOTEMPTY )
return 0 ;
err ( udev , " rmdir(%s) failed: %m \n " , p ) ;
break ;
2005-06-26 20:55:24 +04:00
}
2008-10-16 22:23:56 +04:00
dbg ( udev , " removed '%s' \n " , p ) ;
}
return 0 ;
2005-06-26 20:55:24 +04:00
}
2008-10-16 22:23:56 +04:00
/* Reset permissions on the device node, before unlinking it to make sure,
* that permisions of possible hard links will be removed too .
*/
int unlink_secure ( struct udev * udev , const char * filename )
2005-06-26 20:55:24 +04:00
{
2008-10-16 22:23:56 +04:00
int retval ;
2005-06-26 20:55:24 +04:00
2008-10-16 22:23:56 +04:00
retval = chown ( filename , 0 , 0 ) ;
if ( retval )
err ( udev , " chown(%s, 0, 0) failed: %m \n " , filename ) ;
2005-03-04 23:33:57 +03:00
2008-10-16 22:23:56 +04:00
retval = chmod ( filename , 0000 ) ;
if ( retval )
err ( udev , " chmod(%s, 0000) failed: %m \n " , filename ) ;
2004-03-27 12:21:46 +03:00
2008-10-16 22:23:56 +04:00
retval = unlink ( filename ) ;
if ( errno = = ENOENT )
retval = 0 ;
2007-04-25 03:52:00 +04:00
2008-10-16 22:23:56 +04:00
if ( retval )
err ( udev , " unlink(%s) failed: %m \n " , filename ) ;
2005-08-27 18:15:41 +04:00
2008-10-16 22:23:56 +04:00
return retval ;
2005-08-27 18:15:41 +04:00
}
2008-09-06 17:45:31 +04:00
uid_t lookup_user ( struct udev * udev , const char * user )
2006-08-20 20:25:57 +04:00
{
struct passwd * pw ;
uid_t uid = 0 ;
errno = 0 ;
pw = getpwnam ( user ) ;
if ( pw = = NULL ) {
if ( errno = = 0 | | errno = = ENOENT | | errno = = ESRCH )
2008-09-06 17:45:31 +04:00
err ( udev , " specified user '%s' unknown \n " , user ) ;
2006-08-20 20:25:57 +04:00
else
2008-09-29 19:01:32 +04:00
err ( udev , " error resolving user '%s': %m \n " , user ) ;
2006-08-20 20:25:57 +04:00
} else
uid = pw - > pw_uid ;
return uid ;
}
2008-09-06 17:45:31 +04:00
extern gid_t lookup_group ( struct udev * udev , const char * group )
2006-08-20 20:25:57 +04:00
{
struct group * gr ;
gid_t gid = 0 ;
errno = 0 ;
gr = getgrnam ( group ) ;
if ( gr = = NULL ) {
if ( errno = = 0 | | errno = = ENOENT | | errno = = ESRCH )
2008-09-06 17:45:31 +04:00
err ( udev , " specified group '%s' unknown \n " , group ) ;
2006-08-20 20:25:57 +04:00
else
2008-09-29 19:01:32 +04:00
err ( udev , " error resolving group '%s': %m \n " , group ) ;
2006-08-20 20:25:57 +04:00
} else
gid = gr - > gr_gid ;
return gid ;
}
2008-10-18 17:25:05 +04:00
int run_program ( struct udev * udev , const char * command , char * * envp ,
char * result , size_t ressize , size_t * reslen )
{
int status ;
int outpipe [ 2 ] = { - 1 , - 1 } ;
int errpipe [ 2 ] = { - 1 , - 1 } ;
pid_t pid ;
char arg [ UTIL_PATH_SIZE ] ;
char program [ UTIL_PATH_SIZE ] ;
char * argv [ ( sizeof ( arg ) / 2 ) + 1 ] ;
int devnull ;
int i ;
int err = 0 ;
/* build argv from command */
util_strlcpy ( arg , command , sizeof ( arg ) ) ;
i = 0 ;
if ( strchr ( arg , ' ' ) ! = NULL ) {
char * pos = arg ;
while ( pos ! = NULL & & pos [ 0 ] ! = ' \0 ' ) {
if ( pos [ 0 ] = = ' \' ' ) {
/* do not separate quotes */
pos + + ;
argv [ i ] = strsep ( & pos , " \' " ) ;
while ( pos ! = NULL & & pos [ 0 ] = = ' ' )
pos + + ;
} else {
argv [ i ] = strsep ( & pos , " " ) ;
}
dbg ( udev , " arg[%i] '%s' \n " , i , argv [ i ] ) ;
i + + ;
}
argv [ i ] = NULL ;
} else {
argv [ 0 ] = arg ;
argv [ 1 ] = NULL ;
}
info ( udev , " '%s' \n " , command ) ;
/* prepare pipes from child to parent */
if ( result ! = NULL | | udev_get_log_priority ( udev ) > = LOG_INFO ) {
if ( pipe ( outpipe ) ! = 0 ) {
err ( udev , " pipe failed: %m \n " ) ;
return - 1 ;
}
}
if ( udev_get_log_priority ( udev ) > = LOG_INFO ) {
if ( pipe ( errpipe ) ! = 0 ) {
err ( udev , " pipe failed: %m \n " ) ;
return - 1 ;
}
}
/* allow programs in /lib/udev/ to be called without the path */
if ( strchr ( argv [ 0 ] , ' / ' ) = = NULL ) {
util_strlcpy ( program , UDEV_PREFIX " /lib/udev/ " , sizeof ( program ) ) ;
util_strlcat ( program , argv [ 0 ] , sizeof ( program ) ) ;
argv [ 0 ] = program ;
}
pid = fork ( ) ;
switch ( pid ) {
case 0 :
/* child closes parent ends of pipes */
if ( outpipe [ READ_END ] > 0 )
close ( outpipe [ READ_END ] ) ;
if ( errpipe [ READ_END ] > 0 )
close ( errpipe [ READ_END ] ) ;
/* discard child output or connect to pipe */
devnull = open ( " /dev/null " , O_RDWR ) ;
if ( devnull > 0 ) {
dup2 ( devnull , STDIN_FILENO ) ;
if ( outpipe [ WRITE_END ] < 0 )
dup2 ( devnull , STDOUT_FILENO ) ;
if ( errpipe [ WRITE_END ] < 0 )
dup2 ( devnull , STDERR_FILENO ) ;
close ( devnull ) ;
} else
err ( udev , " open /dev/null failed: %m \n " ) ;
if ( outpipe [ WRITE_END ] > 0 ) {
dup2 ( outpipe [ WRITE_END ] , STDOUT_FILENO ) ;
close ( outpipe [ WRITE_END ] ) ;
}
if ( errpipe [ WRITE_END ] > 0 ) {
dup2 ( errpipe [ WRITE_END ] , STDERR_FILENO ) ;
close ( errpipe [ WRITE_END ] ) ;
}
execve ( argv [ 0 ] , argv , envp ) ;
if ( errno = = ENOENT | | errno = = ENOTDIR ) {
/* may be on a filesytem which is not mounted right now */
info ( udev , " program '%s' not found \n " , argv [ 0 ] ) ;
} else {
/* other problems */
err ( udev , " exec of program '%s' failed \n " , argv [ 0 ] ) ;
}
_exit ( 1 ) ;
case - 1 :
err ( udev , " fork of '%s' failed: %m \n " , argv [ 0 ] ) ;
return - 1 ;
default :
/* read from child if requested */
if ( outpipe [ READ_END ] > 0 | | errpipe [ READ_END ] > 0 ) {
ssize_t count ;
size_t respos = 0 ;
/* parent closes child ends of pipes */
if ( outpipe [ WRITE_END ] > 0 )
close ( outpipe [ WRITE_END ] ) ;
if ( errpipe [ WRITE_END ] > 0 )
close ( errpipe [ WRITE_END ] ) ;
/* read child output */
while ( outpipe [ READ_END ] > 0 | | errpipe [ READ_END ] > 0 ) {
int fdcount ;
fd_set readfds ;
FD_ZERO ( & readfds ) ;
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 ) ;
if ( fdcount < 0 ) {
if ( errno = = EINTR )
continue ;
err = - 1 ;
break ;
}
/* get stdout */
if ( outpipe [ READ_END ] > 0 & & FD_ISSET ( outpipe [ READ_END ] , & readfds ) ) {
char inbuf [ 1024 ] ;
char * pos ;
char * line ;
count = read ( outpipe [ READ_END ] , inbuf , sizeof ( inbuf ) - 1 ) ;
if ( count < = 0 ) {
close ( outpipe [ READ_END ] ) ;
outpipe [ READ_END ] = - 1 ;
if ( count < 0 ) {
err ( udev , " stdin read failed: %m \n " ) ;
err = - 1 ;
}
continue ;
}
inbuf [ count ] = ' \0 ' ;
/* store result for rule processing */
if ( result ) {
if ( respos + count < ressize ) {
memcpy ( & result [ respos ] , inbuf , count ) ;
respos + = count ;
} else {
err ( udev , " ressize %ld too short \n " , ( long ) ressize ) ;
err = - 1 ;
}
}
pos = inbuf ;
while ( ( line = strsep ( & pos , " \n " ) ) )
if ( pos | | line [ 0 ] ! = ' \0 ' )
info ( udev , " '%s' (stdout) '%s' \n " , argv [ 0 ] , line ) ;
}
/* get stderr */
if ( errpipe [ READ_END ] > 0 & & FD_ISSET ( errpipe [ READ_END ] , & readfds ) ) {
char errbuf [ 1024 ] ;
char * pos ;
char * line ;
count = read ( errpipe [ READ_END ] , errbuf , sizeof ( errbuf ) - 1 ) ;
if ( count < = 0 ) {
close ( errpipe [ READ_END ] ) ;
errpipe [ READ_END ] = - 1 ;
if ( count < 0 )
err ( udev , " stderr read failed: %m \n " ) ;
continue ;
}
errbuf [ count ] = ' \0 ' ;
pos = errbuf ;
while ( ( line = strsep ( & pos , " \n " ) ) )
if ( pos | | line [ 0 ] ! = ' \0 ' )
info ( udev , " '%s' (stderr) '%s' \n " , argv [ 0 ] , line ) ;
}
}
if ( outpipe [ READ_END ] > 0 )
close ( outpipe [ READ_END ] ) ;
if ( errpipe [ READ_END ] > 0 )
close ( errpipe [ READ_END ] ) ;
/* return the childs stdout string */
if ( result ) {
result [ respos ] = ' \0 ' ;
dbg ( udev , " result='%s' \n " , result ) ;
if ( reslen )
* reslen = respos ;
}
}
waitpid ( pid , & status , 0 ) ;
if ( WIFEXITED ( status ) ) {
info ( udev , " '%s' returned with status %i \n " , argv [ 0 ] , WEXITSTATUS ( status ) ) ;
if ( WEXITSTATUS ( status ) ! = 0 )
err = - 1 ;
} else {
err ( udev , " '%s' abnormal exit \n " , argv [ 0 ] ) ;
err = - 1 ;
}
}
return err ;
}