#define VERSION(ver) #ver
+/*
+ * The errno which happend the last time (have to be thread specific)
+ */
+__thread int last_errno;
+
+#define MAXLEN 256
+
+/* the value have to be thread specific */
+__thread char errtext[MAXLEN];
+
/*
* Remember to bump this up for major API changes.
*/
"Cgroup parsing failed",
"Cgroup, rules file does not exist",
"Cgroup mounting failed",
+ "The config file can not be opend",
};
static int cg_chown_file(FTS *fts, FTSENT *ent, uid_t owner, gid_t group)
dbg("Failed to open configuration file %s with"
" error: %s\n", CGRULES_CONF_FILE,
strerror(errno));
- ret = errno;
+ last_errno = errno;
goto finish;
}
buff = calloc(CGROUP_RULE_MAXLINE, sizeof(char));
if (!buff) {
dbg("Out of memory? Error: %s\n", strerror(errno));
- ret = errno;
+ last_errno = errno;
+ ret = ECGOTHER;
goto close_unlock;
}
newrule = calloc(1, sizeof(struct cgroup_rule));
if (!newrule) {
dbg("Out of memory? Error: %s\n", strerror(errno));
- ret = errno;
+ last_errno = errno;
+ ret = ECGOTHER;
goto cleanup;
}
*/
buf = malloc(FILENAME_MAX);
if (!buf) {
+ last_errno = errno;
ret = ECGOTHER;
goto unlock_exit;
}
dbg("Error writing tid %d to %s:%s\n",
tid, path, strerror(errno));
fclose(tasks);
+ last_errno = errno;
return ECGOTHER;
}
ret = fflush(tasks);
if (ret) {
+ last_errno = errno;
dbg("Error writing tid %d to %s:%s\n",
tid, path, strerror(errno));
fclose(tasks);
}
ret = fprintf(tasks, "%d", tid);
if (ret < 0) {
+ last_errno = errno;
dbg("Error writing tid %d to %s:%s\n",
tid, path, strerror(errno));
fclose(tasks);
}
ret = fflush(tasks);
if (ret) {
+ last_errno = errno;
dbg("Error writing tid %d to %s:%s\n",
tid, path, strerror(errno));
fclose(tasks);
buf = getcwd(cwd, FILENAME_MAX);
- if (!buf)
+ if (!buf) {
+ last_errno = errno;
return ECGOTHER;
+ }
real_path = strdup(path);
- if (!real_path)
+ if (!real_path) {
+ last_errno = errno;
return ECGOTHER;
+ }
do {
while (real_path[j] != '\0' && real_path[j] != '/')
ret = mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
wd = strdup(str);
if (!wd) {
+ last_errno = errno;
ret = ECGOTHER;
break;
}
ret = asprintf(&path, "%s%s", base,
cgroup->controller[i]->values[j]->name);
if (ret < 0) {
+ last_errno = errno;
error = ECGOTHER;
goto err;
}
base = strdup(path);
if (!base) {
+ last_errno = errno;
error = ECGOTHER;
goto err;
}
ret = asprintf(&path, "%s%s", base,
cgroup->controller[k]->values[j]->name);
if (ret < 0) {
+ last_errno = errno;
error = ECGOTHER;
goto err;
}
free(path);
ret = asprintf(&path, "%s/tasks", base);
if (ret < 0) {
+ last_errno = errno;
error = ECGOTHER;
goto err;
}
error = chown(path, cgroup->tasks_uid,
cgroup->tasks_gid);
if (error) {
+ last_errno = errno;
error = ECGOTHER;
goto err;
}
cgroup->controller[i]->name))
continue;
error = rmdir(path);
+ last_errno = errno;
}
open_err:
if (ignore_migration) {
cgroup->controller[i]->name))
continue;
error = rmdir(path);
- if (error < 0 && errno == ENOENT)
+ if (error < 0 && errno == ENOENT) {
+ last_errno = errno;
error = 0;
+ }
}
}
if (error)
return ECGROUPVALUENOTEXIST;
*value = malloc(CG_VALUE_MAX);
- if (!*value)
+ if (!*value) {
+ last_errno = errno;
return ECGOTHER;
+ }
/*
* using %as crashes when we try to read from files like
ret = asprintf(&control_path, "%s/tasks", path);
if (ret < 0) {
+ last_errno = errno;
error = ECGOTHER;
goto unlock_error;
}
if (stat(control_path, &stat_buffer)) {
+ last_errno = errno;
free(control_path);
error = ECGOTHER;
goto unlock_error;
dir = opendir(path);
if (!dir) {
+ last_errno = errno;
error = ECGOTHER;
goto unlock_error;
}
if (temp) {
controllers[j] = strdup(temp);
- if (!controllers[j])
+ if (!controllers[j]) {
+ last_errno = errno;
return ECGOTHER;
+ }
}
j++;
} while (temp);
*/
if (ret != 3 || ret == EOF) {
dbg("read failed for pid_cgroup_fd ret %d\n", ret);
+ last_errno = errno;
ret = ECGOTHER;
goto done;
}
== 0) {
*current_path = strdup(cgroup_path);
if (!*current_path) {
+ last_errno = errno;
ret = ECGOTHER;
goto done;
}
char *cgroup_strerror(int code)
{
assert((code >= ECGROUPNOTCOMPILED) && (code < ECGSENTINEL));
+ if (code == ECGOTHER) {
+ snprintf(errtext, MAXLEN, "%s: error message: %s",
+ cgroup_strerror_codes[code % ECGROUPNOTCOMPILED],
+ strerror(cgroup_get_last_errno()));
+ return errtext;
+ }
return cgroup_strerror_codes[code % ECGROUPNOTCOMPILED];
}
+
+/**
+ * Return last errno, which caused ECGOTHER error.
+ */
+int cgroup_get_last_errno()
+{
+ return last_errno;
+}
CGFLAG_USECACHE = 0x01,
};
+/**
+ * per thread errno variable, to be used when return code is ECGOTHER
+ */
+extern __thread int last_errno;
+
enum cgroup_errors {
ECGROUPNOTCOMPILED=50000,
ECGROUPNOTMOUNTED,
/* Represents error coming from other libraries like glibc. libcgroup
* users need to check errno upon encoutering ECGOTHER.
*/
- ECGOTHER,
+ ECGOTHER, /* OS error, see errno */
ECGROUPNOTEQUAL,
ECGCONTROLLERNOTEQUAL,
ECGROUPPARSEFAIL, /* Failed to parse rules configuration file. */
*/
char *cgroup_strerror(int code);
+/**
+ * Return last errno, which caused ECGOTHER error.
+ */
+int cgroup_get_last_errno();
+
+
/* The wrappers for filling libcg structures */
struct cgroup *cgroup_new_cgroup(const char *name);