2009-11-18 00:42:52 +01:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
# include <assert.h>
2010-01-21 00:51:37 +01:00
# include <errno.h>
2009-11-18 00:42:52 +01:00
# include "macro.h"
# include "job.h"
Job * job_new ( Manager * m , JobType type , Name * name ) {
Job * j ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
assert ( name ) ;
if ( ! ( j = new0 ( Job , 1 ) ) )
return NULL ;
j - > manager = m ;
j - > id = m - > current_job_id + + ;
j - > type = type ;
j - > name = name ;
2010-01-20 02:12:51 +01:00
/* We don't link it here, that's what job_dependency() is for */
2009-11-18 00:42:52 +01:00
return j ;
}
void job_free ( Job * j ) {
assert ( j ) ;
/* Detach from next 'bigger' objects */
if ( j - > linked ) {
2010-01-19 04:15:20 +01:00
if ( j - > name - > meta . job = = j )
j - > name - > meta . job = NULL ;
2009-11-18 00:42:52 +01:00
hashmap_remove ( j - > manager - > jobs , UINT32_TO_PTR ( j - > id ) ) ;
2010-01-23 01:52:57 +01:00
j - > linked = false ;
2009-11-18 00:42:52 +01:00
}
2010-01-23 01:52:57 +01:00
/* Detach from next 'smaller' objects */
2010-01-20 20:47:49 +01:00
manager_transaction_unlink_job ( j - > manager , j ) ;
2009-11-18 00:42:52 +01:00
free ( j ) ;
}
2010-01-19 00:22:34 +01:00
2010-01-20 02:12:51 +01:00
JobDependency * job_dependency_new ( Job * subject , Job * object , bool matters ) {
JobDependency * l ;
assert ( object ) ;
/* Adds a new job link, which encodes that the 'subject' job
* needs the ' object ' job in some way . If ' subject ' is NULL
* this means the ' anchor ' job ( i . e . the one the user
* explcitily asked for ) is the requester . */
2010-01-20 02:35:46 +01:00
if ( ! ( l = new0 ( JobDependency , 1 ) ) )
2010-01-20 02:12:51 +01:00
return NULL ;
l - > subject = subject ;
l - > object = object ;
l - > matters = matters ;
2010-01-26 07:02:51 +01:00
if ( subject )
LIST_PREPEND ( JobDependency , subject , subject - > subject_list , l ) ;
else
LIST_PREPEND ( JobDependency , subject , object - > manager - > transaction_anchor , l ) ;
2010-01-20 02:12:51 +01:00
2010-01-26 07:02:51 +01:00
LIST_PREPEND ( JobDependency , object , object - > object_list , l ) ;
2010-01-20 02:12:51 +01:00
return l ;
}
void job_dependency_free ( JobDependency * l ) {
assert ( l ) ;
2010-01-26 07:02:51 +01:00
if ( l - > subject )
LIST_REMOVE ( JobDependency , subject , l - > subject - > subject_list , l ) ;
2010-01-20 02:12:51 +01:00
else
2010-01-26 07:02:51 +01:00
LIST_REMOVE ( JobDependency , subject , l - > object - > manager - > transaction_anchor , l ) ;
2010-01-20 02:12:51 +01:00
2010-01-26 07:02:51 +01:00
LIST_REMOVE ( JobDependency , object , l - > object - > object_list , l ) ;
2010-01-20 02:12:51 +01:00
free ( l ) ;
}
void job_dependency_delete ( Job * subject , Job * object , bool * matters ) {
JobDependency * l ;
assert ( object ) ;
2010-01-26 07:02:51 +01:00
LIST_FOREACH ( object , l , object - > object_list ) {
2010-01-20 02:12:51 +01:00
assert ( l - > object = = object ) ;
if ( l - > subject = = subject )
break ;
}
if ( ! l ) {
if ( matters )
* matters = false ;
return ;
}
if ( matters )
* matters = l - > matters ;
job_dependency_free ( l ) ;
}
2010-01-21 00:51:37 +01:00
const char * job_type_to_string ( JobType t ) {
2010-01-19 00:22:34 +01:00
static const char * const job_type_table [ _JOB_TYPE_MAX ] = {
2010-01-19 02:56:37 +01:00
[ JOB_START ] = " start " ,
2010-01-23 01:52:57 +01:00
[ JOB_VERIFY_ACTIVE ] = " verify-active " ,
2010-01-19 02:56:37 +01:00
[ JOB_STOP ] = " stop " ,
[ JOB_RELOAD ] = " reload " ,
2010-01-20 02:12:51 +01:00
[ JOB_RELOAD_OR_START ] = " reload-or-start " ,
2010-01-19 02:56:37 +01:00
[ JOB_RESTART ] = " restart " ,
[ JOB_TRY_RESTART ] = " try-restart " ,
2010-01-19 00:22:34 +01:00
} ;
2010-01-21 00:51:37 +01:00
if ( t < 0 | | t > = _JOB_TYPE_MAX )
return " n/a " ;
return job_type_table [ t ] ;
}
void job_dump ( Job * j , FILE * f , const char * prefix ) {
2010-01-19 00:22:34 +01:00
static const char * const job_state_table [ _JOB_STATE_MAX ] = {
2010-01-19 02:56:37 +01:00
[ JOB_WAITING ] = " waiting " ,
2010-01-23 01:52:57 +01:00
[ JOB_RUNNING ] = " running "
2010-01-19 00:22:34 +01:00
} ;
assert ( j ) ;
assert ( f ) ;
2010-01-20 02:35:46 +01:00
fprintf ( f ,
2010-01-26 07:02:51 +01:00
" %s→ Job %u: \n "
2010-01-20 02:35:46 +01:00
" %s \t Action: %s → %s \n "
2010-01-23 01:52:57 +01:00
" %s \t State: %s \n "
" %s \t Forced: %s \n " ,
2010-01-20 02:35:46 +01:00
prefix , j - > id ,
2010-01-21 00:51:37 +01:00
prefix , name_id ( j - > name ) , job_type_to_string ( j - > type ) ,
2010-01-23 01:52:57 +01:00
prefix , job_state_table [ j - > state ] ,
prefix , yes_no ( j - > forced ) ) ;
2010-01-19 00:22:34 +01:00
}
2010-01-20 02:12:51 +01:00
bool job_is_anchor ( Job * j ) {
JobDependency * l ;
assert ( j ) ;
2010-01-26 07:02:51 +01:00
LIST_FOREACH ( object , l , j - > object_list )
2010-01-20 02:12:51 +01:00
if ( ! l - > subject )
return true ;
return false ;
}
2010-01-21 00:51:37 +01:00
static bool types_match ( JobType a , JobType b , JobType c , JobType d ) {
return
( a = = c & & b = = d ) | |
( a = = d & & b = = c ) ;
}
int job_type_merge ( JobType * a , JobType b ) {
if ( * a = = b )
return 0 ;
/* Merging is associative! a merged with b merged with c is
* the same as a merged with c merged with b . */
/* Mergeability is transitive! if a can be merged with b and b
* with c then a also with c */
/* Also, if a merged with b cannot be merged with c, then
* either a or b cannot be merged with c either */
2010-01-23 01:52:57 +01:00
if ( types_match ( * a , b , JOB_START , JOB_VERIFY_ACTIVE ) )
2010-01-21 00:51:37 +01:00
* a = JOB_START ;
else if ( types_match ( * a , b , JOB_START , JOB_RELOAD ) | |
types_match ( * a , b , JOB_START , JOB_RELOAD_OR_START ) | |
2010-01-23 01:52:57 +01:00
types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_RELOAD_OR_START ) | |
2010-01-21 00:51:37 +01:00
types_match ( * a , b , JOB_RELOAD , JOB_RELOAD_OR_START ) )
* a = JOB_RELOAD_OR_START ;
else if ( types_match ( * a , b , JOB_START , JOB_RESTART ) | |
types_match ( * a , b , JOB_START , JOB_TRY_RESTART ) | |
2010-01-23 01:52:57 +01:00
types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_RESTART ) | |
2010-01-21 00:51:37 +01:00
types_match ( * a , b , JOB_RELOAD , JOB_RESTART ) | |
types_match ( * a , b , JOB_RELOAD_OR_START , JOB_RESTART ) | |
types_match ( * a , b , JOB_RELOAD_OR_START , JOB_TRY_RESTART ) | |
types_match ( * a , b , JOB_RESTART , JOB_TRY_RESTART ) )
* a = JOB_RESTART ;
2010-01-23 01:52:57 +01:00
else if ( types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_RELOAD ) )
2010-01-21 00:51:37 +01:00
* a = JOB_RELOAD ;
2010-01-23 01:52:57 +01:00
else if ( types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_TRY_RESTART ) | |
2010-01-21 00:51:37 +01:00
types_match ( * a , b , JOB_RELOAD , JOB_TRY_RESTART ) )
* a = JOB_TRY_RESTART ;
else
return - EEXIST ;
return 0 ;
}
2010-01-23 01:52:57 +01:00
bool job_type_is_mergeable ( JobType a , JobType b ) {
2010-01-21 00:51:37 +01:00
return job_type_merge ( & a , b ) > = 0 ;
}
bool job_type_is_superset ( JobType a , JobType b ) {
2010-01-23 01:52:57 +01:00
/* Checks whether operation a is a "superset" of b in its
* actions */
2010-01-21 00:51:37 +01:00
if ( a = = b )
return true ;
switch ( a ) {
case JOB_START :
2010-01-23 01:52:57 +01:00
return b = = JOB_VERIFY_ACTIVE ;
2010-01-21 00:51:37 +01:00
case JOB_RELOAD :
2010-01-23 01:52:57 +01:00
return
b = = JOB_VERIFY_ACTIVE ;
2010-01-21 00:51:37 +01:00
case JOB_RELOAD_OR_START :
return
b = = JOB_RELOAD | |
2010-01-23 01:52:57 +01:00
b = = JOB_START | |
b = = JOB_VERIFY_ACTIVE ;
2010-01-21 00:51:37 +01:00
case JOB_RESTART :
return
b = = JOB_START | |
2010-01-23 01:52:57 +01:00
b = = JOB_VERIFY_ACTIVE | |
2010-01-21 00:51:37 +01:00
b = = JOB_RELOAD | |
b = = JOB_RELOAD_OR_START | |
b = = JOB_TRY_RESTART ;
case JOB_TRY_RESTART :
return
2010-01-23 01:52:57 +01:00
b = = JOB_VERIFY_ACTIVE | |
2010-01-21 00:51:37 +01:00
b = = JOB_RELOAD ;
default :
return false ;
}
}
2010-01-21 02:59:12 +01:00
bool job_type_is_conflicting ( JobType a , JobType b ) {
2010-01-21 03:26:34 +01:00
assert ( a > = 0 & & a < _JOB_TYPE_MAX ) ;
assert ( b > = 0 & & b < _JOB_TYPE_MAX ) ;
2010-01-21 02:59:12 +01:00
2010-01-23 01:52:57 +01:00
return ( a = = JOB_STOP ) ! = ( b = = JOB_STOP ) ;
2010-01-21 02:59:12 +01:00
}
2010-01-21 03:26:34 +01:00
2010-01-23 01:52:57 +01:00
bool job_is_runnable ( Job * j ) {
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-23 01:52:57 +01:00
Name * other ;
assert ( j ) ;
assert ( j - > linked ) ;
/* Checks whether there is any job running for the names this
* job needs to be running after ( in the case of a ' positive '
* job type ) or before ( in the case of a ' negative ' job type
* . */
if ( j - > type = = JOB_START | |
j - > type = = JOB_VERIFY_ACTIVE | |
j - > type = = JOB_RELOAD | |
j - > type = = JOB_RELOAD_OR_START ) {
/* Immediate result is that the job is or might be
* started . In this case lets wait for the
* dependencies , regardless whether they are
* starting or stopping something . */
2010-01-26 04:18:44 +01:00
SET_FOREACH ( other , j - > name - > meta . dependencies [ NAME_AFTER ] , i )
2010-01-23 01:52:57 +01:00
if ( other - > meta . job )
return false ;
}
/* Also, if something else is being stopped and we should
* change state after it , then lets wait . */
2010-01-26 04:18:44 +01:00
SET_FOREACH ( other , j - > name - > meta . dependencies [ NAME_BEFORE ] , i )
2010-01-23 01:52:57 +01:00
if ( other - > meta . job & &
( other - > meta . job - > type = = JOB_STOP | |
other - > meta . job - > type = = JOB_RESTART | |
other - > meta . job - > type = = JOB_TRY_RESTART ) )
return false ;
/* This means that for a service a and a service b where b
* shall be started after a :
*
* start a + start b → 1 st step start a , 2 nd step start b
* start a + stop b → 1 st step stop b , 2 nd step start a
* stop a + start b → 1 st step stop a , 2 nd step start b
* stop a + stop b → 1 st step stop b , 2 nd step stop a
*
* This has the side effect that restarts are properly
* synchronized too . */
return true ;
}
int job_run_and_invalidate ( Job * j ) {
int r ;
assert ( j ) ;
2010-01-26 04:18:44 +01:00
if ( j - > in_run_queue ) {
LIST_REMOVE ( Job , run_queue , j - > manager - > run_queue , j ) ;
j - > in_run_queue = false ;
}
2010-01-23 01:52:57 +01:00
if ( j - > state ! = JOB_WAITING )
return 0 ;
2010-01-26 04:18:44 +01:00
if ( ! job_is_runnable ( j ) )
return - EAGAIN ;
2010-01-23 22:56:47 +01:00
j - > state = JOB_RUNNING ;
2010-01-23 01:52:57 +01:00
switch ( j - > type ) {
case JOB_START :
r = name_start ( j - > name ) ;
if ( r = = - EBADR )
r = 0 ;
break ;
case JOB_VERIFY_ACTIVE : {
NameActiveState t = name_active_state ( j - > name ) ;
if ( NAME_IS_ACTIVE_OR_RELOADING ( t ) )
r = - EALREADY ;
else if ( t = = NAME_ACTIVATING )
r = - EAGAIN ;
else
r = - ENOEXEC ;
break ;
}
case JOB_STOP :
r = name_stop ( j - > name ) ;
break ;
case JOB_RELOAD :
r = name_reload ( j - > name ) ;
break ;
case JOB_RELOAD_OR_START :
if ( name_active_state ( j - > name ) = = NAME_ACTIVE )
r = name_reload ( j - > name ) ;
else
r = name_start ( j - > name ) ;
break ;
case JOB_RESTART : {
NameActiveState t = name_active_state ( j - > name ) ;
if ( t = = NAME_INACTIVE | | t = = NAME_ACTIVATING ) {
j - > type = JOB_START ;
r = name_start ( j - > name ) ;
} else
r = name_stop ( j - > name ) ;
break ;
}
case JOB_TRY_RESTART : {
NameActiveState t = name_active_state ( j - > name ) ;
if ( t = = NAME_INACTIVE | | t = = NAME_DEACTIVATING )
r = - ENOEXEC ;
else if ( t = = NAME_ACTIVATING ) {
j - > type = JOB_START ;
r = name_start ( j - > name ) ;
} else
r = name_stop ( j - > name ) ;
break ;
}
default :
2010-01-26 07:02:51 +01:00
assert_not_reached ( " Unknown job type " ) ;
2010-01-23 01:52:57 +01:00
}
2010-01-23 22:56:47 +01:00
if ( r = = - EALREADY )
2010-01-23 01:52:57 +01:00
r = job_finish_and_invalidate ( j , true ) ;
2010-01-23 22:56:47 +01:00
else if ( r = = - EAGAIN ) {
j - > state = JOB_WAITING ;
return - EAGAIN ;
} else if ( r < 0 )
2010-01-23 01:52:57 +01:00
r = job_finish_and_invalidate ( j , false ) ;
return r ;
}
int job_finish_and_invalidate ( Job * j , bool success ) {
Name * n ;
Name * other ;
NameType t ;
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-23 01:52:57 +01:00
assert ( j ) ;
2010-01-26 04:18:44 +01:00
/* Patch restart jobs so that they become normal start jobs */
2010-01-23 01:52:57 +01:00
if ( success & & ( j - > type = = JOB_RESTART | | j - > type = = JOB_TRY_RESTART ) ) {
j - > state = JOB_RUNNING ;
j - > type = JOB_START ;
2010-01-26 04:18:44 +01:00
job_schedule_run ( j ) ;
return 0 ;
2010-01-23 01:52:57 +01:00
}
n = j - > name ;
t = j - > type ;
job_free ( j ) ;
/* Fail depending jobs on failure */
if ( ! success ) {
if ( t = = JOB_START | |
t = = JOB_VERIFY_ACTIVE | |
t = = JOB_RELOAD_OR_START ) {
2010-01-26 04:18:44 +01:00
SET_FOREACH ( other , n - > meta . dependencies [ NAME_REQUIRED_BY ] , i )
2010-01-23 01:52:57 +01:00
if ( other - > meta . job & &
( other - > meta . type = = JOB_START | |
other - > meta . type = = JOB_VERIFY_ACTIVE | |
other - > meta . type = = JOB_RELOAD_OR_START ) )
job_finish_and_invalidate ( other - > meta . job , false ) ;
2010-01-26 04:18:44 +01:00
SET_FOREACH ( other , n - > meta . dependencies [ NAME_SOFT_REQUIRED_BY ] , i )
2010-01-23 01:52:57 +01:00
if ( other - > meta . job & &
! other - > meta . job - > forced & &
( other - > meta . type = = JOB_START | |
other - > meta . type = = JOB_VERIFY_ACTIVE | |
other - > meta . type = = JOB_RELOAD_OR_START ) )
job_finish_and_invalidate ( other - > meta . job , false ) ;
} else if ( t = = JOB_STOP ) {
2010-01-26 04:18:44 +01:00
SET_FOREACH ( other , n - > meta . dependencies [ NAME_CONFLICTS ] , i )
2010-01-23 01:52:57 +01:00
if ( other - > meta . job & &
( t = = JOB_START | |
t = = JOB_VERIFY_ACTIVE | |
t = = JOB_RELOAD_OR_START ) )
job_finish_and_invalidate ( other - > meta . job , false ) ;
}
}
/* Try to start the next jobs that can be started */
2010-01-26 04:18:44 +01:00
SET_FOREACH ( other , n - > meta . dependencies [ NAME_AFTER ] , i )
2010-01-23 01:52:57 +01:00
if ( other - > meta . job )
2010-01-26 04:18:44 +01:00
job_schedule_run ( other - > meta . job ) ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_BEFORE ] , i )
2010-01-23 01:52:57 +01:00
if ( other - > meta . job )
2010-01-26 04:18:44 +01:00
job_schedule_run ( other - > meta . job ) ;
2010-01-23 01:52:57 +01:00
return 0 ;
}
2010-01-26 04:18:44 +01:00
void job_schedule_run ( Job * j ) {
assert ( j ) ;
assert ( j - > linked ) ;
if ( j - > in_run_queue )
return ;
LIST_PREPEND ( Job , run_queue , j - > manager - > run_queue , j ) ;
j - > in_run_queue = true ;
}