2005-07-22 20:35:58 +04:00
/*
2006-04-06 00:29:33 +04:00
* Copyright ( C ) 2004 - 2006 Kay Sievers < kay . sievers @ vrfy . org >
2005-07-22 20:35:58 +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 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 . ,
2006-08-28 02:29:11 +04:00
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
2005-07-22 20:35:58 +04:00
*
*/
# include <stdlib.h>
# include <stdio.h>
# include <stddef.h>
# include <unistd.h>
# include <fcntl.h>
# include <errno.h>
# include <ctype.h>
2006-04-28 02:32:32 +04:00
# include <sys/ioctl.h>
# include <sys/socket.h>
2006-04-06 00:29:33 +04:00
# include <net/if.h>
# include <linux/sockios.h>
2006-01-09 23:18:00 +03:00
2005-07-22 20:35:58 +04:00
# include "udev.h"
2006-01-09 23:18:00 +03:00
# include "udev_rules.h"
2005-07-22 20:35:58 +04:00
2006-01-09 23:18:00 +03:00
struct udevice * udev_device_init ( void )
2005-07-22 20:35:58 +04:00
{
2006-01-09 23:18:00 +03:00
struct udevice * udev ;
2005-07-22 20:35:58 +04:00
2006-01-09 23:18:00 +03:00
udev = malloc ( sizeof ( struct udevice ) ) ;
if ( udev = = NULL )
return NULL ;
2005-07-22 20:35:58 +04:00
memset ( udev , 0x00 , sizeof ( struct udevice ) ) ;
2006-01-09 23:18:00 +03:00
2005-07-22 20:35:58 +04:00
INIT_LIST_HEAD ( & udev - > symlink_list ) ;
INIT_LIST_HEAD ( & udev - > run_list ) ;
INIT_LIST_HEAD ( & udev - > env_list ) ;
2006-01-09 23:18:00 +03:00
/* set sysfs device to local storage, can be overridden if needed */
udev - > dev = & udev - > dev_local ;
2005-07-22 20:35:58 +04:00
2006-01-09 23:18:00 +03:00
/* default node permissions */
udev - > mode = 0660 ;
strcpy ( udev - > owner , " root " ) ;
strcpy ( udev - > group , " root " ) ;
2005-07-22 20:35:58 +04:00
2006-01-09 23:18:00 +03:00
return udev ;
2005-07-22 20:35:58 +04:00
}
2006-01-09 23:18:00 +03:00
void udev_device_cleanup ( struct udevice * udev )
2005-07-22 20:35:58 +04:00
{
2005-08-27 18:15:41 +04:00
name_list_cleanup ( & udev - > symlink_list ) ;
name_list_cleanup ( & udev - > run_list ) ;
name_list_cleanup ( & udev - > env_list ) ;
2006-01-09 23:18:00 +03:00
free ( udev ) ;
}
dev_t udev_device_get_devt ( struct udevice * udev )
{
const char * attr ;
2006-08-24 12:25:34 +04:00
unsigned int maj , min ;
2006-01-09 23:18:00 +03:00
/* read it from sysfs */
attr = sysfs_attr_get_value ( udev - > dev - > devpath , " dev " ) ;
if ( attr ! = NULL ) {
2006-08-24 12:25:34 +04:00
if ( sscanf ( attr , " %u:%u " , & maj , & min ) = = 2 )
return makedev ( maj , min ) ;
2006-01-09 23:18:00 +03:00
}
return makedev ( 0 , 0 ) ;
}
2006-07-03 02:58:35 +04:00
static int rename_netif ( struct udevice * udev )
2006-04-06 00:29:33 +04:00
{
int sk ;
struct ifreq ifr ;
int retval ;
2006-08-19 18:06:25 +04:00
info ( " changing net interface name from '%s' to '%s' " , udev - > dev - > kernel , udev - > name ) ;
2006-04-06 00:29:33 +04:00
if ( udev - > test_run )
return 0 ;
sk = socket ( PF_INET , SOCK_DGRAM , 0 ) ;
if ( sk < 0 ) {
err ( " error opening socket: %s " , strerror ( errno ) ) ;
return - 1 ;
}
memset ( & ifr , 0x00 , sizeof ( struct ifreq ) ) ;
2006-08-19 18:06:25 +04:00
strlcpy ( ifr . ifr_name , udev - > dev - > kernel , IFNAMSIZ ) ;
2006-04-06 00:29:33 +04:00
strlcpy ( ifr . ifr_newname , udev - > name , IFNAMSIZ ) ;
retval = ioctl ( sk , SIOCSIFNAME , & ifr ) ;
2006-07-03 02:58:35 +04:00
if ( retval ! = 0 ) {
int loop ;
2006-04-06 00:29:33 +04:00
2006-07-03 02:58:35 +04:00
/* see if the destination interface name already exists */
if ( errno ! = EEXIST ) {
2006-07-29 03:00:16 +04:00
err ( " error changing netif name %s to %s: %s " , ifr . ifr_name , ifr . ifr_newname , strerror ( errno ) ) ;
2006-07-03 02:58:35 +04:00
goto exit ;
}
/* free our own name, another process may wait for us */
2006-08-19 18:06:25 +04:00
strlcpy ( ifr . ifr_newname , udev - > dev - > kernel , IFNAMSIZ ) ;
2006-07-03 02:58:35 +04:00
strlcat ( ifr . ifr_newname , " _rename " , IFNAMSIZ ) ;
retval = ioctl ( sk , SIOCSIFNAME , & ifr ) ;
if ( retval ! = 0 ) {
2006-07-29 03:00:16 +04:00
err ( " error changing netif name %s to %s: %s " , ifr . ifr_name , ifr . ifr_newname , strerror ( errno ) ) ;
2006-07-03 02:58:35 +04:00
goto exit ;
}
/* wait 30 seconds for our target to become available */
strlcpy ( ifr . ifr_name , ifr . ifr_newname , IFNAMSIZ ) ;
strlcpy ( ifr . ifr_newname , udev - > name , IFNAMSIZ ) ;
loop = 30 * 20 ;
while ( loop - - ) {
retval = ioctl ( sk , SIOCSIFNAME , & ifr ) ;
if ( retval ! = 0 ) {
if ( errno ! = EEXIST ) {
2006-07-29 03:00:16 +04:00
err ( " error changing net interface name %s to %s: %s " ,
ifr . ifr_name , ifr . ifr_newname , strerror ( errno ) ) ;
2006-07-03 02:58:35 +04:00
break ;
}
dbg ( " wait for netif '%s' to become free, loop=%i " , udev - > name , ( 30 * 20 ) - loop ) ;
usleep ( 1000 * 1000 / 20 ) ;
}
}
}
exit :
close ( sk ) ;
2006-04-06 00:29:33 +04:00
return retval ;
}
2006-01-09 23:18:00 +03:00
int udev_device_event ( struct udev_rules * rules , struct udevice * udev )
{
int retval = 0 ;
2006-04-06 00:29:33 +04:00
/* add device node */
2006-06-14 20:12:01 +04:00
if ( major ( udev - > devt ) ! = 0 & &
2006-07-22 00:31:58 +04:00
( strcmp ( udev - > action , " add " ) = = 0 | | strcmp ( udev - > action , " change " ) = = 0 ) ) {
2006-04-13 00:08:05 +04:00
struct udevice * udev_old ;
2006-04-06 00:29:33 +04:00
dbg ( " device node add '%s' " , udev - > dev - > devpath ) ;
2006-04-13 00:08:05 +04:00
2006-01-09 23:18:00 +03:00
udev_rules_get_name ( rules , udev ) ;
if ( udev - > ignore_device ) {
info ( " device event will be ignored " ) ;
2006-04-06 00:29:33 +04:00
goto exit ;
2006-01-09 23:18:00 +03:00
}
2006-04-06 00:29:33 +04:00
if ( udev - > name [ 0 ] = = ' \0 ' ) {
2006-01-09 23:18:00 +03:00
info ( " device node creation supressed " ) ;
2006-04-06 00:29:33 +04:00
goto exit ;
}
2006-04-13 00:08:05 +04:00
/* read current database entry, we may want to cleanup symlinks */
udev_old = udev_device_init ( ) ;
if ( udev_old ! = NULL ) {
2006-06-14 18:32:52 +04:00
if ( udev_db_get_device ( udev_old , udev - > dev - > devpath ) ! = 0 ) {
udev_device_cleanup ( udev_old ) ;
udev_old = NULL ;
} else
info ( " device '%s' already in database, validate currently present symlinks " ,
udev - > dev - > devpath ) ;
2006-04-13 00:08:05 +04:00
}
2006-06-14 18:32:52 +04:00
/* create node and symlinks */
2006-04-13 00:08:05 +04:00
retval = udev_node_add ( udev , udev_old ) ;
2006-06-14 18:32:52 +04:00
if ( retval = = 0 ) {
/* store record in database */
2006-04-06 00:29:33 +04:00
udev_db_add_device ( udev ) ;
2006-06-14 18:32:52 +04:00
/* remove possibly left-over symlinks */
if ( udev_old ! = NULL ) {
struct name_entry * link_loop ;
struct name_entry * link_old_loop ;
struct name_entry * link_old_tmp_loop ;
/* remove still valid symlinks from old list */
list_for_each_entry_safe ( link_old_loop , link_old_tmp_loop , & udev_old - > symlink_list , node )
list_for_each_entry ( link_loop , & udev - > symlink_list , node )
if ( strcmp ( link_old_loop - > name , link_loop - > name ) = = 0 ) {
dbg ( " symlink '%s' still valid, keep it " , link_old_loop - > name ) ;
list_del ( & link_old_loop - > node ) ;
free ( link_old_loop ) ;
}
udev_node_remove_symlinks ( udev_old ) ;
udev_device_cleanup ( udev_old ) ;
}
}
2006-04-06 00:29:33 +04:00
goto exit ;
2006-01-09 23:18:00 +03:00
}
2006-04-06 00:29:33 +04:00
/* add netif */
if ( strcmp ( udev - > dev - > subsystem , " net " ) = = 0 & & strcmp ( udev - > action , " add " ) = = 0 ) {
dbg ( " netif add '%s' " , udev - > dev - > devpath ) ;
udev_rules_get_name ( rules , udev ) ;
if ( udev - > ignore_device ) {
info ( " device event will be ignored " ) ;
goto exit ;
}
/* look if we want to change the name of the netif */
2006-08-19 18:06:25 +04:00
if ( strcmp ( udev - > name , udev - > dev - > kernel ) ! = 0 ) {
2006-04-06 00:29:33 +04:00
char * pos ;
2006-07-03 02:58:35 +04:00
retval = rename_netif ( udev ) ;
2006-04-06 00:29:33 +04:00
if ( retval ! = 0 )
goto exit ;
info ( " renamed netif to '%s' " , udev - > name ) ;
2006-04-06 22:40:58 +04:00
/* export old name */
2006-08-19 18:06:25 +04:00
setenv ( " INTERFACE_OLD " , udev - > dev - > kernel , 1 ) ;
2006-04-06 22:40:58 +04:00
2006-04-06 00:29:33 +04:00
/* now fake the devpath, because the kernel name changed silently */
pos = strrchr ( udev - > dev - > devpath , ' / ' ) ;
if ( pos ! = NULL ) {
pos [ 1 ] = ' \0 ' ;
strlcat ( udev - > dev - > devpath , udev - > name , sizeof ( udev - > dev - > devpath ) ) ;
2006-08-19 18:06:25 +04:00
strlcpy ( udev - > dev - > kernel , udev - > name , sizeof ( udev - > dev - > kernel ) ) ;
2006-04-06 00:29:33 +04:00
setenv ( " DEVPATH " , udev - > dev - > devpath , 1 ) ;
setenv ( " INTERFACE " , udev - > name , 1 ) ;
}
}
goto exit ;
}
/* remove device node */
2006-01-09 23:18:00 +03:00
if ( major ( udev - > devt ) ! = 0 & & strcmp ( udev - > action , " remove " ) = = 0 ) {
struct name_entry * name_loop ;
2006-04-06 00:29:33 +04:00
/* import and delete database entry */
if ( udev_db_get_device ( udev , udev - > dev - > devpath ) = = 0 ) {
udev_db_delete_device ( udev ) ;
if ( udev - > ignore_remove ) {
dbg ( " remove event for '%s' requested to be ignored by rule " , udev - > name ) ;
return 0 ;
}
/* restore stored persistent data */
list_for_each_entry ( name_loop , & udev - > env_list , node )
putenv ( name_loop - > name ) ;
} else {
2006-08-19 18:06:25 +04:00
dbg ( " '%s' not found in database, using kernel name '%s' " , udev - > dev - > devpath , udev - > dev - > kernel ) ;
strlcpy ( udev - > name , udev - > dev - > kernel , sizeof ( udev - > name ) ) ;
2006-04-06 00:29:33 +04:00
}
2006-01-09 23:18:00 +03:00
udev_rules_get_run ( rules , udev ) ;
if ( udev - > ignore_device ) {
info ( " device event will be ignored " ) ;
2006-04-06 00:29:33 +04:00
goto exit ;
2006-01-09 23:18:00 +03:00
}
2006-04-06 00:29:33 +04:00
retval = udev_node_remove ( udev ) ;
goto exit ;
2006-01-09 23:18:00 +03:00
}
2006-04-06 00:29:33 +04:00
/* default devices */
2006-01-09 23:18:00 +03:00
udev_rules_get_run ( rules , udev ) ;
2006-01-25 04:21:07 +03:00
if ( udev - > ignore_device )
2006-01-09 23:18:00 +03:00
info ( " device event will be ignored " ) ;
2006-04-06 00:29:33 +04:00
exit :
2006-01-09 23:18:00 +03:00
return retval ;
2005-07-22 20:35:58 +04:00
}