2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-03-31 18:29:55 +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 <assert.h>
# include <unistd.h>
# include <sys/types.h>
# include <signal.h>
# include <sys/mount.h>
2010-07-11 02:50:49 +04:00
# include <fcntl.h>
2010-06-22 01:27:18 +04:00
2010-03-31 18:29:55 +04:00
# include "cgroup.h"
2010-06-22 01:27:18 +04:00
# include "cgroup-util.h"
2010-03-31 18:29:55 +04:00
# include "log.h"
int cgroup_bonding_realize ( CGroupBonding * b ) {
int r ;
assert ( b ) ;
assert ( b - > path ) ;
assert ( b - > controller ) ;
2010-06-22 01:27:18 +04:00
if ( b - > realized )
2010-03-31 18:29:55 +04:00
return 0 ;
2010-06-22 01:27:18 +04:00
if ( ( r = cg_create ( b - > controller , b - > path ) ) < 0 )
return r ;
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
b - > realized = true ;
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
if ( b - > only_us & & b - > clean_up )
cg_trim ( b - > controller , b - > path , false ) ;
2010-03-31 18:29:55 +04:00
return 0 ;
}
int cgroup_bonding_realize_list ( CGroupBonding * first ) {
CGroupBonding * b ;
2010-06-22 01:27:18 +04:00
int r ;
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
LIST_FOREACH ( by_unit , b , first )
2010-03-31 18:29:55 +04:00
if ( ( r = cgroup_bonding_realize ( b ) ) < 0 )
return r ;
return 0 ;
}
void cgroup_bonding_free ( CGroupBonding * b ) {
assert ( b ) ;
if ( b - > unit ) {
CGroupBonding * f ;
LIST_REMOVE ( CGroupBonding , by_unit , b - > unit - > meta . cgroup_bondings , b ) ;
assert_se ( f = hashmap_get ( b - > unit - > meta . manager - > cgroup_bondings , b - > path ) ) ;
LIST_REMOVE ( CGroupBonding , by_path , f , b ) ;
if ( f )
hashmap_replace ( b - > unit - > meta . manager - > cgroup_bondings , b - > path , f ) ;
else
hashmap_remove ( b - > unit - > meta . manager - > cgroup_bondings , b - > path ) ;
}
2010-06-22 01:27:18 +04:00
if ( b - > realized & & b - > only_us & & b - > clean_up ) {
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
if ( cgroup_bonding_is_empty ( b ) > 0 )
cg_delete ( b - > controller , b - > path ) ;
else
cg_trim ( b - > controller , b - > path , false ) ;
2010-03-31 18:29:55 +04:00
}
2010-04-21 05:15:58 +04:00
free ( b - > controller ) ;
free ( b - > path ) ;
2010-03-31 18:29:55 +04:00
free ( b ) ;
}
void cgroup_bonding_free_list ( CGroupBonding * first ) {
CGroupBonding * b , * n ;
LIST_FOREACH_SAFE ( by_unit , b , n , first )
cgroup_bonding_free ( b ) ;
}
2010-07-10 19:34:42 +04:00
void cgroup_bonding_trim ( CGroupBonding * b , bool delete_root ) {
assert ( b ) ;
if ( b - > realized & & b - > only_us & & b - > clean_up )
cg_trim ( b - > controller , b - > path , delete_root ) ;
}
void cgroup_bonding_trim_list ( CGroupBonding * first , bool delete_root ) {
CGroupBonding * b ;
LIST_FOREACH ( by_unit , b , first )
cgroup_bonding_trim ( b , delete_root ) ;
}
2010-03-31 18:29:55 +04:00
int cgroup_bonding_install ( CGroupBonding * b , pid_t pid ) {
int r ;
assert ( b ) ;
assert ( pid > = 0 ) ;
2010-06-22 01:27:18 +04:00
if ( ( r = cg_create_and_attach ( b - > controller , b - > path , pid ) ) < 0 )
return r ;
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
b - > realized = true ;
2010-03-31 18:29:55 +04:00
return 0 ;
}
int cgroup_bonding_install_list ( CGroupBonding * first , pid_t pid ) {
CGroupBonding * b ;
2010-06-22 01:27:18 +04:00
int r ;
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
LIST_FOREACH ( by_unit , b , first )
2010-03-31 18:29:55 +04:00
if ( ( r = cgroup_bonding_install ( b , pid ) ) < 0 )
return r ;
return 0 ;
}
2010-09-01 01:24:47 +04:00
int cgroup_bonding_kill ( CGroupBonding * b , int sig , Set * s ) {
2010-03-31 18:29:55 +04:00
int r ;
assert ( b ) ;
2010-06-22 01:27:18 +04:00
assert ( sig > = 0 ) ;
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
if ( ( r = cgroup_bonding_realize ( b ) ) < 0 )
2010-04-08 02:52:14 +04:00
return r ;
2010-06-22 01:27:18 +04:00
assert ( b - > realized ) ;
2010-09-01 01:24:47 +04:00
return cg_kill_recursive ( b - > controller , b - > path , sig , true , false , s ) ;
2010-03-31 18:29:55 +04:00
}
2010-09-01 01:24:47 +04:00
int cgroup_bonding_kill_list ( CGroupBonding * first , int sig , Set * s ) {
2010-03-31 18:29:55 +04:00
CGroupBonding * b ;
2010-09-01 01:24:47 +04:00
Set * allocated_set = NULL ;
int ret = - EAGAIN , r ;
if ( ! s )
if ( ! ( s = allocated_set = set_new ( trivial_hash_func , trivial_compare_func ) ) )
return - ENOMEM ;
2010-03-31 18:29:55 +04:00
LIST_FOREACH ( by_unit , b , first ) {
2010-09-01 01:24:47 +04:00
if ( ( r = cgroup_bonding_kill ( b , sig , s ) ) < 0 ) {
2010-06-22 01:27:18 +04:00
if ( r = = - EAGAIN | | r = = - ESRCH )
2010-04-08 02:52:14 +04:00
continue ;
2010-03-31 18:29:55 +04:00
2010-09-01 01:24:47 +04:00
ret = r ;
goto finish ;
2010-04-08 02:52:14 +04:00
}
2010-09-01 01:24:47 +04:00
if ( ret < 0 | | r > 0 )
ret = r ;
2010-03-31 18:29:55 +04:00
}
2010-09-01 01:24:47 +04:00
finish :
if ( allocated_set )
set_free ( allocated_set ) ;
return ret ;
2010-03-31 18:29:55 +04:00
}
/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
* cannot know */
int cgroup_bonding_is_empty ( CGroupBonding * b ) {
int r ;
assert ( b ) ;
2010-06-22 01:27:18 +04:00
if ( ( r = cg_is_empty_recursive ( b - > controller , b - > path , true ) ) < 0 )
return r ;
2010-03-31 18:29:55 +04:00
2010-06-22 01:27:18 +04:00
/* If it is empty it is empty */
if ( r > 0 )
2010-03-31 18:29:55 +04:00
return 1 ;
2010-06-22 01:27:18 +04:00
/* It's not only us using this cgroup, so we just don't know */
return b - > only_us ? 0 : - EAGAIN ;
2010-03-31 18:29:55 +04:00
}
int cgroup_bonding_is_empty_list ( CGroupBonding * first ) {
CGroupBonding * b ;
LIST_FOREACH ( by_unit , b , first ) {
int r ;
if ( ( r = cgroup_bonding_is_empty ( b ) ) < 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 ( r ! = - EAGAIN )
return r ;
} else
return r ;
}
return - EAGAIN ;
}
int manager_setup_cgroup ( Manager * m ) {
2010-07-11 02:50:49 +04:00
char * current = NULL , * path = NULL ;
2010-03-31 18:29:55 +04:00
int r ;
2010-04-21 05:18:05 +04:00
char suffix [ 32 ] ;
2010-03-31 18:29:55 +04:00
assert ( m ) ;
2010-07-12 20:16:44 +04:00
/* 1. Determine hierarchy */
2010-07-11 02:50:49 +04:00
if ( ( r = cg_get_by_pid ( SYSTEMD_CGROUP_CONTROLLER , 0 , & current ) ) < 0 )
goto finish ;
2010-03-31 18:29:55 +04:00
2010-11-16 01:55:53 +03:00
if ( m - > running_as = = MANAGER_SYSTEM )
strcpy ( suffix , " /system " ) ;
else {
snprintf ( suffix , sizeof ( suffix ) , " /systemd-%lu " , ( unsigned long ) getpid ( ) ) ;
char_array_0 ( suffix ) ;
}
2010-04-21 05:18:05 +04:00
2010-03-31 18:29:55 +04:00
free ( m - > cgroup_hierarchy ) ;
2010-07-11 02:50:49 +04:00
if ( endswith ( current , suffix ) ) {
2010-04-21 05:18:05 +04:00
/* We probably got reexecuted and can continue to use our root cgroup */
2010-07-11 02:50:49 +04:00
m - > cgroup_hierarchy = current ;
current = NULL ;
2010-04-21 05:18:05 +04:00
2010-07-11 02:50:49 +04:00
} else {
/* We need a new root cgroup */
2010-04-21 05:18:05 +04:00
m - > cgroup_hierarchy = NULL ;
2010-08-12 00:04:22 +04:00
if ( asprintf ( & m - > cgroup_hierarchy , " %s%s " , streq ( current , " / " ) ? " " : current , suffix ) < 0 ) {
2010-07-11 02:50:49 +04:00
r = - ENOMEM ;
goto finish ;
}
2010-03-31 18:29:55 +04:00
}
2010-07-12 20:16:44 +04:00
/* 2. Show data */
2010-07-11 02:50:49 +04:00
if ( ( r = cg_get_path ( SYSTEMD_CGROUP_CONTROLLER , m - > cgroup_hierarchy , NULL , & path ) ) < 0 )
goto finish ;
2010-03-31 18:29:55 +04:00
2010-07-11 02:50:49 +04:00
log_debug ( " Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER " . File system hierarchy is at %s. " , path ) ;
2010-07-12 20:16:44 +04:00
/* 3. Install agent */
2010-11-11 05:04:45 +03:00
if ( ( r = cg_install_release_agent ( SYSTEMD_CGROUP_CONTROLLER , SYSTEMD_CGROUP_AGENT_PATH ) ) < 0 )
2010-03-31 18:29:55 +04:00
log_warning ( " Failed to install release agent, ignoring: %s " , strerror ( - r ) ) ;
2010-07-11 02:50:49 +04:00
else if ( r > 0 )
log_debug ( " Installed release agent. " ) ;
2010-03-31 18:29:55 +04:00
else
2010-07-11 02:50:49 +04:00
log_debug ( " Release agent already installed. " ) ;
2010-03-31 18:29:55 +04:00
2010-07-12 20:16:44 +04:00
/* 4. Realize the group */
2010-07-11 02:50:49 +04:00
if ( ( r = cg_create_and_attach ( SYSTEMD_CGROUP_CONTROLLER , m - > cgroup_hierarchy , 0 ) ) < 0 ) {
2010-03-31 18:29:55 +04:00
log_error ( " Failed to create root cgroup hierarchy: %s " , strerror ( - r ) ) ;
2010-07-11 02:50:49 +04:00
goto finish ;
}
2010-07-12 20:16:44 +04:00
/* 5. And pin it, so that it cannot be unmounted */
2010-07-11 02:50:49 +04:00
if ( m - > pin_cgroupfs_fd > = 0 )
close_nointr_nofail ( m - > pin_cgroupfs_fd ) ;
if ( ( m - > pin_cgroupfs_fd = open ( path , O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK ) ) < 0 ) {
r = - errno ;
goto finish ;
}
log_debug ( " Created root group. " ) ;
finish :
free ( current ) ;
free ( path ) ;
2010-03-31 18:29:55 +04:00
return r ;
}
2010-07-11 02:50:49 +04:00
void manager_shutdown_cgroup ( Manager * m , bool delete ) {
2010-03-31 18:29:55 +04:00
assert ( m ) ;
2010-07-11 02:50:49 +04:00
if ( delete & & m - > cgroup_hierarchy )
cg_delete ( SYSTEMD_CGROUP_CONTROLLER , m - > cgroup_hierarchy ) ;
2010-03-31 18:29:55 +04:00
2010-07-11 02:50:49 +04:00
if ( m - > pin_cgroupfs_fd > = 0 ) {
close_nointr_nofail ( m - > pin_cgroupfs_fd ) ;
m - > pin_cgroupfs_fd = - 1 ;
}
free ( m - > cgroup_hierarchy ) ;
m - > cgroup_hierarchy = NULL ;
2010-03-31 18:29:55 +04:00
}
int cgroup_notify_empty ( Manager * m , const char * group ) {
CGroupBonding * l , * b ;
assert ( m ) ;
assert ( group ) ;
if ( ! ( l = hashmap_get ( m - > cgroup_bondings , group ) ) )
return 0 ;
LIST_FOREACH ( by_path , b , l ) {
int t ;
if ( ! b - > unit )
continue ;
if ( ( t = cgroup_bonding_is_empty_list ( b ) ) < 0 ) {
/* If we don't know, we don't know */
if ( t ! = - EAGAIN )
log_warning ( " Failed to check whether cgroup is empty: %s " , strerror ( errno ) ) ;
continue ;
}
if ( t > 0 )
if ( UNIT_VTABLE ( b - > unit ) - > cgroup_notify_empty )
UNIT_VTABLE ( b - > unit ) - > cgroup_notify_empty ( b - > unit ) ;
}
return 0 ;
}
2010-06-16 07:10:31 +04:00
Unit * cgroup_unit_by_pid ( Manager * m , pid_t pid ) {
CGroupBonding * l , * b ;
char * group = NULL ;
assert ( m ) ;
if ( pid < = 1 )
return NULL ;
2010-08-12 00:04:22 +04:00
if ( cg_get_by_pid ( SYSTEMD_CGROUP_CONTROLLER , pid , & group ) < 0 )
2010-06-16 07:10:31 +04:00
return NULL ;
l = hashmap_get ( m - > cgroup_bondings , group ) ;
2010-08-13 04:08:34 +04:00
if ( ! l ) {
char * slash ;
while ( ( slash = strrchr ( group , ' / ' ) ) ) {
if ( slash = = group )
break ;
* slash = 0 ;
if ( ( l = hashmap_get ( m - > cgroup_bondings , group ) ) )
break ;
}
}
2010-06-16 07:10:31 +04:00
free ( group ) ;
LIST_FOREACH ( by_path , b , l ) {
if ( ! b - > unit )
continue ;
if ( b - > only_us )
return b - > unit ;
}
return NULL ;
}
2010-03-31 18:29:55 +04:00
CGroupBonding * cgroup_bonding_find_list ( CGroupBonding * first , const char * controller ) {
CGroupBonding * b ;
assert ( controller ) ;
LIST_FOREACH ( by_unit , b , first )
if ( streq ( b - > controller , controller ) )
return b ;
return NULL ;
}
2010-04-18 05:04:54 +04:00
char * cgroup_bonding_to_string ( CGroupBonding * b ) {
char * r ;
assert ( b ) ;
if ( asprintf ( & r , " %s:%s " , b - > controller , b - > path ) < 0 )
return NULL ;
return r ;
}
2010-10-27 05:16:49 +04:00
pid_t cgroup_bonding_search_main_pid ( CGroupBonding * b ) {
FILE * f ;
pid_t pid = 0 , npid ;
int r ;
assert ( b ) ;
if ( ! b - > only_us )
return 0 ;
if ( ( r = cg_enumerate_processes ( b - > controller , b - > path , & f ) ) < 0 )
return 0 ;
while ( ( r = cg_read_pid ( f , & npid ) ) > 0 ) {
if ( npid = = pid )
continue ;
if ( pid ! = 0 ) {
/* Dang, there's more than one PID in this
* group , so we don ' t know what process is the
* main process . */
pid = 0 ;
break ;
}
pid = npid ;
}
fclose ( f ) ;
return pid ;
}
pid_t cgroup_bonding_search_main_pid_list ( CGroupBonding * first ) {
CGroupBonding * b ;
pid_t pid ;
/* Try to find a main pid from this cgroup, but checking if
* there ' s only one PID in the cgroup and returning it . Later
* on we might want to add additional , smarter heuristics
* here . */
LIST_FOREACH ( by_unit , b , first )
if ( ( pid = cgroup_bonding_search_main_pid ( b ) ) ! = 0 )
return pid ;
return 0 ;
}