2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-05-24 07:25:33 +04:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 02:20:58 +04:00
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2010-05-24 07:25:33 +04:00
( 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
2012-04-12 02:20:58 +04:00
Lesser General Public License for more details .
2010-05-24 07:25:33 +04:00
2012-04-12 02:20:58 +04:00
You should have received a copy of the GNU Lesser General Public License
2010-05-24 07:25:33 +04:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <sys/inotify.h>
# include <sys/epoll.h>
# include <sys/ioctl.h>
# include <errno.h>
# include <unistd.h>
# include "unit.h"
# include "unit-name.h"
# include "path.h"
2012-04-10 23:54:31 +04:00
# include "mkdir.h"
2010-05-24 07:25:33 +04:00
# include "dbus-path.h"
2010-07-03 21:48:33 +04:00
# include "special.h"
2012-05-07 23:36:12 +04:00
# include "path-util.h"
2013-03-02 02:44:25 +04:00
# include "macro.h"
2013-11-20 00:12:59 +04:00
# include "bus-util.h"
# include "bus-error.h"
2010-05-24 07:25:33 +04:00
static const UnitActiveState state_translation_table [ _PATH_STATE_MAX ] = {
[ PATH_DEAD ] = UNIT_INACTIVE ,
[ PATH_WAITING ] = UNIT_ACTIVE ,
[ PATH_RUNNING ] = UNIT_ACTIVE ,
2010-08-31 02:23:34 +04:00
[ PATH_FAILED ] = UNIT_FAILED
2010-05-24 07:25:33 +04:00
} ;
2013-11-20 00:12:59 +04:00
static int path_dispatch_io ( sd_event_source * source , int fd , uint32_t revents , void * userdata ) ;
int path_spec_watch ( PathSpec * s , sd_event_io_handler_t handler ) {
2012-01-07 02:08:54 +04:00
2011-12-03 04:38:30 +04:00
static const int flags_table [ _PATH_TYPE_MAX ] = {
[ PATH_EXISTS ] = IN_DELETE_SELF | IN_MOVE_SELF | IN_ATTRIB ,
[ PATH_EXISTS_GLOB ] = IN_DELETE_SELF | IN_MOVE_SELF | IN_ATTRIB ,
[ PATH_CHANGED ] = IN_DELETE_SELF | IN_MOVE_SELF | IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO ,
2011-12-03 13:22:26 +04:00
[ PATH_MODIFIED ] = IN_DELETE_SELF | IN_MOVE_SELF | IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_MODIFY ,
2011-12-03 04:38:30 +04:00
[ PATH_DIRECTORY_NOT_EMPTY ] = IN_DELETE_SELF | IN_MOVE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO
} ;
bool exists = false ;
2013-03-03 10:32:34 +04:00
char * slash , * oldslash = NULL ;
2011-12-03 04:38:30 +04:00
int r ;
2011-04-10 03:30:14 +04:00
2011-12-03 04:38:30 +04:00
assert ( s ) ;
2013-11-20 00:12:59 +04:00
assert ( s - > unit ) ;
assert ( handler ) ;
2011-04-10 03:30:14 +04:00
2013-11-20 00:12:59 +04:00
path_spec_unwatch ( s ) ;
2011-12-03 04:38:30 +04:00
2013-03-02 02:44:25 +04:00
s - > inotify_fd = inotify_init1 ( IN_NONBLOCK | IN_CLOEXEC ) ;
if ( s - > inotify_fd < 0 ) {
2011-12-03 04:38:30 +04:00
r = - errno ;
goto fail ;
}
2014-02-20 02:54:58 +04:00
r = sd_event_add_io ( s - > unit - > manager - > event , & s - > event_source , s - > inotify_fd , EPOLLIN , handler , s ) ;
2013-03-02 02:44:25 +04:00
if ( r < 0 )
2011-12-03 04:38:30 +04:00
goto fail ;
2013-03-03 10:32:34 +04:00
/* This assumes the path was passed through path_kill_slashes()! */
2011-12-03 04:38:30 +04:00
2013-03-06 22:28:55 +04:00
for ( slash = strchr ( s - > path , ' / ' ) ; ; slash = strchr ( slash + 1 , ' / ' ) ) {
char * cut = NULL ;
2011-12-03 04:38:30 +04:00
int flags ;
2013-03-03 10:32:34 +04:00
char tmp ;
if ( slash ) {
2013-03-06 22:28:55 +04:00
cut = slash + ( slash = = s - > path ) ;
tmp = * cut ;
* cut = ' \0 ' ;
2013-03-03 10:32:34 +04:00
flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO ;
2013-03-06 22:28:55 +04:00
} else
2013-03-03 10:32:34 +04:00
flags = flags_table [ s - > type ] ;
2011-12-03 04:38:30 +04:00
2013-03-06 22:28:55 +04:00
r = inotify_add_watch ( s - > inotify_fd , s - > path , flags ) ;
2013-03-03 10:32:34 +04:00
if ( r < 0 ) {
2013-03-06 22:28:55 +04:00
if ( errno = = EACCES | | errno = = ENOENT ) {
if ( cut )
* cut = tmp ;
2013-03-03 10:32:34 +04:00
break ;
2013-03-06 22:28:55 +04:00
}
2011-12-03 04:38:30 +04:00
2014-07-17 06:52:53 +04:00
log_warning ( " Failed to add watch on %s: %s " , s - > path ,
errno = = ENOSPC ? " too many watches " : strerror ( - r ) ) ;
2013-03-03 10:49:11 +04:00
r = - errno ;
2013-03-06 22:28:55 +04:00
if ( cut )
* cut = tmp ;
2013-03-03 10:49:11 +04:00
goto fail ;
2013-03-03 10:32:34 +04:00
} else {
exists = true ;
/* Path exists, we don't need to watch parent
too closely . */
if ( oldslash ) {
2013-03-06 22:28:55 +04:00
char * cut2 = oldslash + ( oldslash = = s - > path ) ;
char tmp2 = * cut2 ;
* cut2 = ' \0 ' ;
2013-03-03 10:32:34 +04:00
2013-03-06 22:28:55 +04:00
inotify_add_watch ( s - > inotify_fd , s - > path , IN_MOVE_SELF ) ;
2013-03-03 10:32:34 +04:00
/* Error is ignored, the worst can happen is
we get spurious events . */
2013-03-06 22:28:55 +04:00
* cut2 = tmp2 ;
2013-03-03 10:32:34 +04:00
}
2013-03-03 10:49:11 +04:00
}
2013-03-03 10:32:34 +04:00
2013-03-06 22:28:55 +04:00
if ( cut )
* cut = tmp ;
if ( slash )
2013-03-03 10:32:34 +04:00
oldslash = slash ;
2013-03-06 22:28:55 +04:00
else {
2013-03-03 10:32:34 +04:00
/* whole path has been iterated over */
s - > primary_wd = r ;
break ;
}
}
2013-03-03 10:49:11 +04:00
if ( ! exists ) {
log_error ( " Failed to add watch on any of the components of %s: %m " ,
s - > path ) ;
2013-03-03 10:32:34 +04:00
r = - errno ; /* either EACCESS or ENOENT */
2013-03-03 10:49:11 +04:00
goto fail ;
}
2011-12-03 04:38:30 +04:00
return 0 ;
fail :
2013-11-20 00:12:59 +04:00
path_spec_unwatch ( s ) ;
2011-12-03 04:38:30 +04:00
return r ;
2011-04-10 03:30:14 +04:00
}
2013-11-20 00:12:59 +04:00
void path_spec_unwatch ( PathSpec * s ) {
assert ( s ) ;
2010-11-15 23:30:52 +03:00
2013-11-20 00:12:59 +04:00
s - > event_source = sd_event_source_unref ( s - > event_source ) ;
2014-03-18 22:22:43 +04:00
s - > inotify_fd = safe_close ( s - > inotify_fd ) ;
2010-11-15 23:30:52 +03:00
}
2013-11-20 00:12:59 +04:00
int path_spec_fd_event ( PathSpec * s , uint32_t revents ) {
2013-04-18 11:11:22 +04:00
_cleanup_free_ uint8_t * buf = NULL ;
2011-12-03 04:38:30 +04:00
struct inotify_event * e ;
ssize_t k ;
int l ;
int r = 0 ;
2013-11-20 00:12:59 +04:00
if ( revents ! = EPOLLIN ) {
2013-01-05 05:46:49 +04:00
log_error ( " Got invalid poll event on inotify. " ) ;
2013-03-02 02:58:56 +04:00
return - EINVAL ;
2011-12-03 04:38:30 +04:00
}
if ( ioctl ( s - > inotify_fd , FIONREAD , & l ) < 0 ) {
log_error ( " FIONREAD failed: %m " ) ;
2013-03-02 02:58:56 +04:00
return - errno ;
2011-12-03 04:38:30 +04:00
}
assert ( l > 0 ) ;
2013-01-05 05:46:49 +04:00
buf = malloc ( l ) ;
2013-03-02 02:58:56 +04:00
if ( ! buf )
return log_oom ( ) ;
2011-12-03 04:38:30 +04:00
2013-01-05 05:46:49 +04:00
k = read ( s - > inotify_fd , buf , l ) ;
if ( k < 0 ) {
2011-12-03 04:38:30 +04:00
log_error ( " Failed to read inotify event: %m " ) ;
2013-03-02 02:58:56 +04:00
return - errno ;
2011-12-03 04:38:30 +04:00
}
e = ( struct inotify_event * ) buf ;
while ( k > 0 ) {
size_t step ;
2011-12-08 15:09:10 +04:00
if ( ( s - > type = = PATH_CHANGED | | s - > type = = PATH_MODIFIED ) & &
s - > primary_wd = = e - > wd )
2011-12-03 04:38:30 +04:00
r = 1 ;
step = sizeof ( struct inotify_event ) + e - > len ;
assert ( step < = ( size_t ) k ) ;
e = ( struct inotify_event * ) ( ( uint8_t * ) e + step ) ;
k - = step ;
}
2013-03-02 02:58:56 +04:00
2011-12-03 04:38:30 +04:00
return r ;
}
2012-01-07 02:08:54 +04:00
static bool path_spec_check_good ( PathSpec * s , bool initial ) {
2011-12-03 04:38:30 +04:00
bool good = false ;
switch ( s - > type ) {
case PATH_EXISTS :
good = access ( s - > path , F_OK ) > = 0 ;
break ;
case PATH_EXISTS_GLOB :
good = glob_exists ( s - > path ) > 0 ;
break ;
case PATH_DIRECTORY_NOT_EMPTY : {
int k ;
k = dir_is_empty ( s - > path ) ;
good = ! ( k = = - ENOENT | | k > 0 ) ;
break ;
}
2011-12-08 15:09:10 +04:00
case PATH_CHANGED :
case PATH_MODIFIED : {
2011-12-03 04:38:30 +04:00
bool b ;
b = access ( s - > path , F_OK ) > = 0 ;
good = ! initial & & b ! = s - > previous_exists ;
s - > previous_exists = b ;
break ;
}
default :
;
}
return good ;
}
2012-01-07 02:08:54 +04:00
static void path_spec_mkdir ( PathSpec * s , mode_t mode ) {
2011-12-03 04:38:30 +04:00
int r ;
if ( s - > type = = PATH_EXISTS | | s - > type = = PATH_EXISTS_GLOB )
return ;
2013-01-05 05:46:49 +04:00
r = mkdir_p_label ( s - > path , mode ) ;
if ( r < 0 )
2011-12-03 04:38:30 +04:00
log_warning ( " mkdir(%s) failed: %s " , s - > path , strerror ( - r ) ) ;
}
2012-01-07 02:08:54 +04:00
static void path_spec_dump ( PathSpec * s , FILE * f , const char * prefix ) {
2011-12-03 04:38:30 +04:00
fprintf ( f ,
" %s%s: %s \n " ,
prefix ,
path_type_to_string ( s - > type ) ,
s - > path ) ;
}
2012-01-07 02:08:54 +04:00
void path_spec_done ( PathSpec * s ) {
assert ( s ) ;
2011-12-03 04:38:30 +04:00
assert ( s - > inotify_fd = = - 1 ) ;
2012-01-07 02:08:54 +04:00
2011-12-03 04:38:30 +04:00
free ( s - > path ) ;
}
static void path_init ( Unit * u ) {
Path * p = PATH ( u ) ;
assert ( u ) ;
2012-01-15 15:04:08 +04:00
assert ( u - > load_state = = UNIT_STUB ) ;
2011-12-03 04:38:30 +04:00
p - > directory_mode = 0755 ;
}
2013-01-17 05:27:06 +04:00
void path_free_specs ( Path * p ) {
2010-05-24 07:25:33 +04:00
PathSpec * s ;
assert ( p ) ;
while ( ( s = p - > specs ) ) {
2013-11-20 00:12:59 +04:00
path_spec_unwatch ( s ) ;
2013-10-14 08:10:14 +04:00
LIST_REMOVE ( spec , p - > specs , s ) ;
2012-01-07 02:08:54 +04:00
path_spec_done ( s ) ;
2010-05-24 07:25:33 +04:00
free ( s ) ;
}
}
2013-01-17 05:27:06 +04:00
static void path_done ( Unit * u ) {
Path * p = PATH ( u ) ;
assert ( p ) ;
path_free_specs ( p ) ;
}
2013-09-26 22:14:24 +04:00
static int path_add_mount_links ( Path * p ) {
2010-05-24 07:25:33 +04:00
PathSpec * s ;
int r ;
assert ( p ) ;
LIST_FOREACH ( spec , s , p - > specs ) {
2013-09-26 22:14:24 +04:00
r = unit_require_mounts_for ( UNIT ( p ) , s - > path ) ;
2013-03-02 03:04:36 +04:00
if ( r < 0 )
2010-05-24 07:25:33 +04:00
return r ;
2013-03-02 03:04:36 +04:00
}
2010-05-24 07:25:33 +04:00
return 0 ;
}
static int path_verify ( Path * p ) {
assert ( p ) ;
2012-01-15 15:25:20 +04:00
if ( UNIT ( p ) - > load_state ! = UNIT_LOADED )
2010-05-24 07:25:33 +04:00
return 0 ;
if ( ! p - > specs ) {
2013-01-05 21:00:35 +04:00
log_error_unit ( UNIT ( p ) - > id ,
" %s lacks path setting. Refusing. " , UNIT ( p ) - > id ) ;
2010-05-24 07:25:33 +04:00
return - EINVAL ;
}
return 0 ;
}
2010-07-16 20:57:21 +04:00
static int path_add_default_dependencies ( Path * p ) {
int r ;
assert ( p ) ;
2013-03-26 00:20:08 +04:00
r = unit_add_dependency_by_name ( UNIT ( p ) , UNIT_BEFORE ,
SPECIAL_PATHS_TARGET , NULL , true ) ;
if ( r < 0 )
return r ;
2010-10-29 08:04:03 +04:00
2013-03-26 00:20:08 +04:00
if ( UNIT ( p ) - > manager - > running_as = = SYSTEMD_SYSTEM ) {
2013-03-02 03:04:36 +04:00
r = unit_add_two_dependencies_by_name ( UNIT ( p ) , UNIT_AFTER , UNIT_REQUIRES ,
SPECIAL_SYSINIT_TARGET , NULL , true ) ;
if ( r < 0 )
2010-07-16 20:57:21 +04:00
return r ;
2010-10-29 08:04:03 +04:00
}
2010-07-16 20:57:21 +04:00
2013-03-02 03:04:36 +04:00
return unit_add_two_dependencies_by_name ( UNIT ( p ) , UNIT_BEFORE , UNIT_CONFLICTS ,
SPECIAL_SHUTDOWN_TARGET , NULL , true ) ;
2010-07-16 20:57:21 +04:00
}
2010-05-24 07:25:33 +04:00
static int path_load ( Unit * u ) {
Path * p = PATH ( u ) ;
int r ;
assert ( u ) ;
2012-01-15 15:04:08 +04:00
assert ( u - > load_state = = UNIT_STUB ) ;
2010-05-24 07:25:33 +04:00
2013-03-02 03:04:36 +04:00
r = unit_load_fragment_and_dropin ( u ) ;
if ( r < 0 )
2010-05-24 07:25:33 +04:00
return r ;
2012-01-15 15:04:08 +04:00
if ( u - > load_state = = UNIT_LOADED ) {
2010-05-24 07:25:33 +04:00
2013-04-23 22:53:16 +04:00
if ( set_isempty ( u - > dependencies [ UNIT_TRIGGERS ] ) ) {
2012-01-07 02:08:54 +04:00
Unit * x ;
r = unit_load_related_unit ( u , " .service " , & x ) ;
if ( r < 0 )
2010-05-24 07:25:33 +04:00
return r ;
2013-04-23 22:53:16 +04:00
r = unit_add_two_dependencies ( u , UNIT_BEFORE , UNIT_TRIGGERS , x , true ) ;
if ( r < 0 )
return r ;
2012-01-07 02:08:54 +04:00
}
2013-03-02 03:04:36 +04:00
r = path_add_mount_links ( p ) ;
if ( r < 0 )
2010-05-24 07:25:33 +04:00
return r ;
2010-07-03 21:48:33 +04:00
2013-03-02 03:04:36 +04:00
if ( UNIT ( p ) - > default_dependencies ) {
r = path_add_default_dependencies ( p ) ;
if ( r < 0 )
2010-07-03 21:48:33 +04:00
return r ;
2013-03-02 03:04:36 +04:00
}
2010-05-24 07:25:33 +04:00
}
return path_verify ( p ) ;
}
static void path_dump ( Unit * u , FILE * f , const char * prefix ) {
Path * p = PATH ( u ) ;
2013-04-23 22:53:16 +04:00
Unit * trigger ;
2010-05-24 07:25:33 +04:00
PathSpec * s ;
2010-08-12 00:04:22 +04:00
assert ( p ) ;
assert ( f ) ;
2010-05-24 07:25:33 +04:00
2013-04-23 22:53:16 +04:00
trigger = UNIT_TRIGGER ( u ) ;
2010-05-24 07:25:33 +04:00
fprintf ( f ,
" %sPath State: %s \n "
2012-02-03 08:04:48 +04:00
" %sResult: %s \n "
2011-04-10 03:30:14 +04:00
" %sUnit: %s \n "
" %sMakeDirectory: %s \n "
" %sDirectoryMode: %04o \n " ,
2010-05-24 07:25:33 +04:00
prefix , path_state_to_string ( p - > state ) ,
2012-02-03 08:04:48 +04:00
prefix , path_result_to_string ( p - > result ) ,
2013-04-23 22:53:16 +04:00
prefix , trigger ? trigger - > id : " n/a " ,
2011-04-10 03:30:14 +04:00
prefix , yes_no ( p - > make_directory ) ,
prefix , p - > directory_mode ) ;
2010-05-24 07:25:33 +04:00
LIST_FOREACH ( spec , s , p - > specs )
2012-01-07 02:08:54 +04:00
path_spec_dump ( s , f , prefix ) ;
2010-05-24 07:25:33 +04:00
}
static void path_unwatch ( Path * p ) {
PathSpec * s ;
assert ( p ) ;
LIST_FOREACH ( spec , s , p - > specs )
2013-11-20 00:12:59 +04:00
path_spec_unwatch ( s ) ;
2010-05-24 07:25:33 +04:00
}
static int path_watch ( Path * p ) {
int r ;
PathSpec * s ;
assert ( p ) ;
2013-03-02 03:04:36 +04:00
LIST_FOREACH ( spec , s , p - > specs ) {
2013-11-20 00:12:59 +04:00
r = path_spec_watch ( s , path_dispatch_io ) ;
2013-03-02 03:04:36 +04:00
if ( r < 0 )
2010-05-24 07:25:33 +04:00
return r ;
2013-03-02 03:04:36 +04:00
}
2010-05-24 07:25:33 +04:00
return 0 ;
}
static void path_set_state ( Path * p , PathState state ) {
PathState old_state ;
assert ( p ) ;
old_state = p - > state ;
p - > state = state ;
2010-11-15 02:49:21 +03:00
if ( state ! = PATH_WAITING & &
( state ! = PATH_RUNNING | | p - > inotify_triggered ) )
2010-05-24 07:25:33 +04:00
path_unwatch ( p ) ;
if ( state ! = old_state )
log_debug ( " %s changed %s -> %s " ,
2012-01-15 15:25:20 +04:00
UNIT ( p ) - > id ,
2010-05-24 07:25:33 +04:00
path_state_to_string ( old_state ) ,
path_state_to_string ( state ) ) ;
2011-01-20 15:17:22 +03:00
unit_notify ( UNIT ( p ) , state_translation_table [ old_state ] , state_translation_table [ state ] , true ) ;
2010-05-24 07:25:33 +04:00
}
2011-04-28 05:22:05 +04:00
static void path_enter_waiting ( Path * p , bool initial , bool recheck ) ;
2010-05-24 07:25:33 +04:00
static int path_coldplug ( Unit * u ) {
Path * p = PATH ( u ) ;
assert ( p ) ;
assert ( p - > state = = PATH_DEAD ) ;
if ( p - > deserialized_state ! = p - > state ) {
if ( p - > deserialized_state = = PATH_WAITING | |
p - > deserialized_state = = PATH_RUNNING )
2011-04-28 05:22:05 +04:00
path_enter_waiting ( p , true , true ) ;
2010-05-24 07:25:33 +04:00
else
path_set_state ( p , p - > deserialized_state ) ;
}
return 0 ;
}
2012-02-03 08:04:48 +04:00
static void path_enter_dead ( Path * p , PathResult f ) {
2010-05-24 07:25:33 +04:00
assert ( p ) ;
2012-02-03 08:04:48 +04:00
if ( f ! = PATH_SUCCESS )
p - > result = f ;
2010-05-24 07:25:33 +04:00
2012-02-03 08:04:48 +04:00
path_set_state ( p , p - > result ! = PATH_SUCCESS ? PATH_FAILED : PATH_DEAD ) ;
2010-05-24 07:25:33 +04:00
}
static void path_enter_running ( Path * p ) {
2013-11-20 00:12:59 +04:00
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL ;
2010-05-24 07:25:33 +04:00
int r ;
2010-07-08 04:43:18 +04:00
2010-05-24 07:25:33 +04:00
assert ( p ) ;
2013-04-23 22:53:16 +04:00
2010-07-13 02:27:27 +04:00
/* Don't start job if we are supposed to go down */
2013-04-26 04:57:41 +04:00
if ( unit_stop_pending ( UNIT ( p ) ) )
2010-07-13 02:27:27 +04:00
return ;
2013-04-23 22:53:16 +04:00
r = manager_add_job ( UNIT ( p ) - > manager , JOB_START , UNIT_TRIGGER ( UNIT ( p ) ) ,
2013-03-02 03:04:36 +04:00
JOB_REPLACE , true , & error , NULL ) ;
if ( r < 0 )
2010-05-24 07:25:33 +04:00
goto fail ;
2010-11-15 02:49:21 +03:00
p - > inotify_triggered = false ;
2013-03-02 03:04:36 +04:00
r = path_watch ( p ) ;
if ( r < 0 )
2010-11-15 02:49:21 +03:00
goto fail ;
2010-05-24 07:25:33 +04:00
path_set_state ( p , PATH_RUNNING ) ;
return ;
fail :
2013-03-02 03:04:36 +04:00
log_warning ( " %s failed to queue unit startup job: %s " ,
2013-11-20 00:12:59 +04:00
UNIT ( p ) - > id , bus_error_message ( & error , r ) ) ;
2012-02-03 08:04:48 +04:00
path_enter_dead ( p , PATH_FAILURE_RESOURCES ) ;
2010-05-24 07:25:33 +04:00
}
2011-04-28 05:22:05 +04:00
static bool path_check_good ( Path * p , bool initial ) {
2010-05-24 07:25:33 +04:00
PathSpec * s ;
bool good = false ;
2011-04-28 05:22:05 +04:00
assert ( p ) ;
2010-11-15 02:49:21 +03:00
2010-05-24 07:25:33 +04:00
LIST_FOREACH ( spec , s , p - > specs ) {
2012-01-07 02:08:54 +04:00
good = path_spec_check_good ( s , initial ) ;
2010-05-24 07:25:33 +04:00
if ( good )
break ;
}
2011-04-28 05:22:05 +04:00
return good ;
}
2010-05-24 07:25:33 +04:00
2011-04-28 05:22:05 +04:00
static void path_enter_waiting ( Path * p , bool initial , bool recheck ) {
int r ;
2011-03-04 03:49:37 +03:00
2011-04-28 05:22:05 +04:00
if ( recheck )
if ( path_check_good ( p , initial ) ) {
2012-01-15 15:25:20 +04:00
log_debug ( " %s got triggered. " , UNIT ( p ) - > id ) ;
2011-04-28 05:22:05 +04:00
path_enter_running ( p ) ;
return ;
}
2013-03-02 03:04:36 +04:00
r = path_watch ( p ) ;
if ( r < 0 )
2011-04-28 05:22:05 +04:00
goto fail ;
/* Hmm, so now we have created inotify watches, but the file
* might have appeared / been removed by now , so we must
* recheck */
if ( recheck )
if ( path_check_good ( p , false ) ) {
2012-01-15 15:25:20 +04:00
log_debug ( " %s got triggered. " , UNIT ( p ) - > id ) ;
2011-04-28 05:22:05 +04:00
path_enter_running ( p ) ;
return ;
}
2010-05-24 07:25:33 +04:00
path_set_state ( p , PATH_WAITING ) ;
return ;
fail :
2013-03-02 03:04:36 +04:00
log_warning ( " %s failed to enter waiting state: %s " ,
UNIT ( p ) - > id , strerror ( - r ) ) ;
2012-02-03 08:04:48 +04:00
path_enter_dead ( p , PATH_FAILURE_RESOURCES ) ;
2010-05-24 07:25:33 +04:00
}
2011-04-10 03:30:14 +04:00
static void path_mkdir ( Path * p ) {
PathSpec * s ;
assert ( p ) ;
if ( ! p - > make_directory )
return ;
2011-12-03 04:38:30 +04:00
LIST_FOREACH ( spec , s , p - > specs )
2012-01-07 02:08:54 +04:00
path_spec_mkdir ( s , p - > directory_mode ) ;
2011-04-10 03:30:14 +04:00
}
2010-05-24 07:25:33 +04:00
static int path_start ( Unit * u ) {
Path * p = PATH ( u ) ;
assert ( p ) ;
2010-08-31 02:23:34 +04:00
assert ( p - > state = = PATH_DEAD | | p - > state = = PATH_FAILED ) ;
2010-05-24 07:25:33 +04:00
2013-04-23 22:53:16 +04:00
if ( UNIT_TRIGGER ( u ) - > load_state ! = UNIT_LOADED )
2010-05-24 07:25:33 +04:00
return - ENOENT ;
2011-04-10 03:30:14 +04:00
path_mkdir ( p ) ;
2012-02-03 08:04:48 +04:00
p - > result = PATH_SUCCESS ;
2011-04-28 05:22:05 +04:00
path_enter_waiting ( p , true , true ) ;
2010-10-08 04:31:36 +04:00
2010-05-24 07:25:33 +04:00
return 0 ;
}
static int path_stop ( Unit * u ) {
Path * p = PATH ( u ) ;
assert ( p ) ;
assert ( p - > state = = PATH_WAITING | | p - > state = = PATH_RUNNING ) ;
2012-02-03 08:04:48 +04:00
path_enter_dead ( p , PATH_SUCCESS ) ;
2010-05-24 07:25:33 +04:00
return 0 ;
}
static int path_serialize ( Unit * u , FILE * f , FDSet * fds ) {
Path * p = PATH ( u ) ;
assert ( u ) ;
assert ( f ) ;
assert ( fds ) ;
unit_serialize_item ( u , f , " state " , path_state_to_string ( p - > state ) ) ;
2012-02-03 08:04:48 +04:00
unit_serialize_item ( u , f , " result " , path_result_to_string ( p - > result ) ) ;
2010-05-24 07:25:33 +04:00
return 0 ;
}
static int path_deserialize_item ( Unit * u , const char * key , const char * value , FDSet * fds ) {
Path * p = PATH ( u ) ;
assert ( u ) ;
assert ( key ) ;
assert ( value ) ;
assert ( fds ) ;
if ( streq ( key , " state " ) ) {
PathState state ;
2013-03-02 03:04:36 +04:00
state = path_state_from_string ( value ) ;
if ( state < 0 )
2010-05-24 07:25:33 +04:00
log_debug ( " Failed to parse state value %s " , value ) ;
else
p - > deserialized_state = state ;
2012-02-03 08:04:48 +04:00
} else if ( streq ( key , " result " ) ) {
PathResult f ;
f = path_result_from_string ( value ) ;
if ( f < 0 )
log_debug ( " Failed to parse result value %s " , value ) ;
else if ( f ! = PATH_SUCCESS )
p - > result = f ;
2010-05-24 07:25:33 +04:00
} else
log_debug ( " Unknown serialization key '%s' " , key ) ;
return 0 ;
}
2013-05-03 06:51:50 +04:00
_pure_ static UnitActiveState path_active_state ( Unit * u ) {
2010-05-24 07:25:33 +04:00
assert ( u ) ;
return state_translation_table [ PATH ( u ) - > state ] ;
}
2013-05-03 06:51:50 +04:00
_pure_ static const char * path_sub_state_to_string ( Unit * u ) {
2010-05-24 07:25:33 +04:00
assert ( u ) ;
return path_state_to_string ( PATH ( u ) - > state ) ;
}
2013-11-20 00:12:59 +04:00
static int path_dispatch_io ( sd_event_source * source , int fd , uint32_t revents , void * userdata ) {
PathSpec * s = userdata ;
Path * p ;
2011-12-03 04:38:30 +04:00
int changed ;
2010-05-24 07:25:33 +04:00
2013-11-20 00:12:59 +04:00
assert ( s ) ;
assert ( s - > unit ) ;
2010-05-24 07:25:33 +04:00
assert ( fd > = 0 ) ;
2013-11-20 00:12:59 +04:00
p = PATH ( s - > unit ) ;
2010-11-15 02:49:21 +03:00
if ( p - > state ! = PATH_WAITING & &
p - > state ! = PATH_RUNNING )
2013-11-20 00:12:59 +04:00
return 0 ;
2010-05-24 07:25:33 +04:00
2012-01-15 15:04:08 +04:00
/* log_debug("inotify wakeup on %s.", u->id); */
2010-05-24 07:25:33 +04:00
LIST_FOREACH ( spec , s , p - > specs )
2012-01-07 02:08:54 +04:00
if ( path_spec_owns_inotify_fd ( s , fd ) )
2010-05-24 07:25:33 +04:00
break ;
if ( ! s ) {
log_error ( " Got event on unknown fd. " ) ;
goto fail ;
}
2013-11-20 00:12:59 +04:00
changed = path_spec_fd_event ( s , revents ) ;
2011-12-03 04:38:30 +04:00
if ( changed < 0 )
2010-05-24 07:25:33 +04:00
goto fail ;
2010-11-15 02:49:21 +03:00
/* If we are already running, then remember that one event was
* dispatched so that we restart the service only if something
* actually changed on disk */
p - > inotify_triggered = true ;
if ( changed )
path_enter_running ( p ) ;
else
2011-04-28 05:22:05 +04:00
path_enter_waiting ( p , false , true ) ;
2010-11-15 02:49:21 +03:00
2013-11-20 00:12:59 +04:00
return 0 ;
2010-05-24 07:25:33 +04:00
fail :
2012-02-03 08:04:48 +04:00
path_enter_dead ( p , PATH_FAILURE_RESOURCES ) ;
2013-11-20 00:12:59 +04:00
return 0 ;
2010-05-24 07:25:33 +04:00
}
2013-04-23 22:53:16 +04:00
static void path_trigger_notify ( Unit * u , Unit * other ) {
Path * p = PATH ( u ) ;
2010-05-24 07:25:33 +04:00
2013-04-23 22:53:16 +04:00
assert ( u ) ;
assert ( other ) ;
2010-05-24 07:25:33 +04:00
2013-04-23 22:53:16 +04:00
/* Invoked whenever the unit we trigger changes state or gains
* or loses a job */
2010-05-24 07:25:33 +04:00
2013-04-23 22:53:16 +04:00
if ( other - > load_state ! = UNIT_LOADED )
return ;
2010-05-24 07:25:33 +04:00
2013-04-23 22:53:16 +04:00
if ( p - > state = = PATH_RUNNING & &
UNIT_IS_INACTIVE_OR_FAILED ( unit_active_state ( other ) ) ) {
log_debug_unit ( UNIT ( p ) - > id ,
" %s got notified about unit deactivation. " ,
UNIT ( p ) - > id ) ;
2010-11-15 02:49:21 +03:00
2013-04-23 22:53:16 +04:00
/* Hmm, so inotify was triggered since the
* last activation , so I guess we need to
* recheck what is going on . */
path_enter_waiting ( p , false , p - > inotify_triggered ) ;
2010-05-24 07:25:33 +04:00
}
}
2010-08-31 02:23:34 +04:00
static void path_reset_failed ( Unit * u ) {
2010-07-18 06:58:01 +04:00
Path * p = PATH ( u ) ;
assert ( p ) ;
2010-08-31 02:23:34 +04:00
if ( p - > state = = PATH_FAILED )
2010-07-18 06:58:01 +04:00
path_set_state ( p , PATH_DEAD ) ;
2012-02-03 08:04:48 +04:00
p - > result = PATH_SUCCESS ;
2010-07-18 06:58:01 +04:00
}
2010-05-24 07:25:33 +04:00
static const char * const path_state_table [ _PATH_STATE_MAX ] = {
[ PATH_DEAD ] = " dead " ,
[ PATH_WAITING ] = " waiting " ,
[ PATH_RUNNING ] = " running " ,
2010-08-31 02:23:34 +04:00
[ PATH_FAILED ] = " failed "
2010-05-24 07:25:33 +04:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( path_state , PathState ) ;
static const char * const path_type_table [ _PATH_TYPE_MAX ] = {
[ PATH_EXISTS ] = " PathExists " ,
2011-07-07 04:07:39 +04:00
[ PATH_EXISTS_GLOB ] = " PathExistsGlob " ,
2014-03-08 00:38:48 +04:00
[ PATH_DIRECTORY_NOT_EMPTY ] = " DirectoryNotEmpty " ,
2010-05-24 07:25:33 +04:00
[ PATH_CHANGED ] = " PathChanged " ,
2011-12-03 13:22:26 +04:00
[ PATH_MODIFIED ] = " PathModified " ,
2010-05-24 07:25:33 +04:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( path_type , PathType ) ;
2012-02-03 08:04:48 +04:00
static const char * const path_result_table [ _PATH_RESULT_MAX ] = {
[ PATH_SUCCESS ] = " success " ,
2014-03-08 00:38:48 +04:00
[ PATH_FAILURE_RESOURCES ] = " resources " ,
2012-02-03 08:04:48 +04:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( path_result , PathResult ) ;
2010-05-24 07:25:33 +04:00
const UnitVTable path_vtable = {
2012-01-15 13:53:49 +04:00
. object_size = sizeof ( Path ) ,
2013-11-20 00:12:59 +04:00
2011-08-01 02:43:05 +04:00
. sections =
" Unit \0 "
" Path \0 "
" Install \0 " ,
2010-05-24 07:25:33 +04:00
2011-04-10 03:30:14 +04:00
. init = path_init ,
2010-05-24 07:25:33 +04:00
. done = path_done ,
. load = path_load ,
. coldplug = path_coldplug ,
. dump = path_dump ,
. start = path_start ,
. stop = path_stop ,
. serialize = path_serialize ,
. deserialize_item = path_deserialize_item ,
. active_state = path_active_state ,
. sub_state_to_string = path_sub_state_to_string ,
2013-04-23 22:53:16 +04:00
. trigger_notify = path_trigger_notify ,
2010-08-31 02:23:34 +04:00
. reset_failed = path_reset_failed ,
2010-07-18 06:58:01 +04:00
2010-08-20 04:26:05 +04:00
. bus_interface = " org.freedesktop.systemd1.Path " ,
2013-12-22 06:43:03 +04:00
. bus_vtable = bus_path_vtable
2010-05-24 07:25:33 +04:00
} ;