2003-07-21 07:48:48 +04:00
/*
* udev - add . c
*
* Userspace devfs
*
* Copyright ( C ) 2003 Greg Kroah - Hartman < greg @ kroah . com >
*
*
* 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 <string.h>
# include <stdio.h>
# include <fcntl.h>
# include <unistd.h>
# include <errno.h>
2003-10-23 06:39:54 +04:00
# include <sys/stat.h>
2003-11-24 07:56:18 +03:00
# include <sys/types.h>
# include <grp.h>
# ifndef __KLIBC__
# include <pwd.h>
2004-03-02 09:42:30 +03:00
# include <utmp.h>
2003-11-24 07:56:18 +03:00
# endif
2003-07-21 07:48:48 +04:00
2004-02-24 06:07:25 +03:00
# include "libsysfs/sysfs/libsysfs.h"
2003-07-21 07:48:48 +04:00
# include "udev.h"
2004-03-23 09:22:20 +03:00
# include "udev_lib.h"
2003-07-21 07:48:48 +04:00
# include "udev_version.h"
2003-12-08 20:40:40 +03:00
# include "udev_dbus.h"
2004-02-28 11:52:20 +03:00
# include "udev_selinux.h"
[PATCH] add udev logging to info log
On Thu, Jan 15, 2004 at 05:14:16AM +0100, Kay Sievers wrote:
> On Wed, Jan 14, 2004 at 01:10:43PM -0800, Greg KH wrote:
> > On Wed, Jan 14, 2004 at 02:34:26PM -0600, Clay Haapala wrote:
> > > On Wed, 14 Jan 2004, Chris Friesen spake thusly:
> > > >
> > > > Maybe for ones with a matching rule, you could print something like:
> > > >
> > > >
> > > Is the act of printing/syslogging a rule in an of itself?
> >
> > No, as currently the only way stuff ends up in the syslog is if
> > DEBUG=true is used on the build line.
> >
> > But it's sounding like we might want to change that... :)
>
> How about this in the syslog after connect/disconnect?
>
> Jan 15 05:07:45 pim udev[28007]: configured rule in '/etc/udev/udev.rules' at line 17 applied, 'video*' becomes 'video/webcam%n'
> Jan 15 05:07:45 pim udev[28007]: creating device node '/udev/video/webcam0'
> Jan 15 05:07:47 pim udev[28015]: removing device node '/udev/video/webcam0'
Here is a slightly better version. I've created a logging.h file and
moved the debug macros from udev.h in there.
If you type:
'make' - you will get a binary that prints one or two lines to syslog
if a device node is created or deleted
'make LOG=false' - you get a binary that prints asolutely nothing
'make DEBUG=true' - the same as today, it will print all debug lines
2004-01-16 08:53:20 +03:00
# include "logging.h"
2003-07-21 07:48:48 +04:00
# include "namedev.h"
2003-08-06 10:57:23 +04:00
# include "udevdb.h"
2003-11-24 07:56:18 +03:00
# include "klibc_fixups.h"
2003-07-21 07:48:48 +04:00
2004-03-02 09:42:30 +03:00
# define LOCAL_USER "$local"
2003-07-21 07:48:48 +04:00
/*
* Right now the major / minor of a device is stored in a file called
* " dev " in sysfs .
* The number is stored as :
2003-10-21 07:31:24 +04:00
* MM : mm
2003-07-21 07:48:48 +04:00
* MM is the major
* mm is the minor
2003-10-21 07:31:24 +04:00
* The value is in decimal .
2003-07-21 07:48:48 +04:00
*/
2003-11-14 07:08:10 +03:00
static int get_major_minor ( struct sysfs_class_device * class_dev , struct udevice * udev )
2003-07-21 07:48:48 +04:00
{
2003-08-05 08:20:19 +04:00
int retval = - ENODEV ;
2003-12-16 08:53:28 +03:00
struct sysfs_attribute * attr = NULL ;
2003-07-21 07:48:48 +04:00
2003-12-16 08:53:28 +03:00
attr = sysfs_get_classdev_attr ( class_dev , " dev " ) ;
if ( attr = = NULL )
2003-08-05 08:20:19 +04:00
goto exit ;
2003-12-16 08:53:28 +03:00
dbg ( " dev='%s' " , attr - > value ) ;
2003-07-21 07:48:48 +04:00
2003-12-16 08:53:28 +03:00
if ( sscanf ( attr - > value , " %u:%u " , & udev - > major , & udev - > minor ) ! = 2 )
2003-08-05 08:20:19 +04:00
goto exit ;
2003-11-25 09:27:17 +03:00
dbg ( " found major=%d, minor=%d " , udev - > major , udev - > minor ) ;
2003-07-21 07:48:48 +04:00
retval = 0 ;
2003-08-05 08:20:19 +04:00
exit :
2003-07-21 07:48:48 +04:00
return retval ;
}
2003-12-07 20:12:07 +03:00
static int create_path ( char * file )
{
char p [ NAME_SIZE ] ;
char * pos ;
int retval ;
struct stat stats ;
2004-02-27 06:37:47 +03:00
strfieldcpy ( p , file ) ;
2003-12-07 20:12:07 +03:00
pos = strchr ( p + 1 , ' / ' ) ;
while ( 1 ) {
pos = strchr ( pos + 1 , ' / ' ) ;
if ( pos = = NULL )
break ;
* pos = 0x00 ;
if ( stat ( p , & stats ) ) {
retval = mkdir ( p , 0755 ) ;
2004-02-12 09:28:51 +03:00
if ( retval ! = 0 ) {
2003-12-07 20:12:07 +03:00
dbg ( " mkdir(%s) failed with error '%s' " ,
p , strerror ( errno ) ) ;
return retval ;
}
dbg ( " created '%s' " , p ) ;
}
* pos = ' / ' ;
}
return 0 ;
}
2004-02-17 08:44:28 +03:00
static int make_node ( char * filename , int major , int minor , unsigned int mode , uid_t uid , gid_t gid )
{
int retval ;
retval = mknod ( filename , mode , makedev ( major , minor ) ) ;
if ( retval ! = 0 ) {
dbg ( " mknod(%s, %#o, %u, %u) failed with error '%s' " ,
filename , mode , major , minor , strerror ( errno ) ) ;
return retval ;
}
dbg ( " chmod(%s, %#o) " , filename , mode ) ;
retval = chmod ( filename , mode ) ;
if ( retval ! = 0 ) {
dbg ( " chmod(%s, %#o) failed with error '%s' " ,
filename , mode , strerror ( errno ) ) ;
return retval ;
}
if ( uid ! = 0 | | gid ! = 0 ) {
dbg ( " chown(%s, %u, %u) " , filename , uid , gid ) ;
retval = chown ( filename , uid , gid ) ;
if ( retval ! = 0 ) {
dbg ( " chown(%s, %u, %u) failed with error '%s' " ,
filename , uid , gid , strerror ( errno ) ) ;
return retval ;
}
}
return 0 ;
}
2004-03-02 09:42:30 +03:00
/* get the local logged in user */
static void set_to_local_user ( char * user )
{
struct utmp * u ;
time_t recent = 0 ;
2004-03-10 06:50:15 +03:00
strfieldcpymax ( user , default_owner_str , OWNER_SIZE ) ;
2004-03-02 09:42:30 +03:00
setutent ( ) ;
while ( 1 ) {
u = getutent ( ) ;
if ( u = = NULL )
break ;
/* is this a user login ? */
if ( u - > ut_type ! = USER_PROCESS )
continue ;
/* is this a local login ? */
if ( strcmp ( u - > ut_host , " " ) )
continue ;
if ( u - > ut_time > recent ) {
recent = u - > ut_time ;
2004-03-10 06:50:15 +03:00
strfieldcpymax ( user , u - > ut_user , OWNER_SIZE ) ;
2004-03-02 09:42:30 +03:00
dbg ( " local user is '%s' " , user ) ;
break ;
}
}
endutent ( ) ;
}
2004-03-02 10:31:06 +03:00
/* Used to unlink existing files to ensure that our new file/symlink is created */
static int unlink_entry ( char * filename )
2003-07-21 07:48:48 +04:00
{
2003-12-30 12:33:35 +03:00
struct stat stats ;
2004-03-02 10:31:06 +03:00
int retval = 0 ;
if ( lstat ( filename , & stats ) = = 0 ) {
if ( ( stats . st_mode & S_IFMT ) ! = S_IFDIR ) {
retval = unlink ( filename ) ;
if ( retval ) {
2004-03-02 11:21:27 +03:00
dbg ( " unlink(%s) failed with error '%s' " ,
2004-03-02 10:31:06 +03:00
filename , strerror ( errno ) ) ;
}
}
}
return retval ;
}
static int create_node ( struct udevice * dev , int fake )
{
2004-03-04 05:16:35 +03:00
char filename [ NAME_SIZE ] ;
char linkname [ NAME_SIZE ] ;
char linktarget [ NAME_SIZE ] ;
char partitionname [ NAME_SIZE ] ;
2003-07-21 07:48:48 +04:00
int retval = 0 ;
2003-11-12 14:50:33 +03:00
uid_t uid = 0 ;
gid_t gid = 0 ;
2003-12-07 20:12:07 +03:00
int i ;
int tail ;
[PATCH] better fix for NAME="foo-%c{N}" gets a truncated name
On Wed, Mar 03, 2004 at 04:56:34PM -0800, Greg KH wrote:
> On Wed, Mar 03, 2004 at 03:57:04PM -0800, Patrick Mansfield wrote:
> >
> > Here is a patch for some new tests.
>
> Applied, thanks.
Here is a small improvement, which looks much better.
Hey Pat, thanks a lot for finding the recent bug, hope this one will
not break it again :)
2004-03-05 05:55:34 +03:00
char * pos ;
int len ;
2003-12-07 20:12:07 +03:00
2004-02-27 06:37:47 +03:00
strfieldcpy ( filename , udev_root ) ;
strfieldcat ( filename , dev - > name ) ;
2003-10-21 09:48:44 +04:00
switch ( dev - > type ) {
2003-08-05 08:59:50 +04:00
case ' b ' :
2003-10-21 09:48:44 +04:00
dev - > mode | = S_IFBLK ;
2003-08-05 08:59:50 +04:00
break ;
case ' c ' :
case ' u ' :
2003-10-21 09:48:44 +04:00
dev - > mode | = S_IFCHR ;
2003-08-05 08:59:50 +04:00
break ;
case ' p ' :
2003-10-21 09:48:44 +04:00
dev - > mode | = S_IFIFO ;
2003-08-05 08:59:50 +04:00
break ;
default :
2003-10-21 09:48:44 +04:00
dbg ( " unknown node type %c \n " , dev - > type ) ;
2003-08-05 08:59:50 +04:00
return - EINVAL ;
}
2003-08-05 08:39:33 +04:00
2003-12-07 20:12:07 +03:00
/* create parent directories if needed */
if ( strrchr ( dev - > name , ' / ' ) )
create_path ( filename ) ;
2003-11-12 14:48:01 +03:00
2004-02-12 09:28:51 +03:00
if ( dev - > owner [ 0 ] ! = ' \0 ' ) {
2003-11-12 14:47:57 +03:00
char * endptr ;
unsigned long id = strtoul ( dev - > owner , & endptr , 10 ) ;
2004-01-20 06:44:24 +03:00
if ( endptr [ 0 ] = = ' \0 ' )
2003-11-12 14:47:57 +03:00
uid = ( uid_t ) id ;
2003-11-24 07:56:18 +03:00
else {
2004-03-04 11:51:28 +03:00
struct passwd * pw ;
2004-03-02 09:42:30 +03:00
if ( strncmp ( dev - > owner , LOCAL_USER , sizeof ( LOCAL_USER ) ) = = 0 )
set_to_local_user ( dev - > owner ) ;
2004-03-04 11:51:28 +03:00
pw = getpwnam ( dev - > owner ) ;
2004-01-20 06:44:24 +03:00
if ( pw = = NULL )
dbg ( " specified user unknown '%s' " , dev - > owner ) ;
2003-11-24 07:56:18 +03:00
else
uid = pw - > pw_uid ;
}
2003-11-12 14:47:57 +03:00
}
2004-02-12 09:28:51 +03:00
if ( dev - > group [ 0 ] ! = ' \0 ' ) {
2003-11-12 14:47:57 +03:00
char * endptr ;
unsigned long id = strtoul ( dev - > group , & endptr , 10 ) ;
2004-01-20 06:44:24 +03:00
if ( endptr [ 0 ] = = ' \0 ' )
2003-11-12 14:47:57 +03:00
gid = ( gid_t ) id ;
2003-11-24 07:56:18 +03:00
else {
struct group * gr = getgrnam ( dev - > group ) ;
2004-01-20 06:44:24 +03:00
if ( gr = = NULL )
dbg ( " specified group unknown '%s' " , dev - > group ) ;
2003-11-24 07:56:18 +03:00
else
gid = gr - > gr_gid ;
}
2003-11-12 14:47:57 +03:00
}
2004-02-17 08:58:25 +03:00
if ( ! fake ) {
2004-03-02 10:31:06 +03:00
unlink_entry ( filename ) ;
2004-02-17 08:44:28 +03:00
info ( " creating device node '%s' " , filename ) ;
make_node ( filename , dev - > major , dev - > minor , dev - > mode , uid , gid ) ;
2004-02-17 08:58:25 +03:00
} else {
info ( " creating device node '%s', major = '%d', minor = '%d', "
" mode = '%#o', uid = '%d', gid = '%d' " , filename ,
dev - > major , dev - > minor , ( mode_t ) dev - > mode , uid , gid ) ;
}
2004-02-17 08:44:28 +03:00
/* create partitions if requested */
if ( dev - > partitions > 0 ) {
info ( " creating device partition nodes '%s[1-%i]' " , filename , dev - > partitions ) ;
2004-02-17 08:58:25 +03:00
if ( ! fake ) {
for ( i = 1 ; i < = dev - > partitions ; i + + ) {
2004-02-28 12:59:02 +03:00
strfieldcpy ( partitionname , filename ) ;
strintcat ( partitionname , i ) ;
2004-03-02 10:31:06 +03:00
unlink_entry ( partitionname ) ;
2004-02-17 08:58:25 +03:00
make_node ( partitionname , dev - > major ,
dev - > minor + i , dev - > mode , uid , gid ) ;
}
2004-02-17 08:44:28 +03:00
}
2003-12-07 20:12:07 +03:00
}
2004-02-28 11:52:20 +03:00
if ( ! fake )
selinux_add_node ( filename ) ;
2003-12-07 20:12:07 +03:00
/* create symlink if requested */
2004-03-04 05:16:35 +03:00
foreach_strpart ( dev - > symlink , " " , pos , len ) {
2004-03-10 06:50:15 +03:00
strfieldcpymax ( linkname , pos , len + 1 ) ;
2004-03-04 05:16:35 +03:00
strfieldcpy ( filename , udev_root ) ;
strfieldcat ( filename , linkname ) ;
dbg ( " symlink '%s' to node '%s' requested " , filename , dev - > name ) ;
if ( ! fake )
if ( strrchr ( linkname , ' / ' ) )
create_path ( filename ) ;
/* optimize relative link */
linktarget [ 0 ] = ' \0 ' ;
i = 0 ;
tail = 0 ;
while ( ( dev - > name [ i ] = = linkname [ i ] ) & & dev - > name [ i ] ) {
if ( dev - > name [ i ] = = ' / ' )
tail = i + 1 ;
i + + ;
}
while ( linkname [ i ] ! = ' \0 ' ) {
if ( linkname [ i ] = = ' / ' )
strfieldcat ( linktarget , " ../ " ) ;
i + + ;
}
2003-12-07 20:12:07 +03:00
2004-03-04 05:16:35 +03:00
strfieldcat ( linktarget , & dev - > name [ tail ] ) ;
2003-12-07 20:12:07 +03:00
2004-03-04 05:16:35 +03:00
if ( ! fake )
unlink_entry ( filename ) ;
2003-12-30 12:33:35 +03:00
2004-03-04 05:16:35 +03:00
dbg ( " symlink(%s, %s) " , linktarget , filename ) ;
if ( ! fake ) {
retval = symlink ( linktarget , filename ) ;
if ( retval ! = 0 )
dbg ( " symlink(%s, %s) failed with error '%s' " ,
linktarget , filename , strerror ( errno ) ) ;
2003-12-10 11:47:00 +03:00
}
2003-11-12 14:47:57 +03:00
}
2003-07-21 07:48:48 +04:00
return retval ;
}
2003-10-20 08:55:43 +04:00
static struct sysfs_class_device * get_class_dev ( char * device_name )
2003-07-21 07:48:48 +04:00
{
char dev_path [ SYSFS_PATH_MAX ] ;
2003-08-05 09:13:35 +04:00
struct sysfs_class_device * class_dev = NULL ;
2004-02-27 06:37:47 +03:00
strfieldcpy ( dev_path , sysfs_path ) ;
strfieldcat ( dev_path , device_name ) ;
2003-11-25 09:27:17 +03:00
dbg ( " looking at '%s' " , dev_path ) ;
2003-07-21 07:48:48 +04:00
/* open up the sysfs class device for this thing... */
2003-12-20 05:29:10 +03:00
class_dev = sysfs_open_class_device_path ( dev_path ) ;
2003-07-21 07:48:48 +04:00
if ( class_dev = = NULL ) {
2003-12-20 05:29:10 +03:00
dbg ( " sysfs_open_class_device_path failed " ) ;
2003-08-05 09:13:35 +04:00
goto exit ;
2003-07-21 07:48:48 +04:00
}
2003-11-25 09:27:17 +03:00
dbg ( " class_dev->name='%s' " , class_dev - > name ) ;
2003-07-21 07:48:48 +04:00
2003-08-05 09:13:35 +04:00
exit :
2003-07-21 07:48:48 +04:00
return class_dev ;
}
2003-10-21 08:03:15 +04:00
/* wait for the "dev" file to show up in the directory in sysfs.
* If it doesn ' t happen in about 10 seconds , give up .
*/
# define SECONDS_TO_WAIT_FOR_DEV 10
2003-10-22 08:45:19 +04:00
static int sleep_for_dev ( char * path )
2003-10-21 08:03:15 +04:00
{
char filename [ SYSFS_PATH_MAX + 6 ] ;
2003-10-23 11:50:27 +04:00
int loop = SECONDS_TO_WAIT_FOR_DEV ;
int retval ;
2003-10-21 08:03:15 +04:00
2004-02-27 06:37:47 +03:00
strfieldcpy ( filename , sysfs_path ) ;
strfieldcat ( filename , path ) ;
strfieldcat ( filename , " /dev " ) ;
2003-10-21 08:03:15 +04:00
2003-10-23 11:50:27 +04:00
while ( loop - - ) {
struct stat buf ;
2003-11-25 09:27:17 +03:00
dbg ( " looking for '%s' " , filename ) ;
2003-10-21 08:03:15 +04:00
retval = stat ( filename , & buf ) ;
2004-02-12 09:28:51 +03:00
if ( retval = = 0 )
2003-10-21 08:03:15 +04:00
goto exit ;
2003-11-25 09:27:17 +03:00
/* sleep to give the kernel a chance to create the dev file */
2003-10-21 08:03:15 +04:00
sleep ( 1 ) ;
}
retval = - ENODEV ;
exit :
return retval ;
}
2004-02-13 07:19:21 +03:00
int udev_add_device ( char * path , char * subsystem , int fake )
2003-07-21 07:48:48 +04:00
{
2003-11-14 07:08:10 +03:00
struct sysfs_class_device * class_dev = NULL ;
2003-10-21 09:48:44 +04:00
struct udevice dev ;
2003-07-21 07:48:48 +04:00
int retval = - EINVAL ;
2003-11-24 09:25:13 +03:00
memset ( & dev , 0x00 , sizeof ( dev ) ) ;
2003-07-21 07:48:48 +04:00
/* for now, the block layer is the only place where block devices are */
if ( strcmp ( subsystem , " block " ) = = 0 )
2003-10-21 09:48:44 +04:00
dev . type = ' b ' ;
2003-07-21 07:48:48 +04:00
else
2003-10-21 09:48:44 +04:00
dev . type = ' c ' ;
2003-07-21 07:48:48 +04:00
2003-10-21 09:48:44 +04:00
retval = sleep_for_dev ( path ) ;
2004-02-12 09:28:51 +03:00
if ( retval ! = 0 )
2003-10-21 08:03:15 +04:00
goto exit ;
2003-08-05 09:13:35 +04:00
2003-10-21 09:48:44 +04:00
class_dev = get_class_dev ( path ) ;
2003-07-21 07:48:48 +04:00
if ( class_dev = = NULL )
goto exit ;
2003-11-14 07:08:10 +03:00
retval = get_major_minor ( class_dev , & dev ) ;
2004-02-12 09:28:51 +03:00
if ( retval ! = 0 ) {
2003-10-18 10:32:17 +04:00
dbg ( " get_major_minor failed " ) ;
2003-07-21 07:48:48 +04:00
goto exit ;
}
2003-11-14 07:08:10 +03:00
retval = namedev_name_device ( class_dev , & dev ) ;
2004-02-12 09:28:51 +03:00
if ( retval ! = 0 )
2003-11-14 07:08:10 +03:00
goto exit ;
2004-02-13 07:19:21 +03:00
if ( ! fake ) {
retval = udevdb_add_dev ( path , & dev ) ;
if ( retval ! = 0 )
dbg ( " udevdb_add_dev failed, but we are going to try "
" to create the node anyway. But remove might not "
" work properly for this device. " ) ;
2003-10-18 10:32:17 +04:00
2004-02-13 07:19:21 +03:00
}
2003-11-25 09:27:17 +03:00
dbg ( " name='%s' " , dev . name ) ;
2004-02-13 07:19:21 +03:00
retval = create_node ( & dev , fake ) ;
2003-07-21 07:48:48 +04:00
2004-02-13 07:19:21 +03:00
if ( ( retval = = 0 ) & & ( ! fake ) )
2003-12-08 20:40:40 +03:00
sysbus_send_create ( & dev , path ) ;
[PATCH] D-BUS patch for udev-008
Attached is a patch against udev-008 to send out a D-BUS message when a
device node is added or removed.
Using D-BUS lingo, udev acquires the org.kernel.udev service and sends
out a NodeCreated or NodeDeleted signal on the
org.kernel.udev.NodeMonitor interface. Each signal carries two
parameters: the node in question and the corresponding sysfs path.
[Note: the D-BUS concepts of service, interface, object can be a bit
confusing at first glance]
An example program listening for these messages looks like this
#!/usr/bin/python
import dbus
import gtk
def udev_signal_received(dbus_iface, member, service, object_path, message):
[filename, sysfs_path] = message.get_args_list()
if member=='NodeCreated':
print 'Node %s created for %s'%(filename, sysfs_path)
elif member=='NodeDeleted':
print 'Node %s deleted for %s'%(filename, sysfs_path)
def main():
bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
bus.add_signal_receiver(udev_signal_received,
'org.kernel.udev.NodeMonitor', # interface
'org.kernel.udev', # service
'/org/kernel/udev/NodeMonitor') # object
gtk.mainloop()
if __name__ == '__main__':
main()
and this is the output when hot-plugging some usb-storage.
[david@laptop udev-008]$ ~/node_monitor.py
Node /udev/sda created for /block/sda
Node /udev/sda1 created for /block/sda/sda1
Node /udev/sda1 deleted for /block/sda/sda1
Node /udev/sda deleted for /block/sda
The patch requires D-BUS 0.20 or later while the python example program
requires D-BUS from CVS as I only recently applied a patch against the
python bindings.
2003-12-08 20:19:19 +03:00
2003-07-21 07:48:48 +04:00
exit :
2003-11-14 07:08:10 +03:00
if ( class_dev )
sysfs_close_class_device ( class_dev ) ;
2003-07-21 07:48:48 +04:00
return retval ;
}