/*****************************************************************************\
 *  xcgroup.c - cgroup related primitives
 *****************************************************************************
 *  Copyright (C) 2009 CEA/DAM/DIF
 *  Written by Matthieu Hautreux <matthieu.hautreux@cea.fr>
 *
 *  This file is part of SLURM, a resource management program.
 *  For details, see <https://computing.llnl.gov/linux/slurm/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  SLURM 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.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  SLURM 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 SLURM; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#if HAVE_CONFIG_H
#   include "config.h"
#endif

#if HAVE_STDINT_H
#  include <stdint.h>
#endif
#if HAVE_INTTYPES_H
#  include <inttypes.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <slurm/slurm.h>
#include <slurm/slurm_errno.h>
#include "src/common/log.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/slurmd/slurmstepd/slurmstepd_job.h"

#include "xcgroup.h"

#ifndef PATH_MAX
#define PATH_MAX 256
#endif

/* internal functions */
size_t _file_getsize(int fd);
int _file_read_uint32s(char* file_path,uint32_t** pvalues,int* pnb);
int _file_write_uint32s(char* file_path,uint32_t* values,int nb);
int _file_read_uint64s(char* file_path,uint64_t** pvalues,int* pnb);
int _file_write_uint64s(char* file_path,uint64_t* values,int nb);
int _file_read_content(char* file_path,char** content,size_t *csize);
int _file_write_content(char* file_path,char* content,size_t csize);
int _xcgroup_cpuset_init(char* file_path);

/* xcgroup primitives */
int xcgroup_is_available(void)
{
	char* value;
	size_t s;

	if ( xcgroup_get_param(CGROUP_BASEDIR,"release_agent",
			       &value,&s) != XCGROUP_SUCCESS )
		return 0;
	else {
		xfree(value);
		return 1;
	}

}

int xcgroup_set_release_agent(char* agent)
{
	int fstatus;
	char* rag;
	char* value;
	size_t s;

	if ( agent == NULL )
		return XCGROUP_ERROR;

	rag = (char*) xstrdup("release_agent=");
	fstatus = xcgroup_get_param(CGROUP_BASEDIR,"release_agent",
				    &value,&s);
	if (  fstatus == XCGROUP_SUCCESS ) {
		if ( strcmp(value,agent) != 0 ) {
			xstrcat(rag,agent);
			fstatus = xcgroup_set_params(CGROUP_BASEDIR,rag);
		}
		xfree(value);
	}

	xfree(rag);
	return fstatus;
}

int xcgroup_mount(char* mount_opts)
{
	char* mount_cmd_fmt;
	char mount_cmd[1024];

	mode_t cmask;
	mode_t omask;

	cmask = S_IWGRP | S_IWOTH;
	omask = umask(cmask);

	if ( mkdir(CGROUP_BASEDIR,0755) && errno != EEXIST) {
		debug("unable to create cgroup directory '%s'"
		      " : %m",CGROUP_BASEDIR);
		umask(omask);
		return XCGROUP_ERROR;
	}
	umask(omask);

	if ( mount_opts == NULL ||
	     strlen(mount_opts) == 0 ) {
		mount_cmd_fmt="/bin/mount -t cgroup none " CGROUP_BASEDIR;
	}
	else
		mount_cmd_fmt="/bin/mount -o %s -t cgroup none " CGROUP_BASEDIR;

	if ( snprintf(mount_cmd,1024,mount_cmd_fmt,
		      mount_opts) >= 1024 ) {
		debug2("unable to build mount cmd line");
		return XCGROUP_ERROR;
	}
	else
		debug3("cgroup mount cmd line is '%s'",mount_cmd);

	if ( system(mount_cmd) )
		return XCGROUP_ERROR;
	else
		return XCGROUP_SUCCESS;

}

int xcgroup_create(char* file_path,xcgroup_opts_t* opts)
{
	int fstatus;
	uid_t uid;
	gid_t gid;
	int create_only;
	int notify;

	mode_t cmask;
	mode_t omask;

	uid=opts->uid;
	gid=opts->gid;
	create_only=opts->create_only;
	notify=opts->notify;

	fstatus = XCGROUP_ERROR;

	/* save current mask and apply working one */
	cmask = S_IWGRP | S_IWOTH;
	omask = umask(cmask);

	/* build cgroup */
	if ( mkdir(file_path,0755) ) {
		if ( create_only || errno != EEXIST ) {
			debug2("unable to create cgroup '%s' : %m",
			       file_path);
			umask(omask);
			return fstatus;
		}
	}
	umask(omask);

	/* initialize cpuset support (if enabled in cgroup ) */
	if ( _xcgroup_cpuset_init(file_path) != XCGROUP_SUCCESS ) {
		debug2("unable to initialize cpuset cgroup component");
		rmdir(file_path);
		return fstatus;
	}

	/* change cgroup ownership as requested */
	if ( chown(file_path,uid,gid) ) {
		debug2("unable to chown %d:%d cgroup '%s' : %m",
		       uid,gid,file_path);
		return fstatus;
	}

	/* following operations failure might not result in a general
	 * failure so set output status to success */
	fstatus = XCGROUP_SUCCESS;

	/* set notify on release flag */
	if ( notify == 1 )
		xcgroup_set_params(file_path,"notify_on_release=1");
	else if ( notify == 0 )
		xcgroup_set_params(file_path,"notify_on_release=0");

	return fstatus;
}

int xcgroup_destroy(char* file_path)
{

	/*
	 * nothing to be done here, notify_on_release was set
	 * so hope that all will works perfectly...
	 *
	 * with memory cgroup some pages can still be accounted
	 * to the cgroup but no more processes are present, this results
	 * in a directory not being removed until the pages are accounted
	 * to an other cgroup...
	 * echoing 1 into memory.force_empty can purge this memory but
	 * as slurmstepd is still present in the cgroup and use pages,
	 * this is not sufficient as it could leave some other pages too..
	 * we should have a way to ask the cgroup to force_empty
	 * on last process exit but I did not find any for now
	 */
	//xcgroup_set_params(file_path,"memory.force_empty=1");

	return XCGROUP_SUCCESS;
}

int xcgroup_add_pids(char* cpath,pid_t* pids,int npids)
{
	int fstatus;
	char file_path[PATH_MAX];

	fstatus = XCGROUP_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/tasks",
		      cpath) >= PATH_MAX ) {
		debug2("unable to add pids to '%s' : %m",cpath);
		return fstatus;
	}

	fstatus = _file_write_uint32s(file_path,(uint32_t*)pids,npids);
	if ( fstatus != XCGROUP_SUCCESS )
		debug2("unable to add pids to '%s'",cpath);
	return fstatus;
}

int
xcgroup_get_pids(char* cpath, pid_t **pids, int *npids)
{
	int fstatus;
	char file_path[PATH_MAX];

	fstatus = XCGROUP_ERROR;

	if ( pids == NULL || npids == NULL )
		return SLURM_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/tasks",
		      cpath) >= PATH_MAX ) {
		debug2("unable to get pids of '%s' : %m",cpath);
		return fstatus;
	}

	fstatus = _file_read_uint32s(file_path,(uint32_t**)pids,npids);
	if ( fstatus != XCGROUP_SUCCESS )
		debug2("unable to get pids of '%s'",cpath);
	return fstatus;
}

int
xcgroup_find_by_pid(char* cpath, pid_t pid)
{
	int fstatus = SLURM_ERROR;
	char file_path[PATH_MAX];
	char* buf;
	size_t fsize;
	char* p;
	char* e;
	char* entry;

	/* build pid cgroup meta filepath */
	if ( snprintf(file_path,PATH_MAX,"/proc/%u/cgroup",
		      pid) >= PATH_MAX ) {
		debug2("unable to build cgroup meta filepath for pid=%u : %m",
		       pid);
		return XCGROUP_ERROR;
	}

	/* read file content */
	fstatus = _file_read_content(file_path,&buf,&fsize);
	if ( fstatus == XCGROUP_SUCCESS ) {
		fstatus = XCGROUP_ERROR;
		p = buf;
		if ( index(p,'\n') != NULL ) {
			e = index(p,'\n');
			*e='\0';
			entry = rindex(p,':');
			if ( entry != NULL ) {
				entry++;
				snprintf(cpath,PATH_MAX,"%s%s",
					 CGROUP_BASEDIR,entry);
				fstatus = XCGROUP_SUCCESS;
			}
		}
		xfree(buf);
	}

	return fstatus;
}

int xcgroup_set_memlimit(char* cpath,uint32_t memlimit)
{
	int fstatus;
	char file_path[PATH_MAX];
	uint64_t ml;

	fstatus = XCGROUP_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/memory.limit_in_bytes",
		      cpath) >= PATH_MAX ) {
		debug2("unable to set memory limit of '%s' : %m",cpath);
		return fstatus;
	}

	ml = (uint64_t) memlimit * 1024 * 1024;
	fstatus = _file_write_uint64s(file_path,&ml,1);
	if ( fstatus != XCGROUP_SUCCESS )
		debug2("unable to set memory limit of '%s' : %m",cpath);
	else
		debug3("memory limit set to %uMB for '%s'",memlimit,cpath);

	return fstatus;
}

int xcgroup_get_memlimit(char* cpath,uint32_t* memlimit)
{
	int fstatus;
	char file_path[PATH_MAX];
	uint64_t* ml;
	int i;

	fstatus = XCGROUP_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/memory.limit_in_bytes",
		      cpath) >= PATH_MAX ) {
		debug2("unable to get memory limit of '%s' : %m",cpath);
		return fstatus;
	}

	fstatus = _file_read_uint64s(file_path,&ml,&i);
	if ( fstatus != XCGROUP_SUCCESS ||
	     i == 0 )
		debug2("unable to get memory limit of '%s' : %m",cpath);
	else {
		if ( *ml == 0 ) {
			*memlimit = 0;
		}
		else {
			/* convert into MB */
			*ml /= 1024 * 1024;
			/* memlimit is stored into a uint32_t */
			/* so cap the memlimit value to the max value */
			/* of an uint32_t */
			*memlimit = -1 ;
			if ( *ml < *memlimit ) {
				*memlimit = *ml;
			}
		}
		debug3("memory limit of '%s' is %uMB",cpath,*memlimit);
		xfree(ml);
	}

	return fstatus;
}

int xcgroup_set_memswlimit(char* cpath,uint32_t memlimit)
{
	int fstatus;
	char file_path[PATH_MAX];
	uint64_t ml;

	fstatus = XCGROUP_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/memory.memsw.limit_in_bytes",
		      cpath) >= PATH_MAX ) {
		debug2("unable to set memsw limit of '%s' : %m",cpath);
		return fstatus;
	}

	ml = (uint64_t) memlimit * 1024 * 1024;
	fstatus = _file_write_uint64s(file_path,&ml,1);
	if ( fstatus != XCGROUP_SUCCESS )
		debug2("unable to set memsw limit of '%s' : %m",cpath);
	else
		debug3("mem+swap limit set to %uMB for '%s'",memlimit,cpath);

	return fstatus;
}

int xcgroup_get_memswlimit(char* cpath,uint32_t* memlimit)
{
	int fstatus;
	char file_path[PATH_MAX];
	uint64_t *ml;
	int i;

	fstatus = XCGROUP_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/memory.memsw.limit_in_bytes",
		      cpath) >= PATH_MAX ) {
		debug2("unable to get memsw limit of '%s' : %m",cpath);
		return fstatus;
	}

	fstatus = _file_read_uint64s(file_path,&ml,&i);
	if ( fstatus != XCGROUP_SUCCESS ||
	     i ==0 )
		debug2("unable to get memsw limit of '%s' : %m",cpath);
	else {
		if ( *ml == 0 ) {
			*memlimit = 0;
		}
		else {
			/* convert into MB */
			*ml /= 1024 * 1024;
			/* memlimit is stored into a uint32_t */
			/* so cap the memlimit value to the max value */
			/* of an uint32_t */
			*memlimit = -1 ;
			if ( *ml < *memlimit ) {
				*memlimit = *ml;
			}
		}
		debug3("mem+swap limit of '%s' is %uMB",cpath,*memlimit);
		xfree(ml);
	}

	return fstatus;
}

int xcgroup_set_mem_use_hierarchy(char* cpath,int flag)
{
	if ( flag )
		return xcgroup_set_params(cpath,"memory.use_hierarchy=1");
	else
		return xcgroup_set_params(cpath,"memory.use_hierarchy=0");
}

int xcgroup_set_cpuset_cpus(char* cpath,char* range)
{
	int fstatus;
	char file_path[PATH_MAX];

	fstatus = XCGROUP_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/cpuset.cpus",
		      cpath) >= PATH_MAX ) {
		debug2("unable to set cpuset.cpus to '%s' for '%s' : %m",
		       range,cpath);
		return fstatus;
	}

	fstatus = _file_write_content(file_path,range,strlen(range));
	if ( fstatus != XCGROUP_SUCCESS )
		debug2("unable to set cpuset.cpus to '%s' for '%s' : %m",
		       range,cpath);
	else
		debug3("cpuset.cpus set to '%s' for '%s'",range,cpath);

	return fstatus;
}

int xcgroup_set_params(char* cpath,char* parameters)
{
	int fstatus;
	char file_path[PATH_MAX];
	char* params;
	char* value;
	char* p;
	char* next;

	fstatus = XCGROUP_ERROR;

	params = (char*) xstrdup(parameters);

	p = params;
	while ( p != NULL && *p != '\0' ) {
		next = index(p,' ');
		if ( next ) {
			*next='\0';
			next++;
			while ( *next == ' ' )
				next++;
		}
		value = index(p,'=');
		if ( value != NULL ) {
			*value='\0';
			value++;
			if ( snprintf(file_path,PATH_MAX,"%s/%s",cpath,p)
			     >= PATH_MAX ) {
				debug2("unable to build filepath for '%s' and"
				       " parameter '%s' : %m",cpath,p);
				goto next_loop;
			}
			fstatus = _file_write_content(file_path,value,
						      strlen(value));
			if ( fstatus != XCGROUP_SUCCESS )
				debug2("unable to set parameter '%s' to "
				       "'%s' for '%s'",p,value,cpath);
			else
				debug3("parameter '%s' set to '%s' for '%s'",
				       p,value,cpath);
		}
		else
			debug2("bad paramters format for entry '%s'",p);
	next_loop:
		p = next;
	}

	xfree(params);

	return fstatus;
}

int xcgroup_get_param(char* cpath,char* parameter,char **content,size_t *csize)
{
	int fstatus;
	char file_path[PATH_MAX];

	fstatus = XCGROUP_ERROR;

	if ( snprintf(file_path,PATH_MAX,"%s/%s",cpath,parameter)
	     >= PATH_MAX ) {
		debug2("unable to build filepath for '%s' and"
		       " parameter '%s' : %m",cpath,parameter);
	}
	else {
		fstatus = _file_read_content(file_path,content,csize);
		if ( fstatus != XCGROUP_SUCCESS )
			debug2("unable to get parameter '%s'", parameter);
	}

	return fstatus;
}


size_t _file_getsize(int fd)
{
	int rc;
	size_t fsize;
	off_t offset;
	char c;

	/* store current position and rewind */
	offset = lseek(fd,0,SEEK_CUR);
	if ( offset < 0 )
		return -1;
	lseek(fd,0,SEEK_SET);

	/* get file size */
	fsize=0;
	do {
		rc = read(fd,(void*)&c,1);
		if ( rc > 0 )
			fsize++;
	}
	while ( (rc < 0 && errno == EINTR) || rc > 0 );

	/* restore position */
	lseek(fd,offset,SEEK_SET);

	if ( rc < 0 )
		return -1;
	else
		return fsize;
}

int
_file_write_uint64s(char* file_path,uint64_t* values,int nb)
{
	int fstatus;
	int rc;
	int fd;
	char tstr[256];
	uint64_t value;
	int i;

	/* open file for writing */
	fd = open(file_path, O_WRONLY, 0700);
	if (fd < 0) {
		debug2("unable to open '%s' for writing : %m",
		       file_path);
		return XCGROUP_ERROR;
	}

	/* add one value per line */
	fstatus = XCGROUP_SUCCESS;
	for ( i=0 ; i < nb ; i++ ) {

		value = values[i];

		rc = snprintf(tstr, sizeof(tstr), "%"PRIu64"", value);
		if ( rc < 0 ) {
			debug2("unable to build %"PRIu64" string value, "
			       "skipping", value);
			fstatus = XCGROUP_ERROR;
			continue;
		}

		do {
			rc = write(fd, tstr, strlen(tstr)+1);
		}
		while ( rc != 0 && errno == EINTR);
		if (rc < 1) {
			debug2("unable to add value '%s' to file '%s' : %m",
			       tstr,file_path);
			fstatus = XCGROUP_ERROR;
		}

	}

	/* close file */
	close(fd);

	return fstatus;
}


int
_file_read_uint64s(char* file_path,uint64_t** pvalues,int* pnb)
{
	int rc;
	int fd;

	size_t fsize;
	char* buf;
	char* p;

	uint64_t* pa=NULL;
	int i;

	/* check input pointers */
	if ( pvalues == NULL || pnb == NULL )
		return XCGROUP_ERROR;

	/* open file for reading */
	fd = open(file_path, O_RDONLY, 0700);
	if (fd < 0) {
		debug2("unable to open '%s' for reading : %m",
		       file_path);
		return XCGROUP_ERROR;
	}

	/* get file size */
	fsize=_file_getsize(fd);
	if ( fsize == -1 ) {
		close(fd);
		return XCGROUP_ERROR;
	}

	/* read file contents */
	buf = (char*) xmalloc((fsize+1)*sizeof(char));
	do {
		rc = read(fd,buf,fsize);
	}
	while ( rc < 0 && errno == EINTR );
	close(fd);
	buf[fsize]='\0';

	/* count values (splitted by \n) */
	i=0;
	if ( rc > 0 ) {
		p = buf;
		while ( index(p,'\n') != NULL ) {
			i++;
			p = index(p,'\n') + 1;
		}
	}

	/* build uint32_t list */
	if ( i > 0 ) {
		pa = (uint64_t*) xmalloc(sizeof(uint64_t) * i);
		p = buf;
		i = 0;
		while ( index(p,'\n') != NULL ) {
			long long unsigned int ll_tmp;
			sscanf(p,"%llu", &ll_tmp);
			pa[i++] = ll_tmp;
			p = index(p,'\n') + 1;
		}
	}

	/* free buffer */
	xfree(buf);

	/* set output values */
	*pvalues = pa;
	*pnb = i;

	return XCGROUP_SUCCESS;
}

int
_file_write_uint32s(char* file_path,uint32_t* values,int nb)
{
	int fstatus;
	int rc;
	int fd;
	char tstr[256];
	uint32_t value;
	int i;

	/* open file for writing */
	fd = open(file_path, O_WRONLY, 0700);
	if (fd < 0) {
		debug2("unable to open '%s' for writing : %m",
		       file_path);
		return XCGROUP_ERROR;
	}

	/* add one value per line */
	fstatus = XCGROUP_SUCCESS;
	for ( i=0 ; i < nb ; i++ ) {

		value = values[i];

		rc = snprintf(tstr, sizeof(tstr), "%u",value);
		if ( rc < 0 ) {
			debug2("unable to build %u string value, skipping",
			       value);
			fstatus = XCGROUP_ERROR;
			continue;
		}

		do {
			rc = write(fd, tstr, strlen(tstr)+1);
		}
		while ( rc != 0 && errno == EINTR);
		if (rc < 1) {
			debug2("unable to add value '%s' to file '%s' : %m",
			       tstr,file_path);
			fstatus = XCGROUP_ERROR;
		}

	}

	/* close file */
	close(fd);

	return fstatus;
}


int
_file_read_uint32s(char* file_path,uint32_t** pvalues,int* pnb)
{
	int rc;
	int fd;

	size_t fsize;
	char* buf;
	char* p;

	uint32_t* pa=NULL;
	int i;

	/* check input pointers */
	if ( pvalues == NULL || pnb == NULL )
		return XCGROUP_ERROR;

	/* open file for reading */
	fd = open(file_path, O_RDONLY, 0700);
	if (fd < 0) {
		debug2("unable to open '%s' for reading : %m",
		       file_path);
		return XCGROUP_ERROR;
	}

	/* get file size */
	fsize=_file_getsize(fd);
	if ( fsize == -1 ) {
		close(fd);
		return XCGROUP_ERROR;
	}

	/* read file contents */
	buf = (char*) xmalloc((fsize+1)*sizeof(char));
	do {
		rc = read(fd,buf,fsize);
	}
	while ( rc < 0 && errno == EINTR );
	close(fd);
	buf[fsize]='\0';

	/* count values (splitted by \n) */
	i=0;
	if ( rc > 0 ) {
		p = buf;
		while ( index(p,'\n') != NULL ) {
			i++;
			p = index(p,'\n') + 1;
		}
	}

	/* build uint32_t list */
	if ( i > 0 ) {
		pa = (uint32_t*) xmalloc(sizeof(uint32_t) * i);
		p = buf;
		i = 0;
		while ( index(p,'\n') != NULL ) {
			sscanf(p,"%u",pa+i);
			p = index(p,'\n') + 1;
			i++;
		}
	}

	/* free buffer */
	xfree(buf);

	/* set output values */
	*pvalues = pa;
	*pnb = i;

	return XCGROUP_SUCCESS;
}

int
_file_write_content(char* file_path, char* content,size_t csize)
{
	int fstatus;
	int rc;
	int fd;

	/* open file for writing */
	fd = open(file_path, O_WRONLY, 0700);
	if (fd < 0) {
		debug2("unable to open '%s' for writing : %m",
		       file_path);
		return XCGROUP_ERROR;
	}

	/* write content */
	do {
		rc = write(fd,content,csize);
	}
	while ( rc != 0 && errno == EINTR);

	/* check read size */
	if (rc < csize) {
		debug2("unable to write %zd bytes to file '%s' : %m",
		       csize, file_path);
		fstatus = XCGROUP_ERROR;
	}
	else
		fstatus = XCGROUP_SUCCESS;

	/* close file */
	close(fd);

	return fstatus;
}

int
_file_read_content(char* file_path,char** content,size_t *csize)
{
	int fstatus;
	int rc;
	int fd;

	size_t fsize;
	char* buf;

	fstatus = XCGROUP_ERROR;

	/* check input pointers */
	if ( content == NULL || csize == NULL )
		return fstatus;

	/* open file for reading */
	fd = open(file_path, O_RDONLY, 0700);
	if (fd < 0) {
		debug2("unable to open '%s' for reading : %m",
		       file_path);
		return fstatus;
	}

	/* get file size */
	fsize=_file_getsize(fd);
	if ( fsize == -1 ) {
		close(fd);
		return fstatus;
	}

	/* read file contents */
	buf = (char*) xmalloc((fsize+1)*sizeof(char));
	buf[fsize]='\0';
	do {
		rc = read(fd,buf,fsize);
	}
	while ( rc < 0 && errno == EINTR );

	/* set output values */
	if ( rc >= 0 ) {
		*content = buf;
		*csize = rc;
		fstatus = XCGROUP_SUCCESS;
	}

	/* close file */
	close(fd);

	return fstatus;
}


int _xcgroup_cpuset_init(char* file_path)
{
	int fstatus;
	char path[PATH_MAX];

	char* cpuset_metafiles[] = {
		"cpuset.cpus",
		"cpuset.mems"
	};
	char* cpuset_meta;
	char* cpuset_conf;
	size_t csize;

	int i;

	fstatus = XCGROUP_ERROR;

	/* when cgroups are configured with cpuset, at least
	 * cpuset.cpus and cpuset.mems must be set or the cgroup
	 * will not be available at all.
	 * we duplicate the ancestor configuration in the init step */
	for ( i = 0 ; i < 2 ; i++ ) {

		cpuset_meta = cpuset_metafiles[i];

		/* try to read ancestor configuration */
		if ( snprintf(path,PATH_MAX,"%s/../%s",
			      file_path,cpuset_meta) >= PATH_MAX ) {
			debug2("unable to get ancestor %s for cgroup '%s' : %m",
			       cpuset_meta,file_path);
			return fstatus;
		}
		if ( _file_read_content(path,&cpuset_conf,&csize) !=
		     XCGROUP_SUCCESS ) {
			debug3("assuming no cpuset support for '%s'",path);
			return XCGROUP_SUCCESS;
		}

		/* duplicate ancestor conf in current cgroup */
		if ( snprintf(path,PATH_MAX,"%s/%s",
			      file_path,cpuset_meta) >= PATH_MAX ) {
			debug2("unable to set %s for cgroup '%s' : %m",
			       cpuset_meta,file_path);
			return fstatus;
		}
		if ( _file_write_content(path,cpuset_conf,csize) !=
		     XCGROUP_SUCCESS ) {
			debug2("unable to write %s configuration (%s) of '%s'",
			       cpuset_meta,cpuset_conf,file_path);
			return fstatus;
		}

	}

	return XCGROUP_SUCCESS;
}
