| /*****************************************************************************\ |
| * 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 <http://www.schedmd.com/slurmdocs/>. |
| * 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/file.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <dirent.h> |
| #include <sys/mount.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); |
| |
| |
| /* |
| * ----------------------------------------------------------------------------- |
| * xcgroup_ns primitives xcgroup_ns primitives xcgroup_ns primitives |
| * xcgroup_ns primitives xcgroup_ns primitives xcgroup_ns primitives |
| * xcgroup_ns primitives xcgroup_ns primitives xcgroup_ns primitives |
| * ----------------------------------------------------------------------------- |
| */ |
| |
| /* |
| * create a cgroup namespace for tasks containment |
| * |
| * returned values: |
| * - XCGROUP_ERROR |
| * - XCGROUP_SUCCESS |
| */ |
| int xcgroup_ns_create(slurm_cgroup_conf_t *conf, |
| xcgroup_ns_t* cgns, char* mnt_point, char* mnt_args, |
| char* subsys, char* notify_prog) { |
| |
| cgns->mnt_point = xstrdup(conf->cgroup_mountpoint); |
| xstrcat(cgns->mnt_point, mnt_point); |
| |
| cgns->mnt_args = xstrdup(mnt_args); |
| cgns->subsystems = xstrdup(subsys); |
| cgns->notify_prog = xstrdup(notify_prog); |
| return XCGROUP_SUCCESS; |
| } |
| |
| /* |
| * destroy a cgroup namespace |
| * |
| * returned values: |
| * - XCGROUP_ERROR |
| * - XCGROUP_SUCCESS |
| */ |
| int xcgroup_ns_destroy(xcgroup_ns_t* cgns) { |
| |
| xfree(cgns->mnt_point); |
| xfree(cgns->mnt_args); |
| xfree(cgns->subsystems); |
| xfree(cgns->notify_prog); |
| |
| return XCGROUP_SUCCESS; |
| } |
| |
| /* |
| * mount a cgroup namespace |
| * |
| * returned values: |
| * - XCGROUP_ERROR |
| * - XCGROUP_SUCCESS |
| * |
| * If an error occurs, errno will be set. |
| */ |
| int xcgroup_ns_mount(xcgroup_ns_t* cgns) |
| { |
| int fstatus; |
| char* options; |
| char opt_combined[1024]; |
| |
| char* mnt_point; |
| char* p; |
| |
| xcgroup_t cg; |
| |
| mode_t cmask; |
| mode_t omask; |
| |
| cmask = S_IWGRP | S_IWOTH; |
| omask = umask(cmask); |
| |
| fstatus = mkdir(cgns->mnt_point, 0755); |
| if (fstatus && errno != EEXIST) { |
| if (cgns->mnt_point[0] != '/') { |
| debug("unable to create cgroup ns directory '%s'" |
| " : do not start with '/'", cgns->mnt_point); |
| umask(omask); |
| return XCGROUP_ERROR; |
| } |
| mnt_point = xstrdup(cgns->mnt_point); |
| p = mnt_point; |
| while ((p = index(p+1, '/')) != NULL) { |
| *p = '\0'; |
| fstatus = mkdir(mnt_point, 0755); |
| if (fstatus && errno != EEXIST) { |
| debug("unable to create cgroup ns required " |
| "directory '%s'", mnt_point); |
| xfree(mnt_point); |
| umask(omask); |
| return XCGROUP_ERROR; |
| } |
| *p='/'; |
| } |
| xfree(mnt_point); |
| fstatus = mkdir(cgns->mnt_point, 0755); |
| } |
| |
| if (fstatus && errno != EEXIST) { |
| debug("unable to create cgroup ns directory '%s'" |
| " : %m", cgns->mnt_point); |
| umask(omask); |
| return XCGROUP_ERROR; |
| } |
| umask(omask); |
| |
| if (cgns->mnt_args == NULL || |
| strlen(cgns->mnt_args) == 0) |
| options = cgns->subsystems; |
| else { |
| if (snprintf(opt_combined, sizeof(opt_combined), "%s,%s", |
| cgns->subsystems, cgns->mnt_args) |
| >= sizeof(opt_combined)) { |
| debug2("unable to build cgroup options string"); |
| return XCGROUP_ERROR; |
| } |
| options = opt_combined; |
| } |
| |
| if (mount("cgroup", cgns->mnt_point, "cgroup", |
| MS_NOSUID|MS_NOEXEC|MS_NODEV, options)) |
| return XCGROUP_ERROR; |
| else { |
| /* we then set the release_agent if necessary */ |
| if (cgns->notify_prog) { |
| if (xcgroup_create(cgns, &cg, "/", 0, 0) == |
| XCGROUP_ERROR) |
| return XCGROUP_SUCCESS; |
| xcgroup_set_param(&cg, "release_agent", |
| cgns->notify_prog); |
| } |
| return XCGROUP_SUCCESS; |
| } |
| } |
| |
| /* |
| * umount a cgroup namespace |
| * |
| * returned values: |
| * - XCGROUP_ERROR |
| * - XCGROUP_SUCCESS |
| * |
| * If an error occurs, errno will be set. |
| */ |
| int xcgroup_ns_umount(xcgroup_ns_t* cgns) |
| { |
| if (umount(cgns->mnt_point)) |
| return XCGROUP_ERROR; |
| return XCGROUP_SUCCESS; |
| } |
| |
| /* |
| * check that a cgroup namespace is ready to be used |
| * |
| * returned values: |
| * - XCGROUP_ERROR : not available |
| * - XCGROUP_SUCCESS : ready to be used |
| */ |
| int xcgroup_ns_is_available(xcgroup_ns_t* cgns) |
| { |
| int fstatus; |
| char* value; |
| size_t s; |
| xcgroup_t cg; |
| |
| if (xcgroup_create(cgns, &cg, "/", 0, 0) == XCGROUP_ERROR) |
| return 0; |
| |
| if (xcgroup_get_param(&cg, "release_agent", |
| &value, &s) != XCGROUP_SUCCESS) |
| fstatus = 0; |
| else { |
| xfree(value); |
| fstatus = 1; |
| } |
| |
| xcgroup_destroy(&cg); |
| |
| return fstatus; |
| } |
| |
| /* |
| * Look for the cgroup in a specific cgroup namespace that owns |
| * a particular pid |
| * |
| * returned values: |
| * - XCGROUP_ERROR |
| * - XCGROUP_SUCCESS |
| */ |
| int xcgroup_ns_find_by_pid(xcgroup_ns_t* cgns, xcgroup_t* cg, pid_t pid) |
| { |
| int fstatus = SLURM_ERROR; |
| char file_path[PATH_MAX]; |
| char* buf; |
| size_t fsize; |
| char* p; |
| char* e; |
| char* entry; |
| char* subsys; |
| int found=0; |
| |
| /* 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 |
| * multiple lines of the form : |
| * num_mask:subsystems:relative_path |
| */ |
| fstatus = _file_read_content(file_path, &buf, &fsize); |
| if (fstatus == XCGROUP_SUCCESS) { |
| fstatus = XCGROUP_ERROR; |
| p = buf; |
| while (found==0 && (e = index(p, '\n')) != NULL) { |
| *e='\0'; |
| /* get subsystems entry */ |
| subsys = index(p, ':'); |
| p = e + 1; |
| if (subsys == NULL) |
| continue; |
| subsys++; |
| /* get relative path entry */ |
| entry = index(subsys, ':'); |
| if (entry == NULL) |
| continue; |
| *entry='\0'; |
| /* check subsystem versus ns one */ |
| if (strcmp(cgns->subsystems, subsys) != 0) { |
| debug("skipping cgroup subsys %s(%s)", |
| subsys, cgns->subsystems); |
| continue; |
| } |
| else |
| found=1; |
| entry++; |
| fstatus = xcgroup_load(cgns, cg, entry); |
| break; |
| } |
| xfree(buf); |
| } |
| |
| return fstatus; |
| } |
| |
| |
| /* |
| * ----------------------------------------------------------------------------- |
| * xcgroup primitives xcgroup primitives xcgroup primitives xcgroup primitives |
| * xcgroup primitives xcgroup primitives xcgroup primitives xcgroup primitives |
| * xcgroup primitives xcgroup primitives xcgroup primitives xcgroup primitives |
| * ----------------------------------------------------------------------------- |
| */ |
| |
| int xcgroup_create(xcgroup_ns_t* cgns, xcgroup_t* cg, |
| char* uri, uid_t uid, gid_t gid) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| |
| /* build cgroup absolute path*/ |
| if (snprintf(file_path, PATH_MAX, "%s%s", cgns->mnt_point, |
| uri) >= PATH_MAX) { |
| debug2("unable to build cgroup '%s' absolute path in ns '%s' " |
| ": %m", uri, cgns->subsystems); |
| return fstatus; |
| } |
| |
| /* fill xcgroup structure */ |
| cg->ns = cgns; |
| cg->name = xstrdup(uri); |
| cg->path = xstrdup(file_path); |
| cg->uid = uid; |
| cg->gid = gid; |
| |
| return XCGROUP_SUCCESS; |
| } |
| |
| int xcgroup_destroy(xcgroup_t* cg) |
| { |
| cg->ns = NULL; |
| xfree(cg->name); |
| xfree(cg->path); |
| cg->uid = -1; |
| cg->gid = -1; |
| return XCGROUP_SUCCESS; |
| } |
| |
| int xcgroup_lock(xcgroup_t* cg) |
| { |
| int fstatus = XCGROUP_ERROR; |
| |
| if ((cg->fd = open(cg->path, O_RDONLY)) < 0) { |
| debug2("xcgroup_lock: error from open of cgroup '%s' : %m", |
| cg->path); |
| return fstatus; |
| } |
| |
| if (flock(cg->fd, LOCK_EX) < 0) { |
| debug2("xcgroup_lock: error locking cgroup '%s' : %m", |
| cg->path); |
| close(cg->fd); |
| } |
| else |
| fstatus = XCGROUP_SUCCESS; |
| |
| return fstatus; |
| } |
| |
| int xcgroup_unlock(xcgroup_t* cg) |
| { |
| int fstatus = XCGROUP_ERROR; |
| |
| if (flock(cg->fd, LOCK_UN) < 0) { |
| debug2("xcgroup_lock: error unlocking cgroup '%s' : %m", |
| cg->path); |
| } |
| else |
| fstatus = XCGROUP_SUCCESS; |
| |
| close(cg->fd); |
| return fstatus; |
| } |
| |
| int xcgroup_instanciate(xcgroup_t* cg) |
| { |
| int fstatus = XCGROUP_ERROR; |
| mode_t cmask; |
| mode_t omask; |
| |
| xcgroup_ns_t* cgns; |
| char* file_path; |
| uid_t uid; |
| gid_t gid; |
| int create_only; |
| int notify; |
| |
| /* init variables based on input cgroup */ |
| cgns = cg->ns; |
| file_path = cg->path; |
| uid = cg->uid; |
| gid = cg->gid; |
| create_only=0; |
| notify=1; |
| |
| /* 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); |
| |
| /* 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 && cgns->notify_prog) |
| xcgroup_set_params(cg, "notify_on_release=1"); |
| else |
| xcgroup_set_params(cg, "notify_on_release=0"); |
| return fstatus; |
| } |
| |
| int xcgroup_load(xcgroup_ns_t* cgns, xcgroup_t* cg, char* uri) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| |
| struct stat buf; |
| |
| /* build cgroup absolute path*/ |
| if (snprintf(file_path, PATH_MAX, "%s%s", cgns->mnt_point, |
| uri) >= PATH_MAX) { |
| debug2("unable to build cgroup '%s' absolute path in ns '%s' " |
| ": %m", uri, cgns->subsystems); |
| return fstatus; |
| } |
| |
| if (stat((const char*)file_path, &buf)) { |
| debug2("unable to get cgroup '%s' entry '%s' properties" |
| ": %m", cgns->mnt_point, file_path); |
| return fstatus; |
| } |
| |
| /* fill xcgroup structure */ |
| cg->ns = cgns; |
| cg->name = xstrdup(uri); |
| cg->path = xstrdup(file_path); |
| cg->uid = buf.st_uid; |
| cg->gid = buf.st_gid; |
| |
| return XCGROUP_SUCCESS; |
| } |
| |
| int xcgroup_delete(xcgroup_t* cg) |
| { |
| if (rmdir(cg->path)) |
| return XCGROUP_ERROR; |
| else |
| return XCGROUP_SUCCESS; |
| } |
| |
| static int cgroup_procs_readable (xcgroup_t *cg) |
| { |
| struct stat st; |
| char *path = NULL; |
| int rc = 0; |
| |
| xstrfmtcat (path, "%s/%s", cg->path, "cgroup.procs"); |
| if ((stat (path, &st) >= 0) && (st.st_mode & S_IRUSR)) |
| rc = 1; |
| xfree (path); |
| return (rc); |
| } |
| |
| static int cgroup_procs_writable (xcgroup_t *cg) |
| { |
| struct stat st; |
| char *path = NULL; |
| int rc = 0; |
| |
| xstrfmtcat (path, "%s/%s", cg->path, "cgroup.procs"); |
| if ((stat (path, &st) >= 0) && (st.st_mode & S_IWUSR)) |
| rc = 1; |
| xfree (path); |
| return (rc); |
| } |
| |
| // This call is not intended to be used to move thread pids |
| int xcgroup_add_pids(xcgroup_t* cg, pid_t* pids, int npids) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char* path = NULL; |
| |
| // If possible use cgroup.procs to add the processes atomically |
| if (cgroup_procs_writable (cg)) |
| xstrfmtcat (path, "%s/%s", cg->path, "cgroup.procs"); |
| else |
| xstrfmtcat (path, "%s/%s", cg->path, "tasks"); |
| |
| fstatus = _file_write_uint32s(path, (uint32_t*)pids, npids); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to add pids to '%s'", cg->path); |
| |
| xfree(path); |
| return fstatus; |
| } |
| |
| // This call is not intended to be used to get thread pids |
| int xcgroup_get_pids(xcgroup_t* cg, pid_t **pids, int *npids) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char* path = NULL; |
| |
| if (pids == NULL || npids == NULL) |
| return SLURM_ERROR; |
| |
| if (cgroup_procs_readable (cg)) |
| xstrfmtcat (path, "%s/%s", cg->path, "cgroup.procs"); |
| else |
| xstrfmtcat (path, "%s/%s", cg->path, "tasks"); |
| |
| fstatus = _file_read_uint32s(path, (uint32_t**)pids, npids); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to get pids of '%s'", cg->path); |
| |
| xfree(path); |
| return fstatus; |
| } |
| |
| int xcgroup_set_params(xcgroup_t* cg, char* parameters) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| char* cpath = cg->path; |
| char* params; |
| char* value; |
| char* p; |
| char* next; |
| |
| 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 parameters format for entry '%s'", p); |
| next_loop: |
| p = next; |
| } |
| |
| xfree(params); |
| return fstatus; |
| } |
| |
| int xcgroup_set_param(xcgroup_t* cg, char* param, char* content) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| char* cpath = cg->path; |
| |
| if (snprintf(file_path, PATH_MAX, "%s/%s", cpath, param) >= PATH_MAX) { |
| debug2("unable to build filepath for '%s' and" |
| " parameter '%s' : %m", cpath, param); |
| return fstatus; |
| } |
| |
| fstatus = _file_write_content(file_path, content, strlen(content)); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to set parameter '%s' to " |
| "'%s' for '%s'", param, content, cpath); |
| else |
| debug3("parameter '%s' set to '%s' for '%s'", |
| param, content, cpath); |
| |
| return fstatus; |
| } |
| |
| int xcgroup_get_param(xcgroup_t* cg, char* param, char **content, size_t *csize) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| char* cpath = cg->path; |
| |
| if (snprintf(file_path, PATH_MAX, "%s/%s", cpath, param) >= PATH_MAX) { |
| debug2("unable to build filepath for '%s' and" |
| " parameter '%s' : %m", cpath, param); |
| } |
| else { |
| fstatus = _file_read_content(file_path, content, csize); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to get parameter '%s' for '%s'", |
| param, cpath); |
| } |
| return fstatus; |
| } |
| |
| int xcgroup_set_uint32_param(xcgroup_t* cg, char* param, uint32_t value) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| char* cpath = cg->path; |
| |
| if (snprintf(file_path, PATH_MAX, "%s/%s", cpath, param) >= PATH_MAX) { |
| debug2("unable to build filepath for '%s' and" |
| " parameter '%s' : %m", cpath, param); |
| return fstatus; |
| } |
| |
| fstatus = _file_write_uint32s(file_path, &value, 1); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to set parameter '%s' to " |
| "'%u' for '%s'", param, value, cpath); |
| else |
| debug3("parameter '%s' set to '%u' for '%s'", |
| param, value, cpath); |
| |
| return fstatus; |
| } |
| |
| int xcgroup_get_uint32_param(xcgroup_t* cg, char* param, uint32_t* value) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| char* cpath = cg->path; |
| uint32_t* values; |
| int vnb; |
| |
| if (snprintf(file_path, PATH_MAX, "%s/%s", cpath, param) >= PATH_MAX) { |
| debug2("unable to build filepath for '%s' and" |
| " parameter '%s' : %m", cpath, param); |
| } |
| else { |
| fstatus = _file_read_uint32s(file_path, &values, &vnb); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to get parameter '%s' for '%s'", |
| param, cpath); |
| else if (vnb < 1) { |
| debug2("empty parameter '%s' for '%s'", |
| param, cpath); |
| } |
| else { |
| *value = values[0]; |
| xfree(values); |
| fstatus = XCGROUP_SUCCESS; |
| } |
| } |
| return fstatus; |
| } |
| |
| int xcgroup_set_uint64_param(xcgroup_t* cg, char* param, uint64_t value) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| char* cpath = cg->path; |
| |
| if (snprintf(file_path, PATH_MAX, "%s/%s", cpath, param) >= PATH_MAX) { |
| debug2("unable to build filepath for '%s' and" |
| " parameter '%s' : %m", cpath, param); |
| return fstatus; |
| } |
| |
| fstatus = _file_write_uint64s(file_path, &value, 1); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to set parameter '%s' to " |
| "'%"PRIu64"' for '%s'", param, value, cpath); |
| else |
| debug3("parameter '%s' set to '%"PRIu64"' for '%s'", |
| param, value, cpath); |
| |
| return fstatus; |
| } |
| |
| int xcgroup_get_uint64_param(xcgroup_t* cg, char* param, uint64_t* value) |
| { |
| int fstatus = XCGROUP_ERROR; |
| char file_path[PATH_MAX]; |
| char* cpath = cg->path; |
| uint64_t* values; |
| int vnb; |
| |
| if (snprintf(file_path, PATH_MAX, "%s/%s", cpath, param) >= PATH_MAX) { |
| debug2("unable to build filepath for '%s' and" |
| " parameter '%s' : %m", cpath, param); |
| } |
| else { |
| fstatus = _file_read_uint64s(file_path, &values, &vnb); |
| if (fstatus != XCGROUP_SUCCESS) |
| debug2("unable to get parameter '%s' for '%s'", |
| param, cpath); |
| else if (vnb < 1) { |
| debug2("empty parameter '%s' for '%s'", |
| param, cpath); |
| } |
| else { |
| *value = values[0]; |
| xfree(values); |
| fstatus = XCGROUP_SUCCESS; |
| } |
| } |
| return fstatus; |
| } |
| |
| static int cgroup_move_process_by_task (xcgroup_t *cg, pid_t pid) |
| { |
| DIR *dir; |
| struct dirent *entry; |
| char path [PATH_MAX]; |
| |
| if (snprintf (path, PATH_MAX, "/proc/%d/task", (int) pid) >= PATH_MAX) { |
| error ("xcgroup: move_process_by_task: path overflow!"); |
| return XCGROUP_ERROR; |
| } |
| |
| dir = opendir (path); |
| if (!dir) { |
| error ("xcgroup: opendir(%s): %m", path); |
| return XCGROUP_ERROR; |
| } |
| |
| while ((entry = readdir (dir))) { |
| if (entry->d_name[0] != '.') |
| xcgroup_set_param (cg, "tasks", entry->d_name); |
| } |
| closedir (dir); |
| return XCGROUP_SUCCESS; |
| } |
| |
| int xcgroup_move_process (xcgroup_t *cg, pid_t pid) |
| { |
| if (!cgroup_procs_writable (cg)) |
| return cgroup_move_process_by_task (cg, pid); |
| |
| return xcgroup_set_uint32_param (cg, "cgroup.procs", pid); |
| } |
| |
| |
| /* |
| * ----------------------------------------------------------------------------- |
| * internal primitives internal primitives internal primitives |
| * internal primitives internal primitives internal primitives |
| * internal primitives internal primitives internal primitives |
| * ----------------------------------------------------------------------------- |
| */ |
| |
| 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); |
| if ( errno != ESRCH ) |
| 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 uint64_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); |
| if ( errno != ESRCH ) |
| 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 %lu bytes to file '%s' : %m", |
| (long unsigned int) 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; |
| } |