2009-02-11 20:38:56 +03:00
/*
2010-03-10 20:54:35 +03:00
* Copyright ( C ) 2004 - 2010 Kay Sievers < kay . sievers @ vrfy . org >
2009-02-11 20:38:56 +03:00
* Copyright ( C ) 2009 Canonical Ltd .
2009-02-13 18:02:36 +03:00
* Copyright ( C ) 2009 Scott James Remnant < scott @ netsplit . com >
2009-02-11 20:38:56 +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 , either version 2 of the License , or
* ( at your option ) any later version .
*
* 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/>.
*/
# include <sys/types.h>
# include <errno.h>
# include <fcntl.h>
# include <stdio.h>
# include <dirent.h>
# include <stddef.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <sys/inotify.h>
# include "udev.h"
2009-06-04 03:44:04 +04:00
static int inotify_fd = - 1 ;
2009-02-11 20:38:56 +03:00
/* inotify descriptor, will be shared with rules directory;
* set to cloexec since we need our children to be able to add
* watches for us
*/
2009-06-04 03:44:04 +04:00
int udev_watch_init ( struct udev * udev )
2009-02-11 20:38:56 +03:00
{
2009-10-30 14:31:59 +03:00
inotify_fd = inotify_init1 ( IN_CLOEXEC ) ;
if ( inotify_fd < 0 )
2009-02-11 20:38:56 +03:00
err ( udev , " inotify_init failed: %m \n " ) ;
2009-06-04 03:44:04 +04:00
return inotify_fd ;
2009-02-11 20:38:56 +03:00
}
/* move any old watches directory out of the way, and then restore
* the watches
*/
void udev_watch_restore ( struct udev * udev )
{
char filename [ UTIL_PATH_SIZE ] , oldname [ UTIL_PATH_SIZE ] ;
if ( inotify_fd < 0 )
return ;
2011-03-23 04:31:09 +03:00
util_strscpyl ( oldname , sizeof ( oldname ) , udev_get_run_path ( udev ) , " /watch.old " , NULL ) ;
util_strscpyl ( filename , sizeof ( filename ) , udev_get_run_path ( udev ) , " /watch " , NULL ) ;
2009-02-11 20:38:56 +03:00
if ( rename ( filename , oldname ) = = 0 ) {
DIR * dir ;
struct dirent * ent ;
dir = opendir ( oldname ) ;
if ( dir = = NULL ) {
err ( udev , " unable to open old watches dir '%s', old watches will not be restored: %m " , oldname ) ;
return ;
}
2009-05-20 19:57:52 +04:00
for ( ent = readdir ( dir ) ; ent ! = NULL ; ent = readdir ( dir ) ) {
2009-05-20 20:58:52 +04:00
char device [ UTIL_PATH_SIZE ] ;
2009-05-20 19:57:52 +04:00
char * s ;
size_t l ;
2009-02-11 20:38:56 +03:00
ssize_t len ;
struct udev_device * dev ;
2010-03-10 20:54:35 +03:00
if ( ent - > d_name [ 0 ] = = ' . ' )
2009-02-11 20:38:56 +03:00
continue ;
2009-05-20 20:58:52 +04:00
s = device ;
l = util_strpcpy ( & s , sizeof ( device ) , udev_get_sys_path ( udev ) ) ;
len = readlinkat ( dirfd ( dir ) , ent - > d_name , s , l ) ;
2010-04-21 16:44:33 +04:00
if ( len < = 0 | | len = = ( ssize_t ) l )
2010-03-10 20:54:35 +03:00
goto unlink ;
2009-05-20 19:57:52 +04:00
s [ len ] = ' \0 ' ;
2010-03-10 20:54:35 +03:00
2010-12-12 22:07:15 +03:00
dev = udev_device_new_from_id_filename ( udev , s ) ;
2010-03-10 20:54:35 +03:00
if ( dev = = NULL )
goto unlink ;
2009-02-11 20:38:56 +03:00
info ( udev , " restoring old watch on '%s' \n " , udev_device_get_devnode ( dev ) ) ;
udev_watch_begin ( udev , dev ) ;
udev_device_unref ( dev ) ;
2010-03-10 20:54:35 +03:00
unlink :
2009-05-20 20:58:52 +04:00
unlinkat ( dirfd ( dir ) , ent - > d_name , 0 ) ;
2009-02-11 20:38:56 +03:00
}
closedir ( dir ) ;
rmdir ( oldname ) ;
} else if ( errno ! = ENOENT ) {
err ( udev , " unable to move watches dir '%s', old watches will not be restored: %m " , filename ) ;
}
}
void udev_watch_begin ( struct udev * udev , struct udev_device * dev )
{
2009-02-24 14:58:48 +03:00
char filename [ UTIL_PATH_SIZE ] ;
2009-02-11 20:38:56 +03:00
int wd ;
2009-02-24 17:11:17 +03:00
if ( inotify_fd < 0 )
2009-02-11 20:38:56 +03:00
return ;
2009-02-23 20:46:00 +03:00
info ( udev , " adding watch on '%s' \n " , udev_device_get_devnode ( dev ) ) ;
2009-02-11 23:56:35 +03:00
wd = inotify_add_watch ( inotify_fd , udev_device_get_devnode ( dev ) , IN_CLOSE_WRITE ) ;
2009-02-11 20:38:56 +03:00
if ( wd < 0 ) {
err ( udev , " inotify_add_watch(%d, %s, %o) failed: %m \n " ,
inotify_fd , udev_device_get_devnode ( dev ) , IN_CLOSE_WRITE ) ;
2009-08-30 22:42:06 +04:00
return ;
2009-02-11 20:38:56 +03:00
}
2011-03-23 04:31:09 +03:00
snprintf ( filename , sizeof ( filename ) , " %s/watch/%d " , udev_get_run_path ( udev ) , wd ) ;
2009-02-11 20:38:56 +03:00
util_create_path ( udev , filename ) ;
unlink ( filename ) ;
2010-12-10 03:13:35 +03:00
symlink ( udev_device_get_id_filename ( dev ) , filename ) ;
2009-02-23 20:34:07 +03:00
udev_device_set_watch_handle ( dev , wd ) ;
2009-02-11 20:38:56 +03:00
}
2009-02-23 20:43:53 +03:00
void udev_watch_end ( struct udev * udev , struct udev_device * dev )
2009-02-11 20:38:56 +03:00
{
2009-02-23 20:43:53 +03:00
int wd ;
2009-02-24 14:58:48 +03:00
char filename [ UTIL_PATH_SIZE ] ;
2009-02-11 20:38:56 +03:00
2009-02-24 17:11:17 +03:00
if ( inotify_fd < 0 )
2009-02-23 20:21:24 +03:00
return ;
2009-02-23 20:43:53 +03:00
wd = udev_device_get_watch_handle ( dev ) ;
if ( wd < 0 )
2009-02-11 20:38:56 +03:00
return ;
2009-02-23 20:43:53 +03:00
info ( udev , " removing watch on '%s' \n " , udev_device_get_devnode ( dev ) ) ;
2009-02-11 20:38:56 +03:00
inotify_rm_watch ( inotify_fd , wd ) ;
2011-03-23 04:31:09 +03:00
snprintf ( filename , sizeof ( filename ) , " %s/watch/%d " , udev_get_run_path ( udev ) , wd ) ;
2009-02-11 20:38:56 +03:00
unlink ( filename ) ;
2009-02-23 20:43:53 +03:00
udev_device_set_watch_handle ( dev , - 1 ) ;
2009-02-11 20:38:56 +03:00
}
2009-02-23 20:43:53 +03:00
struct udev_device * udev_watch_lookup ( struct udev * udev , int wd )
2009-02-11 20:38:56 +03:00
{
2009-02-24 14:58:48 +03:00
char filename [ UTIL_PATH_SIZE ] ;
2010-03-10 20:54:35 +03:00
char majmin [ UTIL_PATH_SIZE ] ;
2009-05-20 19:57:52 +04:00
char * s ;
size_t l ;
2009-02-11 20:38:56 +03:00
ssize_t len ;
if ( inotify_fd < 0 | | wd < 0 )
return NULL ;
2011-03-23 04:31:09 +03:00
snprintf ( filename , sizeof ( filename ) , " %s/watch/%d " , udev_get_run_path ( udev ) , wd ) ;
2010-03-10 20:54:35 +03:00
s = majmin ;
l = util_strpcpy ( & s , sizeof ( majmin ) , udev_get_sys_path ( udev ) ) ;
2009-05-20 19:57:52 +04:00
len = readlink ( filename , s , l ) ;
2010-04-21 16:44:33 +04:00
if ( len < = 0 | | ( size_t ) len = = l )
2009-05-20 19:57:52 +04:00
return NULL ;
s [ len ] = ' \0 ' ;
2010-03-10 20:54:35 +03:00
2010-12-12 22:07:15 +03:00
return udev_device_new_from_id_filename ( udev , s ) ;
2009-02-11 20:38:56 +03:00
}