#include <sys/param.h>
#include <sys/inotify.h>
#include <sys/mount.h>
+#include <sys/wait.h>
#include <netinet/in.h>
#include <net/if.h>
#include <stdbool.h>
#include "cgmanager.h"
-static NihDBusProxy *cgroup_manager = NULL;
-static int32_t api_version;
+static __thread NihDBusProxy *cgroup_manager = NULL;
+static __thread int32_t api_version;
static void cgm_dbus_disconnect(void)
{
connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error);
if (!connection) {
- fprintf(stderr, "Failed opening dbus connection: %s: %s",
+ fprintf(stderr, "Failed opening dbus connection: %s: %s\n",
dbus_error.name, dbus_error.message);
dbus_error_free(&dbus_error);
return false;
if (!cgroup_manager) {
NihError *nerr;
nerr = nih_error_get();
- fprintf(stderr, "Error opening cgmanager proxy: %s", nerr->message);
+ fprintf(stderr, "Error opening cgmanager proxy: %s\n", nerr->message);
nih_free(nerr);
cgm_dbus_disconnect();
return false;
if (cgmanager_get_api_version_sync(NULL, cgroup_manager, &api_version) != 0) {
NihError *nerr;
nerr = nih_error_get();
- fprintf(stderr, "Error cgroup manager api version: %s", nerr->message);
+ fprintf(stderr, "Error cgroup manager api version: %s\n", nerr->message);
nih_free(nerr);
cgm_dbus_disconnect();
return false;
if ( cgmanager_list_controllers_sync(NULL, cgroup_manager, contrls) != 0 ) {
NihError *nerr;
nerr = nih_error_get();
- fprintf(stderr, "call to list_controllers failed: %s", nerr->message);
+ fprintf(stderr, "call to list_controllers failed: %s\n", nerr->message);
nih_free(nerr);
cgm_dbus_disconnect();
return false;
(CgmanagerListKeysOutputElement ***)keys) != 0 ) {
NihError *nerr;
nerr = nih_error_get();
- fprintf(stderr, "call to list_keys failed: %s", nerr->message);
+ fprintf(stderr, "call to list_keys (%s:%s) failed: %s\n", controller, cgroup, nerr->message);
nih_free(nerr);
cgm_dbus_disconnect();
return false;
if ( cgmanager_list_children_sync(NULL, cgroup_manager, controller, cgroup, list) != 0 ) {
NihError *nerr;
nerr = nih_error_get();
- fprintf(stderr, "call to list_children failed: %s", nerr->message);
+ fprintf(stderr, "call to list_children (%s:%s) failed: %s\n", controller, cgroup, nerr->message);
nih_free(nerr);
cgm_dbus_disconnect();
return false;
if ( cgmanager_get_pid_cgroup_sync(NULL, cgroup_manager, controller, pid, &output) != 0 ) {
NihError *nerr;
nerr = nih_error_get();
- fprintf(stderr, "call to get_pid_cgroup failed: %s", nerr->message);
+ fprintf(stderr, "call to get_pid_cgroup (%s) failed: %s\n", controller, nerr->message);
nih_free(nerr);
cgm_dbus_disconnect();
return NULL;
if ( cgmanager_move_pid_abs_sync(NULL, cgroup_manager, "all", "/", (int32_t) getpid()) != 0 ) {
NihError *nerr;
nerr = nih_error_get();
- fprintf(stderr, "call to move_pid_abs failed: %s", nerr->message);
+ fprintf(stderr, "call to move_pid_abs (all:/) failed: %s\n", nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ cgm_dbus_disconnect();
+ return true;
+}
+
+bool cgm_move_pid(const char *controller, const char *cgroup, pid_t pid)
+{
+ if (!cgm_dbus_connect()) {
+ return false;
+ }
+
+ if ( cgmanager_move_pid_sync(NULL, cgroup_manager, controller, cgroup,
+ (int32_t) pid) != 0 ) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ fprintf(stderr, "call to move_pid (%s:%s, %d) failed: %s\n", controller, cgroup, pid, nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ cgm_dbus_disconnect();
+ return true;
+}
+
+bool cgm_get_value(const char *controller, const char *cgroup, const char *file,
+ char **value)
+{
+ if (!cgm_dbus_connect()) {
+ return false;
+ }
+
+ if ( cgmanager_get_value_sync(NULL, cgroup_manager, controller, cgroup,
+ file, value) != 0 ) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ fprintf(stderr, "call to get_value (%s:%s, %s) failed: %s\n", controller, cgroup, file, nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ cgm_dbus_disconnect();
+ return true;
+}
+
+bool cgm_set_value(const char *controller, const char *cgroup, const char *file,
+ const char *value)
+{
+ if (!cgm_dbus_connect()) {
+ return false;
+ }
+
+ if ( cgmanager_set_value_sync(NULL, cgroup_manager, controller, cgroup,
+ file, value) != 0 ) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ fprintf(stderr, "call to set_value (%s:%s, %s, %s) failed: %s\n", controller, cgroup, file, value, nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ cgm_dbus_disconnect();
+ return true;
+}
+
+static int wait_for_pid(pid_t pid)
+{
+ int status, ret;
+
+again:
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1) {
+ if (errno == EINTR)
+ goto again;
+ return -1;
+ }
+ if (ret != pid)
+ goto again;
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ return -1;
+ return 0;
+}
+
+bool cgm_create(const char *controller, const char *cg, uid_t uid, gid_t gid)
+{
+ int32_t e;
+ pid_t pid = fork();
+
+ if (pid) {
+ if (wait_for_pid(pid) != 0)
+ return false;
+ return true;
+ }
+
+ if (setgroups(0, NULL))
+ exit(1);
+ if (setresgid(gid, gid, gid))
+ exit(1);
+ if (setresuid(uid, uid, uid))
+ exit(1);
+
+ if (!cgm_dbus_connect()) {
+ exit(1);
+ }
+
+ if ( cgmanager_create_sync(NULL, cgroup_manager, controller, cg, &e) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ fprintf(stderr, "call to create failed (%s:%s): %s\n", controller, cg, nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ exit(1);
+ }
+
+ cgm_dbus_disconnect();
+ exit(0);
+}
+
+bool cgm_chown_file(const char *controller, const char *cg, uid_t uid, gid_t gid)
+{
+ if (!cgm_dbus_connect()) {
+ return false;
+ }
+
+ if ( cgmanager_chown_sync(NULL, cgroup_manager, controller, cg, uid, gid) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ fprintf(stderr, "call to chown (%s:%s, %d, %d) failed: %s\n", controller, cg, uid, gid, nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ cgm_dbus_disconnect();
+ return true;
+}
+
+bool cgm_chmod_file(const char *controller, const char *file, mode_t mode)
+{
+ if (!cgm_dbus_connect()) {
+ return false;
+ }
+
+ if ( cgmanager_chmod_sync(NULL, cgroup_manager, controller, file, "", mode) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ fprintf(stderr, "call to chmod (%s:%s, %d) failed: %s\n", controller, file, mode, nerr->message);
+ nih_free(nerr);
+ cgm_dbus_disconnect();
+ return false;
+ }
+
+ cgm_dbus_disconnect();
+ return true;
+}
+
+bool cgm_remove(const char *controller, const char *cg)
+{
+ /*
+ * tempting to make remove be recursive, but this is a filesystem,
+ * so best to opt for least surprise
+ */
+ int32_t r = 0, e;
+
+ if (!cgm_dbus_connect()) {
+ return false;
+ }
+
+ if ( cgmanager_remove_sync(NULL, cgroup_manager, controller, cg, r, &e) != 0) {
+ NihError *nerr;
+ nerr = nih_error_get();
+ fprintf(stderr, "call to remove (%s:%s) failed: %s\n", controller, cg, nerr->message);
nih_free(nerr);
cgm_dbus_disconnect();
return false;