2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-01-23 03:52:57 +03:00
2010-02-03 15:03:47 +03:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd 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 .
systemd 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 systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2010-01-28 08:46:33 +03:00
# include <errno.h>
2010-01-29 08:45:59 +03:00
# include <sys/epoll.h>
2010-01-28 08:46:33 +03:00
# include <libudev.h>
2010-01-26 23:39:06 +03:00
# include "unit.h"
2010-01-23 03:52:57 +03:00
# include "device.h"
# include "strv.h"
2010-01-28 08:46:33 +03:00
# include "log.h"
2010-04-15 05:11:11 +04:00
# include "unit-name.h"
2010-04-18 05:08:16 +04:00
# include "dbus-device.h"
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
static const UnitActiveState state_translation_table [ _DEVICE_STATE_MAX ] = {
[ DEVICE_DEAD ] = UNIT_INACTIVE ,
2010-07-16 23:32:34 +04:00
[ DEVICE_PLUGGED ] = UNIT_ACTIVE
2010-01-29 05:18:09 +03:00
} ;
2010-07-20 22:33:19 +04:00
static void device_unset_sysfs ( Device * d ) {
Device * first ;
assert ( d ) ;
2010-07-21 07:00:29 +04:00
if ( ! d - > sysfs )
return ;
/* Remove this unit from the chain of devices which share the
* same sysfs path . */
first = hashmap_get ( d - > meta . manager - > devices_by_sysfs , d - > sysfs ) ;
LIST_REMOVE ( Device , same_sysfs , first , d ) ;
2010-07-20 22:33:19 +04:00
2010-07-21 07:00:29 +04:00
if ( first )
hashmap_remove_and_replace ( d - > meta . manager - > devices_by_sysfs , d - > sysfs , first - > sysfs , first ) ;
else
hashmap_remove ( d - > meta . manager - > devices_by_sysfs , d - > sysfs ) ;
free ( d - > sysfs ) ;
d - > sysfs = NULL ;
2010-07-20 22:33:19 +04:00
}
2010-07-17 06:09:28 +04:00
static void device_init ( Unit * u ) {
Device * d = DEVICE ( u ) ;
assert ( d ) ;
assert ( d - > meta . load_state = = UNIT_STUB ) ;
2010-07-20 22:33:19 +04:00
/* In contrast to all other unit types we timeout jobs waiting
* for devices by default . This is because they otherwise wait
* indefinetely for plugged in devices , something which cannot
* happen for the other units since their operations time out
* anyway . */
2010-07-17 06:09:28 +04:00
d - > meta . job_timeout = DEFAULT_TIMEOUT_USEC ;
}
2010-01-26 23:39:06 +03:00
static void device_done ( Unit * u ) {
Device * d = DEVICE ( u ) ;
2010-01-26 06:18:44 +03:00
assert ( d ) ;
2010-04-10 19:53:17 +04:00
2010-07-20 22:33:19 +04:00
device_unset_sysfs ( d ) ;
2010-04-10 19:53:17 +04:00
}
2010-01-29 05:18:09 +03:00
static void device_set_state ( Device * d , DeviceState state ) {
DeviceState old_state ;
assert ( d ) ;
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
old_state = d - > state ;
d - > state = state ;
2010-01-23 03:52:57 +03:00
2010-04-10 19:53:17 +04:00
if ( state ! = old_state )
2010-04-23 22:25:55 +04:00
log_debug ( " %s changed %s -> %s " ,
2010-06-19 18:55:49 +04:00
d - > meta . id ,
2010-04-21 05:27:44 +04:00
device_state_to_string ( old_state ) ,
device_state_to_string ( state ) ) ;
2010-01-29 05:18:09 +03:00
unit_notify ( UNIT ( d ) , state_translation_table [ old_state ] , state_translation_table [ state ] ) ;
}
static int device_coldplug ( Unit * u ) {
Device * d = DEVICE ( u ) ;
assert ( d ) ;
assert ( d - > state = = DEVICE_DEAD ) ;
if ( d - > sysfs )
2010-07-16 23:32:34 +04:00
device_set_state ( d , DEVICE_PLUGGED ) ;
2010-01-29 05:18:09 +03:00
return 0 ;
}
static void device_dump ( Unit * u , FILE * f , const char * prefix ) {
2010-01-28 08:46:33 +03:00
Device * d = DEVICE ( u ) ;
2010-01-23 03:52:57 +03:00
2010-01-28 08:46:33 +03:00
assert ( d ) ;
2010-01-23 03:52:57 +03:00
fprintf ( f ,
2010-01-28 08:46:33 +03:00
" %sDevice State: %s \n "
" %sSysfs Path: %s \n " ,
2010-04-21 05:27:44 +04:00
prefix , device_state_to_string ( d - > state ) ,
2010-01-29 05:18:09 +03:00
prefix , strna ( d - > sysfs ) ) ;
}
static UnitActiveState device_active_state ( Unit * u ) {
assert ( u ) ;
return state_translation_table [ DEVICE ( u ) - > state ] ;
2010-01-28 08:46:33 +03:00
}
2010-04-13 22:59:01 +04:00
static const char * device_sub_state_to_string ( Unit * u ) {
assert ( u ) ;
2010-04-21 05:27:44 +04:00
return device_state_to_string ( DEVICE ( u ) - > state ) ;
2010-04-13 22:59:01 +04:00
}
2010-07-20 22:33:19 +04:00
static int device_add_escaped_name ( Unit * u , const char * dn ) {
2010-01-28 08:46:33 +03:00
char * e ;
int r ;
assert ( u ) ;
assert ( dn ) ;
assert ( dn [ 0 ] = = ' / ' ) ;
2010-04-21 05:27:44 +04:00
if ( ! ( e = unit_name_from_path ( dn , " .device " ) ) )
2010-01-28 08:46:33 +03:00
return - ENOMEM ;
r = unit_add_name ( u , e ) ;
free ( e ) ;
if ( r < 0 & & r ! = - EEXIST )
return r ;
return 0 ;
}
2010-04-08 06:34:21 +04:00
static int device_find_escape_name ( Manager * m , const char * dn , Unit * * _u ) {
char * e ;
Unit * u ;
assert ( m ) ;
assert ( dn ) ;
assert ( dn [ 0 ] = = ' / ' ) ;
assert ( _u ) ;
2010-04-21 05:27:44 +04:00
if ( ! ( e = unit_name_from_path ( dn , " .device " ) ) )
2010-04-08 06:34:21 +04:00
return - ENOMEM ;
u = manager_get_unit ( m , e ) ;
free ( e ) ;
if ( u ) {
* _u = u ;
return 1 ;
}
return 0 ;
}
2010-07-20 22:33:19 +04:00
static int device_update_unit ( Manager * m , struct udev_device * dev , const char * path , bool main ) {
const char * sysfs , * model ;
2010-01-28 08:46:33 +03:00
Unit * u = NULL ;
int r ;
bool delete ;
assert ( m ) ;
2010-04-08 02:53:21 +04:00
if ( ! ( sysfs = udev_device_get_syspath ( dev ) ) )
return - ENOMEM ;
2010-07-20 22:33:19 +04:00
if ( ( r = device_find_escape_name ( m , path , & u ) ) < 0 )
2010-04-08 06:34:21 +04:00
return r ;
2010-01-28 08:46:33 +03:00
2010-08-20 06:04:05 +04:00
if ( u & & DEVICE ( u ) - > sysfs & & ! path_equal ( DEVICE ( u ) - > sysfs , sysfs ) )
2010-07-20 22:33:19 +04:00
return - EEXIST ;
2010-01-28 08:46:33 +03:00
2010-04-08 06:34:21 +04:00
if ( ! u ) {
delete = true ;
if ( ! ( u = unit_new ( m ) ) )
return - ENOMEM ;
2010-01-28 08:46:33 +03:00
2010-07-20 22:33:19 +04:00
if ( ( r = device_add_escaped_name ( u , path ) ) < 0 )
2010-01-28 08:46:33 +03:00
goto fail ;
2010-08-06 06:17:51 +04:00
unit_add_to_load_queue ( u ) ;
} else
delete = false ;
/* If this was created via some dependency and has not
* actually been seen yet - > sysfs will not be
* initialized . Hence initialize it if necessary . */
if ( ! DEVICE ( u ) - > sysfs ) {
Device * first ;
2010-01-28 08:46:33 +03:00
if ( ! ( DEVICE ( u ) - > sysfs = strdup ( sysfs ) ) ) {
r = - ENOMEM ;
goto fail ;
}
2010-07-20 22:33:19 +04:00
if ( ! m - > devices_by_sysfs )
if ( ! ( m - > devices_by_sysfs = hashmap_new ( string_hash_func , string_compare_func ) ) ) {
r = - ENOMEM ;
goto fail ;
}
2010-01-28 08:46:33 +03:00
2010-07-20 22:33:19 +04:00
first = hashmap_get ( m - > devices_by_sysfs , sysfs ) ;
LIST_PREPEND ( Device , same_sysfs , first , DEVICE ( u ) ) ;
if ( ( r = hashmap_replace ( m - > devices_by_sysfs , DEVICE ( u ) - > sysfs , first ) ) < 0 )
2010-01-28 08:46:33 +03:00
goto fail ;
2010-08-06 06:17:51 +04:00
}
2010-07-20 22:33:19 +04:00
2010-04-08 06:34:21 +04:00
if ( ( model = udev_device_get_property_value ( dev , " ID_MODEL_FROM_DATABASE " ) ) | |
( model = udev_device_get_property_value ( dev , " ID_MODEL " ) ) ) {
if ( ( r = unit_set_description ( u , model ) ) < 0 )
goto fail ;
2010-05-14 04:29:45 +04:00
} else
2010-07-20 22:33:19 +04:00
if ( ( r = unit_set_description ( u , path ) ) < 0 )
2010-05-14 04:29:45 +04:00
goto fail ;
2010-04-08 06:34:21 +04:00
2010-07-20 22:33:19 +04:00
if ( main ) {
/* The additional systemd udev properties we only
* interpret for the main object */
const char * wants , * alias ;
if ( ( alias = udev_device_get_property_value ( dev , " SYSTEMD_ALIAS " ) ) ) {
if ( ! is_path ( alias ) )
log_warning ( " SYSTEMD_ALIAS for %s is not a path, ignoring: %s " , sysfs , alias ) ;
else {
if ( ( r = device_add_escaped_name ( u , alias ) ) < 0 )
goto fail ;
2010-03-31 22:08:05 +04:00
}
2010-07-20 22:33:19 +04:00
}
2010-01-28 08:46:33 +03:00
2010-07-20 22:33:19 +04:00
if ( ( wants = udev_device_get_property_value ( dev , " SYSTEMD_WANTS " ) ) ) {
char * state , * w ;
size_t l ;
2010-01-28 08:46:33 +03:00
2010-07-20 22:33:19 +04:00
FOREACH_WORD_QUOTED ( w , l , wants , state ) {
char * e ;
if ( ! ( e = strndup ( w , l ) ) ) {
r = - ENOMEM ;
goto fail ;
}
r = unit_add_dependency_by_name ( u , UNIT_WANTS , e , NULL , true ) ;
free ( e ) ;
if ( r < 0 )
goto fail ;
}
2010-01-28 08:46:33 +03:00
}
2010-07-21 07:00:29 +04:00
}
2010-01-29 08:45:59 +03:00
2010-02-05 02:38:41 +03:00
unit_add_to_dbus_queue ( u ) ;
2010-01-28 08:46:33 +03:00
return 0 ;
fail :
2010-07-13 04:17:26 +04:00
log_warning ( " Failed to load device unit: %s " , strerror ( - r ) ) ;
2010-01-28 08:46:33 +03:00
if ( delete & & u )
unit_free ( u ) ;
2010-07-13 04:17:26 +04:00
2010-01-28 08:46:33 +03:00
return r ;
}
2010-07-20 22:33:19 +04:00
static int device_process_new_device ( Manager * m , struct udev_device * dev , bool update_state ) {
const char * sysfs , * dn ;
struct udev_list_entry * item = NULL , * first = NULL ;
assert ( m ) ;
if ( ! ( sysfs = udev_device_get_syspath ( dev ) ) )
return - ENOMEM ;
/* Add the main unit named after the sysfs path */
device_update_unit ( m , dev , sysfs , true ) ;
/* Add an additional unit for the device node */
if ( ( dn = udev_device_get_devnode ( dev ) ) )
device_update_unit ( m , dev , dn , false ) ;
/* Add additional units for all symlinks */
first = udev_device_get_devlinks_list_entry ( dev ) ;
udev_list_entry_foreach ( item , first ) {
const char * p ;
2010-08-20 06:04:05 +04:00
struct stat st ;
2010-07-20 22:33:19 +04:00
/* Don't bother with the /dev/block links */
p = udev_list_entry_get_name ( item ) ;
if ( path_startswith ( p , " /dev/block/ " ) | |
path_startswith ( p , " /dev/char/ " ) )
continue ;
2010-08-20 06:04:05 +04:00
/* Verify that the symlink in the FS actually belongs
* to this device . This is useful to deal with
* conflicting devices , e . g . when two disks want the
* same / dev / disk / by - label / xxx link because they have
* the same label . We want to make sure that the same
* device that won the symlink wins in systemd , so we
* check the device node major / minor */
if ( stat ( p , & st ) > = 0 )
if ( ( ! S_ISBLK ( st . st_mode ) & & ! S_ISCHR ( st . st_mode ) ) | |
st . st_rdev ! = udev_device_get_devnum ( dev ) )
continue ;
2010-07-20 22:33:19 +04:00
device_update_unit ( m , dev , p , false ) ;
}
if ( update_state ) {
Device * d , * l ;
manager_dispatch_load_queue ( m ) ;
l = hashmap_get ( m - > devices_by_sysfs , sysfs ) ;
LIST_FOREACH ( same_sysfs , d , l )
device_set_state ( d , DEVICE_PLUGGED ) ;
}
return 0 ;
}
2010-01-29 08:45:59 +03:00
static int device_process_path ( Manager * m , const char * path , bool update_state ) {
2010-01-28 08:46:33 +03:00
int r ;
struct udev_device * dev ;
assert ( m ) ;
assert ( path ) ;
if ( ! ( dev = udev_device_new_from_syspath ( m - > udev , path ) ) ) {
log_warning ( " Failed to get udev device object from udev for path %s. " , path ) ;
return - ENOMEM ;
}
2010-01-29 08:45:59 +03:00
r = device_process_new_device ( m , dev , update_state ) ;
2010-01-28 08:46:33 +03:00
udev_device_unref ( dev ) ;
return r ;
}
2010-01-29 08:45:59 +03:00
static int device_process_removed_device ( Manager * m , struct udev_device * dev ) {
const char * sysfs ;
Device * d ;
assert ( m ) ;
assert ( dev ) ;
if ( ! ( sysfs = udev_device_get_syspath ( dev ) ) )
return - ENOMEM ;
2010-07-20 22:33:19 +04:00
/* Remove all units of this sysfs path */
while ( ( d = hashmap_get ( m - > devices_by_sysfs , sysfs ) ) ) {
device_unset_sysfs ( d ) ;
device_set_state ( d , DEVICE_DEAD ) ;
}
2010-01-29 08:45:59 +03:00
return 0 ;
}
2010-07-21 07:00:29 +04:00
static Unit * device_following ( Unit * u ) {
Device * d = DEVICE ( u ) ;
Device * other , * first = NULL ;
assert ( d ) ;
if ( startswith ( u - > meta . id , " sys- " ) )
return NULL ;
/* Make everybody follow the unit that's named after the sysfs path */
for ( other = d - > same_sysfs_next ; other ; other = other - > same_sysfs_next )
if ( startswith ( other - > meta . id , " sys- " ) )
return UNIT ( other ) ;
for ( other = d - > same_sysfs_prev ; other ; other = other - > same_sysfs_prev ) {
if ( startswith ( other - > meta . id , " sys- " ) )
return UNIT ( other ) ;
first = other ;
}
return UNIT ( first ) ;
}
2010-11-15 01:47:53 +03:00
static int device_following_set ( Unit * u , Set * * _s ) {
Device * d = DEVICE ( u ) ;
Device * other ;
Set * s ;
int r ;
assert ( d ) ;
assert ( _s ) ;
if ( ! d - > same_sysfs_prev & & ! d - > same_sysfs_next ) {
* _s = NULL ;
return 0 ;
}
if ( ! ( s = set_new ( NULL , NULL ) ) )
return - ENOMEM ;
for ( other = d - > same_sysfs_next ; other ; other = other - > same_sysfs_next )
if ( ( r = set_put ( s , other ) ) < 0 )
goto fail ;
for ( other = d - > same_sysfs_prev ; other ; other = other - > same_sysfs_prev )
if ( ( r = set_put ( s , other ) ) < 0 )
goto fail ;
* _s = s ;
return 1 ;
fail :
set_free ( s ) ;
return r ;
}
2010-01-28 08:46:33 +03:00
static void device_shutdown ( Manager * m ) {
assert ( m ) ;
2010-04-21 05:27:44 +04:00
if ( m - > udev_monitor ) {
2010-01-29 08:45:59 +03:00
udev_monitor_unref ( m - > udev_monitor ) ;
2010-04-21 05:27:44 +04:00
m - > udev_monitor = NULL ;
}
2010-01-29 08:45:59 +03:00
2010-04-21 05:27:44 +04:00
if ( m - > udev ) {
2010-01-28 08:46:33 +03:00
udev_unref ( m - > udev ) ;
2010-04-21 05:27:44 +04:00
m - > udev = NULL ;
}
2010-07-20 22:33:19 +04:00
hashmap_free ( m - > devices_by_sysfs ) ;
m - > devices_by_sysfs = NULL ;
2010-01-28 08:46:33 +03:00
}
static int device_enumerate ( Manager * m ) {
2010-01-29 08:45:59 +03:00
struct epoll_event ev ;
2010-01-28 08:46:33 +03:00
int r ;
struct udev_enumerate * e = NULL ;
struct udev_list_entry * item = NULL , * first = NULL ;
assert ( m ) ;
2010-04-21 05:27:44 +04:00
if ( ! m - > udev ) {
if ( ! ( m - > udev = udev_new ( ) ) )
return - ENOMEM ;
2010-01-28 08:46:33 +03:00
2010-04-21 05:27:44 +04:00
if ( ! ( m - > udev_monitor = udev_monitor_new_from_netlink ( m - > udev , " udev " ) ) ) {
r = - ENOMEM ;
goto fail ;
}
2010-01-29 08:45:59 +03:00
2011-01-08 04:30:07 +03:00
/* This will fail if we are unprivileged, but that
* should not matter much , as user instances won ' t run
* during boot . */
udev_monitor_set_receive_buffer_size ( m - > udev_monitor , 128 * 1024 * 1024 ) ;
2010-11-26 06:51:30 +03:00
2010-05-22 03:00:28 +04:00
if ( udev_monitor_filter_add_match_tag ( m - > udev_monitor , " systemd " ) < 0 ) {
r = - ENOMEM ;
goto fail ;
}
2010-04-21 05:27:44 +04:00
if ( udev_monitor_enable_receiving ( m - > udev_monitor ) < 0 ) {
r = - EIO ;
goto fail ;
}
2010-01-29 08:45:59 +03:00
2010-04-21 05:27:44 +04:00
m - > udev_watch . type = WATCH_UDEV ;
m - > udev_watch . fd = udev_monitor_get_fd ( m - > udev_monitor ) ;
2010-01-29 08:45:59 +03:00
2010-04-21 05:27:44 +04:00
zero ( ev ) ;
ev . events = EPOLLIN ;
ev . data . ptr = & m - > udev_watch ;
2010-01-29 08:45:59 +03:00
2010-04-21 05:27:44 +04:00
if ( epoll_ctl ( m - > epoll_fd , EPOLL_CTL_ADD , m - > udev_watch . fd , & ev ) < 0 )
return - errno ;
}
2010-01-29 08:45:59 +03:00
2010-04-15 08:19:54 +04:00
if ( ! ( e = udev_enumerate_new ( m - > udev ) ) ) {
r = - ENOMEM ;
goto fail ;
}
2010-05-22 03:00:28 +04:00
if ( udev_enumerate_add_match_tag ( e , " systemd " ) < 0 ) {
r = - EIO ;
goto fail ;
}
2010-01-28 08:46:33 +03:00
2010-04-15 08:19:54 +04:00
if ( udev_enumerate_scan_devices ( e ) < 0 ) {
r = - EIO ;
goto fail ;
}
2010-01-28 08:46:33 +03:00
2010-04-15 08:19:54 +04:00
first = udev_enumerate_get_list_entry ( e ) ;
udev_list_entry_foreach ( item , first )
device_process_path ( m , udev_list_entry_get_name ( item ) , false ) ;
2010-01-28 08:46:33 +03:00
2010-04-15 08:19:54 +04:00
udev_enumerate_unref ( e ) ;
2010-01-28 08:46:33 +03:00
return 0 ;
fail :
if ( e )
udev_enumerate_unref ( e ) ;
device_shutdown ( m ) ;
return r ;
2010-01-23 03:52:57 +03:00
}
2010-01-29 08:45:59 +03:00
void device_fd_event ( Manager * m , int events ) {
struct udev_device * dev ;
int r ;
const char * action ;
assert ( m ) ;
2010-11-26 06:51:30 +03:00
if ( events ! = EPOLLIN ) {
static RATELIMIT_DEFINE ( limit , 10 * USEC_PER_SEC , 5 ) ;
if ( ! ratelimit_test ( & limit ) )
log_error ( " Failed to get udev event: %m " ) ;
2010-11-26 18:06:30 +03:00
if ( ! ( events & EPOLLIN ) )
return ;
2010-11-26 06:51:30 +03:00
}
2010-01-29 08:45:59 +03:00
if ( ! ( dev = udev_monitor_receive_device ( m - > udev_monitor ) ) ) {
2010-11-26 06:51:30 +03:00
/*
* libudev might filter - out devices which pass the bloom filter ,
* so getting NULL here is not neccessarily an error
*/
2010-01-29 08:45:59 +03:00
return ;
}
if ( ! ( action = udev_device_get_action ( dev ) ) ) {
log_error ( " Failed to get udev action string. " ) ;
goto fail ;
}
if ( streq ( action , " remove " ) ) {
if ( ( r = device_process_removed_device ( m , dev ) ) < 0 ) {
log_error ( " Failed to process udev device event: %s " , strerror ( - r ) ) ;
goto fail ;
}
} else {
if ( ( r = device_process_new_device ( m , dev , true ) ) < 0 ) {
log_error ( " Failed to process udev device event: %s " , strerror ( - r ) ) ;
goto fail ;
}
}
fail :
udev_device_unref ( dev ) ;
}
2010-04-21 05:27:44 +04:00
static const char * const device_state_table [ _DEVICE_STATE_MAX ] = {
[ DEVICE_DEAD ] = " dead " ,
2010-07-16 23:32:34 +04:00
[ DEVICE_PLUGGED ] = " plugged "
2010-04-21 05:27:44 +04:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( device_state , DeviceState ) ;
2010-01-26 23:39:06 +03:00
const UnitVTable device_vtable = {
2010-01-23 03:52:57 +03:00
. suffix = " .device " ,
2010-04-15 05:11:11 +04:00
. no_instances = true ,
2010-04-18 05:07:42 +04:00
. no_snapshots = true ,
2010-04-22 04:42:59 +04:00
. no_isolate = true ,
2010-04-15 05:11:11 +04:00
2010-07-17 06:09:28 +04:00
. init = device_init ,
2010-04-10 19:53:17 +04:00
. load = unit_load_fragment_and_dropin_optional ,
2010-01-26 06:18:44 +03:00
. done = device_done ,
2010-01-29 05:18:09 +03:00
. coldplug = device_coldplug ,
2010-01-23 03:52:57 +03:00
. dump = device_dump ,
2010-01-29 05:18:09 +03:00
. active_state = device_active_state ,
2010-04-13 22:59:01 +04:00
. sub_state_to_string = device_sub_state_to_string ,
2010-01-28 08:46:33 +03:00
2010-08-20 04:26:05 +04:00
. bus_interface = " org.freedesktop.systemd1.Device " ,
2010-04-18 05:08:16 +04:00
. bus_message_handler = bus_device_message_handler ,
2010-08-20 04:26:05 +04:00
. bus_invalidating_properties = bus_device_invalidating_properties ,
2010-04-18 05:08:16 +04:00
2010-07-21 07:00:29 +04:00
. following = device_following ,
2010-11-15 01:47:53 +03:00
. following_set = device_following_set ,
2010-07-21 07:00:29 +04:00
2010-01-29 05:18:09 +03:00
. enumerate = device_enumerate ,
. shutdown = device_shutdown
2010-01-23 03:52:57 +03:00
} ;