2009-11-18 02:42:52 +03:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
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/>.
* * */
2009-11-18 02:42:52 +03:00
# include <assert.h>
2010-01-21 02:51:37 +03:00
# include <errno.h>
2009-11-18 02:42:52 +03:00
2010-01-30 03:55:42 +03:00
# include "set.h"
# include "unit.h"
2009-11-18 02:42:52 +03:00
# include "macro.h"
2010-01-30 03:55:42 +03:00
# include "strv.h"
# include "load-fragment.h"
# include "load-dropin.h"
2010-01-29 05:18:09 +03:00
# include "log.h"
2009-11-18 02:42:52 +03:00
2010-01-26 23:39:06 +03:00
Job * job_new ( Manager * m , JobType type , Unit * unit ) {
2009-11-18 02:42:52 +03:00
Job * j ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
2010-01-26 23:39:06 +03:00
assert ( unit ) ;
2009-11-18 02:42:52 +03:00
if ( ! ( j = new0 ( Job , 1 ) ) )
return NULL ;
j - > manager = m ;
j - > id = m - > current_job_id + + ;
j - > type = type ;
2010-01-26 23:39:06 +03:00
j - > unit = unit ;
2009-11-18 02:42:52 +03:00
2010-01-20 04:12:51 +03:00
/* We don't link it here, that's what job_dependency() is for */
2009-11-18 02:42:52 +03:00
return j ;
}
void job_free ( Job * j ) {
assert ( j ) ;
/* Detach from next 'bigger' objects */
2010-01-26 21:25:02 +03:00
if ( j - > installed ) {
2010-02-05 02:38:41 +03:00
bus_job_send_removed_signal ( j ) ;
2010-01-26 23:39:06 +03:00
if ( j - > unit - > meta . job = = j )
j - > unit - > meta . job = NULL ;
2009-11-18 02:42:52 +03:00
hashmap_remove ( j - > manager - > jobs , UINT32_TO_PTR ( j - > id ) ) ;
2010-01-26 21:25:02 +03:00
j - > installed = false ;
2009-11-18 02:42:52 +03:00
}
2010-01-23 03:52:57 +03:00
/* Detach from next 'smaller' objects */
2010-01-20 22:47:49 +03:00
manager_transaction_unlink_job ( j - > manager , j ) ;
2009-11-18 02:42:52 +03:00
2010-02-05 02:38:41 +03:00
if ( j - > in_run_queue )
LIST_REMOVE ( Job , run_queue , j - > manager - > run_queue , j ) ;
if ( j - > in_dbus_queue )
LIST_REMOVE ( Job , dbus_queue , j - > manager - > dbus_job_queue , j ) ;
2009-11-18 02:42:52 +03:00
free ( j ) ;
}
2010-01-19 02:22:34 +03:00
2010-01-20 04:12:51 +03: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 04:35:46 +03:00
if ( ! ( l = new0 ( JobDependency , 1 ) ) )
2010-01-20 04:12:51 +03:00
return NULL ;
l - > subject = subject ;
l - > object = object ;
l - > matters = matters ;
2010-01-26 09:02:51 +03:00
if ( subject )
LIST_PREPEND ( JobDependency , subject , subject - > subject_list , l ) ;
else
LIST_PREPEND ( JobDependency , subject , object - > manager - > transaction_anchor , l ) ;
2010-01-20 04:12:51 +03:00
2010-01-26 09:02:51 +03:00
LIST_PREPEND ( JobDependency , object , object - > object_list , l ) ;
2010-01-20 04:12:51 +03:00
return l ;
}
void job_dependency_free ( JobDependency * l ) {
assert ( l ) ;
2010-01-26 09:02:51 +03:00
if ( l - > subject )
LIST_REMOVE ( JobDependency , subject , l - > subject - > subject_list , l ) ;
2010-01-20 04:12:51 +03:00
else
2010-01-26 09:02:51 +03:00
LIST_REMOVE ( JobDependency , subject , l - > object - > manager - > transaction_anchor , l ) ;
2010-01-20 04:12:51 +03:00
2010-01-26 09:02:51 +03:00
LIST_REMOVE ( JobDependency , object , l - > object - > object_list , l ) ;
2010-01-20 04:12:51 +03:00
free ( l ) ;
}
void job_dependency_delete ( Job * subject , Job * object , bool * matters ) {
JobDependency * l ;
assert ( object ) ;
2010-01-26 09:02:51 +03:00
LIST_FOREACH ( object , l , object - > object_list ) {
2010-01-20 04:12:51 +03: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 02:51:37 +03:00
void job_dump ( Job * j , FILE * f , const char * prefix ) {
2010-01-19 02:22:34 +03:00
assert ( j ) ;
assert ( f ) ;
2010-01-20 04:35:46 +03:00
fprintf ( f ,
2010-01-26 09:02:51 +03:00
" %s→ Job %u: \n "
2010-01-20 04:35:46 +03:00
" %s \t Action: %s → %s \n "
2010-01-23 03:52:57 +03:00
" %s \t State: %s \n "
" %s \t Forced: %s \n " ,
2010-01-20 04:35:46 +03:00
prefix , j - > id ,
2010-01-26 23:39:06 +03:00
prefix , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ,
2010-01-30 03:55:42 +03:00
prefix , job_state_to_string ( j - > state ) ,
2010-01-23 03:52:57 +03:00
prefix , yes_no ( j - > forced ) ) ;
2010-01-19 02:22:34 +03:00
}
2010-01-20 04:12:51 +03:00
bool job_is_anchor ( Job * j ) {
JobDependency * l ;
assert ( j ) ;
2010-01-26 09:02:51 +03:00
LIST_FOREACH ( object , l , j - > object_list )
2010-01-20 04:12:51 +03:00
if ( ! l - > subject )
return true ;
return false ;
}
2010-01-21 02:51:37 +03: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 03:52:57 +03:00
if ( types_match ( * a , b , JOB_START , JOB_VERIFY_ACTIVE ) )
2010-01-21 02:51:37 +03: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 03:52:57 +03:00
types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_RELOAD_OR_START ) | |
2010-01-21 02:51:37 +03: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 03:52:57 +03:00
types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_RESTART ) | |
2010-01-21 02:51:37 +03: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 03:52:57 +03:00
else if ( types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_RELOAD ) )
2010-01-21 02:51:37 +03:00
* a = JOB_RELOAD ;
2010-01-23 03:52:57 +03:00
else if ( types_match ( * a , b , JOB_VERIFY_ACTIVE , JOB_TRY_RESTART ) | |
2010-01-21 02:51:37 +03:00
types_match ( * a , b , JOB_RELOAD , JOB_TRY_RESTART ) )
* a = JOB_TRY_RESTART ;
else
return - EEXIST ;
return 0 ;
}
2010-01-23 03:52:57 +03:00
bool job_type_is_mergeable ( JobType a , JobType b ) {
2010-01-21 02:51:37 +03:00
return job_type_merge ( & a , b ) > = 0 ;
}
bool job_type_is_superset ( JobType a , JobType b ) {
2010-01-23 03:52:57 +03:00
/* Checks whether operation a is a "superset" of b in its
* actions */
2010-01-21 02:51:37 +03:00
if ( a = = b )
return true ;
switch ( a ) {
case JOB_START :
2010-01-23 03:52:57 +03:00
return b = = JOB_VERIFY_ACTIVE ;
2010-01-21 02:51:37 +03:00
case JOB_RELOAD :
2010-01-23 03:52:57 +03:00
return
b = = JOB_VERIFY_ACTIVE ;
2010-01-21 02:51:37 +03:00
case JOB_RELOAD_OR_START :
return
b = = JOB_RELOAD | |
2010-01-23 03:52:57 +03:00
b = = JOB_START | |
b = = JOB_VERIFY_ACTIVE ;
2010-01-21 02:51:37 +03:00
case JOB_RESTART :
return
b = = JOB_START | |
2010-01-23 03:52:57 +03:00
b = = JOB_VERIFY_ACTIVE | |
2010-01-21 02:51:37 +03:00
b = = JOB_RELOAD | |
b = = JOB_RELOAD_OR_START | |
b = = JOB_TRY_RESTART ;
case JOB_TRY_RESTART :
return
2010-01-23 03:52:57 +03:00
b = = JOB_VERIFY_ACTIVE | |
2010-01-21 02:51:37 +03:00
b = = JOB_RELOAD ;
default :
return false ;
}
}
2010-01-21 04:59:12 +03:00
bool job_type_is_conflicting ( JobType a , JobType b ) {
2010-01-21 05:26:34 +03:00
assert ( a > = 0 & & a < _JOB_TYPE_MAX ) ;
assert ( b > = 0 & & b < _JOB_TYPE_MAX ) ;
2010-01-21 04:59:12 +03:00
2010-01-23 03:52:57 +03:00
return ( a = = JOB_STOP ) ! = ( b = = JOB_STOP ) ;
2010-01-21 04:59:12 +03:00
}
2010-01-21 05:26:34 +03:00
2010-01-23 03:52:57 +03:00
bool job_is_runnable ( Job * j ) {
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-26 23:39:06 +03:00
Unit * other ;
2010-01-23 03:52:57 +03:00
assert ( j ) ;
2010-01-26 21:25:02 +03:00
assert ( j - > installed ) ;
2010-01-23 03:52:57 +03:00
2010-01-26 23:39:06 +03:00
/* Checks whether there is any job running for the units this
2010-01-23 03:52:57 +03:00
* 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 23:39:06 +03:00
SET_FOREACH ( other , j - > unit - > meta . dependencies [ UNIT_AFTER ] , i )
2010-01-23 03:52:57 +03: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 23:39:06 +03:00
SET_FOREACH ( other , j - > unit - > meta . dependencies [ UNIT_BEFORE ] , i )
2010-01-23 03:52:57 +03: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 ;
2010-01-26 21:25:02 +03:00
2010-01-23 03:52:57 +03:00
assert ( j ) ;
2010-01-26 21:25:02 +03:00
assert ( j - > installed ) ;
2010-01-23 03:52:57 +03:00
2010-01-26 06:18:44 +03:00
if ( j - > in_run_queue ) {
LIST_REMOVE ( Job , run_queue , j - > manager - > run_queue , j ) ;
j - > in_run_queue = false ;
}
2010-01-23 03:52:57 +03:00
if ( j - > state ! = JOB_WAITING )
return 0 ;
2010-01-26 06:18:44 +03:00
if ( ! job_is_runnable ( j ) )
return - EAGAIN ;
2010-01-24 00:56:47 +03:00
j - > state = JOB_RUNNING ;
2010-02-05 02:38:41 +03:00
job_add_to_dbus_queue ( j ) ;
2010-01-24 00:56:47 +03:00
2010-01-23 03:52:57 +03:00
switch ( j - > type ) {
case JOB_START :
2010-01-26 23:39:06 +03:00
r = unit_start ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
if ( r = = - EBADR )
r = 0 ;
break ;
case JOB_VERIFY_ACTIVE : {
2010-01-26 23:39:06 +03:00
UnitActiveState t = unit_active_state ( j - > unit ) ;
if ( UNIT_IS_ACTIVE_OR_RELOADING ( t ) )
2010-01-23 03:52:57 +03:00
r = - EALREADY ;
2010-01-26 23:39:06 +03:00
else if ( t = = UNIT_ACTIVATING )
2010-01-23 03:52:57 +03:00
r = - EAGAIN ;
else
r = - ENOEXEC ;
break ;
}
case JOB_STOP :
2010-01-26 23:39:06 +03:00
r = unit_stop ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
break ;
case JOB_RELOAD :
2010-01-26 23:39:06 +03:00
r = unit_reload ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
break ;
case JOB_RELOAD_OR_START :
2010-01-26 23:39:06 +03:00
if ( unit_active_state ( j - > unit ) = = UNIT_ACTIVE )
r = unit_reload ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
else
2010-01-26 23:39:06 +03:00
r = unit_start ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
break ;
case JOB_RESTART : {
2010-01-26 23:39:06 +03:00
UnitActiveState t = unit_active_state ( j - > unit ) ;
if ( t = = UNIT_INACTIVE | | t = = UNIT_ACTIVATING ) {
2010-01-23 03:52:57 +03:00
j - > type = JOB_START ;
2010-01-26 23:39:06 +03:00
r = unit_start ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
} else
2010-01-26 23:39:06 +03:00
r = unit_stop ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
break ;
}
case JOB_TRY_RESTART : {
2010-01-26 23:39:06 +03:00
UnitActiveState t = unit_active_state ( j - > unit ) ;
if ( t = = UNIT_INACTIVE | | t = = UNIT_DEACTIVATING )
2010-01-23 03:52:57 +03:00
r = - ENOEXEC ;
2010-01-26 23:39:06 +03:00
else if ( t = = UNIT_ACTIVATING ) {
2010-01-23 03:52:57 +03:00
j - > type = JOB_START ;
2010-01-26 23:39:06 +03:00
r = unit_start ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
} else
2010-01-26 23:39:06 +03:00
r = unit_stop ( j - > unit ) ;
2010-01-23 03:52:57 +03:00
break ;
}
default :
2010-01-26 09:02:51 +03:00
assert_not_reached ( " Unknown job type " ) ;
2010-01-23 03:52:57 +03:00
}
2010-01-24 00:56:47 +03:00
if ( r = = - EALREADY )
2010-01-23 03:52:57 +03:00
r = job_finish_and_invalidate ( j , true ) ;
2010-01-24 00:56:47 +03:00
else if ( r = = - EAGAIN ) {
j - > state = JOB_WAITING ;
return - EAGAIN ;
} else if ( r < 0 )
2010-01-23 03:52:57 +03:00
r = job_finish_and_invalidate ( j , false ) ;
return r ;
}
int job_finish_and_invalidate ( Job * j , bool success ) {
2010-01-26 23:39:06 +03:00
Unit * u ;
Unit * other ;
UnitType t ;
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-23 03:52:57 +03:00
assert ( j ) ;
2010-01-26 21:25:02 +03:00
assert ( j - > installed ) ;
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
log_debug ( " Job %s/%s finished, success=%s " , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) , yes_no ( success ) ) ;
2010-02-05 02:38:41 +03:00
job_add_to_dbus_queue ( j ) ;
2010-01-29 05:18:09 +03:00
2010-01-26 06:18:44 +03:00
/* Patch restart jobs so that they become normal start jobs */
2010-01-23 03:52:57 +03:00
if ( success & & ( j - > type = = JOB_RESTART | | j - > type = = JOB_TRY_RESTART ) ) {
2010-01-29 05:18:09 +03:00
log_debug ( " Converting job %s/%s → %s/%s " ,
unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ,
unit_id ( j - > unit ) , job_type_to_string ( JOB_START ) ) ;
2010-01-23 03:52:57 +03:00
j - > state = JOB_RUNNING ;
j - > type = JOB_START ;
2010-01-29 05:18:09 +03:00
2010-02-05 02:38:41 +03:00
job_add_to_run_queue ( j ) ;
2010-01-26 06:18:44 +03:00
return 0 ;
2010-01-23 03:52:57 +03:00
}
2010-01-26 23:39:06 +03:00
u = j - > unit ;
2010-01-23 03:52:57 +03:00
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 23:39:06 +03:00
SET_FOREACH ( other , u - > meta . dependencies [ UNIT_REQUIRED_BY ] , i )
2010-01-23 03:52:57 +03: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 23:39:06 +03:00
SET_FOREACH ( other , u - > meta . dependencies [ UNIT_SOFT_REQUIRED_BY ] , i )
2010-01-23 03:52:57 +03: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 23:39:06 +03:00
SET_FOREACH ( other , u - > meta . dependencies [ UNIT_CONFLICTS ] , i )
2010-01-23 03:52:57 +03: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 23:39:06 +03:00
SET_FOREACH ( other , u - > meta . dependencies [ UNIT_AFTER ] , i )
2010-01-23 03:52:57 +03:00
if ( other - > meta . job )
2010-02-05 02:38:41 +03:00
job_add_to_run_queue ( other - > meta . job ) ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( other , u - > meta . dependencies [ UNIT_BEFORE ] , i )
2010-01-23 03:52:57 +03:00
if ( other - > meta . job )
2010-02-05 02:38:41 +03:00
job_add_to_run_queue ( other - > meta . job ) ;
2010-01-23 03:52:57 +03:00
return 0 ;
}
2010-01-26 06:18:44 +03:00
2010-02-05 02:38:41 +03:00
void job_add_to_run_queue ( Job * j ) {
2010-01-26 06:18:44 +03:00
assert ( j ) ;
2010-01-26 21:25:02 +03:00
assert ( j - > installed ) ;
2010-01-26 06:18:44 +03:00
if ( j - > in_run_queue )
return ;
LIST_PREPEND ( Job , run_queue , j - > manager - > run_queue , j ) ;
j - > in_run_queue = true ;
}
2010-01-30 03:55:42 +03:00
2010-02-05 02:38:41 +03:00
void job_add_to_dbus_queue ( Job * j ) {
assert ( j ) ;
assert ( j - > installed ) ;
if ( j - > in_dbus_queue )
return ;
LIST_PREPEND ( Job , dbus_queue , j - > manager - > dbus_job_queue , j ) ;
j - > in_dbus_queue = true ;
}
2010-02-01 05:33:24 +03:00
char * job_dbus_path ( Job * j ) {
char * p ;
assert ( j ) ;
if ( asprintf ( & p , " /org/freedesktop/systemd1/job/%lu " , ( unsigned long ) j - > id ) < 0 )
return NULL ;
return p ;
}
2010-01-30 03:55:42 +03:00
static const char * const job_state_table [ _JOB_STATE_MAX ] = {
[ JOB_WAITING ] = " waiting " ,
[ JOB_RUNNING ] = " running "
} ;
DEFINE_STRING_TABLE_LOOKUP ( job_state , JobState ) ;
static const char * const job_type_table [ _JOB_TYPE_MAX ] = {
[ JOB_START ] = " start " ,
[ JOB_VERIFY_ACTIVE ] = " verify-active " ,
[ JOB_STOP ] = " stop " ,
[ JOB_RELOAD ] = " reload " ,
[ JOB_RELOAD_OR_START ] = " reload-or-start " ,
[ JOB_RESTART ] = " restart " ,
[ JOB_TRY_RESTART ] = " try-restart " ,
} ;
DEFINE_STRING_TABLE_LOOKUP ( job_type , JobType ) ;
2010-02-03 14:37:42 +03:00
static const char * const job_mode_table [ _JOB_MODE_MAX ] = {
[ JOB_FAIL ] = " fail " ,
[ JOB_REPLACE ] = " replace "
} ;
DEFINE_STRING_TABLE_LOOKUP ( job_mode , JobMode ) ;