2014-02-20 22:26:54 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
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 Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <stdlib.h>
# include <errno.h>
# include <string.h>
# include <unistd.h>
# include <sys/statvfs.h>
# include <fnmatch.h>
2014-11-06 03:40:37 +03:00
# include "sd-id128.h"
2014-02-20 22:26:54 +04:00
# include "util.h"
# include "condition-util.h"
# include "virt.h"
# include "path-util.h"
# include "fileio.h"
# include "unit.h"
2014-02-21 05:06:04 +04:00
# include "architecture.h"
2014-11-06 03:40:37 +03:00
# include "virt.h"
# include "smack-util.h"
# include "apparmor-util.h"
# include "ima-util.h"
# include "selinux-util.h"
# include "audit.h"
2014-02-20 22:26:54 +04:00
Condition * condition_new ( ConditionType type , const char * parameter , bool trigger , bool negate ) {
Condition * c ;
2014-11-06 03:40:37 +03:00
int r ;
2014-02-20 22:26:54 +04:00
assert ( type < _CONDITION_TYPE_MAX ) ;
c = new0 ( Condition , 1 ) ;
if ( ! c )
return NULL ;
c - > type = type ;
c - > trigger = trigger ;
c - > negate = negate ;
2014-11-06 03:40:37 +03:00
r = free_and_strdup ( & c - > parameter , parameter ) ;
if ( r < 0 ) {
free ( c ) ;
return NULL ;
2014-02-20 22:26:54 +04:00
}
return c ;
}
void condition_free ( Condition * c ) {
assert ( c ) ;
free ( c - > parameter ) ;
free ( c ) ;
}
void condition_free_list ( Condition * first ) {
Condition * c , * n ;
LIST_FOREACH_SAFE ( conditions , c , n , first )
condition_free ( c ) ;
}
2014-11-06 02:49:44 +03:00
int condition_test_kernel_command_line ( Condition * c ) {
2014-11-05 21:43:55 +03:00
_cleanup_free_ char * line = NULL ;
const char * p ;
2014-02-20 22:26:54 +04:00
bool equal ;
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_KERNEL_COMMAND_LINE ) ;
r = proc_cmdline ( & line ) ;
if ( r < 0 )
2014-11-06 02:49:44 +03:00
return r ;
if ( r = = 0 )
2014-02-20 22:26:54 +04:00
return c - > negate ;
equal = ! ! strchr ( c - > parameter , ' = ' ) ;
2014-11-05 21:43:55 +03:00
p = line ;
for ( ; ; ) {
_cleanup_free_ char * word = NULL ;
bool found ;
r = unquote_first_word ( & p , & word ) ;
2014-11-06 02:49:44 +03:00
if ( r < 0 )
return r ;
if ( r = = 0 )
2014-11-05 21:43:55 +03:00
return c - > negate ;
if ( equal )
found = streq ( word , c - > parameter ) ;
else {
const char * f ;
f = startswith ( word , c - > parameter ) ;
found = f & & ( * f = = ' = ' | | * f = = 0 ) ;
2014-02-20 22:26:54 +04:00
}
2014-11-05 21:43:55 +03:00
if ( found )
return ! c - > negate ;
2014-02-20 22:26:54 +04:00
}
2014-11-05 21:43:55 +03:00
return c - > negate ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 02:49:44 +03:00
int condition_test_virtualization ( Condition * c ) {
2014-02-24 01:05:18 +04:00
int b , v ;
2014-02-20 22:26:54 +04:00
const char * id ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_VIRTUALIZATION ) ;
v = detect_virtualization ( & id ) ;
2014-11-06 02:49:44 +03:00
if ( v < 0 )
return v ;
2014-02-20 22:26:54 +04:00
/* First, compare with yes/no */
b = parse_boolean ( c - > parameter ) ;
if ( v > 0 & & b > 0 )
return ! c - > negate ;
if ( v = = 0 & & b = = 0 )
return ! c - > negate ;
/* Then, compare categorization */
if ( v = = VIRTUALIZATION_VM & & streq ( c - > parameter , " vm " ) )
return ! c - > negate ;
if ( v = = VIRTUALIZATION_CONTAINER & & streq ( c - > parameter , " container " ) )
return ! c - > negate ;
/* Finally compare id */
return ( v > 0 & & streq ( c - > parameter , id ) ) = = ! c - > negate ;
}
2014-11-06 02:49:44 +03:00
int condition_test_architecture ( Condition * c ) {
int a , b ;
2014-02-21 05:06:04 +04:00
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_ARCHITECTURE ) ;
a = uname_architecture ( ) ;
if ( a < 0 )
2014-11-06 02:49:44 +03:00
return a ;
2014-02-21 05:06:04 +04:00
if ( streq ( c - > parameter , " native " ) )
b = native_architecture ( ) ;
else
b = architecture_from_string ( c - > parameter ) ;
if ( b < 0 )
2014-11-06 02:49:44 +03:00
return b ;
2014-02-21 05:06:04 +04:00
return ( a = = b ) = = ! c - > negate ;
}
2014-11-06 02:49:44 +03:00
int condition_test_host ( Condition * c ) {
2014-06-13 14:39:58 +04:00
_cleanup_free_ char * h = NULL ;
2014-02-20 22:26:54 +04:00
sd_id128_t x , y ;
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_HOST ) ;
if ( sd_id128_from_string ( c - > parameter , & x ) > = 0 ) {
r = sd_id128_get_machine ( & y ) ;
if ( r < 0 )
2014-11-06 02:49:44 +03:00
return r ;
2014-02-20 22:26:54 +04:00
2014-02-21 05:06:04 +04:00
return sd_id128_equal ( x , y ) = = ! c - > negate ;
2014-02-20 22:26:54 +04:00
}
h = gethostname_malloc ( ) ;
if ( ! h )
2014-11-06 02:49:44 +03:00
return - ENOMEM ;
2014-02-20 22:26:54 +04:00
2014-06-13 14:39:58 +04:00
return ( fnmatch ( c - > parameter , h , FNM_CASEFOLD ) = = 0 ) = = ! c - > negate ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 02:49:44 +03:00
int condition_test_ac_power ( Condition * c ) {
2014-02-20 22:26:54 +04:00
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_AC_POWER ) ;
r = parse_boolean ( c - > parameter ) ;
if ( r < 0 )
2014-11-06 02:49:44 +03:00
return r ;
2014-02-20 22:26:54 +04:00
return ( ( on_ac_power ( ) ! = 0 ) = = ! ! r ) = = ! c - > negate ;
}
2014-11-06 03:40:37 +03:00
static int condition_test_security ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_SECURITY ) ;
if ( streq ( c - > parameter , " selinux " ) )
return mac_selinux_use ( ) = = ! c - > negate ;
if ( streq ( c - > parameter , " smack " ) )
return mac_smack_use ( ) = = ! c - > negate ;
if ( streq ( c - > parameter , " apparmor " ) )
return mac_apparmor_use ( ) = = ! c - > negate ;
if ( streq ( c - > parameter , " audit " ) )
return use_audit ( ) = = ! c - > negate ;
if ( streq ( c - > parameter , " ima " ) )
return use_ima ( ) = = ! c - > negate ;
return c - > negate ;
}
static int condition_test_capability ( Condition * c ) {
_cleanup_fclose_ FILE * f = NULL ;
cap_value_t value ;
char line [ LINE_MAX ] ;
unsigned long long capabilities = - 1 ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_CAPABILITY ) ;
/* If it's an invalid capability, we don't have it */
if ( cap_from_name ( c - > parameter , & value ) < 0 )
return - EINVAL ;
/* If it's a valid capability we default to assume
* that we have it */
f = fopen ( " /proc/self/status " , " re " ) ;
if ( ! f )
return - errno ;
while ( fgets ( line , sizeof ( line ) , f ) ) {
truncate_nl ( line ) ;
if ( startswith ( line , " CapBnd: " ) ) {
( void ) sscanf ( line + 7 , " %llx " , & capabilities ) ;
break ;
}
}
return ! ! ( capabilities & ( 1ULL < < value ) ) = = ! c - > negate ;
}
static int condition_test_needs_update ( Condition * c ) {
const char * p ;
struct stat usr , other ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_NEEDS_UPDATE ) ;
/* If the file system is read-only we shouldn't suggest an update */
if ( path_is_read_only_fs ( c - > parameter ) > 0 )
return c - > negate ;
/* Any other failure means we should allow the condition to be true,
* so that we rather invoke too many update tools then too
* few . */
if ( ! path_is_absolute ( c - > parameter ) )
return ! c - > negate ;
p = strappenda ( c - > parameter , " /.updated " ) ;
if ( lstat ( p , & other ) < 0 )
return ! c - > negate ;
if ( lstat ( " /usr/ " , & usr ) < 0 )
return ! c - > negate ;
return ( usr . st_mtim . tv_sec > other . st_mtim . tv_sec | |
( usr . st_mtim . tv_sec = = other . st_mtim . tv_sec & & usr . st_mtim . tv_nsec > other . st_mtim . tv_nsec ) ) = = ! c - > negate ;
}
static int condition_test_first_boot ( Condition * c ) {
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_FIRST_BOOT ) ;
r = parse_boolean ( c - > parameter ) ;
if ( r < 0 )
return r ;
return ( ( access ( " /run/systemd/first-boot " , F_OK ) > = 0 ) = = ! ! r ) = = ! c - > negate ;
}
static int condition_test_path_exists ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_EXISTS ) ;
return ( access ( c - > parameter , F_OK ) > = 0 ) = = ! c - > negate ;
}
static int condition_test_path_exists_glob ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_EXISTS_GLOB ) ;
return ( glob_exists ( c - > parameter ) > 0 ) = = ! c - > negate ;
}
static int condition_test_path_is_directory ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_DIRECTORY ) ;
return ( is_dir ( c - > parameter , true ) > 0 ) = = ! c - > negate ;
}
static int condition_test_path_is_symbolic_link ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_SYMBOLIC_LINK ) ;
return ( is_symlink ( c - > parameter ) > 0 ) = = ! c - > negate ;
}
static int condition_test_path_is_mount_point ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_MOUNT_POINT ) ;
return ( path_is_mount_point ( c - > parameter , true ) > 0 ) = = ! c - > negate ;
}
static int condition_test_path_is_read_write ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_READ_WRITE ) ;
return ( path_is_read_only_fs ( c - > parameter ) > 0 ) = = c - > negate ;
}
static int condition_test_directory_not_empty ( Condition * c ) {
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_DIRECTORY_NOT_EMPTY ) ;
r = dir_is_empty ( c - > parameter ) ;
return ! ( r = = - ENOENT | | r > 0 ) = = ! c - > negate ;
}
static int condition_test_file_not_empty ( Condition * c ) {
struct stat st ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_FILE_NOT_EMPTY ) ;
return ( stat ( c - > parameter , & st ) > = 0 & &
S_ISREG ( st . st_mode ) & &
st . st_size > 0 ) = = ! c - > negate ;
}
static int condition_test_file_is_executable ( Condition * c ) {
struct stat st ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_FILE_IS_EXECUTABLE ) ;
return ( stat ( c - > parameter , & st ) > = 0 & &
S_ISREG ( st . st_mode ) & &
( st . st_mode & 0111 ) ) = = ! c - > negate ;
}
static int condition_test_null ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_NULL ) ;
/* Note that during parsing we already evaluate the string and
* store it in c - > negate */
return ! c - > negate ;
}
int condition_test ( Condition * c ) {
static int ( * const condition_tests [ _CONDITION_TYPE_MAX ] ) ( Condition * c ) = {
[ CONDITION_PATH_EXISTS ] = condition_test_path_exists ,
[ CONDITION_PATH_EXISTS_GLOB ] = condition_test_path_exists_glob ,
[ CONDITION_PATH_IS_DIRECTORY ] = condition_test_path_is_directory ,
[ CONDITION_PATH_IS_SYMBOLIC_LINK ] = condition_test_path_is_symbolic_link ,
[ CONDITION_PATH_IS_MOUNT_POINT ] = condition_test_path_is_mount_point ,
[ CONDITION_PATH_IS_READ_WRITE ] = condition_test_path_is_read_write ,
[ CONDITION_DIRECTORY_NOT_EMPTY ] = condition_test_directory_not_empty ,
[ CONDITION_FILE_NOT_EMPTY ] = condition_test_file_not_empty ,
[ CONDITION_FILE_IS_EXECUTABLE ] = condition_test_file_is_executable ,
[ CONDITION_KERNEL_COMMAND_LINE ] = condition_test_kernel_command_line ,
[ CONDITION_VIRTUALIZATION ] = condition_test_virtualization ,
[ CONDITION_SECURITY ] = condition_test_security ,
[ CONDITION_CAPABILITY ] = condition_test_capability ,
[ CONDITION_HOST ] = condition_test_host ,
[ CONDITION_AC_POWER ] = condition_test_ac_power ,
[ CONDITION_ARCHITECTURE ] = condition_test_architecture ,
[ CONDITION_NEEDS_UPDATE ] = condition_test_needs_update ,
[ CONDITION_FIRST_BOOT ] = condition_test_first_boot ,
[ CONDITION_NULL ] = condition_test_null ,
} ;
assert ( c ) ;
assert ( c - > type > = 0 ) ;
assert ( c - > type < _CONDITION_TYPE_MAX ) ;
return condition_tests [ c - > type ] ( c ) ;
}
2014-02-20 22:26:54 +04:00
void condition_dump ( Condition * c , FILE * f , const char * prefix ) {
assert ( c ) ;
assert ( f ) ;
if ( ! prefix )
prefix = " " ;
fprintf ( f ,
" %s \t %s: %s%s%s %s \n " ,
prefix ,
condition_type_to_string ( c - > type ) ,
c - > trigger ? " | " : " " ,
c - > negate ? " ! " : " " ,
c - > parameter ,
2014-11-06 02:49:44 +03:00
CONDITION_STATE_IS_FAILED ( c - > state ) ? " failed " : CONDITION_STATE_IS_SUCCEEDED ( c - > state ) ? " succeeded " : " untested " ) ;
2014-02-20 22:26:54 +04:00
}
void condition_dump_list ( Condition * first , FILE * f , const char * prefix ) {
Condition * c ;
LIST_FOREACH ( conditions , c , first )
condition_dump ( c , f , prefix ) ;
}
static const char * const condition_type_table [ _CONDITION_TYPE_MAX ] = {
[ CONDITION_PATH_EXISTS ] = " ConditionPathExists " ,
[ CONDITION_PATH_EXISTS_GLOB ] = " ConditionPathExistsGlob " ,
[ CONDITION_PATH_IS_DIRECTORY ] = " ConditionPathIsDirectory " ,
[ CONDITION_PATH_IS_SYMBOLIC_LINK ] = " ConditionPathIsSymbolicLink " ,
[ CONDITION_PATH_IS_MOUNT_POINT ] = " ConditionPathIsMountPoint " ,
[ CONDITION_PATH_IS_READ_WRITE ] = " ConditionPathIsReadWrite " ,
[ CONDITION_DIRECTORY_NOT_EMPTY ] = " ConditionDirectoryNotEmpty " ,
[ CONDITION_FILE_NOT_EMPTY ] = " ConditionFileNotEmpty " ,
[ CONDITION_FILE_IS_EXECUTABLE ] = " ConditionFileIsExecutable " ,
[ CONDITION_KERNEL_COMMAND_LINE ] = " ConditionKernelCommandLine " ,
[ CONDITION_VIRTUALIZATION ] = " ConditionVirtualization " ,
[ CONDITION_SECURITY ] = " ConditionSecurity " ,
[ CONDITION_CAPABILITY ] = " ConditionCapability " ,
[ CONDITION_HOST ] = " ConditionHost " ,
[ CONDITION_AC_POWER ] = " ConditionACPower " ,
2014-02-21 05:06:04 +04:00
[ CONDITION_ARCHITECTURE ] = " ConditionArchitecture " ,
2014-06-13 14:52:31 +04:00
[ CONDITION_NEEDS_UPDATE ] = " ConditionNeedsUpdate " ,
2014-07-07 21:25:31 +04:00
[ CONDITION_FIRST_BOOT ] = " ConditionFirstBoot " ,
2014-02-20 22:26:54 +04:00
[ CONDITION_NULL ] = " ConditionNull "
} ;
DEFINE_STRING_TABLE_LOOKUP ( condition_type , ConditionType ) ;