2004-03-02 12:30:38 +03:00
/*
* udevstart . c
*
* Copyright ( C ) 2004 Greg Kroah - Hartman < greg @ kroah . com >
2005-07-22 20:35:58 +04:00
* Copyright ( C ) 2004 Kay Sievers < kay @ vrfy . org >
*
2004-03-02 12:30:38 +03:00
* Quick and dirty way to populate a / dev with udev if your system
2005-07-22 20:35:58 +04:00
* does not have access to a shell . Based originally on a patch
* from :
* Harald Hoyer < harald @ redhat . com >
2004-03-02 12:30:38 +03: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 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>
2004-07-02 09:19:18 +04:00
# include <stddef.h>
2004-03-02 12:30:38 +03:00
# include <string.h>
# include <stdio.h>
2005-03-07 02:01:34 +03:00
# include <unistd.h>
2004-03-02 12:30:38 +03:00
# include <errno.h>
# include <ctype.h>
# include <dirent.h>
2005-03-07 02:01:34 +03:00
# include <signal.h>
2005-08-11 19:32:59 +04:00
# include <syslog.h>
2004-03-02 12:30:38 +03:00
# include <sys/wait.h>
2005-02-10 20:26:09 +03:00
# include <sys/stat.h>
2004-04-01 11:03:07 +04:00
# include <sys/types.h>
2004-03-02 12:30:38 +03:00
2005-03-07 06:29:43 +03:00
# include "udev.h"
2005-07-22 20:35:58 +04:00
# include "udev_rules.h"
2004-03-02 12:30:38 +03:00
2005-06-26 20:55:24 +04:00
static const char * udev_run_str ;
static const char * udev_log_str ;
2005-07-05 17:24:41 +04:00
static struct udev_rules rules ;
2005-06-26 20:55:24 +04:00
2005-03-07 02:01:34 +03:00
# ifdef USE_LOG
2005-03-27 03:11:03 +04:00
void log_message ( int priority , const char * format , . . . )
2005-03-07 02:01:34 +03:00
{
2005-03-27 03:11:03 +04:00
va_list args ;
if ( priority > udev_log_priority )
return ;
va_start ( args , format ) ;
vsyslog ( priority , format , args ) ;
va_end ( args ) ;
2005-03-07 02:01:34 +03:00
}
# endif
2004-03-02 12:30:38 +03:00
2004-07-02 09:19:18 +04:00
struct device {
2005-03-07 07:56:40 +03:00
struct list_head node ;
2005-03-07 06:29:43 +03:00
char path [ PATH_SIZE ] ;
2004-07-02 09:19:18 +04:00
} ;
/* sort files in lexical order */
2006-01-09 23:18:00 +03:00
static int device_list_insert ( const char * path , struct list_head * device_list )
2004-07-02 09:19:18 +04:00
{
struct device * loop_device ;
struct device * new_device ;
2005-08-13 04:19:22 +04:00
const char * devpath = & path [ strlen ( sysfs_path ) ] ;
2004-07-02 09:19:18 +04:00
2005-08-13 04:19:22 +04:00
dbg ( " insert: '%s' \n " , devpath ) ;
2005-02-10 20:26:09 +03:00
2005-03-07 07:56:40 +03:00
list_for_each_entry ( loop_device , device_list , node ) {
2005-08-13 04:19:22 +04:00
if ( strcmp ( loop_device - > path , devpath ) > 0 ) {
2004-07-02 09:19:18 +04:00
break ;
}
}
new_device = malloc ( sizeof ( struct device ) ) ;
if ( new_device = = NULL ) {
dbg ( " error malloc " ) ;
return - ENOMEM ;
}
2005-08-13 04:19:22 +04:00
strlcpy ( new_device - > path , devpath , sizeof ( new_device - > path ) ) ;
2005-03-07 07:56:40 +03:00
list_add_tail ( & new_device - > node , & loop_device - > node ) ;
2006-01-09 23:18:00 +03:00
dbg ( " add '%s' " , new_device - > path ) ;
2004-07-02 09:19:18 +04:00
return 0 ;
}
/* list of devices that we should run last due to any one of a number of reasons */
static char * last_list [ ] = {
" /block/dm " , /* on here because dm wants to have the block devices around before it */
NULL ,
} ;
2004-09-11 08:44:15 +04:00
/* list of devices that we should run first due to any one of a number of reasons */
static char * first_list [ ] = {
2005-08-13 04:19:22 +04:00
" /class/mem " ,
" /class/tty " ,
2004-09-11 08:44:15 +04:00
NULL ,
} ;
2006-01-09 23:18:00 +03:00
static int add_device ( const char * devpath )
2004-10-07 10:20:39 +04:00
{
2006-01-09 23:18:00 +03:00
struct sysfs_device * dev ;
struct udevice * udev ;
2006-01-26 04:16:58 +03:00
int retval = 0 ;
2005-06-26 20:55:24 +04:00
/* clear and set environment for next event */
clearenv ( ) ;
setenv ( " ACTION " , " add " , 1 ) ;
setenv ( " UDEV_START " , " 1 " , 1 ) ;
if ( udev_log_str )
setenv ( " UDEV_LOG " , udev_log_str , 1 ) ;
if ( udev_run_str )
setenv ( " UDEV_RUN " , udev_run_str , 1 ) ;
2005-02-10 20:26:09 +03:00
2006-01-09 23:18:00 +03:00
dev = sysfs_device_get ( devpath ) ;
if ( dev = = NULL )
2005-06-05 17:55:29 +04:00
return - 1 ;
2004-10-19 06:11:51 +04:00
2006-01-09 23:18:00 +03:00
udev = udev_device_init ( ) ;
if ( udev = = NULL )
2005-06-05 17:55:29 +04:00
return - 1 ;
2006-01-09 23:18:00 +03:00
/* override built-in sysfs device */
udev - > dev = dev ;
strcpy ( udev - > action , " add " ) ;
if ( strcmp ( udev - > dev - > subsystem , " net " ) ! = 0 ) {
udev - > devt = udev_device_get_devt ( udev ) ;
if ( major ( udev - > devt ) = = 0 )
return - 1 ;
2005-06-05 17:55:29 +04:00
}
2006-01-09 23:18:00 +03:00
dbg ( " add '%s' " , udev - > dev - > devpath ) ;
setenv ( " DEVPATH " , udev - > dev - > devpath , 1 ) ;
setenv ( " SUBSYSTEM " , udev - > dev - > subsystem , 1 ) ;
udev_rules_get_name ( & rules , udev ) ;
if ( udev - > ignore_device ) {
2005-06-08 13:57:53 +04:00
dbg ( " device event will be ignored " ) ;
2005-06-05 17:55:29 +04:00
goto exit ;
}
2006-01-25 04:21:07 +03:00
if ( udev - > name [ 0 ] ! = ' \0 ' )
2006-04-06 00:29:33 +04:00
retval = udev_device_event ( & rules , udev ) ;
2006-01-26 04:24:13 +03:00
else
2006-01-25 04:21:07 +03:00
info ( " device node creation supressed " ) ;
2004-10-19 06:11:51 +04:00
2006-01-26 04:24:13 +03:00
if ( retval = = 0 & & udev_run ) {
2005-04-02 19:45:35 +04:00
struct name_entry * name_loop ;
dbg ( " executing run list " ) ;
2006-01-09 23:18:00 +03:00
list_for_each_entry ( name_loop , & udev - > run_list , node ) {
2005-08-01 07:07:19 +04:00
if ( strncmp ( name_loop - > name , " socket: " , strlen ( " socket: " ) ) = = 0 )
2006-01-09 23:18:00 +03:00
pass_env_to_socket ( & name_loop - > name [ strlen ( " socket: " ) ] , udev - > dev - > devpath , " add " ) ;
2006-01-24 22:10:48 +03:00
else {
char program [ PATH_SIZE ] ;
strlcpy ( program , name_loop - > name , sizeof ( program ) ) ;
2006-01-25 03:28:31 +03:00
udev_rules_apply_format ( udev , program , sizeof ( program ) ) ;
2006-01-24 22:10:48 +03:00
run_program ( program , udev - > dev - > subsystem , NULL , 0 , NULL , ( udev_log_priority > = LOG_INFO ) ) ;
}
2005-08-01 07:07:19 +04:00
}
2005-04-02 19:45:35 +04:00
}
2006-01-25 04:21:07 +03:00
2005-06-05 17:55:29 +04:00
exit :
2006-01-09 23:18:00 +03:00
udev_device_cleanup ( udev ) ;
2004-10-19 06:28:39 +04:00
return 0 ;
2004-10-07 10:20:39 +04:00
}
2004-07-02 09:19:18 +04:00
static void exec_list ( struct list_head * device_list )
{
struct device * loop_device ;
struct device * tmp_device ;
2004-09-11 08:44:15 +04:00
int i ;
/* handle the "first" type devices first */
2005-03-07 07:56:40 +03:00
list_for_each_entry_safe ( loop_device , tmp_device , device_list , node ) {
2005-02-10 20:26:09 +03:00
for ( i = 0 ; first_list [ i ] ! = NULL ; i + + ) {
2004-09-11 08:44:15 +04:00
if ( strncmp ( loop_device - > path , first_list [ i ] , strlen ( first_list [ i ] ) ) = = 0 ) {
2006-01-09 23:18:00 +03:00
add_device ( loop_device - > path ) ;
2005-03-07 07:56:40 +03:00
list_del ( & loop_device - > node ) ;
2004-09-11 08:44:15 +04:00
free ( loop_device ) ;
break ;
}
}
}
2004-07-02 09:19:18 +04:00
/* handle the devices we are allowed to, excluding the "last" type devices */
2005-03-07 07:56:40 +03:00
list_for_each_entry_safe ( loop_device , tmp_device , device_list , node ) {
2004-07-02 09:19:18 +04:00
int found = 0 ;
2005-02-10 20:26:09 +03:00
for ( i = 0 ; last_list [ i ] ! = NULL ; i + + ) {
2004-07-02 09:19:18 +04:00
if ( strncmp ( loop_device - > path , last_list [ i ] , strlen ( last_list [ i ] ) ) = = 0 ) {
found = 1 ;
break ;
}
}
if ( found )
continue ;
2006-01-09 23:18:00 +03:00
add_device ( loop_device - > path ) ;
2005-03-07 07:56:40 +03:00
list_del ( & loop_device - > node ) ;
2004-07-02 09:19:18 +04:00
free ( loop_device ) ;
}
/* handle the rest of the devices left over, if any */
2005-03-07 07:56:40 +03:00
list_for_each_entry_safe ( loop_device , tmp_device , device_list , node ) {
2006-01-09 23:18:00 +03:00
add_device ( loop_device - > path ) ;
2005-03-07 07:56:40 +03:00
list_del ( & loop_device - > node ) ;
2004-07-02 09:19:18 +04:00
free ( loop_device ) ;
}
}
2005-08-13 04:19:22 +04:00
static int has_devt ( const char * path )
2005-02-10 20:26:09 +03:00
{
2005-03-07 06:29:43 +03:00
char filename [ PATH_SIZE ] ;
2005-02-10 20:26:09 +03:00
struct stat statbuf ;
2005-08-13 04:19:22 +04:00
snprintf ( filename , sizeof ( filename ) , " %s/dev " , path ) ;
2005-03-07 06:29:43 +03:00
filename [ sizeof ( filename ) - 1 ] = ' \0 ' ;
2005-02-10 20:26:09 +03:00
if ( stat ( filename , & statbuf ) = = 0 )
return 1 ;
return 0 ;
}
2005-08-13 04:19:22 +04:00
static void udev_scan_block ( struct list_head * device_list )
2004-03-02 12:30:38 +03:00
{
2005-03-07 06:29:43 +03:00
char base [ PATH_SIZE ] ;
2004-03-03 09:15:20 +03:00
DIR * dir ;
struct dirent * dent ;
2004-03-02 12:30:38 +03:00
2005-03-07 06:29:43 +03:00
snprintf ( base , sizeof ( base ) , " %s/block " , sysfs_path ) ;
base [ sizeof ( base ) - 1 ] = ' \0 ' ;
2005-02-10 20:26:09 +03:00
dir = opendir ( base ) ;
2004-03-03 09:15:20 +03:00
if ( dir ! = NULL ) {
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
2005-03-07 06:29:43 +03:00
char dirname [ PATH_SIZE ] ;
2004-03-03 09:15:20 +03:00
DIR * dir2 ;
struct dirent * dent2 ;
2005-02-10 20:26:09 +03:00
if ( dent - > d_name [ 0 ] = = ' . ' )
2004-03-02 12:30:38 +03:00
continue ;
2005-03-07 06:29:43 +03:00
snprintf ( dirname , sizeof ( dirname ) , " %s/%s " , base , dent - > d_name ) ;
dirname [ sizeof ( dirname ) - 1 ] = ' \0 ' ;
2005-02-10 20:26:09 +03:00
if ( has_devt ( dirname ) )
2006-01-09 23:18:00 +03:00
device_list_insert ( dirname , device_list ) ;
2005-02-10 20:26:09 +03:00
else
continue ;
2004-07-02 09:19:18 +04:00
2005-03-07 02:01:34 +03:00
/* look for partitions */
2004-03-02 12:30:38 +03:00
dir2 = opendir ( dirname ) ;
2004-03-03 09:15:20 +03:00
if ( dir2 ! = NULL ) {
for ( dent2 = readdir ( dir2 ) ; dent2 ! = NULL ; dent2 = readdir ( dir2 ) ) {
2005-03-07 06:29:43 +03:00
char dirname2 [ PATH_SIZE ] ;
2004-03-02 12:30:38 +03:00
2005-02-10 20:26:09 +03:00
if ( dent2 - > d_name [ 0 ] = = ' . ' )
2004-03-02 12:30:38 +03:00
continue ;
2005-03-07 06:29:43 +03:00
snprintf ( dirname2 , sizeof ( dirname2 ) , " %s/%s " , dirname , dent2 - > d_name ) ;
dirname2 [ sizeof ( dirname2 ) - 1 ] = ' \0 ' ;
2005-02-10 20:26:09 +03:00
if ( has_devt ( dirname2 ) )
2006-01-09 23:18:00 +03:00
device_list_insert ( dirname2 , device_list ) ;
2004-03-02 12:30:38 +03:00
}
2004-04-24 08:55:57 +04:00
closedir ( dir2 ) ;
2004-03-02 12:30:38 +03:00
}
}
2004-04-24 08:55:57 +04:00
closedir ( dir ) ;
2004-03-02 12:30:38 +03:00
}
2004-07-02 09:19:18 +04:00
}
2005-08-13 04:19:22 +04:00
static void udev_scan_class ( struct list_head * device_list )
2004-07-02 09:19:18 +04:00
{
2005-03-07 06:29:43 +03:00
char base [ PATH_SIZE ] ;
2004-07-02 09:19:18 +04:00
DIR * dir ;
struct dirent * dent ;
2005-03-07 06:29:43 +03:00
snprintf ( base , sizeof ( base ) , " %s/class " , sysfs_path ) ;
base [ sizeof ( base ) - 1 ] = ' \0 ' ;
2005-02-10 20:26:09 +03:00
dir = opendir ( base ) ;
2004-03-03 09:15:20 +03:00
if ( dir ! = NULL ) {
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
2005-03-07 06:29:43 +03:00
char dirname [ PATH_SIZE ] ;
2004-03-03 09:15:20 +03:00
DIR * dir2 ;
struct dirent * dent2 ;
2005-02-10 20:26:09 +03:00
if ( dent - > d_name [ 0 ] = = ' . ' )
2004-03-02 12:30:38 +03:00
continue ;
2005-03-07 06:29:43 +03:00
snprintf ( dirname , sizeof ( dirname ) , " %s/%s " , base , dent - > d_name ) ;
dirname [ sizeof ( dirname ) - 1 ] = ' \0 ' ;
2005-03-07 02:01:34 +03:00
2004-03-02 12:30:38 +03:00
dir2 = opendir ( dirname ) ;
2004-03-03 09:15:20 +03:00
if ( dir2 ! = NULL ) {
for ( dent2 = readdir ( dir2 ) ; dent2 ! = NULL ; dent2 = readdir ( dir2 ) ) {
2005-03-07 06:29:43 +03:00
char dirname2 [ PATH_SIZE ] ;
2004-03-02 12:30:38 +03:00
2005-02-10 20:26:09 +03:00
if ( dent2 - > d_name [ 0 ] = = ' . ' )
2004-03-02 12:30:38 +03:00
continue ;
2005-03-07 06:29:43 +03:00
snprintf ( dirname2 , sizeof ( dirname2 ) , " %s/%s " , dirname , dent2 - > d_name ) ;
dirname2 [ sizeof ( dirname2 ) - 1 ] = ' \0 ' ;
2005-02-10 20:26:09 +03:00
2005-08-11 19:19:05 +04:00
if ( has_devt ( dirname2 ) | | strcmp ( dent - > d_name , " net " ) = = 0 )
2006-01-09 23:18:00 +03:00
device_list_insert ( dirname2 , device_list ) ;
2004-03-02 12:30:38 +03:00
}
2004-04-24 08:55:57 +04:00
closedir ( dir2 ) ;
2004-03-02 12:30:38 +03:00
}
}
2004-04-24 08:55:57 +04:00
closedir ( dir ) ;
2004-03-02 12:30:38 +03:00
}
}
2005-03-07 02:01:34 +03:00
static void asmlinkage sig_handler ( int signum )
2004-03-02 12:30:38 +03:00
{
2005-03-07 02:01:34 +03:00
switch ( signum ) {
case SIGALRM :
exit ( 1 ) ;
case SIGINT :
case SIGTERM :
exit ( 20 + signum ) ;
}
}
int main ( int argc , char * argv [ ] , char * envp [ ] )
{
2005-08-13 04:19:22 +04:00
LIST_HEAD ( device_list ) ;
2005-03-07 02:01:34 +03:00
struct sigaction act ;
2005-07-03 00:29:46 +04:00
logging_init ( " udevstart " ) ;
2006-01-09 23:18:00 +03:00
udev_config_init ( ) ;
2005-06-26 20:55:24 +04:00
dbg ( " version %s " , UDEV_VERSION ) ;
udev_run_str = getenv ( " UDEV_RUN " ) ;
udev_log_str = getenv ( " UDEV_LOG " ) ;
2005-03-27 03:11:03 +04:00
/* disable all logging if not explicitely requested */
2005-06-26 20:55:24 +04:00
if ( udev_log_str = = NULL )
2005-03-27 03:11:03 +04:00
udev_log_priority = 0 ;
2005-03-07 02:01:34 +03:00
/* set signal handlers */
memset ( & act , 0x00 , sizeof ( act ) ) ;
act . sa_handler = ( void ( * ) ( int ) ) sig_handler ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
sigaction ( SIGALRM , & act , NULL ) ;
sigaction ( SIGINT , & act , NULL ) ;
sigaction ( SIGTERM , & act , NULL ) ;
/* trigger timeout to prevent hanging processes */
2005-07-06 16:42:26 +04:00
alarm ( UDEV_ALARM_TIMEOUT ) ;
2005-03-07 02:01:34 +03:00
2006-01-09 23:18:00 +03:00
sysfs_init ( ) ;
2005-11-05 22:00:31 +03:00
udev_rules_init ( & rules , 1 ) ;
2005-03-07 02:01:34 +03:00
2005-08-13 04:19:22 +04:00
udev_scan_class ( & device_list ) ;
udev_scan_block ( & device_list ) ;
exec_list ( & device_list ) ;
2004-11-12 08:20:22 +03:00
2006-01-09 23:18:00 +03:00
udev_rules_cleanup ( & rules ) ;
sysfs_cleanup ( ) ;
2005-03-27 03:11:03 +04:00
logging_close ( ) ;
2004-03-05 06:10:02 +03:00
return 0 ;
2004-03-02 12:30:38 +03:00
}