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-18 22:12:55 +04:00
int util_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 ;
2009-05-20 19:57:52 +04:00
util_strscpy ( p , sizeof ( p ) , path ) ;
2008-10-16 22:23:56 +04:00
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 ;
2008-10-18 22:12:55 +04:00
if ( util_create_path ( udev , p ) ! = 0 )
2008-10-16 22:23:56 +04:00
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-18 22:12:55 +04:00
int util_delete_path ( struct udev * udev , const char * path )
2008-10-16 22:23:56 +04:00
{
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 .
*/
2008-10-18 22:12:55 +04:00
int util_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-10-18 22:12:55 +04:00
uid_t util_lookup_user ( struct udev * udev , const char * user )
2006-08-20 20:25:57 +04:00
{
2008-10-23 01:59:53 +04:00
char * endptr ;
2008-10-22 18:13:08 +04:00
int buflen = sysconf ( _SC_GETPW_R_SIZE_MAX ) ;
char buf [ buflen ] ;
struct passwd pwbuf ;
2006-08-20 20:25:57 +04:00
struct passwd * pw ;
2008-10-23 01:59:53 +04:00
uid_t uid ;
2006-08-20 20:25:57 +04:00
2008-10-21 14:43:07 +04:00
if ( strcmp ( user , " root " ) = = 0 )
return 0 ;
2008-11-06 04:56:45 +03:00
uid = strtoul ( user , & endptr , 10 ) ;
if ( endptr [ 0 ] = = ' \0 ' )
return uid ;
errno = 0 ;
getpwnam_r ( user , & pwbuf , buf , buflen , & pw ) ;
if ( pw ! = NULL )
return pw - > pw_uid ;
if ( errno = = 0 | | errno = = ENOENT | | errno = = ESRCH )
err ( udev , " specified user '%s' unknown \n " , user ) ;
else
err ( udev , " error resolving user '%s': %m \n " , user ) ;
return 0 ;
}
2006-08-20 20:25:57 +04:00
2009-04-26 15:59:39 +04:00
gid_t util_lookup_group ( struct udev * udev , const char * group )
2008-11-06 04:56:45 +03:00
{
char * endptr ;
int buflen = sysconf ( _SC_GETGR_R_SIZE_MAX ) ;
char buf [ buflen ] ;
struct group grbuf ;
struct group * gr ;
gid_t gid = 0 ;
2006-08-20 20:25:57 +04:00
2008-11-06 04:56:45 +03:00
if ( strcmp ( group , " root " ) = = 0 )
2008-10-23 01:59:53 +04:00
return 0 ;
2008-11-06 04:56:45 +03:00
gid = strtoul ( group , & endptr , 10 ) ;
if ( endptr [ 0 ] = = ' \0 ' )
return gid ;
errno = 0 ;
getgrnam_r ( group , & grbuf , buf , buflen , & gr ) ;
if ( gr ! = NULL )
return gr - > gr_gid ;
if ( errno = = 0 | | errno = = ENOENT | | errno = = ESRCH )
err ( udev , " specified group '%s' unknown \n " , group ) ;
else
err ( udev , " error resolving group '%s': %m \n " , group ) ;
return 0 ;
}
2008-10-18 17:25:05 +04:00
2008-10-22 20:03:38 +04:00
/* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
int util_resolve_subsys_kernel ( struct udev * udev , const char * string ,
char * result , size_t maxsize , int read_value )
{
char temp [ UTIL_PATH_SIZE ] ;
char * subsys ;
char * sysname ;
struct udev_device * dev ;
char * attr ;
if ( string [ 0 ] ! = ' [ ' )
return - 1 ;
2009-05-20 19:57:52 +04:00
util_strscpy ( temp , sizeof ( temp ) , string ) ;
2008-10-22 20:03:38 +04:00
subsys = & temp [ 1 ] ;
sysname = strchr ( subsys , ' / ' ) ;
if ( sysname = = NULL )
return - 1 ;
sysname [ 0 ] = ' \0 ' ;
sysname = & sysname [ 1 ] ;
attr = strchr ( sysname , ' ] ' ) ;
if ( attr = = NULL )
return - 1 ;
attr [ 0 ] = ' \0 ' ;
attr = & attr [ 1 ] ;
if ( attr [ 0 ] = = ' / ' )
attr = & attr [ 1 ] ;
if ( attr [ 0 ] = = ' \0 ' )
attr = NULL ;
if ( read_value & & attr = = NULL )
return - 1 ;
dev = udev_device_new_from_subsystem_sysname ( udev , subsys , sysname ) ;
if ( dev = = NULL )
return - 1 ;
if ( read_value ) {
const char * val ;
val = udev_device_get_sysattr_value ( dev , attr ) ;
if ( val ! = NULL )
2009-05-20 19:57:52 +04:00
util_strscpy ( result , maxsize , val ) ;
2008-10-22 20:03:38 +04:00
else
result [ 0 ] = ' \0 ' ;
info ( udev , " value '[%s/%s]%s' is '%s' \n " , subsys , sysname , attr , result ) ;
} else {
2009-05-20 19:57:52 +04:00
size_t l ;
char * s ;
s = result ;
l = util_strpcpyl ( & s , maxsize , udev_device_get_syspath ( dev ) , NULL ) ;
if ( attr ! = NULL )
util_strpcpyl ( & s , l , " / " , attr , NULL ) ;
2008-10-22 20:03:38 +04:00
info ( udev , " path '[%s/%s]%s' is '%s' \n " , subsys , sysname , attr , result ) ;
}
udev_device_unref ( dev ) ;
return 0 ;
}
2008-10-18 22:12:55 +04:00
int util_run_program ( struct udev * udev , const char * command , char * * envp ,
char * result , size_t ressize , size_t * reslen )
2008-10-18 17:25:05 +04:00
{
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 */
2009-05-20 19:57:52 +04:00
util_strscpy ( arg , sizeof ( arg ) , command ) ;
2008-10-18 17:25:05 +04:00
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 */
2009-05-20 19:57:52 +04:00
if ( argv [ 0 ] [ 0 ] ! = ' / ' ) {
util_strscpyl ( program , sizeof ( program ) , UDEV_PREFIX " /lib/udev/ " , argv [ 0 ] , NULL ) ;
2008-10-18 17:25:05 +04:00
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 {
2008-12-20 02:03:53 +03:00
err ( udev , " '%s' abnormal exit \n " , command ) ;
2008-10-18 17:25:05 +04:00
err = - 1 ;
}
}
return err ;
}