2009-11-18 00:42:52 +01:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
# include <assert.h>
# include <errno.h>
2009-11-19 02:50:52 +01:00
# include <string.h>
2009-11-18 00:42:52 +01:00
# include "set.h"
# include "name.h"
# include "macro.h"
# include "strv.h"
2010-01-20 05:03:52 +01:00
# include "load-fragment.h"
2009-11-18 00:42:52 +01:00
NameType name_type_from_string ( const char * n ) {
NameType t ;
static const char * suffixes [ _NAME_TYPE_MAX ] = {
[ NAME_SERVICE ] = " .service " ,
[ NAME_TIMER ] = " .timer " ,
[ NAME_SOCKET ] = " .socket " ,
[ NAME_MILESTONE ] = " .milestone " ,
[ NAME_DEVICE ] = " .device " ,
[ NAME_MOUNT ] = " .mount " ,
[ NAME_AUTOMOUNT ] = " .automount " ,
[ NAME_SNAPSHOT ] = " .snapshot " ,
} ;
assert ( n ) ;
for ( t = 0 ; t < _NAME_TYPE_MAX ; t + + )
if ( endswith ( n , suffixes [ t ] ) )
return t ;
return _NAME_TYPE_INVALID ;
}
2009-11-19 02:50:52 +01:00
# define VALID_CHARS \
" 0123456789 " \
" abcdefghijklmnopqrstuvwxyz " \
" ABCDEFGHIJKLMNOPQRSTUVWXYZ " \
" -_ "
bool name_is_valid ( const char * n ) {
NameType t ;
const char * e , * i ;
assert ( n ) ;
t = name_type_from_string ( n ) ;
if ( t < 0 | | t > = _NAME_TYPE_MAX )
return false ;
if ( ! ( e = strrchr ( n , ' . ' ) ) )
return false ;
for ( i = n ; i < e ; i + + )
if ( ! strchr ( VALID_CHARS , * i ) )
return false ;
return true ;
}
2009-11-18 00:42:52 +01:00
Name * name_new ( Manager * m ) {
Name * n ;
assert ( m ) ;
if ( ! ( n = new0 ( Name , 1 ) ) )
return NULL ;
2010-01-18 23:50:13 +01:00
if ( ! ( n - > meta . names = set_new ( string_hash_func , string_compare_func ) ) ) {
free ( n ) ;
return NULL ;
}
2009-11-18 00:42:52 +01:00
/* Not much initialization happening here at this time */
n - > meta . manager = m ;
n - > meta . type = _NAME_TYPE_INVALID ;
n - > meta . state = NAME_STUB ;
/* We don't link the name here, that is left for name_link() */
return n ;
}
2010-01-20 02:12:51 +01:00
/* FIXME: Does not rollback on failure! */
int name_link_names ( Name * n , bool replace ) {
2010-01-19 04:15:20 +01:00
char * t ;
void * state ;
int r ;
assert ( n ) ;
if ( ! n - > meta . linked )
return 0 ;
2010-01-20 02:12:51 +01:00
/* Link all names that aren't linked yet. */
2010-01-19 04:15:20 +01:00
SET_FOREACH ( t , n - > meta . names , state )
2010-01-20 02:12:51 +01:00
if ( replace ) {
if ( ( r = hashmap_replace ( n - > meta . manager - > names , t , n ) ) < 0 )
return r ;
} else {
if ( ( r = hashmap_put ( n - > meta . manager - > names , t , n ) ) < 0 )
return r ;
2010-01-19 04:15:20 +01:00
}
return 0 ;
}
2009-11-18 00:42:52 +01:00
int name_link ( Name * n ) {
int r ;
assert ( n ) ;
2010-01-18 23:50:13 +01:00
assert ( ! set_isempty ( n - > meta . names ) ) ;
2009-11-18 00:42:52 +01:00
assert ( ! n - > meta . linked ) ;
2010-01-20 05:03:52 +01:00
if ( ( r = name_sanitize ( n ) ) < 0 )
return r ;
2010-01-19 04:15:20 +01:00
n - > meta . linked = true ;
2009-11-18 00:42:52 +01:00
2010-01-20 05:03:52 +01:00
if ( ( r = name_link_names ( n , false ) ) < 0 ) {
2010-01-19 04:15:20 +01:00
char * t ;
void * state ;
2009-11-18 00:42:52 +01:00
2010-01-19 04:15:20 +01:00
/* Rollback the registered names */
SET_FOREACH ( t , n - > meta . names , state )
2010-01-20 02:12:51 +01:00
hashmap_remove_value ( n - > meta . manager - > names , t , n ) ;
2009-11-18 00:42:52 +01:00
2010-01-19 04:15:20 +01:00
n - > meta . linked = false ;
return r ;
}
2009-11-18 00:42:52 +01:00
2010-01-19 04:15:20 +01:00
if ( n - > meta . state = = NAME_STUB )
LIST_PREPEND ( Meta , n - > meta . manager - > load_queue , & n - > meta ) ;
2009-11-18 00:42:52 +01:00
2010-01-19 04:15:20 +01:00
return 0 ;
2009-11-18 00:42:52 +01:00
}
2010-01-18 23:50:13 +01:00
static void bidi_set_free ( Name * name , Set * s ) {
void * state ;
Name * other ;
assert ( name ) ;
/* Frees the set and makes sure we are dropped from the
* inverse pointers */
SET_FOREACH ( other , s , state ) {
NameDependency d ;
for ( d = 0 ; d < _NAME_DEPENDENCY_MAX ; d + + )
set_remove ( other - > meta . dependencies [ d ] , name ) ;
}
set_free ( s ) ;
}
2009-11-18 00:42:52 +01:00
void name_free ( Name * name ) {
2010-01-18 23:50:13 +01:00
NameDependency d ;
char * t ;
2009-11-18 00:42:52 +01:00
assert ( name ) ;
/* Detach from next 'bigger' objects */
if ( name - > meta . linked ) {
2010-01-19 04:15:20 +01:00
char * t ;
2010-01-18 23:50:13 +01:00
void * state ;
2009-11-18 00:42:52 +01:00
2010-01-18 23:50:13 +01:00
SET_FOREACH ( t , name - > meta . names , state )
2010-01-20 02:12:51 +01:00
hashmap_remove_value ( name - > meta . manager - > names , t , name ) ;
2009-11-18 00:42:52 +01:00
2010-01-18 23:50:13 +01:00
if ( name - > meta . state = = NAME_STUB )
LIST_REMOVE ( Meta , name - > meta . manager - > load_queue , & name - > meta ) ;
2009-11-18 00:42:52 +01:00
}
/* Free data and next 'smaller' objects */
if ( name - > meta . job )
job_free ( name - > meta . job ) ;
2010-01-18 23:50:13 +01:00
for ( d = 0 ; d < _NAME_DEPENDENCY_MAX ; d + + )
bidi_set_free ( name , name - > meta . dependencies [ d ] ) ;
2009-11-18 00:42:52 +01:00
switch ( name - > meta . type ) {
case NAME_SOCKET : {
unsigned i ;
Socket * s = SOCKET ( name ) ;
for ( i = 0 ; i < s - > n_fds ; i + + )
2010-01-19 02:56:37 +01:00
close_nointr ( s - > fds [ i ] ) ;
2009-11-18 00:42:52 +01:00
break ;
}
case NAME_DEVICE : {
Device * d = DEVICE ( name ) ;
free ( d - > sysfs ) ;
break ;
}
case NAME_MOUNT : {
Mount * m = MOUNT ( name ) ;
free ( m - > path ) ;
break ;
}
case NAME_AUTOMOUNT : {
Automount * a = AUTOMOUNT ( name ) ;
free ( a - > path ) ;
break ;
}
default :
;
}
free ( name - > meta . description ) ;
2010-01-18 23:50:13 +01:00
while ( ( t = set_steal_first ( name - > meta . names ) ) )
free ( t ) ;
set_free ( name - > meta . names ) ;
2009-11-18 00:42:52 +01:00
free ( name ) ;
}
bool name_is_ready ( Name * name ) {
assert ( name ) ;
if ( name - > meta . state ! = NAME_LOADED )
return false ;
assert ( name - > meta . type < _NAME_TYPE_MAX ) ;
switch ( name - > meta . type ) {
case NAME_SERVICE : {
Service * s = SERVICE ( name ) ;
return
s - > state = = SERVICE_RUNNING | |
s - > state = = SERVICE_RELOAD_PRE | |
s - > state = = SERVICE_RELOAD | |
s - > state = = SERVICE_RELOAD_POST ;
}
case NAME_TIMER : {
Timer * t = TIMER ( name ) ;
return
t - > state = = TIMER_WAITING | |
t - > state = = TIMER_RUNNING ;
}
case NAME_SOCKET : {
Socket * s = SOCKET ( name ) ;
return
s - > state = = SOCKET_LISTENING | |
s - > state = = SOCKET_RUNNING ;
}
case NAME_MILESTONE :
return MILESTONE ( name ) - > state = = MILESTONE_ACTIVE ;
case NAME_DEVICE :
return DEVICE ( name ) - > state = = DEVICE_AVAILABLE ;
case NAME_MOUNT :
return MOUNT ( name ) - > state = = MOUNT_MOUNTED ;
case NAME_AUTOMOUNT : {
Automount * a = AUTOMOUNT ( name ) ;
return
a - > state = = AUTOMOUNT_WAITING | |
a - > state = = AUTOMOUNT_RUNNING ;
}
case NAME_SNAPSHOT :
return SNAPSHOT ( name ) - > state = = SNAPSHOT_ACTIVE ;
case _NAME_TYPE_MAX :
case _NAME_TYPE_INVALID :
;
}
assert_not_reached ( " Unknown name type. " ) ;
return false ;
}
2010-01-21 02:59:12 +01:00
bool name_is_dead ( Name * name ) {
assert ( name ) ;
if ( name - > meta . state ! = NAME_LOADED )
return true ;
assert ( name - > meta . type < _NAME_TYPE_MAX ) ;
switch ( name - > meta . type ) {
case NAME_SERVICE : {
Service * s = SERVICE ( name ) ;
return
s - > state = = SERVICE_DEAD | |
s - > state = = SERVICE_MAINTAINANCE ;
}
case NAME_TIMER :
return TIMER ( name ) - > state = = TIMER_DEAD ;
case NAME_SOCKET : {
Socket * s = SOCKET ( name ) ;
return
s - > state = = SOCKET_DEAD | |
s - > state = = SOCKET_MAINTAINANCE ;
}
case NAME_MILESTONE :
return MILESTONE ( name ) - > state = = MILESTONE_DEAD ;
case NAME_DEVICE :
return DEVICE ( name ) - > state = = DEVICE_DEAD ;
case NAME_MOUNT : {
Mount * a = MOUNT ( name ) ;
return
a - > state = = AUTOMOUNT_DEAD | |
a - > state = = AUTOMOUNT_MAINTAINANCE ;
}
case NAME_AUTOMOUNT : {
Automount * a = AUTOMOUNT ( name ) ;
return
a - > state = = AUTOMOUNT_DEAD | |
a - > state = = AUTOMOUNT_MAINTAINANCE ;
}
case NAME_SNAPSHOT :
return SNAPSHOT ( name ) - > state = = SNAPSHOT_DEAD ;
case _NAME_TYPE_MAX :
case _NAME_TYPE_INVALID :
;
}
assert_not_reached ( " Unknown name type. " ) ;
return false ;
}
2009-11-18 00:42:52 +01:00
static int ensure_in_set ( Set * * s , void * data ) {
int r ;
assert ( s ) ;
assert ( data ) ;
if ( ! * s )
if ( ! ( * s = set_new ( trivial_hash_func , trivial_compare_func ) ) )
return - ENOMEM ;
2010-01-20 05:03:52 +01:00
if ( ( r = set_put ( * s , data ) ) < 0 )
2009-11-18 00:42:52 +01:00
if ( r ! = - EEXIST )
return r ;
return 0 ;
}
2010-01-18 23:50:13 +01:00
static int ensure_merge ( Set * * s , Set * other ) {
if ( ! other )
return 0 ;
if ( * s )
return set_merge ( * s , other ) ;
if ( ! ( * s = set_copy ( other ) ) )
return - ENOMEM ;
return 0 ;
}
2010-01-20 02:12:51 +01:00
/* FIXME: Does not rollback on failure! */
2010-01-18 23:50:13 +01:00
int name_merge ( Name * name , Name * other ) {
int r ;
NameDependency d ;
assert ( name ) ;
assert ( other ) ;
assert ( name - > meta . manager = = other - > meta . manager ) ;
2010-01-20 02:12:51 +01:00
/* This merges 'other' into 'name'. FIXME: This does not
* rollback on failure . */
2010-01-18 23:50:13 +01:00
if ( name - > meta . type ! = other - > meta . type )
return - EINVAL ;
if ( other - > meta . state ! = NAME_STUB )
return - EINVAL ;
/* Merge names */
if ( ( r = ensure_merge ( & name - > meta . names , other - > meta . names ) ) < 0 )
return r ;
/* Merge dependencies */
for ( d = 0 ; d < _NAME_DEPENDENCY_MAX ; d + + )
if ( ( r = ensure_merge ( & name - > meta . dependencies [ d ] , other - > meta . dependencies [ d ] ) ) < 0 )
return r ;
2010-01-20 02:12:51 +01:00
/* Hookup new deps and names */
if ( name - > meta . linked ) {
2010-01-20 05:03:52 +01:00
if ( ( r = name_sanitize ( name ) ) < 0 )
2010-01-19 04:15:20 +01:00
return r ;
2010-01-20 02:12:51 +01:00
if ( ( r = name_link_names ( name , true ) ) < 0 )
return r ;
}
2010-01-18 23:50:13 +01:00
return 0 ;
}
2010-01-19 00:22:34 +01:00
2010-01-20 05:03:52 +01:00
/* FIXME: Does not rollback on failure! */
static int augment ( Name * n ) {
int r ;
void * state ;
Name * other ;
assert ( n ) ;
/* Adds in the missing links to make all dependencies
* bidirectional . */
SET_FOREACH ( other , n - > meta . dependencies [ NAME_BEFORE ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_AFTER ] , n ) ) < 0 )
return r ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_AFTER ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_BEFORE ] , n ) ) < 0 )
return r ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_CONFLICTS ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_CONFLICTS ] , n ) ) < 0 )
return r ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_REQUIRES ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_REQUIRED_BY ] , n ) ) < 0 )
return r ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_REQUISITE ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_REQUIRED_BY ] , n ) ) < 0 )
return r ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_WANTS ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_WANTED_BY ] , n ) ) < 0 )
return r ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_SOFT_REQUIRES ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_WANTED_BY ] , n ) ) < 0 )
return r ;
SET_FOREACH ( other , n - > meta . dependencies [ NAME_SOFT_REQUISITE ] , state )
if ( ( r = ensure_in_set ( & other - > meta . dependencies [ NAME_WANTED_BY ] , n ) ) < 0 )
return r ;
return 0 ;
}
int name_sanitize ( Name * n ) {
NameDependency d ;
assert ( n ) ;
/* Remove loops */
for ( d = 0 ; d < _NAME_DEPENDENCY_MAX ; d + + )
set_remove ( n - > meta . dependencies [ d ] , n ) ;
return augment ( n ) ;
}
2010-01-19 00:22:34 +01:00
const char * name_id ( Name * n ) {
assert ( n ) ;
return set_first ( n - > meta . names ) ;
}
2010-01-20 02:35:46 +01:00
void name_dump ( Name * n , FILE * f , const char * prefix ) {
2010-01-19 00:22:34 +01:00
static const char * const state_table [ _NAME_STATE_MAX ] = {
2010-01-19 02:56:37 +01:00
[ NAME_STUB ] = " stub " ,
[ NAME_LOADED ] = " loaded " ,
[ NAME_FAILED ] = " failed "
} ;
static const char * const socket_state_table [ _SOCKET_STATE_MAX ] = {
[ SOCKET_DEAD ] = " dead " ,
[ SOCKET_BEFORE ] = " before " ,
[ SOCKET_START_PRE ] = " start-pre " ,
[ SOCKET_START ] = " start " ,
[ SOCKET_START_POST ] = " start-post " ,
[ SOCKET_LISTENING ] = " listening " ,
[ SOCKET_RUNNING ] = " running " ,
[ SOCKET_STOP_PRE ] = " stop-pre " ,
[ SOCKET_STOP ] = " stop " ,
[ SOCKET_STOP_POST ] = " stop-post " ,
[ SOCKET_MAINTAINANCE ] = " maintainance "
2010-01-19 00:22:34 +01:00
} ;
2010-01-20 05:03:52 +01:00
static const char * const dependency_table [ _NAME_DEPENDENCY_MAX ] = {
[ NAME_REQUIRES ] = " Requires " ,
[ NAME_SOFT_REQUIRES ] = " SoftRequires " ,
[ NAME_WANTS ] = " Wants " ,
[ NAME_REQUISITE ] = " Requisite " ,
[ NAME_SOFT_REQUISITE ] = " SoftRequisite " ,
[ NAME_REQUIRED_BY ] = " RequiredBy " ,
[ NAME_WANTED_BY ] = " WantedBy " ,
[ NAME_CONFLICTS ] = " Conflicts " ,
[ NAME_BEFORE ] = " Before " ,
[ NAME_AFTER ] = " After " ,
} ;
2010-01-19 04:15:20 +01:00
void * state ;
char * t ;
2010-01-20 05:03:52 +01:00
NameDependency d ;
2010-01-19 04:15:20 +01:00
2010-01-19 00:22:34 +01:00
assert ( n ) ;
2010-01-20 02:35:46 +01:00
if ( ! prefix )
prefix = " " ;
2010-01-19 04:15:20 +01:00
fprintf ( f ,
2010-01-20 02:35:46 +01:00
" %sName %s: \n "
" %s \t Description: %s \n "
" %s \t Name State: %s \n " ,
prefix , name_id ( n ) ,
prefix , n - > meta . description ? n - > meta . description : name_id ( n ) ,
prefix , state_table [ n - > meta . state ] ) ;
fprintf ( f , " %s \t Names: " , prefix ) ;
2010-01-19 04:15:20 +01:00
SET_FOREACH ( t , n - > meta . names , state )
fprintf ( f , " %s " , t ) ;
fprintf ( f , " \n " ) ;
2010-01-20 05:03:52 +01:00
for ( d = 0 ; d < _NAME_DEPENDENCY_MAX ; d + + ) {
void * state ;
Name * other ;
if ( set_isempty ( n - > meta . dependencies [ d ] ) )
continue ;
fprintf ( f , " %s \t %s: " , prefix , dependency_table [ d ] ) ;
SET_FOREACH ( other , n - > meta . dependencies [ d ] , state )
fprintf ( f , " %s " , name_id ( other ) ) ;
fprintf ( f , " \n " ) ;
}
2010-01-19 02:56:37 +01:00
switch ( n - > meta . type ) {
case NAME_SOCKET : {
int r ;
char * s = NULL ;
const char * t ;
if ( ( r = address_print ( & n - > socket . address , & s ) ) < 0 )
t = strerror ( - r ) ;
else
t = s ;
2010-01-20 02:12:51 +01:00
fprintf ( f ,
2010-01-20 02:35:46 +01:00
" %s \t Address: %s \n "
" %s \t Socket State: %s \n " ,
prefix , t ,
prefix , socket_state_table [ n - > socket . state ] ) ;
2010-01-20 02:12:51 +01:00
2010-01-19 02:56:37 +01:00
free ( s ) ;
break ;
}
default :
;
}
2010-01-19 00:22:34 +01:00
if ( n - > meta . job ) {
2010-01-20 02:35:46 +01:00
char * p ;
if ( asprintf ( & p , " %s \t " , prefix ) > = 0 )
prefix = p ;
else
p = NULL ;
job_dump ( n - > meta . job , f , prefix ) ;
free ( p ) ;
2010-01-19 00:22:34 +01:00
}
}
2010-01-20 05:03:52 +01:00
static int verify_type ( Name * name ) {
char * n ;
void * state ;
assert ( name ) ;
/* Checks that all aliases of this name have the same and valid type */
SET_FOREACH ( n , name - > meta . names , state ) {
NameType t ;
if ( ( t = name_type_from_string ( n ) ) = = _NAME_TYPE_INVALID )
return - EINVAL ;
if ( name - > meta . type = = _NAME_TYPE_INVALID ) {
name - > meta . type = t ;
continue ;
}
if ( name - > meta . type ! = t )
return - EINVAL ;
}
if ( name - > meta . type = = _NAME_TYPE_INVALID )
return - EINVAL ;
return 0 ;
}
static int service_load_sysv ( Service * s ) {
assert ( s ) ;
/* Load service data from SysV init scripts, preferably with
* LSB headers . . . */
return - ENOENT ;
}
static int name_load_fstab ( Name * n ) {
assert ( n ) ;
assert ( n - > meta . type = = NAME_MOUNT | | n - > meta . type = = NAME_AUTOMOUNT ) ;
/* Load mount data from /etc/fstab */
return 0 ;
}
static int snapshot_load ( Snapshot * s ) {
assert ( s ) ;
/* Load snapshots from disk */
return 0 ;
}
static int name_load_dropin ( Name * n ) {
assert ( n ) ;
/* Load dependencies from drop-in directories */
return 0 ;
}
int name_load ( Name * name ) {
int r ;
assert ( name ) ;
if ( name - > meta . state ! = NAME_STUB )
return 0 ;
if ( ( r = verify_type ( name ) ) < 0 )
return r ;
if ( name - > meta . type = = NAME_SERVICE ) {
/* Load a .service file */
if ( ( r = name_load_fragment ( name ) ) = = 0 )
goto finish ;
/* Load a classic init script */
if ( r = = - ENOENT )
if ( ( r = service_load_sysv ( SERVICE ( name ) ) ) = = 0 )
goto finish ;
} else if ( name - > meta . type = = NAME_MOUNT | |
name - > meta . type = = NAME_AUTOMOUNT ) {
if ( ( r = name_load_fstab ( name ) ) = = 0 )
goto finish ;
} else if ( name - > meta . type = = NAME_SNAPSHOT ) {
if ( ( r = snapshot_load ( SNAPSHOT ( name ) ) ) = = 0 )
goto finish ;
} else {
if ( ( r = name_load_fragment ( name ) ) = = 0 )
goto finish ;
}
name - > meta . state = NAME_FAILED ;
return r ;
finish :
if ( ( r = name_load_dropin ( name ) ) < 0 )
return r ;
if ( ( r = name_sanitize ( name ) ) < 0 )
return r ;
if ( ( r = name_link_names ( name , false ) ) < 0 )
return r ;
name - > meta . state = NAME_LOADED ;
return 0 ;
}