@ -3,7 +3,7 @@
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
Copyright 2013 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
@ -19,305 +19,554 @@
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <assert.h>
# include <unistd.h>
# include <sys/types.h>
# include <signal.h>
# include <sys/mount.h>
# include <fcntl.h>
# include "cgroup.h"
# include "cgroup-util.h"
# include "log.h"
# include "strv.h"
# include "path-util.h"
# include "special.h"
# include "cgroup-util.h"
# include "cgroup.h"
int cgroup_bonding_realize ( CGroupBonding * b ) {
int r ;
void cgroup_context_init ( CGroupContext * c ) {
assert ( c ) ;
/* Initialize everything to the kernel defaults, assuming the
* structure is preinitialized to 0 */
c - > cpu_shares = 1024 ;
c - > memory_limit = c - > memory_soft_limit = ( uint64_t ) - 1 ;
c - > blockio_weight = 1000 ;
}
void cgroup_context_free_device_allow ( CGroupContext * c , CGroupDeviceAllow * a ) {
assert ( c ) ;
assert ( a ) ;
LIST_REMOVE ( CGroupDeviceAllow , device_allow , c - > device_allow , a ) ;
free ( a - > path ) ;
free ( a ) ;
}
void cgroup_context_free_blockio_device_weight ( CGroupContext * c , CGroupBlockIODeviceWeight * w ) {
assert ( c ) ;
assert ( w ) ;
LIST_REMOVE ( CGroupBlockIODeviceWeight , device_weights , c - > blockio_device_weights , w ) ;
free ( w - > path ) ;
free ( w ) ;
}
void cgroup_context_free_blockio_device_bandwidth ( CGroupContext * c , CGroupBlockIODeviceBandwidth * b ) {
assert ( c ) ;
assert ( b ) ;
assert ( b - > path ) ;
assert ( b - > controller ) ;
r = cg_create ( b - > controller , b - > path , NULL ) ;
LIST_REMOVE ( CGroupBlockIODeviceBandwidth , device_bandwidths , c - > blockio_device_bandwidths , b ) ;
free ( b - > path ) ;
free ( b ) ;
}
void cgroup_context_done ( CGroupContext * c ) {
assert ( c ) ;
while ( c - > blockio_device_weights )
cgroup_context_free_blockio_device_weight ( c , c - > blockio_device_weights ) ;
while ( c - > blockio_device_bandwidths )
cgroup_context_free_blockio_device_bandwidth ( c , c - > blockio_device_bandwidths ) ;
while ( c - > device_allow )
cgroup_context_free_device_allow ( c , c - > device_allow ) ;
}
void cgroup_context_dump ( CGroupContext * c , FILE * f , const char * prefix ) {
CGroupBlockIODeviceBandwidth * b ;
CGroupBlockIODeviceWeight * w ;
CGroupDeviceAllow * a ;
assert ( c ) ;
assert ( f ) ;
prefix = strempty ( prefix ) ;
fprintf ( f ,
" %sCPUAccounting=%s \n "
" %sBlockIOAccounting=%s \n "
" %sMemoryAccounting=%s \n "
" %sCPUShares=%lu \n "
" %sBlockIOWeight%lu \n "
" %sMemoryLimit=% " PRIu64 " \n "
" %sMemorySoftLimit=% " PRIu64 " \n "
" %sDevicePolicy=%s \n " ,
prefix , yes_no ( c - > cpu_accounting ) ,
prefix , yes_no ( c - > blockio_accounting ) ,
prefix , yes_no ( c - > memory_accounting ) ,
prefix , c - > cpu_shares ,
prefix , c - > blockio_weight ,
prefix , c - > memory_limit ,
prefix , c - > memory_soft_limit ,
prefix , cgroup_device_policy_to_string ( c - > device_policy ) ) ;
LIST_FOREACH ( device_allow , a , c - > device_allow )
fprintf ( f ,
" %sDeviceAllow=%s %s%s%s \n " ,
prefix ,
a - > path ,
a - > r ? " r " : " " , a - > w ? " w " : " " , a - > m ? " m " : " " ) ;
LIST_FOREACH ( device_weights , w , c - > blockio_device_weights )
fprintf ( f ,
" %sBlockIOWeight=%s %lu " ,
prefix ,
w - > path ,
w - > weight ) ;
LIST_FOREACH ( device_bandwidths , b , c - > blockio_device_bandwidths ) {
char buf [ FORMAT_BYTES_MAX ] ;
fprintf ( f ,
" %s%s=%s %s \n " ,
prefix ,
b - > read ? " BlockIOReadBandwidth " : " BlockIOWriteBandwidth " ,
b - > path ,
format_bytes ( buf , sizeof ( buf ) , b - > bandwidth ) ) ;
}
}
static int lookup_blkio_device ( const char * p , dev_t * dev ) {
struct stat st ;
int r ;
assert ( p ) ;
assert ( dev ) ;
r = stat ( p , & st ) ;
if ( r < 0 ) {
log_warning ( " Failed to create cgroup %s:%s: %s " , b - > controller , b - > path , strerror ( - r ) ) ;
return r ;
log_warning ( " Couldn't stat device %s: %m " , p ) ;
return - e rrno ;
}
b - > realized = true ;
if ( S_ISBLK ( st . st_mode ) )
* dev = st . st_rdev ;
else if ( major ( st . st_dev ) ! = 0 ) {
/* If this is not a device node then find the block
* device this file is stored on */
* dev = st . st_dev ;
/* If this is a partition, try to get the originating
* block device */
block_get_whole_disk ( * dev , dev ) ;
} else {
log_warning ( " %s is not a block device and file system block device cannot be determined or is not local. " , p ) ;
return - ENODEV ;
}
return 0 ;
}
int cgroup_bonding_realize_list ( CGroupBonding * first ) {
CGroupBonding * b ;
static int whitelist_device ( const char * path , const char * node , const char * acc ) {
char buf [ 2 + DECIMAL_STR_MAX ( dev_t ) * 2 + 2 + 4 ] ;
struct stat st ;
int r ;
LIST_FOREACH ( by_unit , b , first )
if ( ( r = cgroup_bonding_realize ( b ) ) < 0 & & b - > essential )
return r ;
assert ( path ) ;
assert ( acc ) ;
return 0 ;
if ( stat ( node , & st ) < 0 ) {
log_warning ( " Couldn't stat device %s " , node ) ;
return - errno ;
}
if ( ! S_ISCHR ( st . st_mode ) & & ! S_ISBLK ( st . st_mode ) ) {
log_warning ( " %s is not a device. " , node ) ;
return - ENODEV ;
}
sprintf ( buf ,
" %c %u:%u %s " ,
S_ISCHR ( st . st_mode ) ? ' c ' : ' b ' ,
major ( st . st_rdev ) , minor ( st . st_rdev ) ,
acc ) ;
r = cg_set_attribute ( " devices " , path , " devices.allow " , buf ) ;
if ( r < 0 )
log_warning ( " Failed to set devices.allow on %s: %s " , path , strerror ( - r ) ) ;
return r ;
}
void cgroup_bonding_free ( CGroupBonding * b , bool trim ) {
assert ( b ) ;
void cgroup_context_apply ( CGroupContext * c , CGroupControllerMask mask , const char * path ) {
int r ;
assert ( c ) ;
assert ( path ) ;
if ( b - > unit ) {
CGroupBonding * f ;
if ( mask = = 0 )
return ;
LIST_REMOVE ( CGroupBonding , by_unit , b - > unit - > cgroup_bondings , b ) ;
if ( mask & CGROUP_CPU ) {
char buf [ DECIMAL_STR_MAX ( unsigned long ) + 1 ] ;
if ( streq ( b - > controller , SYSTEMD_CGROUP_CONTROLLER ) ) {
assert_se ( f = hashmap_get ( b - > unit - > manager - > cgroup_bondings , b - > path ) ) ;
LIST_REMOVE ( CGroupBonding , by_path , f , b ) ;
sprintf ( buf , " %lu \n " , c - > cpu_shares ) ;
r = cg_set_attribute ( " cpu " , path , " cpu.shares " , buf ) ;
if ( r < 0 )
log_warning ( " Failed to set cpu.shares on %s: %s " , path , strerror ( - r ) ) ;
}
if ( mask & CGROUP_BLKIO ) {
char buf [ MAX3 ( DECIMAL_STR_MAX ( unsigned long ) + 1 ,
DECIMAL_STR_MAX ( dev_t ) * 2 + 2 + DECIMAL_STR_MAX ( unsigned long ) * 1 ,
DECIMAL_STR_MAX ( dev_t ) * 2 + 2 + DECIMAL_STR_MAX ( uint64_t ) + 1 ) ] ;
CGroupBlockIODeviceWeight * w ;
CGroupBlockIODeviceBandwidth * b ;
sprintf ( buf , " %lu \n " , c - > blockio_weight ) ;
r = cg_set_attribute ( " blkio " , path , " blkio.weight " , buf ) ;
if ( r < 0 )
log_warning ( " Failed to set blkio.weight on %s: %s " , path , strerror ( - r ) ) ;
/* FIXME: no way to reset this list */
LIST_FOREACH ( device_weights , w , c - > blockio_device_weights ) {
dev_t dev ;
r = lookup_blkio_device ( w - > path , & dev ) ;
if ( r < 0 )
continue ;
if ( f )
hashmap_replace ( b - > unit - > manager - > cgroup_bondings , b - > path , f ) ;
else
hashmap_remove ( b - > unit - > manager - > cgroup_bondings , b - > path ) ;
sprintf ( buf , " %u:%u %lu " , major ( dev ) , minor ( dev ) , w - > weight ) ;
r = cg_set_attribute ( " blkio " , path , " blkio.weight_device " , buf ) ;
if ( r < 0 )
log_error ( " Failed to set blkio.weight_device on %s: %s " , path , strerror ( - r ) ) ;
}
/* FIXME: no way to reset this list */
LIST_FOREACH ( device_bandwidths , b , c - > blockio_device_bandwidths ) {
const char * a ;
dev_t dev ;
r = lookup_blkio_device ( b - > path , & dev ) ;
if ( r < 0 )
continue ;
a = b - > read ? " blkio.throttle.read_bps_device " : " blkio.throttle.write_bps_device " ;
sprintf ( buf , " %u:%u % " PRIu64 " \n " , major ( dev ) , minor ( dev ) , b - > bandwidth ) ;
r = cg_set_attribute ( " blkio " , path , a , buf ) ;
if ( r < 0 )
log_error ( " Failed to set %s on %s: %s " , a , path , strerror ( - r ) ) ;
}
}
if ( b - > realized & & b - > ours & & trim )
cg_trim ( b - > controller , b - > path , false ) ;
if ( mask & CGROUP_MEMORY ) {
char buf [ DECIMAL_STR_MAX ( uint64_t ) + 1 ] ;
free ( b - > controller ) ;
free ( b - > path ) ;
free ( b ) ;
}
sprint f( buf , " % " PRIu64 " \n " , c - > memory_limit ) ;
r = cg_set_attribute ( " memory " , path , " memory.limit_in_bytes " , buf ) ;
if ( r < 0 )
log_error ( " Failed to set memory.limit_in_bytes on %s: %s " , path , strerror ( - r ) ) ;
void cgroup_bonding_free_list ( CGroupBonding * first , bool remove_or_trim ) {
CGroupBonding * b , * n ;
sprintf ( buf , " % " PRIu64 " \n " , c - > memory_soft_limit ) ;
cg_set_attribute ( " memory " , path , " memory.soft_limit_in_bytes " , buf ) ;
if ( r < 0 )
log_error ( " Failed to set memory.limit_in_bytes on %s: %s " , path , strerror ( - r ) ) ;
}
LIST_FOREACH_SAFE ( by_unit , b , n , first )
cgroup_bonding_free ( b , remove_or_trim ) ;
}
if ( mask & CGROUP_DEVICE ) {
CGroupDeviceAllow * a ;
void cgroup_bonding_trim ( CGroupBonding * b , bool delete_root ) {
assert ( b ) ;
if ( c - > device_allow | | c - > device_policy ! = CGROUP_AUTO )
r = cg_set_attribute ( " devices " , path , " devices.deny " , " a " ) ;
else
r = cg_set_attribute ( " devices " , path , " devices.allow " , " a " ) ;
if ( r < 0 )
log_error ( " Failed to reset devices.list on %s: %s " , path , strerror ( - r ) ) ;
if ( b - > realized & & b - > ours )
cg_trim ( b - > controller , b - > path , delete_root ) ;
}
if ( c - > device_policy = = CGROUP_CLOSED | |
( c - > device_policy = = CGROUP_AUTO & & c - > device_allow ) ) {
static const char auto_devices [ ] =
" /dev/null \0 " " rw \0 "
" /dev/zero \0 " " rw \0 "
" /dev/full \0 " " rw \0 "
" /dev/random \0 " " rw \0 "
" /dev/urandom \0 " " rw \0 " ;
const char * x , * y ;
NULSTR_FOREACH_PAIR ( x , y , auto_devices )
whitelist_device ( path , x , y ) ;
}
LIST_FOREACH ( device_allow , a , c - > device_allow ) {
char acc [ 4 ] ;
unsigned k = 0 ;
if ( a - > r )
acc [ k + + ] = ' r ' ;
if ( a - > w )
acc [ k + + ] = ' w ' ;
if ( a - > m )
acc [ k + + ] = ' m ' ;
void cgroup_bonding_trim_list ( CGroupBonding * first , bool delete_root ) {
CGroupBonding * b ;
if ( k = = 0 )
continue ;
LIST_FOREACH ( by_unit , b , first )
cgroup_bonding_trim ( b , delete_root ) ;
acc [ k + + ] = 0 ;
whitelist_device ( path , a - > path , acc ) ;
}
}
}
int cgroup_bonding_install ( CGroupBonding * b , pid_t pid , const char * cgroup_suffix ) {
_cleanup_free_ char * p = NULL ;
const char * path ;
int r ;
CGroupControllerMask cgroup_context_get_mask ( CGroupContext * c ) {
CGroupControllerMask mask = 0 ;
assert ( b ) ;
assert ( pid > = 0 ) ;
/* Figure out which controllers we need */
if ( cgroup_suffix ) {
p = strjoin ( b - > path , " / " , cgroup_suffix , NULL ) ;
if ( ! p )
return - ENOMEM ;
if ( c - > cpu_accounting | | c - > cpu_shares ! = 1024 )
mask | = CGROUP_CPUACCT | CGROUP_CPU ;
path = p ;
} else
path = b - > path ;
if ( c - > blockio_accounting | |
c - > blockio_weight ! = 1000 | |
c - > blockio_device_weights | |
c - > blockio_device_bandwidths )
mask | = CGROUP_BLKIO ;
r = cg_create_and_attach ( b - > controller , path , pid ) ;
if ( r < 0 )
return r ;
if ( c - > memory_accounting | |
c - > memory_limit ! = ( uint64_t ) - 1 | |
c - > memory_soft_limit ! = ( uint64_t ) - 1 )
mask | = CGROUP_MEMORY ;
b - > realized = true ;
return 0 ;
if ( c - > device_allow | | c - > device_policy ! = CGROUP_AUTO )
mask | = CGROUP_DEVICE ;
return mask ;
}
int cgroup_bonding_install_list ( CGroupBonding * first , pid_t pid , const char * cgroup_suffix ) {
CGroupBonding * b ;
int r ;
static CGroupControllerMask unit_get_cgroup_mask ( Unit * u ) {
CGroupContext * c ;
LIST_FOREACH ( by_unit , b , first ) {
r = cgroup_bonding_install ( b , pid , cgroup_suffix ) ;
if ( r < 0 & & b - > essential )
return r ;
}
c = unit_get_cgroup_context ( u ) ;
if ( ! c )
return 0 ;
return 0 ;
return cgroup_context_get_mask ( c ) ;
}
int cgroup_bonding_migrate ( CGroupBonding * b , CGroupBonding * list ) {
CGroupBonding * q ;
int ret = 0 ;
static CGroupControllerMask unit_get_members_mask ( Unit * u ) {
CGroupControllerMask mask = 0 ;
Unit * m ;
Iterator i ;
LIST_FOREACH ( by_unit , q , list ) {
int r ;
assert ( u ) ;
if ( q = = b )
continue ;
SET_FOREACH ( m , u - > dependencies [ UNIT_BEFORE ] , i ) {
if ( ! q - > ours )
if ( UNIT_DEREF ( m - > slice ) ! = u )
continue ;
r = cg_migrate_recursive ( q - > controller , q - > path , b - > controller , b - > path , true , false ) ;
if ( r < 0 & & ret = = 0 )
ret = r ;
mask | = unit_get_cgroup_mask ( m ) | unit_get_members_mask ( m ) ;
}
return ret ;
return mask ;
}
int cgroup_bonding_migrate_to ( CGroupBonding * b , const char * target , bool rem ) {
assert ( b ) ;
assert ( target ) ;
static CGroupControllerMask unit_get_siblings_mask ( Unit * u ) {
assert ( u ) ;
return cg_migrate_recursive ( b - > controller , b - > path , b - > controller , target , true , rem ) ;
if ( ! UNIT_ISSET ( u - > slice ) )
return 0 ;
/* Sibling propagation is only relevant for weight-based
* controllers , so let ' s mask out everything else */
return unit_get_members_mask ( UNIT_DEREF ( u - > slice ) ) &
( CGROUP_CPU | CGROUP_BLKIO | CGROUP_CPUACCT ) ;
}
int cgroup_bonding_set_group_access ( CGroupBonding * b , mode_t mode , uid_t uid , gid_t gid ) {
assert ( b ) ;
static int unit_create_cgroups ( Unit * u , CGroupControllerMask mask ) {
char * path = NULL ;
int r ;
if ( ! b - > realized )
return - EINVAL ;
assert ( u ) ;
return cg_set_group_access ( b - > controller , b - > path , mode , uid , gid ) ;
}
path = unit_default_cgroup_path ( u ) ;
if ( ! path )
return - ENOMEM ;
int cgroup_bonding_set_group_access_list ( CGroupBonding * first , mode_t mode , uid_t uid , gid_t gid ) {
CGroupBonding * b ;
int r ;
/* First, create our own group */
r = cg_create_with_mask ( mask , path ) ;
if ( r < 0 )
log_error ( " Failed to create cgroup %s: %s " , path , strerror ( - r ) ) ;
LIST_FOREACH ( by_unit , b , first ) {
r = cgroup_bonding_set_group_access ( b , mode , uid , gid ) ;
/* Then, possibly move things over */
if ( u - > cgroup_path & & ! streq ( path , u - > cgroup_path ) ) {
r = cg_migrate_with_mask ( mask , u - > cgroup_path , path ) ;
if ( r < 0 )
return r ;
log_error ( " Failed to migrate cgroup %s: %s " , path , strerror ( - r ) ) ;
}
/* And remember the new data */
free ( u - > cgroup_path ) ;
u - > cgroup_path = path ;
u - > cgroup_realized = true ;
u - > cgroup_mask = mask ;
return 0 ;
}
int cgroup_bonding_set_task_access ( CGroupBonding * b , mode_t mode , uid_t uid , gid_t gid , int sticky ) {
assert ( b ) ;
static void unit_realize_cgroup_now ( Unit * u ) {
CGroupControllerMask mask ;
if ( ! b - > realized )
return - EINVAL ;
assert ( u ) ;
return cg_set_task_access ( b - > controller , b - > path , mode , uid , gid , sticky ) ;
}
if ( u - > in_cgroup_queue ) {
LIST_REMOVE ( Unit , cgroup_queue , u - > manager - > cgroup_queue , u ) ;
u - > in_cgroup_queue = false ;
}
int cgroup_bonding_set_task_access_list ( CGroupBonding * first , mode_t mode , uid_t uid , gid_t gid , int sticky ) {
CGroupBonding * b ;
int r ;
mask = unit_get_cgroup_mask ( u ) | unit_get_members_mask ( u ) | unit_get_siblings_mask ( u ) ;
mask & = u - > manager - > cgroup_supported ;
LIST_FOREACH ( by_unit , b , first ) {
r = cgroup_bonding_set_task_access ( b , mode , uid , gid , sticky ) ;
if ( r < 0 )
return r ;
}
if ( u - > cgroup_realized & &
u - > cgroup_mask = = mask )
return ;
return 0 ;
/* First, realize parents */
if ( UNIT_ISSET ( u - > slice ) )
unit_realize_cgroup_now ( UNIT_DEREF ( u - > slice ) ) ;
/* And then do the real work */
unit_create_cgroups ( u , mask ) ;
}
int cgroup_bonding_kill ( CGroupBonding * b , int sig , bool sigcont , bool rem , Set * s , const char * cgroup_suffix ) {
char * p = NULL ;
const char * path ;
int r ;
static void unit_add_to_cgroup_queue ( Unit * u ) {
assert ( b ) ;
assert ( sig > = 0 ) ;
if ( u - > in_cgroup_queue )
return ;
/* Don't kill cgroups that aren't ours */
if ( ! b - > ours )
return 0 ;
LIST_PREPEND ( Unit , cgroup_queue , u - > manager - > cgroup_queue , u ) ;
u - > in_cgroup_queue = true ;
}
if ( cgroup_suffix ) {
p = strjoin ( b - > path , " / " , cgroup_suffix , NULL ) ;
if ( ! p )
return - ENOMEM ;
unsigned manager_dispatch_cgroup_queue ( Manager * m ) {
Unit * i ;
unsigned n = 0 ;
path = p ;
} else
path = b - > path ;
while ( ( i = m - > cgroup_queue ) ) {
assert ( i - > in_cgroup_queue ) ;
r = cg_kill_recursive ( b - > controller , path , sig , sigcont , true , rem , s ) ;
free ( p ) ;
unit_realize_cgroup_now ( i ) ;
cgroup_context_apply ( unit_get_cgroup_context ( i ) , i - > cgroup_mask , i - > cgroup_path ) ;
n + + ;
}
return r ;
return n ;
}
int cgroup_bonding_kill_list ( CGroupBonding * first , int sig , bool sigcont , bool rem , Set * s , const char * cgroup_suffix ) {
CGroupBonding * b ;
Set * allocated_set = NULL ;
int ret = - EAGAIN , r ;
static void unit_queue_siblings ( Unit * u ) {
Unit * slice ;
if ( ! first )
return 0 ;
/* This adds the siblings of the specified unit and the
* siblings of all parent units to the cgroup queue . ( But
* neither the specified unit itself nor the parents . ) */
while ( ( slice = UNIT_DEREF ( u - > slice ) ) ) {
Iterator i ;
Unit * m ;
if ( ! s )
if ( ! ( s = allocated_set = set_new ( trivial_hash_func , trivial_compare_func ) ) )
return - ENOMEM ;
SET_FOREACH ( m , slice - > dependencies [ UNIT_BEFORE ] , i ) {
if ( m = = u )
continue ;
LIST_FOREACH ( by_unit , b , first ) {
r = cgroup_bonding_kill ( b , sig , sigcont , rem , s , cgroup_suffix ) ;
if ( r < 0 ) {
if ( r = = - EAGAIN | | r = = - ESRCH )
if ( UNIT_DEREF ( m - > slice ) ! = slice )
continue ;
ret = r ;
goto finish ;
unit_add_to_cgroup_queue ( m ) ;
}
if ( ret < 0 | | r > 0 )
ret = r ;
u = slice ;
}
}
void unit_realize_cgroup ( Unit * u ) {
CGroupContext * c ;
assert ( u ) ;
c = unit_get_cgroup_context ( u ) ;
if ( ! c )
return ;
finish :
if ( allocated_set )
set_free ( allocated_set ) ;
/* So, here's the deal: when realizing the cgroups for this
* unit , we need to first create all parents , but there ' s more
* actually : for the weight - based controllers we also need to
* make sure that all our siblings ( i . e . units that are in the
* same slice as we are ) have cgroup too . Otherwise things
* would become very uneven as each of their processes would
* get as much resources as all our group together . This call
* will synchronously create the parent cgroups , but will
* defer work on the siblings to the next event loop
* iteration . */
return ret ;
/* Add all sibling slices to the cgroup queue. */
unit_queue_siblings ( u ) ;
/* And realize this one now */
unit_realize_cgroup_now ( u ) ;
/* And apply the values */
cgroup_context_apply ( c , u - > cgroup_mask , u - > cgroup_path ) ;
}
/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
* cannot know */
int cgroup_bonding_is_empty ( CGroupBonding * b ) {
void unit_destroy_cgroup ( Unit * u ) {
int r ;
assert ( b ) ;
assert ( u ) ;
if ( ( r = cg_is_empty_recursive ( b - > controller , b - > path , true ) ) < 0 )
return r ;
if ( ! u - > cgroup_path )
return ;
/* If it is empty it is empty */
if ( r > 0 )
return 1 ;
r = cg_trim_with_mask ( u - > cgroup_mask , u - > cgroup_path , true ) ;
if ( r < 0 )
log_error ( " Failed to destroy cgroup %s: %s " , u - > cgroup_path , strerror ( - r ) ) ;
/* It's not only us using this cgroup, so we just don't know */
return b - > ours ? 0 : - EAGAIN ;
free ( u - > cgroup_path ) ;
u - > cgroup_path = NULL ;
u - > cgroup_realized = false ;
u - > cgroup_mask = 0 ;
}
int cgroup_bonding_is_empty_list ( CGroupBonding * first ) {
CGroupBonding * b ;
pid_t unit_search_main_pid ( Unit * u ) {
_cleanup_fclose_ FILE * f = NULL ;
pid_t pid = 0 , npid , mypid ;
assert ( u ) ;
if ( ! u - > cgroup_path )
return 0 ;
if ( cg_enumerate_processes ( SYSTEMD_CGROUP_CONTROLLER , u - > cgroup_path , & f ) < 0 )
return 0 ;
mypid = getpid ( ) ;
while ( cg_read_pid ( f , & npid ) > 0 ) {
pid_t ppid ;
if ( npid = = pid )
continue ;
LIST_FOREACH ( by_unit , b , first ) {
int r ;
/* Ignore processes that aren't our kids */
if ( get_parent_of_pid ( npid , & ppid ) > = 0 & & ppid ! = mypid )
continue ;
r = cgroup_bonding_is_empty ( b ) ;
if ( r < 0 ) {
/* If this returned -EAGAIN, then we don't know if the
* group is empty , so let ' s see if another group can
* tell us */
if ( pid ! = 0 ) {
/* Dang, there's more than one daemonized PID
in this group , so we don ' t know what process
is the main process . */
pid = 0 ;
break ;
}
if ( r ! = - EAGAIN )
return r ;
} else
return r ;
pid = npid ;
}
return - EAGAIN ;
return pid ;
}
int manager_setup_cgroup ( Manager * m ) {
@ -394,8 +643,8 @@ int manager_setup_cgroup(Manager *m) {
return - errno ;
}
/* 6. Remove non-existing controllers from the default controllers list */
cg_shorten_controllers ( m - > default_controllers ) ;
/* 6. Figure out which controllers are supported */
m - > cgroup_supported = cg_mask_supported ( ) ;
return 0 ;
}
@ -417,201 +666,71 @@ void manager_shutdown_cgroup(Manager *m, bool delete) {
m - > cgroup_root = NULL ;
}
int cgroup_bonding_get ( Manager * m , const char * cgroup , CGroupBonding * * bonding ) {
CGroupBonding * b ;
Unit * manager_get_unit_by_cgroup ( Manager * m , const char * cgroup ) {
char * p ;
Unit * u ;
assert ( m ) ;
assert ( cgroup ) ;
assert ( bonding ) ;
b = hashmap_get ( m - > cgroup_bondings , cgroup ) ;
if ( b ) {
* bonding = b ;
return 1 ;
}
u = hashmap_get ( m - > cgroup_unit , cgroup ) ;
if ( u )
return u ;
p = strdupa ( cgroup ) ;
if ( ! p )
return - ENOMEM ;
for ( ; ; ) {
char * e ;
e = strrchr ( p , ' / ' ) ;
if ( e = = p | | ! e ) {
* bonding = NULL ;
return 0 ;
}
if ( e = = p | | ! e )
return NULL ;
* e = 0 ;
b = hashmap_get ( m - > cgroup_bondings , p ) ;
if ( b ) {
* bonding = b ;
return 1 ;
}
u = hashmap_get ( m - > cgroup_unit , p ) ;
if ( u )
return u ;
}
}
int cgroup_notify_empty ( Manager * m , const char * group ) {
CGroupBonding * l , * b ;
Unit * manager_get_unit_by_pid ( Manager * m , pid_t pid ) {
_cleanup_free_ char * cgroup = NULL ;
int r ;
assert ( m ) ;
assert ( group ) ;
r = cgroup_bonding_get ( m , group , & l ) ;
if ( r < = 0 )
return r ;
LIST_FOREACH ( by_path , b , l ) {
int t ;