2017-11-18 19:09:20 +03:00
/* SPDX-License-Identifier: LGPL-2.1+ */
2014-02-20 22:26:54 +04:00
# include <errno.h>
2015-12-03 23:13:37 +03:00
# include <fcntl.h>
2015-10-22 21:12:31 +03:00
# include <fnmatch.h>
2015-12-03 23:13:37 +03:00
# include <limits.h>
2015-10-22 21:12:31 +03:00
# include <stdlib.h>
2014-02-20 22:26:54 +04:00
# include <string.h>
2015-12-03 23:13:37 +03:00
# include <sys/stat.h>
2017-04-09 02:32:13 +03:00
# include <sys/types.h>
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
# include <sys/utsname.h>
2015-12-03 23:13:37 +03:00
# include <time.h>
2014-02-20 22:26:54 +04:00
# include <unistd.h>
2014-11-06 03:40:37 +03:00
# include "sd-id128.h"
2015-10-22 21:12:31 +03:00
2015-10-27 05:01:06 +03:00
# include "alloc-util.h"
2014-11-06 03:40:37 +03:00
# include "apparmor-util.h"
2015-10-22 21:12:31 +03:00
# include "architecture.h"
2015-10-27 01:32:16 +03:00
# include "audit-util.h"
2014-12-10 05:16:14 +03:00
# include "cap-list.h"
2017-12-18 10:53:29 +03:00
# include "cgroup-util.h"
2015-10-25 15:14:12 +03:00
# include "condition.h"
2018-06-20 19:52:52 +03:00
# include "efivars.h"
2015-10-22 21:12:31 +03:00
# include "extract-word.h"
2015-10-25 15:14:12 +03:00
# include "fd-util.h"
2016-09-13 03:04:35 +03:00
# include "fileio.h"
2015-10-27 03:48:17 +03:00
# include "glob-util.h"
2015-05-18 18:10:07 +03:00
# include "hostname-util.h"
2015-10-22 21:12:31 +03:00
# include "ima-util.h"
2015-12-03 23:13:37 +03:00
# include "list.h"
# include "macro.h"
2018-11-29 12:24:39 +03:00
# include "mountpoint-util.h"
2015-10-26 18:18:16 +03:00
# include "parse-util.h"
2015-10-22 21:12:31 +03:00
# include "path-util.h"
2015-10-27 02:06:29 +03:00
# include "proc-cmdline.h"
2017-07-20 17:19:18 +03:00
# include "process-util.h"
2015-10-22 21:12:31 +03:00
# include "selinux-util.h"
# include "smack-util.h"
2015-10-27 00:01:44 +03:00
# include "stat-util.h"
2015-10-27 00:31:05 +03:00
# include "string-table.h"
2015-10-24 23:58:24 +03:00
# include "string-util.h"
2017-11-07 19:12:36 +03:00
# include "tomoyo-util.h"
2017-04-09 02:32:13 +03:00
# include "user-util.h"
2015-10-22 21:12:31 +03:00
# include "util.h"
# include "virt.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
2014-11-06 04:11:08 +03:00
assert ( type > = 0 ) ;
2014-02-20 22:26:54 +04:00
assert ( type < _CONDITION_TYPE_MAX ) ;
2015-02-27 22:05:26 +03:00
assert ( ( ! parameter ) = = ( type = = CONDITION_NULL ) ) ;
2014-02-20 22:26:54 +04:00
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 ) {
2017-11-24 13:31:49 +03:00
return mfree ( c ) ;
2014-02-20 22:26:54 +04:00
}
return c ;
}
void condition_free ( Condition * c ) {
assert ( c ) ;
free ( c - > parameter ) ;
free ( c ) ;
}
2014-12-18 20:33:05 +03:00
Condition * condition_free_list ( Condition * first ) {
2014-02-20 22:26:54 +04:00
Condition * c , * n ;
LIST_FOREACH_SAFE ( conditions , c , n , first )
condition_free ( c ) ;
2014-12-18 20:33:05 +03:00
return NULL ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
static 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 ;
2014-02-20 22:26:54 +04:00
2018-06-11 17:02:03 +03:00
equal = strchr ( c - > parameter , ' = ' ) ;
2014-11-05 21:43:55 +03:00
2016-11-11 20:58:41 +03:00
for ( p = line ; ; ) {
2014-11-05 21:43:55 +03:00
_cleanup_free_ char * word = NULL ;
bool found ;
2015-06-23 19:26:49 +03:00
r = extract_first_word ( & p , & word , NULL , EXTRACT_QUOTES | EXTRACT_RELAX ) ;
2014-11-06 02:49:44 +03:00
if ( r < 0 )
return r ;
if ( r = = 0 )
2014-11-06 04:02:13 +03:00
break ;
2014-11-05 21:43:55 +03:00
if ( equal )
found = streq ( word , c - > parameter ) ;
else {
const char * f ;
f = startswith ( word , c - > parameter ) ;
2017-10-04 17:01:32 +03:00
found = f & & IN_SET ( * f , 0 , ' = ' ) ;
2014-02-20 22:26:54 +04:00
}
2014-11-05 21:43:55 +03:00
if ( found )
2014-11-06 04:02:13 +03:00
return true ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
return false ;
2014-02-20 22:26:54 +04:00
}
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
static int condition_test_kernel_version ( Condition * c ) {
2017-12-23 17:02:58 +03:00
enum {
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
* should be listed first . */
LOWER_OR_EQUAL ,
GREATER_OR_EQUAL ,
LOWER ,
GREATER ,
EQUAL ,
_ORDER_MAX ,
} ;
static const char * const prefix [ _ORDER_MAX ] = {
[ LOWER_OR_EQUAL ] = " <= " ,
[ GREATER_OR_EQUAL ] = " >= " ,
[ LOWER ] = " < " ,
[ GREATER ] = " > " ,
[ EQUAL ] = " = " ,
} ;
const char * p = NULL ;
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
struct utsname u ;
2017-12-23 17:02:58 +03:00
size_t i ;
int k ;
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_KERNEL_VERSION ) ;
assert_se ( uname ( & u ) > = 0 ) ;
2017-12-23 17:02:58 +03:00
for ( i = 0 ; i < _ORDER_MAX ; i + + ) {
p = startswith ( c - > parameter , prefix [ i ] ) ;
if ( p )
break ;
}
/* No prefix? Then treat as glob string */
if ( ! p )
return fnmatch ( skip_leading_chars ( c - > parameter , NULL ) , u . release , 0 ) = = 0 ;
k = str_verscmp ( u . release , skip_leading_chars ( p , NULL ) ) ;
switch ( i ) {
case LOWER :
return k < 0 ;
case LOWER_OR_EQUAL :
return k < = 0 ;
case EQUAL :
return k = = 0 ;
case GREATER_OR_EQUAL :
return k > = 0 ;
case GREATER :
return k > 0 ;
default :
assert_not_reached ( " Can't compare " ) ;
}
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
}
2017-04-09 02:32:13 +03:00
static int condition_test_user ( Condition * c ) {
uid_t id ;
int r ;
_cleanup_free_ char * username = NULL ;
const char * u ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_USER ) ;
r = parse_uid ( c - > parameter , & id ) ;
if ( r > = 0 )
return id = = getuid ( ) | | id = = geteuid ( ) ;
2017-05-19 05:12:14 +03:00
if ( streq ( " @system " , c - > parameter ) )
2017-12-02 14:59:21 +03:00
return uid_is_system ( getuid ( ) ) | | uid_is_system ( geteuid ( ) ) ;
2017-05-19 05:12:14 +03:00
2017-04-09 02:32:13 +03:00
username = getusername_malloc ( ) ;
if ( ! username )
return - ENOMEM ;
if ( streq ( username , c - > parameter ) )
return 1 ;
2017-07-20 17:19:18 +03:00
if ( getpid_cached ( ) = = 1 )
2017-04-09 02:32:13 +03:00
return streq ( c - > parameter , " root " ) ;
u = c - > parameter ;
2018-08-02 19:36:47 +03:00
r = get_user_creds ( & u , & id , NULL , NULL , NULL , USER_CREDS_ALLOW_MISSING ) ;
2017-04-09 02:32:13 +03:00
if ( r < 0 )
return 0 ;
return id = = getuid ( ) | | id = = geteuid ( ) ;
}
2017-12-18 10:53:29 +03:00
static int condition_test_control_group_controller ( Condition * c ) {
int r ;
CGroupMask system_mask , wanted_mask = 0 ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_CONTROL_GROUP_CONTROLLER ) ;
r = cg_mask_supported ( & system_mask ) ;
if ( r < 0 )
return log_debug_errno ( r , " Failed to determine supported controllers: %m " ) ;
r = cg_mask_from_string ( c - > parameter , & wanted_mask ) ;
if ( r < 0 | | wanted_mask < = 0 ) {
/* This won't catch the case that we have an unknown controller
* mixed in with valid ones - - these are only assessed on the
* validity of the valid controllers found . */
log_debug ( " Failed to parse cgroup string: %s " , c - > parameter ) ;
return 1 ;
}
2018-04-20 16:36:20 +03:00
return FLAGS_SET ( system_mask , wanted_mask ) ;
2017-12-18 10:53:29 +03:00
}
2017-04-09 02:32:13 +03:00
static int condition_test_group ( Condition * c ) {
gid_t id ;
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_GROUP ) ;
r = parse_gid ( c - > parameter , & id ) ;
if ( r > = 0 )
return in_gid ( id ) ;
/* Avoid any NSS lookups if we are PID1 */
2017-07-20 17:19:18 +03:00
if ( getpid_cached ( ) = = 1 )
2017-04-09 02:32:13 +03:00
return streq ( c - > parameter , " root " ) ;
return in_group ( c - > parameter ) > 0 ;
}
2014-11-06 04:02:13 +03:00
static 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
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_VIRTUALIZATION ) ;
2016-10-22 05:56:58 +03:00
if ( streq ( c - > parameter , " private-users " ) )
return running_in_userns ( ) ;
2015-09-07 14:42:47 +03:00
v = detect_virtualization ( ) ;
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 ) ;
2016-10-25 05:53:07 +03:00
if ( b > = 0 )
return b = = ! ! v ;
2014-02-20 22:26:54 +04:00
/* Then, compare categorization */
2016-10-25 05:53:07 +03:00
if ( streq ( c - > parameter , " vm " ) )
return VIRTUALIZATION_IS_VM ( v ) ;
2014-02-20 22:26:54 +04:00
2016-10-25 05:53:07 +03:00
if ( streq ( c - > parameter , " container " ) )
return VIRTUALIZATION_IS_CONTAINER ( v ) ;
2014-02-20 22:26:54 +04:00
/* Finally compare id */
2015-09-07 14:42:47 +03:00
return v ! = VIRTUALIZATION_NONE & & streq ( c - > parameter , virtualization_to_string ( v ) ) ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
static int condition_test_architecture ( Condition * c ) {
2014-11-06 02:49:44 +03:00
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 ( ) ;
2016-07-01 01:56:23 +03:00
else {
2014-02-21 05:06:04 +04:00
b = architecture_from_string ( c - > parameter ) ;
2016-07-01 01:56:23 +03:00
if ( b < 0 ) /* unknown architecture? Then it's definitely not ours */
return false ;
}
2014-02-21 05:06:04 +04:00
2014-11-06 04:02:13 +03:00
return a = = b ;
2014-02-21 05:06:04 +04:00
}
2014-11-06 04:02:13 +03:00
static 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-11-06 04:02:13 +03:00
return sd_id128_equal ( x , y ) ;
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-11-06 04:02:13 +03:00
return fnmatch ( c - > parameter , h , FNM_CASEFOLD ) = = 0 ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
static 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
2014-11-06 04:02:13 +03:00
return ( on_ac_power ( ) ! = 0 ) = = ! ! r ;
2014-02-20 22:26:54 +04:00
}
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 " ) )
2017-05-02 19:42:19 +03:00
return mac_selinux_use ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " smack " ) )
2014-11-06 04:02:13 +03:00
return mac_smack_use ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " apparmor " ) )
2014-11-06 04:02:13 +03:00
return mac_apparmor_use ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " audit " ) )
2014-11-06 04:02:13 +03:00
return use_audit ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " ima " ) )
2014-11-06 04:02:13 +03:00
return use_ima ( ) ;
2017-11-07 19:12:36 +03:00
if ( streq ( c - > parameter , " tomoyo " ) )
return mac_tomoyo_use ( ) ;
2018-06-20 19:52:52 +03:00
if ( streq ( c - > parameter , " uefi-secureboot " ) )
return is_efi_secure_boot ( ) ;
2014-11-06 03:40:37 +03:00
2014-11-06 04:02:13 +03:00
return false ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_capability ( Condition * c ) {
2018-10-18 14:40:55 +03:00
unsigned long long capabilities = ( unsigned long long ) - 1 ;
2014-11-06 03:40:37 +03:00
_cleanup_fclose_ FILE * f = NULL ;
2018-10-18 14:40:55 +03:00
int value , r ;
2014-11-06 03:40:37 +03:00
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_CAPABILITY ) ;
/* If it's an invalid capability, we don't have it */
2014-12-10 05:16:14 +03:00
value = capability_from_name ( c - > parameter ) ;
if ( value < 0 )
2014-11-06 03:40:37 +03:00
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 ;
2018-10-18 14:40:55 +03:00
for ( ; ; ) {
_cleanup_free_ char * line = NULL ;
const char * p ;
r = read_line ( f , LONG_LINE_MAX , & line ) ;
if ( r < 0 )
return r ;
if ( r = = 0 )
break ;
p = startswith ( line , " CapBnd: " ) ;
if ( p ) {
if ( sscanf ( line + 7 , " %llx " , & capabilities ) ! = 1 )
return - EIO ;
2014-11-06 03:40:37 +03:00
break ;
}
}
2014-11-06 04:02:13 +03:00
return ! ! ( capabilities & ( 1ULL < < value ) ) ;
2014-11-06 03:40:37 +03:00
}
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 )
2014-11-06 04:02:13 +03:00
return false ;
2014-11-06 03:40:37 +03:00
/* Any other failure means we should allow the condition to be true,
2016-02-08 15:27:22 +03:00
* so that we rather invoke too many update tools than too
2014-11-06 03:40:37 +03:00
* few . */
if ( ! path_is_absolute ( c - > parameter ) )
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
2015-02-03 04:05:59 +03:00
p = strjoina ( c - > parameter , " /.updated " ) ;
2014-11-06 03:40:37 +03:00
if ( lstat ( p , & other ) < 0 )
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
if ( lstat ( " /usr/ " , & usr ) < 0 )
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
2016-09-13 03:04:35 +03:00
/*
* First , compare seconds as they are always accurate . . .
*/
if ( usr . st_mtim . tv_sec ! = other . st_mtim . tv_sec )
return usr . st_mtim . tv_sec > other . st_mtim . tv_sec ;
/*
* . . . then compare nanoseconds .
*
* A false positive is only possible when / usr ' s nanoseconds > 0
* ( otherwise / usr cannot be strictly newer than the target file )
* AND the target file ' s nanoseconds = = 0
* ( otherwise the filesystem supports nsec timestamps , see stat ( 2 ) ) .
*/
if ( usr . st_mtim . tv_nsec > 0 & & other . st_mtim . tv_nsec = = 0 ) {
_cleanup_free_ char * timestamp_str = NULL ;
uint64_t timestamp ;
int r ;
2018-11-12 16:18:03 +03:00
r = parse_env_file ( NULL , p , " TIMESTAMP_NSEC " , & timestamp_str ) ;
2016-09-13 03:04:35 +03:00
if ( r < 0 ) {
2016-10-21 19:26:30 +03:00
log_error_errno ( r , " Failed to parse timestamp file '%s', using mtime: %m " , p ) ;
2016-09-13 03:04:35 +03:00
return true ;
} else if ( r = = 0 ) {
log_debug ( " No data in timestamp file '%s', using mtime " , p ) ;
return true ;
}
r = safe_atou64 ( timestamp_str , & timestamp ) ;
if ( r < 0 ) {
2016-10-21 19:26:30 +03:00
log_error_errno ( r , " Failed to parse timestamp value '%s' in file '%s', using mtime: %m " , timestamp_str , p ) ;
2016-09-13 03:04:35 +03:00
return true ;
}
2016-10-21 19:26:30 +03:00
timespec_store ( & other . st_mtim , timestamp ) ;
2016-09-13 03:04:35 +03:00
}
return usr . st_mtim . tv_nsec > other . st_mtim . tv_nsec ;
2014-11-06 03:40:37 +03:00
}
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 ;
2014-11-06 04:02:13 +03:00
return ( access ( " /run/systemd/first-boot " , F_OK ) > = 0 ) = = ! ! r ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_exists ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_EXISTS ) ;
2014-11-06 04:02:13 +03:00
return access ( c - > parameter , F_OK ) > = 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_exists_glob ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_EXISTS_GLOB ) ;
2014-11-06 04:02:13 +03:00
return glob_exists ( c - > parameter ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_directory ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_DIRECTORY ) ;
2014-11-06 04:02:13 +03:00
return is_dir ( c - > parameter , true ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_symbolic_link ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_SYMBOLIC_LINK ) ;
2014-11-06 04:02:13 +03:00
return is_symlink ( c - > parameter ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_mount_point ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_MOUNT_POINT ) ;
2016-11-18 23:35:21 +03:00
return path_is_mount_point ( c - > parameter , NULL , AT_SYMLINK_FOLLOW ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_read_write ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_READ_WRITE ) ;
2014-11-06 04:02:13 +03:00
return path_is_read_only_fs ( c - > parameter ) < = 0 ;
2014-11-06 03:40:37 +03:00
}
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 ) ;
2014-11-06 04:02:13 +03:00
return r < = 0 & & r ! = - ENOENT ;
2014-11-06 03:40:37 +03:00
}
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 ) & &
2014-11-06 04:02:13 +03:00
st . st_size > 0 ) ;
2014-11-06 03:40:37 +03:00
}
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 ) & &
2014-11-06 04:02:13 +03:00
( st . st_mode & 0111 ) ) ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_null ( Condition * c ) {
assert ( c ) ;
assert ( c - > type = = CONDITION_NULL ) ;
/* Note that during parsing we already evaluate the string and
* store it in c - > negate */
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
}
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 ,
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
[ CONDITION_KERNEL_VERSION ] = condition_test_kernel_version ,
2014-11-06 03:40:37 +03:00
[ 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 ,
2017-04-09 02:32:13 +03:00
[ CONDITION_USER ] = condition_test_user ,
[ CONDITION_GROUP ] = condition_test_group ,
2017-12-18 10:53:29 +03:00
[ CONDITION_CONTROL_GROUP_CONTROLLER ] = condition_test_control_group_controller ,
2014-11-06 03:40:37 +03:00
[ CONDITION_NULL ] = condition_test_null ,
} ;
2014-11-06 04:27:10 +03:00
int r , b ;
2014-11-06 03:40:37 +03:00
assert ( c ) ;
assert ( c - > type > = 0 ) ;
assert ( c - > type < _CONDITION_TYPE_MAX ) ;
2014-11-06 04:02:13 +03:00
r = condition_tests [ c - > type ] ( c ) ;
2014-11-06 04:27:10 +03:00
if ( r < 0 ) {
c - > result = CONDITION_ERROR ;
2014-11-06 04:02:13 +03:00
return r ;
2014-11-06 04:27:10 +03:00
}
2014-11-06 04:02:13 +03:00
2014-11-06 04:27:10 +03:00
b = ( r > 0 ) = = ! c - > negate ;
c - > result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED ;
return b ;
2014-11-06 03:40:37 +03:00
}
2014-11-06 15:43:45 +03:00
void condition_dump ( Condition * c , FILE * f , const char * prefix , const char * ( * to_string ) ( ConditionType t ) ) {
2014-02-20 22:26:54 +04:00
assert ( c ) ;
assert ( f ) ;
2018-01-10 19:11:19 +03:00
prefix = strempty ( prefix ) ;
2014-02-20 22:26:54 +04:00
fprintf ( f ,
" %s \t %s: %s%s%s %s \n " ,
prefix ,
2014-11-06 15:43:45 +03:00
to_string ( c - > type ) ,
2014-02-20 22:26:54 +04:00
c - > trigger ? " | " : " " ,
c - > negate ? " ! " : " " ,
c - > parameter ,
2014-11-06 04:27:10 +03:00
condition_result_to_string ( c - > result ) ) ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 15:43:45 +03:00
void condition_dump_list ( Condition * first , FILE * f , const char * prefix , const char * ( * to_string ) ( ConditionType t ) ) {
2014-02-20 22:26:54 +04:00
Condition * c ;
LIST_FOREACH ( conditions , c , first )
2014-11-06 15:43:45 +03:00
condition_dump ( c , f , prefix , to_string ) ;
2014-02-20 22:26:54 +04:00
}
static const char * const condition_type_table [ _CONDITION_TYPE_MAX ] = {
2014-11-06 15:56:22 +03:00
[ CONDITION_ARCHITECTURE ] = " ConditionArchitecture " ,
[ CONDITION_VIRTUALIZATION ] = " ConditionVirtualization " ,
[ CONDITION_HOST ] = " ConditionHost " ,
[ CONDITION_KERNEL_COMMAND_LINE ] = " ConditionKernelCommandLine " ,
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
[ CONDITION_KERNEL_VERSION ] = " ConditionKernelVersion " ,
2014-11-06 15:56:22 +03:00
[ CONDITION_SECURITY ] = " ConditionSecurity " ,
[ CONDITION_CAPABILITY ] = " ConditionCapability " ,
[ CONDITION_AC_POWER ] = " ConditionACPower " ,
[ CONDITION_NEEDS_UPDATE ] = " ConditionNeedsUpdate " ,
[ CONDITION_FIRST_BOOT ] = " ConditionFirstBoot " ,
2014-02-20 22:26:54 +04:00
[ 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 " ,
2017-04-09 02:32:13 +03:00
[ CONDITION_USER ] = " ConditionUser " ,
[ CONDITION_GROUP ] = " ConditionGroup " ,
2017-12-18 10:53:29 +03:00
[ CONDITION_CONTROL_GROUP_CONTROLLER ] = " ConditionControlGroupController " ,
2014-02-20 22:26:54 +04:00
[ CONDITION_NULL ] = " ConditionNull "
} ;
DEFINE_STRING_TABLE_LOOKUP ( condition_type , ConditionType ) ;
2014-11-06 04:27:10 +03:00
2014-11-06 15:43:45 +03:00
static const char * const assert_type_table [ _CONDITION_TYPE_MAX ] = {
2014-11-06 15:56:22 +03:00
[ CONDITION_ARCHITECTURE ] = " AssertArchitecture " ,
[ CONDITION_VIRTUALIZATION ] = " AssertVirtualization " ,
[ CONDITION_HOST ] = " AssertHost " ,
[ CONDITION_KERNEL_COMMAND_LINE ] = " AssertKernelCommandLine " ,
core,udev,networkd: add ConditionKernelVersion=
This adds a simple condition/assert/match to the service manager, to
udev's .link handling and to networkd, for matching the kernel version
string.
In this version we only do fnmatch() based globbing, but we might want
to extend that to version comparisons later on, if we like, by slightly
extending the syntax with ">=", "<=", ">", "<" and "==" expressions.
2017-12-13 22:34:13 +03:00
[ CONDITION_KERNEL_VERSION ] = " AssertKernelVersion " ,
2014-11-06 15:56:22 +03:00
[ CONDITION_SECURITY ] = " AssertSecurity " ,
[ CONDITION_CAPABILITY ] = " AssertCapability " ,
[ CONDITION_AC_POWER ] = " AssertACPower " ,
[ CONDITION_NEEDS_UPDATE ] = " AssertNeedsUpdate " ,
[ CONDITION_FIRST_BOOT ] = " AssertFirstBoot " ,
2014-11-06 15:43:45 +03:00
[ CONDITION_PATH_EXISTS ] = " AssertPathExists " ,
[ CONDITION_PATH_EXISTS_GLOB ] = " AssertPathExistsGlob " ,
[ CONDITION_PATH_IS_DIRECTORY ] = " AssertPathIsDirectory " ,
[ CONDITION_PATH_IS_SYMBOLIC_LINK ] = " AssertPathIsSymbolicLink " ,
[ CONDITION_PATH_IS_MOUNT_POINT ] = " AssertPathIsMountPoint " ,
[ CONDITION_PATH_IS_READ_WRITE ] = " AssertPathIsReadWrite " ,
[ CONDITION_DIRECTORY_NOT_EMPTY ] = " AssertDirectoryNotEmpty " ,
[ CONDITION_FILE_NOT_EMPTY ] = " AssertFileNotEmpty " ,
[ CONDITION_FILE_IS_EXECUTABLE ] = " AssertFileIsExecutable " ,
2017-04-09 02:32:13 +03:00
[ CONDITION_USER ] = " AssertUser " ,
[ CONDITION_GROUP ] = " AssertGroup " ,
2017-12-18 10:53:29 +03:00
[ CONDITION_CONTROL_GROUP_CONTROLLER ] = " AssertControlGroupController " ,
2014-11-06 15:43:45 +03:00
[ CONDITION_NULL ] = " AssertNull "
} ;
DEFINE_STRING_TABLE_LOOKUP ( assert_type , ConditionType ) ;
2014-11-06 04:27:10 +03:00
static const char * const condition_result_table [ _CONDITION_RESULT_MAX ] = {
[ CONDITION_UNTESTED ] = " untested " ,
[ CONDITION_SUCCEEDED ] = " succeeded " ,
[ CONDITION_FAILED ] = " failed " ,
[ CONDITION_ERROR ] = " error " ,
} ;
DEFINE_STRING_TABLE_LOOKUP ( condition_result , ConditionResult ) ;