2010-01-23 03:52:57 +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/>.
* * */
2010-01-23 03:52:57 +03:00
# include <errno.h>
2010-01-26 06:18:44 +03:00
# include <signal.h>
2010-02-14 03:09:01 +03:00
# include <dirent.h>
# include <unistd.h>
2010-01-23 03:52:57 +03:00
2010-01-26 23:39:06 +03:00
# include "unit.h"
2010-01-23 03:52:57 +03:00
# include "service.h"
# include "load-fragment.h"
# include "load-dropin.h"
2010-01-26 06:18:44 +03:00
# include "log.h"
2010-02-14 03:09:01 +03:00
# include "strv.h"
2010-04-15 05:11:11 +04:00
# include "unit-name.h"
2010-02-14 03:09:01 +03:00
# define COMMENTS "#;\n"
# define NEWLINES "\n\r"
# define LINE_MAX 4096
2010-01-26 06:18:44 +03:00
2010-04-06 04:43:58 +04:00
static const char * const rcnd_table [ ] = {
2010-04-07 20:46:49 +04:00
" /rc0.d " , SPECIAL_RUNLEVEL0_TARGET ,
" /rc1.d " , SPECIAL_RUNLEVEL1_TARGET ,
" /rc2.d " , SPECIAL_RUNLEVEL2_TARGET ,
" /rc3.d " , SPECIAL_RUNLEVEL3_TARGET ,
" /rc4.d " , SPECIAL_RUNLEVEL4_TARGET ,
" /rc5.d " , SPECIAL_RUNLEVEL5_TARGET ,
" /rc6.d " , SPECIAL_RUNLEVEL6_TARGET ,
" /boot.d " , SPECIAL_BASIC_TARGET
2010-04-06 04:43:58 +04:00
} ;
2010-01-27 06:31:52 +03:00
static const UnitActiveState state_translation_table [ _SERVICE_STATE_MAX ] = {
2010-01-26 23:39:06 +03:00
[ SERVICE_DEAD ] = UNIT_INACTIVE ,
[ SERVICE_START_PRE ] = UNIT_ACTIVATING ,
[ SERVICE_START ] = UNIT_ACTIVATING ,
[ SERVICE_START_POST ] = UNIT_ACTIVATING ,
[ SERVICE_RUNNING ] = UNIT_ACTIVE ,
2010-04-13 04:06:27 +04:00
[ SERVICE_EXITED ] = UNIT_ACTIVE ,
2010-01-26 23:39:06 +03:00
[ SERVICE_RELOAD ] = UNIT_ACTIVE_RELOADING ,
[ SERVICE_STOP ] = UNIT_DEACTIVATING ,
[ SERVICE_STOP_SIGTERM ] = UNIT_DEACTIVATING ,
[ SERVICE_STOP_SIGKILL ] = UNIT_DEACTIVATING ,
[ SERVICE_STOP_POST ] = UNIT_DEACTIVATING ,
[ SERVICE_FINAL_SIGTERM ] = UNIT_DEACTIVATING ,
[ SERVICE_FINAL_SIGKILL ] = UNIT_DEACTIVATING ,
[ SERVICE_MAINTAINANCE ] = UNIT_INACTIVE ,
[ SERVICE_AUTO_RESTART ] = UNIT_ACTIVATING ,
2010-01-26 06:18:44 +03:00
} ;
2010-01-23 03:52:57 +03:00
2010-04-11 02:22:36 +04:00
static void service_unwatch_control_pid ( Service * s ) {
assert ( s ) ;
if ( s - > control_pid < = 0 )
return ;
unit_unwatch_pid ( UNIT ( s ) , s - > control_pid ) ;
s - > control_pid = 0 ;
}
static void service_unwatch_main_pid ( Service * s ) {
assert ( s ) ;
if ( s - > main_pid < = 0 )
return ;
unit_unwatch_pid ( UNIT ( s ) , s - > main_pid ) ;
s - > main_pid = 0 ;
}
2010-04-15 08:19:54 +04:00
static void service_close_socket_fd ( Service * s ) {
assert ( s ) ;
if ( s - > socket_fd < 0 )
return ;
close_nointr_nofail ( s - > socket_fd ) ;
s - > socket_fd = - 1 ;
}
2010-01-26 23:39:06 +03:00
static void service_done ( Unit * u ) {
Service * s = SERVICE ( u ) ;
2010-01-26 09:02:51 +03:00
assert ( s ) ;
free ( s - > pid_file ) ;
s - > pid_file = NULL ;
2010-02-14 03:09:01 +03:00
free ( s - > sysv_path ) ;
s - > sysv_path = NULL ;
2010-04-07 22:27:52 +04:00
free ( s - > sysv_runlevels ) ;
s - > sysv_runlevels = NULL ;
2010-01-26 09:02:51 +03:00
exec_context_done ( & s - > exec_context ) ;
2010-04-10 19:53:17 +04:00
exec_command_free_array ( s - > exec_command , _SERVICE_EXEC_COMMAND_MAX ) ;
2010-01-26 09:02:51 +03:00
s - > control_command = NULL ;
/* This will leak a process, but at least no memory or any of
* our resources */
2010-04-11 02:22:36 +04:00
service_unwatch_main_pid ( s ) ;
service_unwatch_control_pid ( s ) ;
2010-01-26 09:02:51 +03:00
2010-04-15 08:19:54 +04:00
service_close_socket_fd ( s ) ;
2010-01-27 06:31:52 +03:00
unit_unwatch_timer ( u , & s - > timer_watch ) ;
2010-01-26 09:02:51 +03:00
}
2010-02-14 03:09:01 +03:00
static int sysv_translate_name ( const char * name , char * * _r ) {
static const char * const table [ ] = {
" $local_fs " , SPECIAL_LOCAL_FS_TARGET ,
" $network " , SPECIAL_NETWORK_TARGET ,
" $named " , SPECIAL_NSS_LOOKUP_TARGET ,
" $portmap " , SPECIAL_RPCBIND_TARGET ,
" $remote_fs " , SPECIAL_REMOTE_FS_TARGET ,
" $syslog " , SPECIAL_SYSLOG_TARGET ,
" $time " , SPECIAL_RTC_SET_TARGET
} ;
unsigned i ;
char * r ;
for ( i = 0 ; i < ELEMENTSOF ( table ) ; i + = 2 )
if ( streq ( table [ i ] , name ) ) {
if ( ! ( r = strdup ( table [ i + 1 ] ) ) )
return - ENOMEM ;
goto finish ;
}
if ( * name = = ' $ ' )
return 0 ;
if ( asprintf ( & r , " %s.service " , name ) < 0 )
return - ENOMEM ;
finish :
if ( _r )
* _r = r ;
return 1 ;
}
static int sysv_chkconfig_order ( Service * s ) {
Meta * other ;
int r ;
assert ( s ) ;
if ( s - > sysv_start_priority < 0 )
return 0 ;
2010-04-06 04:43:58 +04:00
/* For each pair of services where at least one lacks a LSB
* header , we use the start priority value to order things . */
2010-02-14 03:09:01 +03:00
LIST_FOREACH ( units_per_type , other , UNIT ( s ) - > meta . manager - > units_per_type [ UNIT_SERVICE ] ) {
Service * t ;
UnitDependency d ;
t = ( Service * ) other ;
if ( s = = t )
continue ;
if ( t - > sysv_start_priority < 0 )
continue ;
2010-04-10 07:05:05 +04:00
/* If both units have modern headers we don't care
* about the priorities */
if ( ( ! s - > sysv_path | | s - > sysv_has_lsb ) & &
( ! t - > sysv_path | | t - > sysv_has_lsb ) )
2010-04-06 04:43:58 +04:00
continue ;
2010-02-14 03:09:01 +03:00
if ( t - > sysv_start_priority < s - > sysv_start_priority )
d = UNIT_AFTER ;
else if ( t - > sysv_start_priority > s - > sysv_start_priority )
d = UNIT_BEFORE ;
else
continue ;
/* FIXME: Maybe we should compare the name here lexicographically? */
if ( ! ( r = unit_add_dependency ( UNIT ( s ) , d , UNIT ( t ) ) ) < 0 )
return r ;
}
return 0 ;
}
static ExecCommand * exec_command_new ( const char * path , const char * arg1 ) {
ExecCommand * c ;
if ( ! ( c = new0 ( ExecCommand , 1 ) ) )
return NULL ;
if ( ! ( c - > path = strdup ( path ) ) ) {
free ( c ) ;
return NULL ;
}
if ( ! ( c - > argv = strv_new ( path , arg1 , NULL ) ) ) {
free ( c - > path ) ;
free ( c ) ;
return NULL ;
}
return c ;
}
static int sysv_exec_commands ( Service * s ) {
ExecCommand * c ;
assert ( s ) ;
assert ( s - > sysv_path ) ;
if ( ! ( c = exec_command_new ( s - > sysv_path , " start " ) ) )
return - ENOMEM ;
exec_command_append_list ( s - > exec_command + SERVICE_EXEC_START , c ) ;
if ( ! ( c = exec_command_new ( s - > sysv_path , " stop " ) ) )
return - ENOMEM ;
exec_command_append_list ( s - > exec_command + SERVICE_EXEC_STOP , c ) ;
if ( ! ( c = exec_command_new ( s - > sysv_path , " reload " ) ) )
return - ENOMEM ;
exec_command_append_list ( s - > exec_command + SERVICE_EXEC_RELOAD , c ) ;
return 0 ;
}
2010-04-06 04:43:58 +04:00
static int priority_from_rcd ( Service * s , const char * init_script ) {
char * * p ;
unsigned i ;
2010-04-07 17:39:28 +04:00
STRV_FOREACH ( p , UNIT ( s ) - > meta . manager - > sysvrcnd_path )
2010-04-06 04:43:58 +04:00
for ( i = 0 ; i < ELEMENTSOF ( rcnd_table ) ; i + = 2 ) {
char * path ;
DIR * d ;
struct dirent * de ;
if ( asprintf ( & path , " %s/%s " , * p , rcnd_table [ i ] ) < 0 )
return - ENOMEM ;
d = opendir ( path ) ;
free ( path ) ;
if ( ! d ) {
if ( errno ! = ENOENT )
log_warning ( " opendir() failed on %s: %s " , path , strerror ( errno ) ) ;
continue ;
}
while ( ( de = readdir ( d ) ) ) {
int a , b ;
if ( ignore_file ( de - > d_name ) )
continue ;
if ( de - > d_name [ 0 ] ! = ' S ' )
continue ;
if ( strlen ( de - > d_name ) < 4 )
continue ;
if ( ! streq ( de - > d_name + 3 , init_script ) )
continue ;
/* Yay, we found it! Now decode the priority */
a = undecchar ( de - > d_name [ 1 ] ) ;
b = undecchar ( de - > d_name [ 2 ] ) ;
if ( a < 0 | | b < 0 )
continue ;
s - > sysv_start_priority = a * 10 + b ;
2010-04-15 05:11:11 +04:00
log_debug ( " Determined priority %i from link farm for %s " , s - > sysv_start_priority , UNIT ( s ) - > meta . id ) ;
2010-04-06 04:43:58 +04:00
closedir ( d ) ;
return 0 ;
}
closedir ( d ) ;
}
return 0 ;
}
2010-04-10 19:53:17 +04:00
static int service_load_sysv_path ( Service * s , const char * path ) {
2010-02-14 03:09:01 +03:00
FILE * f ;
Unit * u ;
unsigned line = 0 ;
int r ;
enum {
NORMAL ,
DESCRIPTION ,
LSB ,
LSB_DESCRIPTION
} state = NORMAL ;
2010-04-06 04:43:58 +04:00
assert ( s ) ;
assert ( path ) ;
2010-02-14 03:09:01 +03:00
u = UNIT ( s ) ;
if ( ! ( f = fopen ( path , " re " ) ) ) {
r = errno = = ENOENT ? 0 : - errno ;
goto finish ;
}
s - > type = SERVICE_FORKING ;
s - > restart = SERVICE_ONCE ;
free ( s - > sysv_path ) ;
if ( ! ( s - > sysv_path = strdup ( path ) ) ) {
r = - ENOMEM ;
goto finish ;
}
while ( ! feof ( f ) ) {
char l [ LINE_MAX ] , * t ;
if ( ! fgets ( l , sizeof ( l ) , f ) ) {
if ( feof ( f ) )
break ;
r = - errno ;
log_error ( " Failed to read configuration file '%s': %s " , path , strerror ( - r ) ) ;
goto finish ;
}
line + + ;
t = strstrip ( l ) ;
if ( * t ! = ' # ' )
continue ;
if ( state = = NORMAL & & streq ( t , " ### BEGIN INIT INFO " ) ) {
state = LSB ;
2010-04-06 04:43:58 +04:00
s - > sysv_has_lsb = true ;
2010-02-14 03:09:01 +03:00
continue ;
}
if ( ( state = = LSB_DESCRIPTION | | state = = LSB ) & & streq ( t , " ### END INIT INFO " ) ) {
state = NORMAL ;
continue ;
}
t + + ;
t + = strspn ( t , WHITESPACE ) ;
if ( state = = NORMAL ) {
/* Try to parse Red Hat style chkconfig headers */
if ( startswith ( t , " chkconfig: " ) ) {
int start_priority ;
2010-04-07 22:27:52 +04:00
char runlevels [ 16 ] , * k ;
2010-02-14 03:09:01 +03:00
state = NORMAL ;
2010-04-07 22:27:52 +04:00
if ( sscanf ( t + 10 , " %15s %i %*i " ,
runlevels ,
& start_priority ) ! = 2 ) {
2010-02-14 03:09:01 +03:00
log_warning ( " [%s:%u] Failed to parse chkconfig line. Ignoring. " , path , line ) ;
continue ;
}
2010-04-07 22:27:52 +04:00
if ( start_priority < 0 | | start_priority > 99 )
2010-02-14 03:09:01 +03:00
log_warning ( " [%s:%u] Start priority out of range. Ignoring. " , path , line ) ;
2010-04-07 22:27:52 +04:00
else
s - > sysv_start_priority = start_priority ;
char_array_0 ( runlevels ) ;
k = delete_chars ( runlevels , WHITESPACE " - " ) ;
if ( k [ 0 ] ) {
char * d ;
if ( ! ( d = strdup ( k ) ) ) {
r = - ENOMEM ;
goto finish ;
}
free ( s - > sysv_runlevels ) ;
s - > sysv_runlevels = d ;
2010-02-14 03:09:01 +03:00
}
} else if ( startswith ( t , " description: " ) ) {
size_t k = strlen ( t ) ;
char * d ;
if ( t [ k - 1 ] = = ' \\ ' ) {
state = DESCRIPTION ;
t [ k - 1 ] = 0 ;
}
if ( ! ( d = strdup ( strstrip ( t + 12 ) ) ) ) {
r = - ENOMEM ;
goto finish ;
}
free ( u - > meta . description ) ;
u - > meta . description = d ;
} else if ( startswith ( t , " pidfile: " ) ) {
char * fn ;
state = NORMAL ;
fn = strstrip ( t + 8 ) ;
if ( ! path_is_absolute ( fn ) ) {
log_warning ( " [%s:%u] PID file not absolute. Ignoring. " , path , line ) ;
continue ;
}
if ( ! ( fn = strdup ( fn ) ) ) {
r = - ENOMEM ;
goto finish ;
}
free ( s - > pid_file ) ;
s - > pid_file = fn ;
}
} else if ( state = = DESCRIPTION ) {
/* Try to parse Red Hat style description
* continuation */
size_t k = strlen ( t ) ;
char * d ;
if ( t [ k - 1 ] = = ' \\ ' )
t [ k - 1 ] = 0 ;
else
state = NORMAL ;
assert ( u - > meta . description ) ;
if ( asprintf ( & d , " %s %s " , u - > meta . description , strstrip ( t ) ) < 0 ) {
r = - ENOMEM ;
goto finish ;
}
free ( u - > meta . description ) ;
u - > meta . description = d ;
} else if ( state = = LSB | | state = = LSB_DESCRIPTION ) {
if ( startswith ( t , " Provides: " ) ) {
char * i , * w ;
size_t z ;
state = LSB ;
FOREACH_WORD ( w , z , t + 9 , i ) {
char * n , * m ;
if ( ! ( n = strndup ( w , z ) ) ) {
r = - ENOMEM ;
goto finish ;
}
r = sysv_translate_name ( n , & m ) ;
free ( n ) ;
if ( r < 0 )
goto finish ;
if ( r = = 0 )
continue ;
2010-04-01 23:30:40 +04:00
if ( unit_name_to_type ( m ) = = UNIT_SERVICE )
r = unit_add_name ( u , m ) ;
else {
2010-04-15 05:11:11 +04:00
if ( ( r = unit_add_dependency_by_name_inverse ( u , UNIT_REQUIRES , m , NULL ) ) > = 0 )
r = unit_add_dependency_by_name ( u , UNIT_BEFORE , m , NULL ) ;
2010-04-01 23:30:40 +04:00
}
2010-02-14 03:09:01 +03:00
free ( m ) ;
if ( r < 0 )
goto finish ;
}
} else if ( startswith ( t , " Required-Start: " ) | |
startswith ( t , " Should-Start: " ) ) {
char * i , * w ;
size_t z ;
state = LSB ;
FOREACH_WORD ( w , z , strchr ( t , ' : ' ) + 1 , i ) {
char * n , * m ;
if ( ! ( n = strndup ( w , z ) ) ) {
r = - ENOMEM ;
goto finish ;
}
r = sysv_translate_name ( n , & m ) ;
free ( n ) ;
if ( r < 0 )
goto finish ;
if ( r = = 0 )
continue ;
2010-04-15 05:11:11 +04:00
r = unit_add_dependency_by_name ( u , UNIT_AFTER , m , NULL ) ;
2010-02-14 03:09:01 +03:00
free ( m ) ;
if ( r < 0 )
goto finish ;
}
2010-04-07 22:27:52 +04:00
} else if ( startswith ( t , " Default-Start: " ) ) {
char * k , * d ;
state = LSB ;
k = delete_chars ( t + 14 , WHITESPACE " - " ) ;
if ( k [ 0 ] ! = 0 ) {
if ( ! ( d = strdup ( k ) ) ) {
r = - ENOMEM ;
goto finish ;
}
free ( s - > sysv_runlevels ) ;
s - > sysv_runlevels = d ;
}
2010-02-14 03:09:01 +03:00
} else if ( startswith ( t , " Description: " ) ) {
char * d ;
state = LSB_DESCRIPTION ;
if ( ! ( d = strdup ( strstrip ( t + 12 ) ) ) ) {
r = - ENOMEM ;
goto finish ;
}
free ( u - > meta . description ) ;
u - > meta . description = d ;
2010-04-07 22:27:52 +04:00
} else if ( startswith ( t , " Short-Description: " ) & &
! u - > meta . description ) {
2010-02-14 03:09:01 +03:00
char * d ;
/* We use the short description only
* if no long description is set . */
state = LSB ;
if ( ! ( d = strdup ( strstrip ( t + 18 ) ) ) ) {
r = - ENOMEM ;
goto finish ;
}
u - > meta . description = d ;
} else if ( state = = LSB_DESCRIPTION ) {
if ( startswith ( l , " # \t " ) | | startswith ( l , " # " ) ) {
char * d ;
assert ( u - > meta . description ) ;
if ( asprintf ( & d , " %s %s " , u - > meta . description , t ) < 0 ) {
r = - ENOMEM ;
goto finish ;
}
free ( u - > meta . description ) ;
u - > meta . description = d ;
} else
state = LSB ;
}
}
}
2010-04-06 04:43:58 +04:00
/* If init scripts have no LSB header, then we enforce the
* ordering via the chkconfig priorities . We try to determine
* a priority for * all * init scripts here , since they are
* needed as soon as at least one non - LSB script is used . */
2010-02-14 03:09:01 +03:00
2010-04-06 04:43:58 +04:00
if ( s - > sysv_start_priority < 0 ) {
2010-04-15 05:11:11 +04:00
log_debug ( " %s has no chkconfig header, trying to determine SysV priority from link farm. " , u - > meta . id ) ;
2010-04-06 04:43:58 +04:00
if ( ( r = priority_from_rcd ( s , file_name_from_path ( path ) ) ) < 0 )
2010-02-14 03:09:01 +03:00
goto finish ;
2010-04-06 04:43:58 +04:00
if ( s - > sysv_start_priority < 0 )
2010-04-15 05:11:11 +04:00
log_warning ( " %s has neither a chkconfig header nor a directory link, cannot order unit! " , u - > meta . id ) ;
2010-04-06 04:43:58 +04:00
}
2010-02-14 03:09:01 +03:00
if ( ( r = sysv_exec_commands ( s ) ) < 0 )
goto finish ;
2010-04-07 22:28:44 +04:00
if ( ! s - > sysv_runlevels | | chars_intersect ( " 12345 " , s - > sysv_runlevels ) ) {
/* If there a runlevels configured for this service
* but none of the standard ones , then we assume this
* is some special kind of service ( which might be
* needed for early boot ) and don ' t create any links
* to it . */
2010-04-15 05:11:11 +04:00
if ( ( r = unit_add_dependency_by_name ( u , UNIT_REQUIRES , SPECIAL_BASIC_TARGET , NULL ) ) < 0 | |
( r = unit_add_dependency_by_name ( u , UNIT_AFTER , SPECIAL_BASIC_TARGET , NULL ) ) < 0 )
2010-04-07 22:28:44 +04:00
goto finish ;
}
2010-04-05 00:51:16 +04:00
2010-04-13 04:06:27 +04:00
/* Special setting for all SysV services */
s - > valid_no_process = true ;
2010-04-13 20:39:37 +04:00
/* Don't timeout special services during boot (like fsck) */
2010-04-13 22:27:10 +04:00
if ( s - > sysv_runlevels & & ! chars_intersect ( " 12345 " , s - > sysv_runlevels ) )
2010-04-13 20:39:37 +04:00
s - > timeout_usec = - 1 ;
2010-04-10 19:53:17 +04:00
u - > meta . load_state = UNIT_LOADED ;
2010-04-06 04:43:58 +04:00
r = 0 ;
2010-02-14 03:09:01 +03:00
finish :
if ( f )
fclose ( f ) ;
return r ;
}
2010-04-10 19:53:17 +04:00
static int service_load_sysv_name ( Service * s , const char * name ) {
2010-02-14 03:09:01 +03:00
char * * p ;
assert ( s ) ;
assert ( name ) ;
STRV_FOREACH ( p , UNIT ( s ) - > meta . manager - > sysvinit_path ) {
char * path ;
int r ;
if ( asprintf ( & path , " %s/%s " , * p , name ) < 0 )
return - ENOMEM ;
assert ( endswith ( path , " .service " ) ) ;
path [ strlen ( path ) - 8 ] = 0 ;
2010-04-10 19:53:17 +04:00
r = service_load_sysv_path ( s , path ) ;
2010-02-14 03:09:01 +03:00
free ( path ) ;
2010-04-06 04:43:58 +04:00
if ( r < 0 )
2010-02-14 03:09:01 +03:00
return r ;
2010-04-06 04:43:58 +04:00
2010-04-10 19:53:17 +04:00
if ( ( UNIT ( s ) - > meta . load_state ! = UNIT_STUB ) )
2010-04-06 04:43:58 +04:00
break ;
2010-02-14 03:09:01 +03:00
}
return 0 ;
}
2010-04-10 19:53:17 +04:00
static int service_load_sysv ( Service * s ) {
2010-02-14 03:09:01 +03:00
const char * t ;
Iterator i ;
int r ;
2010-01-23 03:52:57 +03:00
assert ( s ) ;
/* Load service data from SysV init scripts, preferably with
* LSB headers . . . */
2010-02-14 03:09:01 +03:00
if ( strv_isempty ( UNIT ( s ) - > meta . manager - > sysvinit_path ) )
return 0 ;
2010-04-15 05:11:11 +04:00
if ( ( t = UNIT ( s ) - > meta . id ) )
2010-04-10 19:53:17 +04:00
if ( ( r = service_load_sysv_name ( s , t ) ) < 0 )
2010-02-14 03:09:01 +03:00
return r ;
2010-04-10 19:53:17 +04:00
if ( UNIT ( s ) - > meta . load_state = = UNIT_STUB )
2010-04-06 04:43:58 +04:00
SET_FOREACH ( t , UNIT ( s ) - > meta . names , i ) {
2010-04-15 05:11:11 +04:00
if ( t = = UNIT ( s ) - > meta . id )
2010-04-10 19:53:17 +04:00
continue ;
if ( ( r = = service_load_sysv_name ( s , t ) ) < 0 )
2010-04-06 04:43:58 +04:00
return r ;
2010-04-10 19:53:17 +04:00
if ( UNIT ( s ) - > meta . load_state ! = UNIT_STUB )
2010-04-06 04:43:58 +04:00
break ;
}
2010-02-14 03:09:01 +03:00
return 0 ;
2010-01-23 03:52:57 +03:00
}
2010-04-10 19:53:17 +04:00
static void service_init ( Unit * u ) {
2010-01-26 23:39:06 +03:00
Service * s = SERVICE ( u ) ;
2010-01-23 03:52:57 +03:00
2010-04-10 19:53:17 +04:00
assert ( u ) ;
assert ( u - > meta . load_state = = UNIT_STUB ) ;
2010-01-26 06:18:44 +03:00
s - > type = 0 ;
s - > restart = 0 ;
s - > timeout_usec = DEFAULT_TIMEOUT_USEC ;
s - > restart_usec = DEFAULT_RESTART_USEC ;
exec_context_init ( & s - > exec_context ) ;
2010-01-27 06:31:52 +03:00
s - > timer_watch . type = WATCH_INVALID ;
2010-01-26 06:18:44 +03:00
s - > state = SERVICE_DEAD ;
2010-01-23 03:52:57 +03:00
2010-02-14 03:09:01 +03:00
s - > sysv_start_priority = - 1 ;
2010-02-15 00:43:08 +03:00
s - > permissions_start_only = false ;
s - > root_directory_start_only = false ;
2010-04-08 02:52:14 +04:00
s - > valid_no_process = false ;
s - > kill_mode = 0 ;
2010-04-06 04:43:58 +04:00
s - > sysv_has_lsb = false ;
2010-04-08 02:52:14 +04:00
s - > main_pid = s - > control_pid = 0 ;
s - > main_pid_known = false ;
s - > failure = false ;
2010-04-06 04:43:58 +04:00
2010-04-15 08:19:54 +04:00
s - > socket_fd = - 1 ;
2010-01-29 06:42:57 +03:00
RATELIMIT_INIT ( s - > ratelimit , 10 * USEC_PER_SEC , 5 ) ;
2010-04-10 19:53:17 +04:00
}
2010-04-13 06:00:03 +04:00
static int service_verify ( Service * s ) {
assert ( s ) ;
if ( UNIT ( s ) - > meta . load_state ! = UNIT_LOADED )
return 0 ;
if ( ! s - > exec_command [ SERVICE_EXEC_START ] ) {
2010-04-15 05:11:11 +04:00
log_error ( " %s lacks ExecStart setting. Refusing. " , UNIT ( s ) - > meta . id ) ;
2010-04-13 06:00:03 +04:00
return - EINVAL ;
}
return 0 ;
}
2010-04-10 19:53:17 +04:00
static int service_load ( Unit * u ) {
int r ;
Service * s = SERVICE ( u ) ;
assert ( s ) ;
2010-01-29 06:42:57 +03:00
2010-01-23 03:52:57 +03:00
/* Load a .service file */
2010-04-10 19:53:17 +04:00
if ( ( r = unit_load_fragment ( u ) ) < 0 )
2010-01-23 03:52:57 +03:00
return r ;
2010-04-01 23:30:40 +04:00
/* Load a classic init script as a fallback, if we couldn't find anything */
2010-04-10 19:53:17 +04:00
if ( u - > meta . load_state = = UNIT_STUB )
if ( ( r = service_load_sysv ( s ) ) < 0 )
2010-04-06 04:43:58 +04:00
return r ;
2010-01-28 04:44:47 +03:00
2010-04-06 04:43:58 +04:00
/* Still nothing found? Then let's give up */
2010-04-10 19:53:17 +04:00
if ( u - > meta . load_state = = UNIT_STUB )
2010-04-06 04:43:58 +04:00
return - ENOENT ;
2010-01-26 06:18:44 +03:00
2010-04-06 04:43:58 +04:00
/* We were able to load something, then let's add in the
* dropin directories . */
if ( ( r = unit_load_dropin ( unit_follow_merge ( u ) ) ) < 0 )
2010-03-31 18:29:55 +04:00
return r ;
2010-04-06 04:43:58 +04:00
/* This is a new unit? Then let's add in some extras */
2010-04-10 19:53:17 +04:00
if ( u - > meta . load_state = = UNIT_LOADED ) {
2010-04-06 04:43:58 +04:00
if ( ( r = unit_add_exec_dependencies ( u , & s - > exec_context ) ) < 0 )
return r ;
if ( ( r = unit_add_default_cgroup ( u ) ) < 0 )
return r ;
if ( ( r = sysv_chkconfig_order ( s ) ) < 0 )
return r ;
2010-03-31 18:29:55 +04:00
}
2010-04-13 06:00:03 +04:00
return service_verify ( s ) ;
2010-01-26 06:18:44 +03:00
}
2010-01-26 23:39:06 +03:00
static void service_dump ( Unit * u , FILE * f , const char * prefix ) {
2010-01-23 03:52:57 +03:00
ServiceExecCommand c ;
2010-01-26 23:39:06 +03:00
Service * s = SERVICE ( u ) ;
2010-02-03 16:21:48 +03:00
const char * prefix2 ;
char * p2 ;
2010-01-23 03:52:57 +03:00
assert ( s ) ;
2010-02-03 16:21:48 +03:00
p2 = strappend ( prefix , " \t " ) ;
prefix2 = p2 ? p2 : prefix ;
2010-01-26 09:02:51 +03:00
2010-01-23 03:52:57 +03:00
fprintf ( f ,
2010-02-15 00:43:08 +03:00
" %sService State: %s \n "
" %sPermissionsStartOnly: %s \n "
2010-03-31 18:29:55 +04:00
" %sRootDirectoryStartOnly: %s \n "
2010-04-02 00:09:03 +04:00
" %sValidNoProcess: %s \n "
2010-04-08 02:52:14 +04:00
" %sKillMode: %s \n "
2010-04-02 00:09:03 +04:00
" %sType: %s \n " ,
2010-02-15 00:43:08 +03:00
prefix , service_state_to_string ( s - > state ) ,
prefix , yes_no ( s - > permissions_start_only ) ,
2010-03-31 18:29:55 +04:00
prefix , yes_no ( s - > root_directory_start_only ) ,
2010-04-02 00:09:03 +04:00
prefix , yes_no ( s - > valid_no_process ) ,
2010-04-08 02:52:14 +04:00
prefix , kill_mode_to_string ( s - > kill_mode ) ,
2010-04-02 00:09:03 +04:00
prefix , service_type_to_string ( s - > type ) ) ;
2010-01-23 03:52:57 +03:00
2010-04-08 05:48:27 +04:00
if ( s - > control_pid > 0 )
fprintf ( f ,
" %sControl PID: %llu \n " ,
prefix , ( unsigned long long ) s - > control_pid ) ;
if ( s - > main_pid > 0 )
fprintf ( f ,
" %sMain PID: %llu \n " ,
prefix , ( unsigned long long ) s - > main_pid ) ;
2010-01-26 06:18:44 +03:00
if ( s - > pid_file )
fprintf ( f ,
" %sPIDFile: %s \n " ,
prefix , s - > pid_file ) ;
2010-01-23 03:52:57 +03:00
exec_context_dump ( & s - > exec_context , f , prefix ) ;
2010-04-10 19:53:17 +04:00
for ( c = 0 ; c < _SERVICE_EXEC_COMMAND_MAX ; c + + ) {
2010-01-23 03:52:57 +03:00
2010-01-26 09:02:51 +03:00
if ( ! s - > exec_command [ c ] )
continue ;
fprintf ( f , " %s→ %s: \n " ,
2010-01-30 03:55:42 +03:00
prefix , service_exec_command_to_string ( c ) ) ;
2010-01-26 09:02:51 +03:00
exec_command_dump_list ( s - > exec_command [ c ] , f , prefix2 ) ;
2010-01-23 03:52:57 +03:00
}
2010-01-26 09:02:51 +03:00
2010-02-14 03:09:01 +03:00
if ( s - > sysv_path )
fprintf ( f ,
2010-04-06 04:43:58 +04:00
" %sSysV Init Script Path: %s \n "
" %sSysV Init Script has LSB Header: %s \n " ,
prefix , s - > sysv_path ,
prefix , yes_no ( s - > sysv_has_lsb ) ) ;
2010-02-14 03:09:01 +03:00
if ( s - > sysv_start_priority > = 0 )
fprintf ( f ,
2010-04-06 04:43:58 +04:00
" %sSysVStartPriority: %i \n " ,
2010-02-14 03:09:01 +03:00
prefix , s - > sysv_start_priority ) ;
2010-04-07 22:27:52 +04:00
if ( s - > sysv_runlevels )
fprintf ( f , " %sSysVRunLevels: %s \n " ,
prefix , s - > sysv_runlevels ) ;
2010-04-06 04:43:58 +04:00
2010-02-03 16:21:48 +03:00
free ( p2 ) ;
2010-01-23 03:52:57 +03:00
}
2010-01-26 06:18:44 +03:00
static int service_load_pid_file ( Service * s ) {
char * k ;
unsigned long p ;
int r ;
assert ( s ) ;
if ( s - > main_pid_known )
return 0 ;
2010-04-11 02:22:36 +04:00
assert ( s - > main_pid < = 0 ) ;
2010-01-26 06:18:44 +03:00
if ( ! s - > pid_file )
return - ENOENT ;
if ( ( r = read_one_line_file ( s - > pid_file , & k ) ) < 0 )
return r ;
if ( ( r = safe_atolu ( k , & p ) ) < 0 ) {
free ( k ) ;
return r ;
}
if ( ( unsigned long ) ( pid_t ) p ! = p )
return - ERANGE ;
2010-04-08 05:48:52 +04:00
if ( kill ( ( pid_t ) p , 0 ) < 0 & & errno ! = EPERM ) {
log_warning ( " PID %llu read from file %s does not exist. Your service or init script might be broken. " ,
( unsigned long long ) p , s - > pid_file ) ;
return - ESRCH ;
}
2010-04-08 06:05:56 +04:00
if ( ( r = unit_watch_pid ( UNIT ( s ) , ( pid_t ) p ) ) < 0 )
/* FIXME: we need to do something here */
return r ;
2010-04-08 05:48:52 +04:00
s - > main_pid = ( pid_t ) p ;
2010-01-26 06:18:44 +03:00
s - > main_pid_known = true ;
return 0 ;
}
2010-01-28 00:41:57 +03:00
static int service_get_sockets ( Service * s , Set * * _set ) {
Set * set ;
2010-01-27 08:19:48 +03:00
Iterator i ;
char * t ;
2010-01-28 00:41:57 +03:00
int r ;
2010-01-27 08:19:48 +03:00
assert ( s ) ;
2010-01-28 00:41:57 +03:00
assert ( _set ) ;
/* Collects all Socket objects that belong to this
* service . Note that a service might have multiple sockets
* via multiple names . */
if ( ! ( set = set_new ( NULL , NULL ) ) )
return - ENOMEM ;
2010-01-27 08:19:48 +03:00
SET_FOREACH ( t , UNIT ( s ) - > meta . names , i ) {
char * k ;
Unit * p ;
/* Look for all socket objects that go by any of our
* units and collect their fds */
2010-01-28 00:41:57 +03:00
if ( ! ( k = unit_name_change_suffix ( t , " .socket " ) ) ) {
r = - ENOMEM ;
goto fail ;
}
2010-01-27 08:19:48 +03:00
p = manager_get_unit ( UNIT ( s ) - > meta . manager , k ) ;
free ( k ) ;
2010-01-28 00:41:57 +03:00
if ( ! p ) continue ;
2010-01-27 08:19:48 +03:00
2010-01-28 00:41:57 +03:00
if ( ( r = set_put ( set , p ) ) < 0 )
goto fail ;
2010-01-27 08:19:48 +03:00
}
2010-01-28 00:41:57 +03:00
* _set = set ;
return 0 ;
fail :
set_free ( set ) ;
return r ;
}
2010-04-10 19:53:17 +04:00
static int service_notify_sockets_dead ( Service * s ) {
2010-01-28 00:41:57 +03:00
Iterator i ;
Set * set ;
2010-02-03 16:21:48 +03:00
Socket * sock ;
2010-01-28 00:41:57 +03:00
int r ;
assert ( s ) ;
/* Notifies all our sockets when we die */
if ( ( r = service_get_sockets ( s , & set ) ) < 0 )
return r ;
2010-02-03 16:21:48 +03:00
SET_FOREACH ( sock , set , i )
socket_notify_service_dead ( sock ) ;
2010-01-28 00:41:57 +03:00
set_free ( set ) ;
2010-01-27 08:19:48 +03:00
return 0 ;
}
2010-01-26 06:18:44 +03:00
static void service_set_state ( Service * s , ServiceState state ) {
ServiceState old_state ;
2010-01-23 03:52:57 +03:00
assert ( s ) ;
2010-01-26 06:18:44 +03:00
old_state = s - > state ;
2010-01-23 03:52:57 +03:00
s - > state = state ;
2010-01-26 06:18:44 +03:00
if ( state ! = SERVICE_START_PRE & &
state ! = SERVICE_START & &
state ! = SERVICE_START_POST & &
state ! = SERVICE_RELOAD & &
state ! = SERVICE_STOP & &
state ! = SERVICE_STOP_SIGTERM & &
state ! = SERVICE_STOP_SIGKILL & &
state ! = SERVICE_STOP_POST & &
state ! = SERVICE_FINAL_SIGTERM & &
state ! = SERVICE_FINAL_SIGKILL & &
state ! = SERVICE_AUTO_RESTART )
2010-01-27 06:31:52 +03:00
unit_unwatch_timer ( UNIT ( s ) , & s - > timer_watch ) ;
2010-01-26 06:18:44 +03:00
2010-01-30 04:07:35 +03:00
if ( state ! = SERVICE_START & &
state ! = SERVICE_START_POST & &
2010-01-26 06:18:44 +03:00
state ! = SERVICE_RUNNING & &
state ! = SERVICE_RELOAD & &
state ! = SERVICE_STOP & &
state ! = SERVICE_STOP_SIGTERM & &
state ! = SERVICE_STOP_SIGKILL )
2010-04-11 02:22:36 +04:00
service_unwatch_main_pid ( s ) ;
2010-01-26 06:18:44 +03:00
if ( state ! = SERVICE_START_PRE & &
state ! = SERVICE_START & &
state ! = SERVICE_START_POST & &
state ! = SERVICE_RELOAD & &
state ! = SERVICE_STOP & &
state ! = SERVICE_STOP_SIGTERM & &
state ! = SERVICE_STOP_SIGKILL & &
state ! = SERVICE_STOP_POST & &
state ! = SERVICE_FINAL_SIGTERM & &
2010-04-10 19:53:17 +04:00
state ! = SERVICE_FINAL_SIGKILL ) {
2010-04-11 02:22:36 +04:00
service_unwatch_control_pid ( s ) ;
2010-01-26 06:18:44 +03:00
s - > control_command = NULL ;
2010-04-10 19:53:17 +04:00
}
2010-01-26 06:18:44 +03:00
2010-01-27 08:19:48 +03:00
if ( state = = SERVICE_DEAD | |
state = = SERVICE_STOP | |
state = = SERVICE_STOP_SIGTERM | |
state = = SERVICE_STOP_SIGKILL | |
state = = SERVICE_STOP_POST | |
state = = SERVICE_FINAL_SIGTERM | |
state = = SERVICE_FINAL_SIGKILL | |
state = = SERVICE_MAINTAINANCE | |
state = = SERVICE_AUTO_RESTART )
2010-04-10 19:53:17 +04:00
service_notify_sockets_dead ( s ) ;
2010-01-27 08:19:48 +03:00
2010-04-15 08:19:54 +04:00
if ( state ! = SERVICE_START_PRE & &
state ! = SERVICE_START & &
! ( state = = SERVICE_DEAD & & UNIT ( s ) - > meta . job ) )
service_close_socket_fd ( s ) ;
2010-04-10 19:53:17 +04:00
if ( old_state ! = state )
2010-04-15 05:11:11 +04:00
log_debug ( " %s changed %s → %s " , UNIT ( s ) - > meta . id , service_state_to_string ( old_state ) , service_state_to_string ( state ) ) ;
2010-01-27 06:31:52 +03:00
unit_notify ( UNIT ( s ) , state_translation_table [ old_state ] , state_translation_table [ state ] ) ;
2010-01-26 06:18:44 +03:00
}
2010-01-26 09:02:51 +03:00
static int service_collect_fds ( Service * s , int * * fds , unsigned * n_fds ) {
Iterator i ;
int r ;
int * rfds = NULL ;
unsigned rn_fds = 0 ;
2010-01-28 00:41:57 +03:00
Set * set ;
2010-02-03 16:21:48 +03:00
Socket * sock ;
2010-01-26 09:02:51 +03:00
assert ( s ) ;
assert ( fds ) ;
assert ( n_fds ) ;
2010-01-28 00:41:57 +03:00
if ( ( r = service_get_sockets ( s , & set ) ) < 0 )
return r ;
2010-02-03 16:21:48 +03:00
SET_FOREACH ( sock , set , i ) {
2010-01-26 09:02:51 +03:00
int * cfds ;
unsigned cn_fds ;
2010-02-03 16:21:48 +03:00
if ( ( r = socket_collect_fds ( sock , & cfds , & cn_fds ) ) < 0 )
2010-01-26 09:02:51 +03:00
goto fail ;
if ( ! cfds )
continue ;
if ( ! rfds ) {
rfds = cfds ;
rn_fds = cn_fds ;
} else {
int * t ;
if ( ! ( t = new ( int , rn_fds + cn_fds ) ) ) {
free ( cfds ) ;
r = - ENOMEM ;
goto fail ;
}
memcpy ( t , rfds , rn_fds ) ;
memcpy ( t + rn_fds , cfds , cn_fds ) ;
free ( rfds ) ;
free ( cfds ) ;
rfds = t ;
rn_fds = rn_fds + cn_fds ;
}
}
* fds = rfds ;
* n_fds = rn_fds ;
2010-01-28 00:41:57 +03:00
set_free ( set ) ;
2010-01-26 09:02:51 +03:00
return 0 ;
fail :
2010-01-28 00:41:57 +03:00
set_free ( set ) ;
2010-01-26 09:02:51 +03:00
free ( rfds ) ;
2010-01-28 00:41:57 +03:00
2010-01-26 09:02:51 +03:00
return r ;
}
2010-02-15 00:43:08 +03:00
static int service_spawn (
Service * s ,
ExecCommand * c ,
bool timeout ,
bool pass_fds ,
bool apply_permissions ,
bool apply_chroot ,
pid_t * _pid ) {
2010-01-26 06:18:44 +03:00
pid_t pid ;
int r ;
2010-01-26 09:02:51 +03:00
int * fds = NULL ;
unsigned n_fds = 0 ;
2010-04-15 05:11:11 +04:00
char * * argv ;
2010-01-26 06:18:44 +03:00
assert ( s ) ;
assert ( c ) ;
assert ( _pid ) ;
2010-04-15 08:19:54 +04:00
if ( pass_fds ) {
if ( s - > socket_fd > = 0 ) {
fds = & s - > socket_fd ;
n_fds = 1 ;
} else if ( ( r = service_collect_fds ( s , & fds , & n_fds ) ) < 0 )
2010-01-26 09:02:51 +03:00
goto fail ;
2010-04-15 08:19:54 +04:00
}
2010-01-26 09:02:51 +03:00
2010-01-26 06:18:44 +03:00
if ( timeout ) {
2010-01-27 06:31:52 +03:00
if ( ( r = unit_watch_timer ( UNIT ( s ) , s - > timeout_usec , & s - > timer_watch ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
} else
2010-01-27 06:31:52 +03:00
unit_unwatch_timer ( UNIT ( s ) , & s - > timer_watch ) ;
2010-01-26 06:18:44 +03:00
2010-04-15 05:11:11 +04:00
if ( ! ( argv = unit_full_printf_strv ( UNIT ( s ) , c - > argv ) ) ) {
r = - ENOMEM ;
goto fail ;
}
r = exec_spawn ( c ,
argv ,
& s - > exec_context ,
fds , n_fds ,
apply_permissions ,
apply_chroot ,
UNIT ( s ) - > meta . manager - > confirm_spawn ,
UNIT ( s ) - > meta . cgroup_bondings ,
& pid ) ;
strv_free ( argv ) ;
if ( r < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
2010-04-15 08:19:54 +04:00
if ( fds ) {
if ( s - > socket_fd > = 0 )
service_close_socket_fd ( s ) ;
else
free ( fds ) ;
}
2010-01-26 23:39:06 +03:00
if ( ( r = unit_watch_pid ( UNIT ( s ) , pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
/* FIXME: we need to do something here */
goto fail ;
* _pid = pid ;
2010-01-23 03:52:57 +03:00
return 0 ;
2010-01-26 06:18:44 +03:00
fail :
2010-01-26 09:02:51 +03:00
free ( fds ) ;
2010-01-26 06:18:44 +03:00
if ( timeout )
2010-01-27 06:31:52 +03:00
unit_unwatch_timer ( UNIT ( s ) , & s - > timer_watch ) ;
2010-01-26 06:18:44 +03:00
return r ;
}
2010-04-13 04:06:27 +04:00
static int main_pid_good ( Service * s ) {
assert ( s ) ;
/* Returns 0 if the pid is dead, 1 if it is good, -1 if we
* don ' t know */
/* If we know the pid file, then lets just check if it is
* still valid */
if ( s - > main_pid_known )
return s - > main_pid > 0 ;
/* We don't know the pid */
return - EAGAIN ;
}
static int control_pid_good ( Service * s ) {
assert ( s ) ;
return s - > control_pid > 0 ;
}
static int cgroup_good ( Service * s ) {
int r ;
assert ( s ) ;
if ( s - > valid_no_process )
return - EAGAIN ;
if ( ( r = cgroup_bonding_is_empty_list ( UNIT ( s ) - > meta . cgroup_bondings ) ) < 0 )
return r ;
return ! r ;
}
2010-01-26 06:18:44 +03:00
static void service_enter_dead ( Service * s , bool success , bool allow_restart ) {
int r ;
assert ( s ) ;
if ( ! success )
s - > failure = true ;
if ( allow_restart & &
( s - > restart = = SERVICE_RESTART_ALWAYS | |
( s - > restart = = SERVICE_RESTART_ON_SUCCESS & & ! s - > failure ) ) ) {
2010-01-27 06:31:52 +03:00
if ( ( r = unit_watch_timer ( UNIT ( s ) , s - > restart_usec , & s - > timer_watch ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
service_set_state ( s , SERVICE_AUTO_RESTART ) ;
} else
service_set_state ( s , s - > failure ? SERVICE_MAINTAINANCE : SERVICE_DEAD ) ;
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run install restart timer: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
service_enter_dead ( s , false , false ) ;
}
static void service_enter_signal ( Service * s , ServiceState state , bool success ) ;
static void service_enter_stop_post ( Service * s , bool success ) {
int r ;
assert ( s ) ;
if ( ! success )
s - > failure = true ;
2010-04-11 02:22:36 +04:00
service_unwatch_control_pid ( s ) ;
2010-04-13 04:06:27 +04:00
if ( ( s - > control_command = s - > exec_command [ SERVICE_EXEC_STOP_POST ] ) ) {
2010-02-15 00:43:08 +03:00
if ( ( r = service_spawn ( s ,
s - > control_command ,
true ,
false ,
! s - > permissions_start_only ,
! s - > root_directory_start_only ,
& s - > control_pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
2010-01-28 03:59:41 +03:00
2010-04-13 04:06:27 +04:00
service_set_state ( s , SERVICE_STOP_POST ) ;
} else
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , true ) ;
2010-01-26 06:18:44 +03:00
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run stop-post executable: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
}
static void service_enter_signal ( Service * s , ServiceState state , bool success ) {
int r ;
bool sent = false ;
assert ( s ) ;
if ( ! success )
s - > failure = true ;
2010-04-13 04:06:27 +04:00
if ( s - > kill_mode ! = KILL_NONE ) {
int sig = ( state = = SERVICE_STOP_SIGTERM | | state = = SERVICE_FINAL_SIGTERM ) ? SIGTERM : SIGKILL ;
2010-01-26 06:18:44 +03:00
2010-04-08 02:52:14 +04:00
if ( s - > kill_mode = = KILL_CONTROL_GROUP ) {
2010-01-26 06:18:44 +03:00
2010-04-08 02:52:14 +04:00
if ( ( r = cgroup_bonding_kill_list ( UNIT ( s ) - > meta . cgroup_bondings , sig ) ) < 0 ) {
if ( r ! = - EAGAIN & & r ! = - ESRCH )
goto fail ;
} else
2010-01-26 06:18:44 +03:00
sent = true ;
}
2010-04-08 02:52:14 +04:00
if ( ! sent ) {
r = 0 ;
2010-04-13 04:06:27 +04:00
2010-04-08 02:52:14 +04:00
if ( s - > main_pid > 0 ) {
if ( kill ( s - > kill_mode = = KILL_PROCESS ? s - > main_pid : - s - > main_pid , sig ) < 0 & & errno ! = ESRCH )
r = - errno ;
else
sent = true ;
}
if ( s - > control_pid > 0 ) {
if ( kill ( s - > kill_mode = = KILL_PROCESS ? s - > control_pid : - s - > control_pid , sig ) < 0 & & errno ! = ESRCH )
r = - errno ;
else
sent = true ;
}
if ( r < 0 )
goto fail ;
}
2010-01-28 03:59:41 +03:00
}
2010-01-26 06:18:44 +03:00
2010-04-13 04:06:27 +04:00
if ( sent ) {
if ( ( r = unit_watch_timer ( UNIT ( s ) , s - > timeout_usec , & s - > timer_watch ) ) < 0 )
goto fail ;
2010-01-28 03:59:41 +03:00
2010-04-13 04:06:27 +04:00
service_set_state ( s , state ) ;
} else if ( state = = SERVICE_STOP_SIGTERM | | state = = SERVICE_STOP_SIGKILL )
service_enter_stop_post ( s , true ) ;
else
2010-01-26 06:18:44 +03:00
service_enter_dead ( s , true , true ) ;
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to kill processes: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
2010-04-13 04:06:27 +04:00
if ( state = = SERVICE_STOP_SIGTERM | | state = = SERVICE_STOP_SIGKILL )
2010-01-26 06:18:44 +03:00
service_enter_stop_post ( s , false ) ;
else
service_enter_dead ( s , false , true ) ;
}
static void service_enter_stop ( Service * s , bool success ) {
int r ;
assert ( s ) ;
if ( ! success )
s - > failure = true ;
2010-04-11 02:22:36 +04:00
service_unwatch_control_pid ( s ) ;
2010-04-13 04:06:27 +04:00
if ( ( s - > control_command = s - > exec_command [ SERVICE_EXEC_STOP ] ) ) {
2010-02-15 00:43:08 +03:00
if ( ( r = service_spawn ( s ,
s - > control_command ,
true ,
false ,
! s - > permissions_start_only ,
! s - > root_directory_start_only ,
& s - > control_pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
2010-04-13 04:06:27 +04:00
service_set_state ( s , SERVICE_STOP ) ;
} else
2010-01-26 06:18:44 +03:00
service_enter_signal ( s , SERVICE_STOP_SIGTERM , true ) ;
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run stop executable: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
service_enter_signal ( s , SERVICE_STOP_SIGTERM , false ) ;
}
2010-04-13 04:06:27 +04:00
static void service_enter_running ( Service * s , bool success ) {
assert ( s ) ;
if ( ! success )
s - > failure = true ;
if ( main_pid_good ( s ) ! = 0 & & cgroup_good ( s ) ! = 0 )
service_set_state ( s , SERVICE_RUNNING ) ;
else if ( s - > valid_no_process )
service_set_state ( s , SERVICE_EXITED ) ;
else
service_enter_stop ( s , true ) ;
}
2010-01-26 06:18:44 +03:00
static void service_enter_start_post ( Service * s ) {
int r ;
assert ( s ) ;
2010-04-11 02:22:36 +04:00
service_unwatch_control_pid ( s ) ;
2010-04-13 04:06:27 +04:00
if ( ( s - > control_command = s - > exec_command [ SERVICE_EXEC_START_POST ] ) ) {
2010-02-15 00:43:08 +03:00
if ( ( r = service_spawn ( s ,
s - > control_command ,
true ,
false ,
! s - > permissions_start_only ,
! s - > root_directory_start_only ,
& s - > control_pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
2010-01-28 03:59:41 +03:00
2010-04-13 04:06:27 +04:00
service_set_state ( s , SERVICE_START_POST ) ;
} else
service_enter_running ( s , true ) ;
2010-01-26 06:18:44 +03:00
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run start-post executable: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
service_enter_stop ( s , false ) ;
}
static void service_enter_start ( Service * s ) {
pid_t pid ;
int r ;
assert ( s ) ;
assert ( s - > exec_command [ SERVICE_EXEC_START ] ) ;
assert ( ! s - > exec_command [ SERVICE_EXEC_START ] - > command_next ) ;
2010-04-13 04:06:27 +04:00
if ( s - > type = = SERVICE_FORKING )
service_unwatch_control_pid ( s ) ;
else
service_unwatch_main_pid ( s ) ;
2010-02-15 00:43:08 +03:00
if ( ( r = service_spawn ( s ,
s - > exec_command [ SERVICE_EXEC_START ] ,
s - > type = = SERVICE_FORKING ,
true ,
true ,
true ,
& pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
if ( s - > type = = SERVICE_SIMPLE ) {
/* For simple services we immediately start
* the START_POST binaries . */
s - > main_pid = pid ;
s - > main_pid_known = true ;
2010-04-13 04:06:27 +04:00
2010-01-26 06:18:44 +03:00
service_enter_start_post ( s ) ;
} else if ( s - > type = = SERVICE_FORKING ) {
/* For forking services we wait until the start
* process exited . */
s - > control_pid = pid ;
2010-04-13 04:06:27 +04:00
2010-01-26 06:18:44 +03:00
s - > control_command = s - > exec_command [ SERVICE_EXEC_START ] ;
2010-04-13 04:06:27 +04:00
service_set_state ( s , SERVICE_START ) ;
2010-01-30 04:07:35 +03:00
} else if ( s - > type = = SERVICE_FINISH ) {
/* For finishing services we wait until the start
* process exited , too , but it is our main process . */
s - > main_pid = pid ;
2010-04-11 02:22:36 +04:00
s - > main_pid_known = true ;
2010-04-13 04:06:27 +04:00
2010-01-30 04:07:35 +03:00
s - > control_command = s - > exec_command [ SERVICE_EXEC_START ] ;
2010-04-13 04:06:27 +04:00
service_set_state ( s , SERVICE_START ) ;
2010-01-26 06:18:44 +03:00
} else
assert_not_reached ( " Unknown service type " ) ;
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run start exectuable: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-04-13 04:06:27 +04:00
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
2010-01-26 06:18:44 +03:00
}
static void service_enter_start_pre ( Service * s ) {
int r ;
assert ( s ) ;
2010-04-11 02:22:36 +04:00
service_unwatch_control_pid ( s ) ;
2010-04-13 04:06:27 +04:00
if ( ( s - > control_command = s - > exec_command [ SERVICE_EXEC_START_PRE ] ) ) {
2010-02-15 00:43:08 +03:00
if ( ( r = service_spawn ( s ,
s - > control_command ,
true ,
false ,
! s - > permissions_start_only ,
! s - > root_directory_start_only ,
& s - > control_pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
2010-04-13 04:06:27 +04:00
service_set_state ( s , SERVICE_START_PRE ) ;
} else
2010-01-26 06:18:44 +03:00
service_enter_start ( s ) ;
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run start-pre executable: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
service_enter_dead ( s , false , true ) ;
}
static void service_enter_restart ( Service * s ) {
int r ;
assert ( s ) ;
2010-04-13 06:51:38 +04:00
service_enter_dead ( s , true , false ) ;
2010-01-26 23:39:06 +03:00
if ( ( r = manager_add_job ( UNIT ( s ) - > meta . manager , JOB_START , UNIT ( s ) , JOB_FAIL , false , NULL ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
2010-04-15 05:11:11 +04:00
log_debug ( " %s scheduled restart job. " , UNIT ( s ) - > meta . id ) ;
2010-01-26 06:18:44 +03:00
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to schedule restart job: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
service_enter_dead ( s , false , false ) ;
}
static void service_enter_reload ( Service * s ) {
int r ;
assert ( s ) ;
2010-04-11 02:22:36 +04:00
service_unwatch_control_pid ( s ) ;
2010-04-13 04:06:27 +04:00
if ( ( s - > control_command = s - > exec_command [ SERVICE_EXEC_RELOAD ] ) ) {
2010-02-15 00:43:08 +03:00
if ( ( r = service_spawn ( s ,
s - > control_command ,
true ,
false ,
! s - > permissions_start_only ,
! s - > root_directory_start_only ,
& s - > control_pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
2010-04-13 04:06:27 +04:00
service_set_state ( s , SERVICE_RELOAD ) ;
} else
service_enter_running ( s , true ) ;
2010-01-26 06:18:44 +03:00
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run reload executable: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
service_enter_stop ( s , false ) ;
}
static void service_run_next ( Service * s , bool success ) {
int r ;
assert ( s ) ;
assert ( s - > control_command ) ;
assert ( s - > control_command - > command_next ) ;
if ( ! success )
s - > failure = true ;
s - > control_command = s - > control_command - > command_next ;
2010-04-11 02:22:36 +04:00
service_unwatch_control_pid ( s ) ;
2010-02-15 00:43:08 +03:00
if ( ( r = service_spawn ( s ,
s - > control_command ,
true ,
false ,
! s - > permissions_start_only ,
! s - > root_directory_start_only ,
& s - > control_pid ) ) < 0 )
2010-01-26 06:18:44 +03:00
goto fail ;
return ;
fail :
2010-04-15 05:11:11 +04:00
log_warning ( " %s failed to run spawn next executable: %s " , UNIT ( s ) - > meta . id , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
2010-04-13 04:06:27 +04:00
if ( s - > state = = SERVICE_START_PRE )
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
else if ( s - > state = = SERVICE_STOP )
service_enter_signal ( s , SERVICE_STOP_SIGTERM , false ) ;
2010-01-26 06:18:44 +03:00
else if ( s - > state = = SERVICE_STOP_POST )
service_enter_dead ( s , false , true ) ;
else
service_enter_stop ( s , false ) ;
2010-01-23 03:52:57 +03:00
}
2010-01-26 23:39:06 +03:00
static int service_start ( Unit * u ) {
Service * s = SERVICE ( u ) ;
2010-01-23 03:52:57 +03:00
assert ( s ) ;
2010-01-26 06:18:44 +03:00
/* We cannot fulfill this request right now, try again later
* please ! */
if ( s - > state = = SERVICE_STOP | |
s - > state = = SERVICE_STOP_SIGTERM | |
s - > state = = SERVICE_STOP_SIGKILL | |
s - > state = = SERVICE_STOP_POST | |
s - > state = = SERVICE_FINAL_SIGTERM | |
s - > state = = SERVICE_FINAL_SIGKILL )
2010-01-23 03:52:57 +03:00
return - EAGAIN ;
2010-01-26 06:18:44 +03:00
/* Already on it! */
if ( s - > state = = SERVICE_START_PRE | |
s - > state = = SERVICE_START | |
s - > state = = SERVICE_START_POST )
return 0 ;
assert ( s - > state = = SERVICE_DEAD | | s - > state = = SERVICE_MAINTAINANCE | | s - > state = = SERVICE_AUTO_RESTART ) ;
2010-01-23 03:52:57 +03:00
2010-01-29 06:42:57 +03:00
/* Make sure we don't enter a busy loop of some kind. */
if ( ! ratelimit_test ( & s - > ratelimit ) ) {
2010-04-15 05:11:11 +04:00
log_warning ( " %s start request repeated too quickly, refusing to start. " , u - > meta . id ) ;
2010-01-29 06:42:57 +03:00
return - EAGAIN ;
}
2010-01-26 06:18:44 +03:00
s - > failure = false ;
s - > main_pid_known = false ;
service_enter_start_pre ( s ) ;
return 0 ;
2010-01-23 03:52:57 +03:00
}
2010-01-26 23:39:06 +03:00
static int service_stop ( Unit * u ) {
Service * s = SERVICE ( u ) ;
2010-01-23 03:52:57 +03:00
assert ( s ) ;
2010-04-10 19:53:17 +04:00
/* Cannot do this now */
2010-01-26 06:18:44 +03:00
if ( s - > state = = SERVICE_START_PRE | |
s - > state = = SERVICE_START | |
s - > state = = SERVICE_START_POST | |
s - > state = = SERVICE_RELOAD )
return - EAGAIN ;
2010-04-10 19:53:17 +04:00
/* Already on it */
if ( s - > state = = SERVICE_STOP | |
s - > state = = SERVICE_STOP_SIGTERM | |
s - > state = = SERVICE_STOP_SIGKILL | |
s - > state = = SERVICE_STOP_POST | |
s - > state = = SERVICE_FINAL_SIGTERM | |
s - > state = = SERVICE_FINAL_SIGKILL )
return 0 ;
2010-01-26 06:18:44 +03:00
if ( s - > state = = SERVICE_AUTO_RESTART ) {
service_set_state ( s , SERVICE_DEAD ) ;
return 0 ;
}
2010-04-13 04:06:27 +04:00
assert ( s - > state = = SERVICE_RUNNING | | s - > state = = SERVICE_EXITED ) ;
2010-01-23 03:52:57 +03:00
2010-01-26 06:18:44 +03:00
service_enter_stop ( s , true ) ;
2010-01-23 03:52:57 +03:00
return 0 ;
}
2010-01-26 23:39:06 +03:00
static int service_reload ( Unit * u ) {
Service * s = SERVICE ( u ) ;
2010-01-26 06:18:44 +03:00
assert ( s ) ;
2010-04-13 04:06:27 +04:00
assert ( s - > state = = SERVICE_RUNNING | | s - > state = = SERVICE_EXITED ) ;
2010-01-26 06:18:44 +03:00
service_enter_reload ( s ) ;
2010-01-23 03:52:57 +03:00
return 0 ;
}
2010-01-26 23:39:06 +03:00
static bool service_can_reload ( Unit * u ) {
Service * s = SERVICE ( u ) ;
2010-01-26 06:18:44 +03:00
assert ( s ) ;
return ! ! s - > exec_command [ SERVICE_EXEC_RELOAD ] ;
}
2010-01-26 23:39:06 +03:00
static UnitActiveState service_active_state ( Unit * u ) {
assert ( u ) ;
2010-01-23 03:52:57 +03:00
2010-01-27 06:31:52 +03:00
return state_translation_table [ SERVICE ( u ) - > state ] ;
2010-01-26 06:18:44 +03:00
}
2010-04-13 22:59:01 +04:00
static const char * service_sub_state_to_string ( Unit * u ) {
assert ( u ) ;
return service_state_to_string ( SERVICE ( u ) - > state ) ;
}
2010-01-26 23:39:06 +03:00
static void service_sigchld_event ( Unit * u , pid_t pid , int code , int status ) {
Service * s = SERVICE ( u ) ;
2010-01-26 06:18:44 +03:00
bool success ;
2010-01-23 03:52:57 +03:00
assert ( s ) ;
2010-01-26 06:18:44 +03:00
assert ( pid > = 0 ) ;
2010-01-27 07:32:31 +03:00
success = code = = CLD_EXITED & & status = = 0 ;
2010-01-26 06:18:44 +03:00
s - > failure = s - > failure | | ! success ;
if ( s - > main_pid = = pid ) {
exec_status_fill ( & s - > main_exec_status , pid , code , status ) ;
s - > main_pid = 0 ;
2010-01-30 04:07:35 +03:00
if ( s - > type = = SERVICE_SIMPLE | | s - > type = = SERVICE_FINISH ) {
2010-01-26 06:18:44 +03:00
assert ( s - > exec_command [ SERVICE_EXEC_START ] ) ;
s - > exec_command [ SERVICE_EXEC_START ] - > exec_status = s - > main_exec_status ;
}
2010-04-15 05:11:11 +04:00
log_debug ( " %s: main process exited, code=%s, status=%i " , u - > meta . id , sigchld_code_to_string ( code ) , status ) ;
2010-01-26 06:18:44 +03:00
/* The service exited, so the service is officially
* gone . */
switch ( s - > state ) {
case SERVICE_START_POST :
case SERVICE_RELOAD :
case SERVICE_STOP :
/* Need to wait until the operation is
* done */
break ;
2010-01-30 04:07:35 +03:00
case SERVICE_START :
assert ( s - > type = = SERVICE_FINISH ) ;
/* This was our main goal, so let's go on */
if ( success )
service_enter_start_post ( s ) ;
else
2010-04-13 04:06:27 +04:00
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
2010-01-30 04:07:35 +03:00
break ;
2010-01-26 06:18:44 +03:00
case SERVICE_RUNNING :
2010-04-13 04:06:27 +04:00
service_enter_running ( s , success ) ;
2010-01-26 06:18:44 +03:00
break ;
case SERVICE_STOP_SIGTERM :
case SERVICE_STOP_SIGKILL :
if ( ! control_pid_good ( s ) )
service_enter_stop_post ( s , success ) ;
2010-01-23 03:52:57 +03:00
2010-01-26 06:18:44 +03:00
/* If there is still a control process, wait for that first */
break ;
2010-01-23 03:52:57 +03:00
2010-01-26 06:18:44 +03:00
default :
assert_not_reached ( " Uh, main process died at wrong time. " ) ;
}
2010-01-23 03:52:57 +03:00
2010-01-26 06:18:44 +03:00
} else if ( s - > control_pid = = pid ) {
assert ( s - > control_command ) ;
exec_status_fill ( & s - > control_command - > exec_status , pid , code , status ) ;
s - > control_pid = 0 ;
2010-04-15 05:11:11 +04:00
log_debug ( " %s: control process exited, code=%s status=%i " , u - > meta . id , sigchld_code_to_string ( code ) , status ) ;
2010-01-26 06:18:44 +03:00
/* If we are shutting things down anyway we
* don ' t care about failing commands . */
2010-04-13 04:06:27 +04:00
if ( s - > control_command - > command_next & & success ) {
2010-01-26 06:18:44 +03:00
/* There is another command to *
* execute , so let ' s do that . */
2010-04-15 05:11:11 +04:00
log_debug ( " %s running next command for state %s " , u - > meta . id , service_state_to_string ( s - > state ) ) ;
2010-01-26 06:18:44 +03:00
service_run_next ( s , success ) ;
2010-04-13 04:06:27 +04:00
} else {
2010-01-26 06:18:44 +03:00
/* No further commands for this step, so let's
* figure out what to do next */
2010-04-15 05:11:11 +04:00
log_debug ( " %s got final SIGCHLD for state %s " , u - > meta . id , service_state_to_string ( s - > state ) ) ;
2010-01-27 07:32:31 +03:00
2010-01-26 06:18:44 +03:00
switch ( s - > state ) {
case SERVICE_START_PRE :
if ( success )
service_enter_start ( s ) ;
else
2010-04-13 04:06:27 +04:00
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
2010-01-26 06:18:44 +03:00
break ;
case SERVICE_START :
assert ( s - > type = = SERVICE_FORKING ) ;
/* Let's try to load the pid
* file here if we can . We
* ignore the return value ,
* since the PID file might
* actually be created by a
* START_POST script */
if ( success ) {
if ( s - > pid_file )
service_load_pid_file ( s ) ;
service_enter_start_post ( s ) ;
} else
2010-04-13 04:06:27 +04:00
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
2010-01-26 06:18:44 +03:00
break ;
case SERVICE_START_POST :
if ( success & & s - > pid_file & & ! s - > main_pid_known ) {
int r ;
/* Hmm, let's see if we can
* load the pid now after the
* start - post scripts got
* executed . */
if ( ( r = service_load_pid_file ( s ) ) < 0 )
2010-04-15 05:11:11 +04:00
log_warning ( " %s: failed to load PID file %s: %s " , UNIT ( s ) - > meta . id , s - > pid_file , strerror ( - r ) ) ;
2010-01-26 06:18:44 +03:00
}
/* Fall through */
case SERVICE_RELOAD :
2010-04-13 04:06:27 +04:00
if ( success )
service_enter_running ( s , true ) ;
else
2010-01-26 06:18:44 +03:00
service_enter_stop ( s , false ) ;
break ;
case SERVICE_STOP :
2010-04-13 04:06:27 +04:00
service_enter_signal ( s , SERVICE_STOP_SIGTERM , success ) ;
2010-01-26 06:18:44 +03:00
break ;
case SERVICE_STOP_SIGTERM :
case SERVICE_STOP_SIGKILL :
if ( main_pid_good ( s ) < = 0 )
service_enter_stop_post ( s , success ) ;
/* If there is still a service
* process around , wait until
* that one quit , too */
break ;
case SERVICE_STOP_POST :
case SERVICE_FINAL_SIGTERM :
case SERVICE_FINAL_SIGKILL :
service_enter_dead ( s , success , true ) ;
break ;
default :
assert_not_reached ( " Uh, control process died at wrong time. " ) ;
}
}
} else
assert_not_reached ( " Got SIGCHLD for unkown PID " ) ;
}
2010-01-27 06:31:52 +03:00
static void service_timer_event ( Unit * u , uint64_t elapsed , Watch * w ) {
2010-01-26 23:39:06 +03:00
Service * s = SERVICE ( u ) ;
2010-01-26 06:18:44 +03:00
assert ( s ) ;
assert ( elapsed = = 1 ) ;
2010-01-27 06:31:52 +03:00
assert ( w = = & s - > timer_watch ) ;
2010-01-26 06:18:44 +03:00
switch ( s - > state ) {
case SERVICE_START_PRE :
case SERVICE_START :
2010-04-15 05:11:11 +04:00
log_warning ( " %s operation timed out. Terminating. " , u - > meta . id ) ;
2010-04-13 04:06:27 +04:00
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
break ;
2010-01-26 06:18:44 +03:00
case SERVICE_START_POST :
case SERVICE_RELOAD :
2010-04-15 05:11:11 +04:00
log_warning ( " %s operation timed out. Stopping. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_stop ( s , false ) ;
break ;
case SERVICE_STOP :
2010-04-15 05:11:11 +04:00
log_warning ( " %s stopping timed out. Terminating. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_signal ( s , SERVICE_STOP_SIGTERM , false ) ;
break ;
case SERVICE_STOP_SIGTERM :
2010-04-15 05:11:11 +04:00
log_warning ( " %s stopping timed out. Killing. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_signal ( s , SERVICE_STOP_SIGKILL , false ) ;
break ;
case SERVICE_STOP_SIGKILL :
/* Uh, wie sent a SIGKILL and it is still not gone?
* Must be something we cannot kill , so let ' s just be
* weirded out and continue */
2010-04-15 05:11:11 +04:00
log_warning ( " %s still around after SIGKILL. Ignoring. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_stop_post ( s , false ) ;
break ;
case SERVICE_STOP_POST :
2010-04-15 05:11:11 +04:00
log_warning ( " %s stopping timed out (2). Terminating. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_signal ( s , SERVICE_FINAL_SIGTERM , false ) ;
break ;
case SERVICE_FINAL_SIGTERM :
2010-04-15 05:11:11 +04:00
log_warning ( " %s stopping timed out (2). Killing. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_signal ( s , SERVICE_FINAL_SIGKILL , false ) ;
break ;
case SERVICE_FINAL_SIGKILL :
2010-04-15 05:11:11 +04:00
log_warning ( " %s still around after SIGKILL (2). Entering maintainance mode. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_dead ( s , false , true ) ;
break ;
case SERVICE_AUTO_RESTART :
2010-04-15 05:11:11 +04:00
log_debug ( " %s holdoff time over, scheduling restart. " , u - > meta . id ) ;
2010-01-26 06:18:44 +03:00
service_enter_restart ( s ) ;
break ;
default :
assert_not_reached ( " Timeout at wrong time. " ) ;
}
2010-01-23 03:52:57 +03:00
}
2010-03-31 18:29:55 +04:00
static void service_cgroup_notify_event ( Unit * u ) {
Service * s = SERVICE ( u ) ;
assert ( u ) ;
2010-04-15 05:11:11 +04:00
log_debug ( " %s: cgroup is empty " , u - > meta . id ) ;
2010-03-31 18:29:55 +04:00
switch ( s - > state ) {
/* Waiting for SIGCHLD is usually more interesting,
* because it includes return codes / signals . Which is
* why we ignore the cgroup events for most cases ,
* except when we don ' t know pid which to expect the
* SIGCHLD for . */
case SERVICE_RUNNING :
2010-04-13 04:06:27 +04:00
service_enter_running ( s , true ) ;
2010-03-31 18:29:55 +04:00
break ;
default :
;
}
}
2010-02-14 03:09:01 +03:00
static int service_enumerate ( Manager * m ) {
char * * p ;
unsigned i ;
DIR * d = NULL ;
char * path = NULL , * fpath = NULL , * name = NULL ;
int r ;
assert ( m ) ;
2010-04-07 18:22:30 +04:00
STRV_FOREACH ( p , m - > sysvrcnd_path )
2010-04-06 04:43:58 +04:00
for ( i = 0 ; i < ELEMENTSOF ( rcnd_table ) ; i + = 2 ) {
2010-02-14 03:09:01 +03:00
struct dirent * de ;
free ( path ) ;
path = NULL ;
2010-04-06 04:43:58 +04:00
if ( asprintf ( & path , " %s/%s " , * p , rcnd_table [ i ] ) < 0 ) {
2010-02-14 03:09:01 +03:00
r = - ENOMEM ;
goto finish ;
}
if ( d )
closedir ( d ) ;
if ( ! ( d = opendir ( path ) ) ) {
if ( errno ! = ENOENT )
log_warning ( " opendir() failed on %s: %s " , path , strerror ( errno ) ) ;
continue ;
}
while ( ( de = readdir ( d ) ) ) {
Unit * runlevel , * service ;
if ( ignore_file ( de - > d_name ) )
continue ;
if ( de - > d_name [ 0 ] ! = ' S ' & & de - > d_name [ 0 ] ! = ' K ' )
continue ;
if ( strlen ( de - > d_name ) < 4 )
continue ;
free ( fpath ) ;
fpath = NULL ;
2010-04-06 04:43:58 +04:00
if ( asprintf ( & fpath , " %s/%s/%s " , * p , rcnd_table [ i ] , de - > d_name ) < 0 ) {
2010-02-14 03:09:01 +03:00
r = - ENOMEM ;
goto finish ;
}
if ( access ( fpath , X_OK ) < 0 ) {
if ( errno ! = ENOENT )
log_warning ( " access() failed on %s: %s " , fpath , strerror ( errno ) ) ;
continue ;
}
free ( name ) ;
name = NULL ;
if ( asprintf ( & name , " %s.service " , de - > d_name + 3 ) < 0 ) {
r = - ENOMEM ;
goto finish ;
}
2010-04-15 05:11:11 +04:00
if ( ( r = manager_load_unit ( m , name , NULL , & service ) ) < 0 )
2010-02-14 03:09:01 +03:00
goto finish ;
2010-04-15 05:11:11 +04:00
if ( ( r = manager_load_unit ( m , rcnd_table [ i + 1 ] , NULL , & runlevel ) ) < 0 )
2010-02-14 03:09:01 +03:00
goto finish ;
if ( de - > d_name [ 0 ] = = ' S ' ) {
if ( ( r = unit_add_dependency ( runlevel , UNIT_WANTS , service ) ) < 0 )
goto finish ;
if ( ( r = unit_add_dependency ( runlevel , UNIT_AFTER , service ) ) < 0 )
goto finish ;
2010-04-06 04:43:58 +04:00
} else if ( de - > d_name [ 0 ] = = ' K ' & &
( streq ( rcnd_table [ i + 1 ] , SPECIAL_RUNLEVEL0_TARGET ) | |
streq ( rcnd_table [ i + 1 ] , SPECIAL_RUNLEVEL6_TARGET ) ) ) {
/* We honour K links only for
* halt / reboot . For the normal
* runlevels we assume the
* stop jobs will be
* implicitly added by the
* core logic . */
2010-02-14 03:09:01 +03:00
if ( ( r = unit_add_dependency ( runlevel , UNIT_CONFLICTS , service ) ) < 0 )
goto finish ;
if ( ( r = unit_add_dependency ( runlevel , UNIT_BEFORE , service ) ) < 0 )
goto finish ;
}
}
}
r = 0 ;
finish :
free ( path ) ;
free ( fpath ) ;
free ( name ) ;
closedir ( d ) ;
return r ;
}
2010-04-15 08:19:54 +04:00
int service_set_socket_fd ( Service * s , int fd ) {
assert ( s ) ;
assert ( fd > = 0 ) ;
/* This is called by the socket code when instantiating a new
* service for a stream socket and the socket needs to be
* configured . */
if ( UNIT ( s ) - > meta . load_state ! = UNIT_LOADED )
return - EINVAL ;
if ( s - > socket_fd > = 0 )
return - EBUSY ;
if ( s - > state ! = SERVICE_DEAD )
return - EAGAIN ;
s - > socket_fd = fd ;
return 0 ;
}
2010-01-30 03:55:42 +03:00
static const char * const service_state_table [ _SERVICE_STATE_MAX ] = {
[ SERVICE_DEAD ] = " dead " ,
[ SERVICE_START_PRE ] = " start-pre " ,
[ SERVICE_START ] = " start " ,
[ SERVICE_START_POST ] = " start-post " ,
[ SERVICE_RUNNING ] = " running " ,
2010-04-13 04:06:27 +04:00
[ SERVICE_EXITED ] = " exited " ,
2010-01-30 03:55:42 +03:00
[ SERVICE_RELOAD ] = " reload " ,
[ SERVICE_STOP ] = " stop " ,
[ SERVICE_STOP_SIGTERM ] = " stop-sigterm " ,
[ SERVICE_STOP_SIGKILL ] = " stop-sigkill " ,
[ SERVICE_STOP_POST ] = " stop-post " ,
[ SERVICE_FINAL_SIGTERM ] = " final-sigterm " ,
[ SERVICE_FINAL_SIGKILL ] = " final-sigkill " ,
[ SERVICE_MAINTAINANCE ] = " maintainance " ,
[ SERVICE_AUTO_RESTART ] = " auto-restart " ,
} ;
DEFINE_STRING_TABLE_LOOKUP ( service_state , ServiceState ) ;
static const char * const service_restart_table [ _SERVICE_RESTART_MAX ] = {
[ SERVICE_ONCE ] = " once " ,
[ SERVICE_RESTART_ON_SUCCESS ] = " restart-on-success " ,
2010-04-13 05:26:20 +04:00
[ SERVICE_RESTART_ALWAYS ] = " restart-always " ,
2010-01-30 03:55:42 +03:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( service_restart , ServiceRestart ) ;
static const char * const service_type_table [ _SERVICE_TYPE_MAX ] = {
[ SERVICE_FORKING ] = " forking " ,
[ SERVICE_SIMPLE ] = " simple " ,
[ SERVICE_FINISH ] = " finish "
} ;
DEFINE_STRING_TABLE_LOOKUP ( service_type , ServiceType ) ;
2010-04-10 19:53:17 +04:00
static const char * const service_exec_command_table [ _SERVICE_EXEC_COMMAND_MAX ] = {
2010-01-30 03:55:42 +03:00
[ SERVICE_EXEC_START_PRE ] = " ExecStartPre " ,
[ SERVICE_EXEC_START ] = " ExecStart " ,
[ SERVICE_EXEC_START_POST ] = " ExecStartPost " ,
[ SERVICE_EXEC_RELOAD ] = " ExecReload " ,
[ SERVICE_EXEC_STOP ] = " ExecStop " ,
[ SERVICE_EXEC_STOP_POST ] = " ExecStopPost " ,
} ;
DEFINE_STRING_TABLE_LOOKUP ( service_exec_command , ServiceExecCommand ) ;
2010-01-26 23:39:06 +03:00
const UnitVTable service_vtable = {
2010-01-23 03:52:57 +03:00
. suffix = " .service " ,
2010-01-26 06:18:44 +03:00
. init = service_init ,
2010-04-10 19:53:17 +04:00
. load = service_load ,
2010-01-26 06:18:44 +03:00
. done = service_done ,
2010-01-23 03:52:57 +03:00
. dump = service_dump ,
. start = service_start ,
. stop = service_stop ,
. reload = service_reload ,
2010-01-26 06:18:44 +03:00
. can_reload = service_can_reload ,
2010-01-23 03:52:57 +03:00
. active_state = service_active_state ,
2010-04-13 22:59:01 +04:00
. sub_state_to_string = service_sub_state_to_string ,
2010-01-23 03:52:57 +03:00
2010-01-26 06:18:44 +03:00
. sigchld_event = service_sigchld_event ,
. timer_event = service_timer_event ,
2010-02-14 03:09:01 +03:00
2010-03-31 18:29:55 +04:00
. cgroup_notify_empty = service_cgroup_notify_event ,
2010-02-14 03:09:01 +03:00
. enumerate = service_enumerate
2010-01-23 03:52:57 +03:00
} ;