2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-06-22 01:27:18 +04:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <unistd.h>
# include <signal.h>
# include <string.h>
# include <stdlib.h>
2010-07-12 20:16:44 +04:00
# include <dirent.h>
2010-07-21 06:32:44 +04:00
# include <sys/stat.h>
# include <sys/types.h>
2010-06-22 01:27:18 +04:00
# include "cgroup-util.h"
# include "log.h"
# include "set.h"
# include "macro.h"
# include "util.h"
2010-07-11 02:50:49 +04:00
int cg_enumerate_processes ( const char * controller , const char * path , FILE * * _f ) {
char * fs ;
int r ;
FILE * f ;
assert ( controller ) ;
assert ( path ) ;
assert ( _f ) ;
if ( ( r = cg_get_path ( controller , path , " cgroup.procs " , & fs ) ) < 0 )
return r ;
f = fopen ( fs , " re " ) ;
free ( fs ) ;
if ( ! f )
return - errno ;
* _f = f ;
return 0 ;
}
int cg_enumerate_tasks ( const char * controller , const char * path , FILE * * _f ) {
char * fs ;
int r ;
FILE * f ;
assert ( controller ) ;
assert ( path ) ;
assert ( _f ) ;
if ( ( r = cg_get_path ( controller , path , " tasks " , & fs ) ) < 0 )
return r ;
f = fopen ( fs , " re " ) ;
free ( fs ) ;
if ( ! f )
return - errno ;
* _f = f ;
return 0 ;
}
int cg_read_pid ( FILE * f , pid_t * _pid ) {
unsigned long ul ;
/* Note that the cgroup.procs might contain duplicates! See
* cgroups . txt for details . */
errno = 0 ;
if ( fscanf ( f , " %lu " , & ul ) ! = 1 ) {
if ( feof ( f ) )
return 0 ;
return errno ? - errno : - EIO ;
}
if ( ul < = 0 )
return - EIO ;
* _pid = ( pid_t ) ul ;
return 1 ;
}
2010-07-12 20:16:44 +04:00
int cg_enumerate_subgroups ( const char * controller , const char * path , DIR * * _d ) {
char * fs ;
int r ;
DIR * d ;
assert ( controller ) ;
assert ( path ) ;
assert ( _d ) ;
/* This is not recursive! */
if ( ( r = cg_get_path ( controller , path , NULL , & fs ) ) < 0 )
return r ;
d = opendir ( fs ) ;
free ( fs ) ;
if ( ! d )
return - errno ;
* _d = d ;
return 0 ;
}
int cg_read_subgroup ( DIR * d , char * * fn ) {
struct dirent * de ;
assert ( d ) ;
errno = 0 ;
while ( ( de = readdir ( d ) ) ) {
char * b ;
if ( de - > d_type ! = DT_DIR )
continue ;
if ( streq ( de - > d_name , " . " ) | |
streq ( de - > d_name , " .. " ) )
continue ;
if ( ! ( b = strdup ( de - > d_name ) ) )
return - ENOMEM ;
* fn = b ;
return 1 ;
}
if ( errno )
return - errno ;
return 0 ;
}
int cg_rmdir ( const char * controller , const char * path ) {
char * p ;
int r ;
if ( ( r = cg_get_path ( controller , path , NULL , & p ) ) < 0 )
return r ;
r = rmdir ( p ) ;
free ( p ) ;
return r < 0 ? - errno : 0 ;
}
2010-09-01 01:24:47 +04:00
int cg_kill ( const char * controller , const char * path , int sig , bool ignore_self , Set * s ) {
2010-07-12 20:16:44 +04:00
bool done = false ;
2010-06-22 01:27:18 +04:00
int r , ret = 0 ;
2010-07-12 20:16:44 +04:00
pid_t my_pid ;
2010-07-11 02:50:49 +04:00
FILE * f = NULL ;
2010-09-01 01:24:47 +04:00
Set * allocated_set = NULL ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( path ) ;
assert ( sig > = 0 ) ;
/* This goes through the tasks list and kills them all. This
* is repeated until no further processes are added to the
* tasks list , to properly handle forking processes */
2010-09-01 01:24:47 +04:00
if ( ! s )
if ( ! ( s = allocated_set = set_new ( trivial_hash_func , trivial_compare_func ) ) )
return - ENOMEM ;
2010-06-22 01:27:18 +04:00
my_pid = getpid ( ) ;
do {
2010-07-11 02:50:49 +04:00
pid_t pid ;
2010-06-22 01:27:18 +04:00
done = true ;
2010-07-12 20:16:44 +04:00
if ( ( r = cg_enumerate_processes ( controller , path , & f ) ) < 0 ) {
2010-07-13 21:00:01 +04:00
if ( ret > = 0 & & r ! = - ENOENT )
2010-07-12 20:16:44 +04:00
ret = r ;
2010-07-11 02:50:49 +04:00
goto finish ;
2010-07-12 20:16:44 +04:00
}
2010-07-11 02:50:49 +04:00
while ( ( r = cg_read_pid ( f , & pid ) ) > 0 ) {
2010-06-22 01:27:18 +04:00
if ( pid = = my_pid & & ignore_self )
2010-07-11 02:50:49 +04:00
continue ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( set_get ( s , LONG_TO_PTR ( pid ) ) = = LONG_TO_PTR ( pid ) )
continue ;
2010-06-22 01:27:18 +04:00
/* If we haven't killed this process yet, kill
* it */
2010-07-13 21:00:01 +04:00
if ( kill ( pid , sig ) < 0 ) {
if ( ret > = 0 & & errno ! = ESRCH )
2010-06-22 01:27:18 +04:00
ret = - errno ;
2010-07-12 20:16:44 +04:00
} else if ( ret = = 0 )
ret = 1 ;
2010-06-22 01:27:18 +04:00
done = false ;
2010-07-12 20:16:44 +04:00
if ( ( r = set_put ( s , LONG_TO_PTR ( pid ) ) ) < 0 ) {
if ( ret > = 0 )
ret = r ;
goto finish ;
}
}
if ( r < 0 ) {
if ( ret > = 0 )
ret = r ;
goto finish ;
2010-06-22 01:27:18 +04:00
}
2010-07-11 02:50:49 +04:00
fclose ( f ) ;
f = NULL ;
2010-06-22 01:27:18 +04:00
/* To avoid racing against processes which fork
* quicker than we can kill them we repeat this until
* no new pids need to be killed . */
2010-07-12 20:16:44 +04:00
} while ( ! done ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
finish :
2010-09-01 01:24:47 +04:00
if ( allocated_set )
set_free ( allocated_set ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( f )
fclose ( f ) ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
return ret ;
2010-06-22 01:27:18 +04:00
}
2010-09-01 01:24:47 +04:00
int cg_kill_recursive ( const char * controller , const char * path , int sig , bool ignore_self , bool rem , Set * s ) {
2010-07-12 20:16:44 +04:00
int r , ret = 0 ;
DIR * d = NULL ;
char * fn ;
2010-09-01 01:24:47 +04:00
Set * allocated_set = NULL ;
2010-06-22 01:27:18 +04:00
assert ( path ) ;
assert ( controller ) ;
assert ( sig > = 0 ) ;
2010-09-01 01:24:47 +04:00
if ( ! s )
if ( ! ( s = allocated_set = set_new ( trivial_hash_func , trivial_compare_func ) ) )
return - ENOMEM ;
ret = cg_kill ( controller , path , sig , ignore_self , s ) ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
if ( ( r = cg_enumerate_subgroups ( controller , path , & d ) ) < 0 ) {
2010-07-13 21:00:01 +04:00
if ( ret > = 0 & & r ! = - ENOENT )
2010-07-12 20:16:44 +04:00
ret = r ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
goto finish ;
}
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
while ( ( r = cg_read_subgroup ( d , & fn ) ) > 0 ) {
char * p = NULL ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
r = asprintf ( & p , " %s/%s " , path , fn ) ;
free ( fn ) ;
if ( r < 0 ) {
if ( ret > = 0 )
ret = - ENOMEM ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
goto finish ;
}
2010-06-22 01:27:18 +04:00
2010-09-01 01:24:47 +04:00
r = cg_kill_recursive ( controller , p , sig , ignore_self , rem , s ) ;
2010-07-12 20:16:44 +04:00
free ( p ) ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
if ( r ! = 0 & & ret > = 0 )
ret = r ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
if ( r < 0 & & ret > = 0 )
ret = r ;
if ( rem )
if ( ( r = cg_rmdir ( controller , path ) ) < 0 ) {
2010-11-18 04:36:51 +03:00
if ( ret > = 0 & &
r ! = - ENOENT & &
r ! = - EBUSY )
2010-07-12 20:16:44 +04:00
ret = r ;
}
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
finish :
if ( d )
closedir ( d ) ;
2010-06-22 01:27:18 +04:00
2010-09-01 01:24:47 +04:00
if ( allocated_set )
set_free ( allocated_set ) ;
2010-06-22 01:27:18 +04:00
return ret ;
}
2010-07-12 20:16:44 +04:00
int cg_kill_recursive_and_wait ( const char * controller , const char * path , bool rem ) {
2010-06-22 01:27:18 +04:00
unsigned i ;
assert ( path ) ;
assert ( controller ) ;
/* This safely kills all processes; first it sends a SIGTERM,
* then checks 8 times after 50 ms whether the group is
* now empty , and finally kills everything that is left with
* SIGKILL */
for ( i = 0 ; i < 10 ; i + + ) {
2010-06-23 04:10:19 +04:00
int sig , r ;
2010-06-22 01:27:18 +04:00
if ( i < = 0 )
sig = SIGTERM ;
else if ( i > = 9 )
sig = SIGKILL ;
else
sig = 0 ;
2010-09-01 01:24:47 +04:00
if ( ( r = cg_kill_recursive ( controller , path , sig , true , rem , NULL ) ) < = 0 )
2010-06-22 01:27:18 +04:00
return r ;
usleep ( 50 * USEC_PER_MSEC ) ;
}
return 0 ;
}
int cg_migrate ( const char * controller , const char * from , const char * to , bool ignore_self ) {
2010-07-12 20:16:44 +04:00
bool done = false ;
Set * s ;
2010-06-22 01:27:18 +04:00
int r , ret = 0 ;
pid_t my_pid ;
2010-07-11 02:50:49 +04:00
FILE * f = NULL ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( from ) ;
assert ( to ) ;
2010-07-12 20:16:44 +04:00
if ( ! ( s = set_new ( trivial_hash_func , trivial_compare_func ) ) )
return - ENOMEM ;
2010-06-22 01:27:18 +04:00
my_pid = getpid ( ) ;
do {
2010-07-11 02:50:49 +04:00
pid_t pid ;
2010-06-22 01:27:18 +04:00
done = true ;
2010-07-12 20:16:44 +04:00
if ( ( r = cg_enumerate_tasks ( controller , from , & f ) ) < 0 ) {
2010-07-13 21:00:01 +04:00
if ( ret > = 0 & & r ! = - ENOENT )
2010-07-12 20:16:44 +04:00
ret = r ;
2010-07-11 02:50:49 +04:00
goto finish ;
2010-07-12 20:16:44 +04:00
}
2010-07-11 02:50:49 +04:00
while ( ( r = cg_read_pid ( f , & pid ) ) > 0 ) {
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
/* This might do weird stuff if we aren't a
* single - threaded program . However , we
* luckily know we are not */
2010-06-22 01:27:18 +04:00
if ( pid = = my_pid & & ignore_self )
2010-07-11 02:50:49 +04:00
continue ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
if ( set_get ( s , LONG_TO_PTR ( pid ) ) = = LONG_TO_PTR ( pid ) )
continue ;
2010-07-11 02:50:49 +04:00
if ( ( r = cg_attach ( controller , to , pid ) ) < 0 ) {
2010-07-13 21:00:01 +04:00
if ( ret > = 0 & & r ! = - ESRCH )
2010-07-12 20:16:44 +04:00
ret = r ;
} else if ( ret = = 0 )
ret = 1 ;
2010-06-22 01:27:18 +04:00
done = false ;
2010-07-12 20:16:44 +04:00
if ( ( r = set_put ( s , LONG_TO_PTR ( pid ) ) ) < 0 ) {
if ( ret > = 0 )
ret = r ;
goto finish ;
}
}
if ( r < 0 ) {
if ( ret > = 0 )
ret = r ;
goto finish ;
2010-06-22 01:27:18 +04:00
}
2010-07-11 02:50:49 +04:00
fclose ( f ) ;
f = NULL ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
} while ( ! done ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
finish :
2010-07-12 20:16:44 +04:00
set_free ( s ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( f )
fclose ( f ) ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
return ret ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
int cg_migrate_recursive ( const char * controller , const char * from , const char * to , bool ignore_self , bool rem ) {
int r , ret = 0 ;
DIR * d = NULL ;
char * fn ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( from ) ;
assert ( to ) ;
2010-07-12 20:16:44 +04:00
ret = cg_migrate ( controller , from , to , ignore_self ) ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
if ( ( r = cg_enumerate_subgroups ( controller , from , & d ) ) < 0 ) {
2010-07-13 21:00:01 +04:00
if ( ret > = 0 & & r ! = - ENOENT )
2010-07-12 20:16:44 +04:00
ret = r ;
goto finish ;
}
while ( ( r = cg_read_subgroup ( d , & fn ) ) > 0 ) {
char * p = NULL ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
r = asprintf ( & p , " %s/%s " , from , fn ) ;
free ( fn ) ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
if ( r < 0 ) {
if ( ret > = 0 )
ret = - ENOMEM ;
goto finish ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
r = cg_migrate_recursive ( controller , p , to , ignore_self , rem ) ;
2010-06-22 01:27:18 +04:00
free ( p ) ;
2010-07-12 20:16:44 +04:00
if ( r ! = 0 & & ret > = 0 )
ret = r ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
if ( r < 0 & & ret > = 0 )
ret = r ;
if ( rem )
if ( ( r = cg_rmdir ( controller , from ) ) < 0 ) {
2010-11-18 04:36:51 +03:00
if ( ret > = 0 & &
r ! = - ENOENT & &
r ! = - EBUSY )
2010-07-12 20:16:44 +04:00
ret = r ;
}
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
finish :
if ( d )
closedir ( d ) ;
2010-06-22 01:27:18 +04:00
return ret ;
}
int cg_get_path ( const char * controller , const char * path , const char * suffix , char * * fs ) {
2010-07-12 05:15:20 +04:00
const char * p ;
2010-06-22 01:27:18 +04:00
char * mp ;
int r ;
assert ( controller ) ;
2010-07-12 05:15:20 +04:00
assert ( fs ) ;
/* This is a very minimal lookup from controller names to
* paths . Since we have mounted most hierarchies ourselves
* should be kinda safe , but eventually we might want to
* extend this to have a fallback to actually check
* / proc / mounts . Might need caching then . */
if ( streq ( controller , SYSTEMD_CGROUP_CONTROLLER ) )
p = " systemd " ;
else if ( startswith ( controller , " name= " ) )
p = controller + 5 ;
else
p = controller ;
2010-08-25 06:58:16 +04:00
if ( asprintf ( & mp , " /sys/fs/cgroup/%s " , p ) < 0 )
2010-07-12 05:15:20 +04:00
return - ENOMEM ;
2010-06-22 01:27:18 +04:00
2010-07-12 05:15:20 +04:00
if ( ( r = path_is_mount_point ( mp ) ) < = 0 ) {
free ( mp ) ;
return r < 0 ? r : - ENOENT ;
}
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( path & & suffix )
2010-06-22 01:27:18 +04:00
r = asprintf ( fs , " %s/%s/%s " , mp , path , suffix ) ;
2010-07-11 02:50:49 +04:00
else if ( path )
2010-06-22 01:27:18 +04:00
r = asprintf ( fs , " %s/%s " , mp , path ) ;
2010-07-11 02:50:49 +04:00
else if ( suffix )
r = asprintf ( fs , " %s/%s " , mp , suffix ) ;
else {
path_kill_slashes ( mp ) ;
* fs = mp ;
return 0 ;
}
2010-06-22 01:27:18 +04:00
free ( mp ) ;
2010-07-11 02:50:49 +04:00
path_kill_slashes ( * fs ) ;
2010-06-22 01:27:18 +04:00
return r < 0 ? - ENOMEM : 0 ;
}
int cg_trim ( const char * controller , const char * path , bool delete_root ) {
char * fs ;
int r ;
assert ( controller ) ;
assert ( path ) ;
if ( ( r = cg_get_path ( controller , path , NULL , & fs ) ) < 0 )
return r ;
r = rm_rf ( fs , true , delete_root ) ;
free ( fs ) ;
2010-07-13 21:00:01 +04:00
return r = = - ENOENT ? 0 : r ;
2010-06-22 01:27:18 +04:00
}
int cg_delete ( const char * controller , const char * path ) {
2010-07-12 20:16:44 +04:00
char * parent ;
2010-06-22 01:27:18 +04:00
int r ;
assert ( controller ) ;
assert ( path ) ;
2010-07-12 20:16:44 +04:00
if ( ( r = parent_of_path ( path , & parent ) ) < 0 )
return r ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
r = cg_migrate_recursive ( controller , path , parent , false , true ) ;
free ( parent ) ;
2010-06-22 01:27:18 +04:00
2010-07-13 21:00:01 +04:00
return r = = - ENOENT ? 0 : r ;
2010-06-22 01:27:18 +04:00
}
int cg_create ( const char * controller , const char * path ) {
2010-07-11 02:50:49 +04:00
char * fs ;
2010-06-22 01:27:18 +04:00
int r ;
assert ( controller ) ;
assert ( path ) ;
2010-07-11 02:50:49 +04:00
if ( ( r = cg_get_path ( controller , path , NULL , & fs ) ) < 0 )
return r ;
2010-06-22 01:27:18 +04:00
2010-07-21 06:32:44 +04:00
r = mkdir_parents ( fs , 0755 ) ;
if ( r > = 0 ) {
if ( mkdir ( fs , 0755 ) > = 0 )
r = 1 ;
else if ( errno = = EEXIST )
r = 0 ;
else
r = - errno ;
}
2010-07-11 02:50:49 +04:00
free ( fs ) ;
2010-06-22 01:27:18 +04:00
return r ;
}
int cg_attach ( const char * controller , const char * path , pid_t pid ) {
2010-07-11 02:50:49 +04:00
char * fs ;
2010-06-22 01:27:18 +04:00
int r ;
2010-07-11 02:50:49 +04:00
char c [ 32 ] ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( path ) ;
assert ( pid > = 0 ) ;
2010-07-11 02:50:49 +04:00
if ( ( r = cg_get_path ( controller , path , " tasks " , & fs ) ) < 0 )
return r ;
2010-06-22 01:27:18 +04:00
if ( pid = = 0 )
pid = getpid ( ) ;
2010-07-11 02:50:49 +04:00
snprintf ( c , sizeof ( c ) , " %lu \n " , ( unsigned long ) pid ) ;
char_array_0 ( c ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
r = write_one_line_file ( fs , c ) ;
free ( fs ) ;
2010-06-22 01:27:18 +04:00
return r ;
}
int cg_create_and_attach ( const char * controller , const char * path , pid_t pid ) {
2010-07-21 06:32:44 +04:00
int r , q ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( path ) ;
assert ( pid > = 0 ) ;
2010-07-11 02:50:49 +04:00
if ( ( r = cg_create ( controller , path ) ) < 0 )
return r ;
2010-06-22 01:27:18 +04:00
2010-07-21 06:32:44 +04:00
if ( ( q = cg_attach ( controller , path , pid ) ) < 0 )
return q ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
/* This does not remove the cgroup on failure */
2010-06-22 01:27:18 +04:00
return r ;
}
int cg_set_group_access ( const char * controller , const char * path , mode_t mode , uid_t uid , gid_t gid ) {
char * fs ;
int r ;
assert ( controller ) ;
assert ( path ) ;
if ( ( r = cg_get_path ( controller , path , NULL , & fs ) ) < 0 )
return r ;
r = chmod_and_chown ( fs , mode , uid , gid ) ;
free ( fs ) ;
return r ;
}
int cg_set_task_access ( const char * controller , const char * path , mode_t mode , uid_t uid , gid_t gid ) {
char * fs ;
int r ;
assert ( controller ) ;
assert ( path ) ;
if ( ( r = cg_get_path ( controller , path , " tasks " , & fs ) ) < 0 )
return r ;
r = chmod_and_chown ( fs , mode , uid , gid ) ;
free ( fs ) ;
return r ;
}
int cg_get_by_pid ( const char * controller , pid_t pid , char * * path ) {
int r ;
char * p = NULL ;
2010-07-11 02:50:49 +04:00
FILE * f ;
char * fs ;
size_t cs ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( path ) ;
2010-07-11 02:50:49 +04:00
assert ( pid > = 0 ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( pid = = 0 )
pid = getpid ( ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( asprintf ( & fs , " /proc/%lu/cgroup " , ( unsigned long ) pid ) < 0 )
return - ENOMEM ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
f = fopen ( fs , " re " ) ;
free ( fs ) ;
2010-07-13 21:00:01 +04:00
if ( ! f )
return errno = = ENOENT ? - ESRCH : - errno ;
2010-07-11 02:50:49 +04:00
cs = strlen ( controller ) ;
while ( ! feof ( f ) ) {
char line [ LINE_MAX ] ;
char * l ;
errno = 0 ;
if ( ! ( fgets ( line , sizeof ( line ) , f ) ) ) {
if ( feof ( f ) )
break ;
r = errno ? - errno : - EIO ;
goto finish ;
}
truncate_nl ( line ) ;
if ( ! ( l = strchr ( line , ' : ' ) ) )
continue ;
l + + ;
if ( strncmp ( l , controller , cs ) ! = 0 )
continue ;
if ( l [ cs ] ! = ' : ' )
continue ;
if ( ! ( p = strdup ( l + cs + 1 ) ) ) {
r = - ENOMEM ;
goto finish ;
}
* path = p ;
r = 0 ;
goto finish ;
}
r = - ENOENT ;
finish :
fclose ( f ) ;
return r ;
2010-06-22 01:27:18 +04:00
}
int cg_install_release_agent ( const char * controller , const char * agent ) {
2010-07-11 02:50:49 +04:00
char * fs = NULL , * contents = NULL , * line = NULL , * sc ;
2010-06-22 01:27:18 +04:00
int r ;
assert ( controller ) ;
assert ( agent ) ;
2010-07-11 02:50:49 +04:00
if ( ( r = cg_get_path ( controller , NULL , " release_agent " , & fs ) ) < 0 )
return r ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( ( r = read_one_line_file ( fs , & contents ) ) < 0 )
2010-06-22 01:27:18 +04:00
goto finish ;
sc = strstrip ( contents ) ;
if ( sc [ 0 ] = = 0 ) {
if ( asprintf ( & line , " %s \n " , agent ) < 0 ) {
r = - ENOMEM ;
goto finish ;
}
2010-07-11 02:50:49 +04:00
if ( ( r = write_one_line_file ( fs , line ) ) < 0 )
2010-06-22 01:27:18 +04:00
goto finish ;
} else if ( ! streq ( sc , agent ) ) {
r = - EEXIST ;
goto finish ;
}
2010-07-11 02:50:49 +04:00
free ( fs ) ;
fs = NULL ;
2010-08-12 00:04:22 +04:00
if ( ( r = cg_get_path ( controller , NULL , " notify_on_release " , & fs ) ) < 0 )
2010-06-22 01:27:18 +04:00
goto finish ;
free ( contents ) ;
contents = NULL ;
2010-07-11 02:50:49 +04:00
if ( ( r = read_one_line_file ( fs , & contents ) ) < 0 )
2010-06-22 01:27:18 +04:00
goto finish ;
sc = strstrip ( contents ) ;
if ( streq ( sc , " 0 " ) ) {
2010-07-11 02:50:49 +04:00
if ( ( r = write_one_line_file ( fs , " 1 \n " ) ) < 0 )
2010-06-22 01:27:18 +04:00
goto finish ;
2010-07-11 02:50:49 +04:00
r = 1 ;
2010-06-22 01:27:18 +04:00
} else if ( ! streq ( sc , " 1 " ) ) {
r = - EIO ;
goto finish ;
2010-07-11 02:50:49 +04:00
} else
r = 0 ;
2010-06-22 01:27:18 +04:00
finish :
2010-07-11 02:50:49 +04:00
free ( fs ) ;
2010-06-22 01:27:18 +04:00
free ( contents ) ;
free ( line ) ;
return r ;
}
int cg_is_empty ( const char * controller , const char * path , bool ignore_self ) {
2010-07-11 02:50:49 +04:00
pid_t pid ;
2010-06-22 01:27:18 +04:00
int r ;
2010-07-11 02:50:49 +04:00
FILE * f ;
bool found = false ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( path ) ;
2010-07-11 02:50:49 +04:00
if ( ( r = cg_enumerate_tasks ( controller , path , & f ) ) < 0 )
2010-07-13 21:00:01 +04:00
return r = = - ENOENT ? 1 : r ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
while ( ( r = cg_read_pid ( f , & pid ) ) > 0 ) {
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( ignore_self & & pid = = getpid ( ) )
continue ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
found = true ;
break ;
2010-06-22 01:27:18 +04:00
}
2010-07-11 02:50:49 +04:00
fclose ( f ) ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
if ( r < 0 )
return r ;
2010-06-22 01:27:18 +04:00
2010-07-11 02:50:49 +04:00
return ! found ;
2010-06-22 01:27:18 +04:00
}
int cg_is_empty_recursive ( const char * controller , const char * path , bool ignore_self ) {
2010-07-12 20:16:44 +04:00
int r ;
DIR * d = NULL ;
char * fn ;
2010-06-22 01:27:18 +04:00
assert ( controller ) ;
assert ( path ) ;
2010-07-12 20:16:44 +04:00
if ( ( r = cg_is_empty ( controller , path , ignore_self ) ) < = 0 )
return r ;
if ( ( r = cg_enumerate_subgroups ( controller , path , & d ) ) < 0 )
2010-07-13 21:00:01 +04:00
return r = = - ENOENT ? 1 : r ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
while ( ( r = cg_read_subgroup ( d , & fn ) ) > 0 ) {
char * p = NULL ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
r = asprintf ( & p , " %s/%s " , path , fn ) ;
free ( fn ) ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
if ( r < 0 ) {
r = - ENOMEM ;
goto finish ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
r = cg_is_empty_recursive ( controller , p , ignore_self ) ;
2010-06-22 01:27:18 +04:00
free ( p ) ;
2010-07-12 20:16:44 +04:00
if ( r < = 0 )
goto finish ;
}
if ( r > = 0 )
r = 1 ;
finish :
if ( d )
closedir ( d ) ;
return r ;
}
int cg_split_spec ( const char * spec , char * * controller , char * * path ) {
const char * e ;
char * t = NULL , * u = NULL ;
assert ( spec ) ;
assert ( controller | | path ) ;
if ( * spec = = ' / ' ) {
if ( path ) {
if ( ! ( t = strdup ( spec ) ) )
return - ENOMEM ;
* path = t ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
if ( controller )
* controller = NULL ;
return 0 ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
if ( ! ( e = strchr ( spec , ' : ' ) ) ) {
if ( strchr ( spec , ' / ' ) | | spec [ 0 ] = = 0 )
return - EINVAL ;
if ( controller ) {
if ( ! ( t = strdup ( spec ) ) )
return - ENOMEM ;
* controller = t ;
}
if ( path )
* path = NULL ;
return 0 ;
2010-06-22 01:27:18 +04:00
}
2010-07-12 20:16:44 +04:00
if ( e [ 1 ] ! = ' / ' | |
e = = spec | |
memchr ( spec , ' / ' , e - spec ) )
return - EINVAL ;
2010-06-22 01:27:18 +04:00
2010-07-12 20:16:44 +04:00
if ( controller )
if ( ! ( t = strndup ( spec , e - spec ) ) )
return - ENOMEM ;
if ( path )
if ( ! ( u = strdup ( e + 1 ) ) ) {
free ( t ) ;
return - ENOMEM ;
}
if ( controller )
* controller = t ;
if ( path )
* path = u ;
return 0 ;
2010-06-22 01:27:18 +04:00
}
2010-07-11 02:50:49 +04:00
2010-07-12 20:16:44 +04:00
int cg_join_spec ( const char * controller , const char * path , char * * spec ) {
assert ( controller ) ;
assert ( path ) ;
2010-07-11 02:50:49 +04:00
2010-07-12 20:16:44 +04:00
if ( ! path_is_absolute ( path ) | |
controller [ 0 ] = = 0 | |
strchr ( controller , ' : ' ) | |
strchr ( controller , ' / ' ) )
return - EINVAL ;
if ( asprintf ( spec , " %s:%s " , controller , path ) < 0 )
return - ENOMEM ;
2010-07-11 02:50:49 +04:00
return 0 ;
}
2010-07-12 20:16:44 +04:00
int cg_fix_path ( const char * path , char * * result ) {
char * t , * c , * p ;
int r ;
assert ( path ) ;
assert ( result ) ;
/* First check if it already is a filesystem path */
if ( path_is_absolute ( path ) & &
2010-08-25 06:58:16 +04:00
path_startswith ( path , " /sys/fs/cgroup " ) & &
2010-07-12 20:16:44 +04:00
access ( path , F_OK ) > = 0 ) {
if ( ! ( t = strdup ( path ) ) )
return - ENOMEM ;
* result = t ;
return 0 ;
}
/* Otherwise treat it as cg spec */
if ( ( r = cg_split_spec ( path , & c , & p ) ) < 0 )
return r ;
r = cg_get_path ( c ? c : SYSTEMD_CGROUP_CONTROLLER , p ? p : " / " , NULL , result ) ;
free ( c ) ;
free ( p ) ;
return r ;
}