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"
2019-03-18 19:42:56 +03:00
# include "env-file.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"
2019-03-18 19:44:18 +03:00
# include "limits-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 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
2019-03-18 19:42:56 +03:00
c = new ( Condition , 1 ) ;
2014-02-20 22:26:54 +04:00
if ( ! c )
return NULL ;
2019-03-18 19:42:56 +03:00
* c = ( Condition ) {
. type = type ,
. trigger = trigger ,
. negate = negate ,
} ;
2014-02-20 22:26:54 +04:00
2019-03-18 19:42:56 +03:00
if ( parameter ) {
c - > parameter = strdup ( parameter ) ;
if ( ! c - > parameter )
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 ) ;
}
2019-03-23 18:22:38 +03:00
Condition * condition_free_list_type ( Condition * head , ConditionType type ) {
Condition * c , * n ;
2014-02-20 22:26:54 +04:00
2019-03-23 18:22:38 +03:00
LIST_FOREACH_SAFE ( conditions , c , n , head )
if ( type < 0 | | c - > type = = type ) {
LIST_REMOVE ( conditions , head , c ) ;
2019-03-08 09:20:01 +03:00
condition_free ( c ) ;
2019-03-23 18:22:38 +03:00
}
2014-12-18 20:33:05 +03:00
2019-03-23 18:22:38 +03:00
assert ( type > = 0 | | ! head ) ;
return head ;
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
}
2019-03-18 18:43:33 +03:00
typedef enum {
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
* should be listed first . */
ORDER_LOWER_OR_EQUAL ,
ORDER_GREATER_OR_EQUAL ,
ORDER_LOWER ,
ORDER_GREATER ,
ORDER_EQUAL ,
2019-03-18 19:43:29 +03:00
ORDER_UNEQUAL ,
2019-03-18 18:43:33 +03:00
_ORDER_MAX ,
_ORDER_INVALID = - 1
} OrderOperator ;
static OrderOperator parse_order ( const char * * s ) {
2017-12-23 17:02:58 +03:00
static const char * const prefix [ _ORDER_MAX ] = {
2019-03-18 18:43:33 +03:00
[ ORDER_LOWER_OR_EQUAL ] = " <= " ,
[ ORDER_GREATER_OR_EQUAL ] = " >= " ,
[ ORDER_LOWER ] = " < " ,
[ ORDER_GREATER ] = " > " ,
[ ORDER_EQUAL ] = " = " ,
2019-03-18 19:43:29 +03:00
[ ORDER_UNEQUAL ] = " != " ,
2017-12-23 17:02:58 +03: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
2019-03-18 18:43:33 +03:00
OrderOperator i ;
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-12-23 17:02:58 +03:00
for ( i = 0 ; i < _ORDER_MAX ; i + + ) {
2019-03-18 18:43:33 +03:00
const char * e ;
e = startswith ( * s , prefix [ i ] ) ;
if ( e ) {
* s = e ;
return i ;
}
2017-12-23 17:02:58 +03:00
}
2019-03-18 18:43:33 +03:00
return _ORDER_INVALID ;
}
2017-12-23 17:02:58 +03:00
2019-03-18 18:43:33 +03:00
static bool test_order ( int k , OrderOperator p ) {
2017-12-23 17:02:58 +03:00
2019-03-18 18:43:33 +03:00
switch ( p ) {
2017-12-23 17:02:58 +03:00
2019-03-18 18:43:33 +03:00
case ORDER_LOWER :
2017-12-23 17:02:58 +03:00
return k < 0 ;
2019-03-18 18:43:33 +03:00
case ORDER_LOWER_OR_EQUAL :
2017-12-23 17:02:58 +03:00
return k < = 0 ;
2019-03-18 18:43:33 +03:00
case ORDER_EQUAL :
2017-12-23 17:02:58 +03:00
return k = = 0 ;
2019-03-18 19:43:29 +03:00
case ORDER_UNEQUAL :
return k ! = 0 ;
2019-03-18 18:43:33 +03:00
case ORDER_GREATER_OR_EQUAL :
2017-12-23 17:02:58 +03:00
return k > = 0 ;
2019-03-18 18:43:33 +03:00
case ORDER_GREATER :
2017-12-23 17:02:58 +03:00
return k > 0 ;
default :
2019-03-18 18:43:33 +03:00
assert_not_reached ( " unknown order " ) ;
2017-12-23 17:02:58 +03: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
}
2019-03-18 18:43:33 +03:00
static int condition_test_kernel_version ( Condition * c ) {
OrderOperator order ;
struct utsname u ;
const char * p ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_KERNEL_VERSION ) ;
assert_se ( uname ( & u ) > = 0 ) ;
p = c - > parameter ;
order = parse_order ( & p ) ;
/* No prefix? Then treat as glob string */
if ( order < 0 )
return fnmatch ( skip_leading_chars ( c - > parameter , NULL ) , u . release , 0 ) = = 0 ;
return test_order ( str_verscmp ( u . release , skip_leading_chars ( p , NULL ) ) , order ) ;
}
2019-03-18 19:44:18 +03:00
static int condition_test_memory ( Condition * c ) {
OrderOperator order ;
uint64_t m , k ;
const char * p ;
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_MEMORY ) ;
m = physical_memory ( ) ;
p = c - > parameter ;
order = parse_order ( & p ) ;
if ( order < 0 )
order = ORDER_GREATER_OR_EQUAL ; /* default to >= check, if nothing is specified. */
r = safe_atou64 ( p , & k ) ;
if ( r < 0 )
return log_debug_errno ( r , " Failed to parse size: %m " ) ;
return test_order ( CMP ( m , k ) , order ) ;
}
static int condition_test_cpus ( Condition * c ) {
OrderOperator order ;
const char * p ;
unsigned k ;
int r , n ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_CPUS ) ;
n = cpus_in_affinity_mask ( ) ;
if ( n < 0 )
return log_debug_errno ( n , " Failed to determine CPUs in affinity mask: %m " ) ;
p = c - > parameter ;
order = parse_order ( & p ) ;
if ( order < 0 )
order = ORDER_GREATER_OR_EQUAL ; /* default to >= check, if nothing is specified. */
r = safe_atou ( p , & k ) ;
if ( r < 0 )
return log_debug_errno ( r , " Failed to parse number of CPUs: %m " ) ;
return test_order ( CMP ( ( unsigned ) n , k ) , order ) ;
}
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 ,
2019-03-18 19:44:18 +03:00
[ CONDITION_CPUS ] = condition_test_cpus ,
[ CONDITION_MEMORY ] = condition_test_memory ,
2014-11-06 03:40:37 +03:00
} ;
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
}
2019-03-08 08:22:31 +03:00
bool condition_test_list ( Condition * first , const char * ( * to_string ) ( ConditionType t ) , condition_test_logger_t logger , void * userdata ) {
Condition * c ;
int triggered = - 1 ;
assert ( ! ! logger = = ! ! to_string ) ;
/* If the condition list is empty, then it is true */
if ( ! first )
return true ;
/* Otherwise, if all of the non-trigger conditions apply and
* if any of the trigger conditions apply ( unless there are
* none ) we return true */
LIST_FOREACH ( conditions , c , first ) {
int r ;
r = condition_test ( c ) ;
if ( logger ) {
if ( r < 0 )
logger ( userdata , LOG_WARNING , r , __FILE__ , __LINE__ , __func__ ,
" Couldn't determine result for %s=%s%s%s, assuming failed: %m " ,
to_string ( c - > type ) ,
c - > trigger ? " | " : " " ,
c - > negate ? " ! " : " " ,
c - > parameter ) ;
else
logger ( userdata , LOG_DEBUG , 0 , __FILE__ , __LINE__ , __func__ ,
" %s=%s%s%s %s. " ,
to_string ( c - > type ) ,
c - > trigger ? " | " : " " ,
c - > negate ? " ! " : " " ,
c - > parameter ,
condition_result_to_string ( c - > result ) ) ;
}
if ( ! c - > trigger & & r < = 0 )
return false ;
if ( c - > trigger & & triggered < = 0 )
triggered = r > 0 ;
}
return triggered ! = 0 ;
}
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 " ,
2019-03-18 19:44:18 +03:00
[ CONDITION_NULL ] = " ConditionNull " ,
[ CONDITION_CPUS ] = " ConditionCPUs " ,
[ CONDITION_MEMORY ] = " ConditionMemory " ,
2014-02-20 22:26:54 +04:00
} ;
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 " ,
2019-03-18 19:44:18 +03:00
[ CONDITION_NULL ] = " AssertNull " ,
[ CONDITION_CPUS ] = " AssertCPUs " ,
[ CONDITION_MEMORY ] = " AssertMemory " ,
2014-11-06 15:43:45 +03:00
} ;
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 ) ;