cgroups: add support for CPU cgroup
authorRobert Swiecki <robert@swiecki.net>
Sun, 4 Feb 2018 03:15:19 +0000 (04:15 +0100)
committerRobert Swiecki <robert@swiecki.net>
Sun, 4 Feb 2018 03:15:19 +0000 (04:15 +0100)
cgroup.c
cmdline.c
config.proto
nsjail.h

index 8683f043ba4fe983cad078f04bc20219300ee824..21d6f68460bd96355b274216f9031ced50c8b2e5 100644 (file)
--- a/cgroup.c
+++ b/cgroup.c
@@ -153,6 +153,44 @@ static bool cgroupInitNsFromParentNetCls(struct nsjconf_t* nsjconf, pid_t pid) {
        return true;
 }
 
+static bool cgroupInitNsFromParentCpu(struct nsjconf_t* nsjconf, pid_t pid) {
+       if (nsjconf->cgroup_cpu_ms_per_sec == 0U) {
+               return true;
+       }
+
+       char cpu_cgroup_path[PATH_MAX];
+       snprintf(cpu_cgroup_path, sizeof(cpu_cgroup_path), "%s/%s/NSJAIL.%d",
+           nsjconf->cgroup_cpu_mount, nsjconf->cgroup_cpu_parent, (int)pid);
+       LOG_D("Create '%s' for PID=%d", cpu_cgroup_path, (int)pid);
+       if (mkdir(cpu_cgroup_path, 0700) == -1 && errno != EEXIST) {
+               PLOG_E("mkdir('%s', 0700) failed", cpu_cgroup_path);
+               return false;
+       }
+
+       char fname[PATH_MAX];
+       char cpu_ms_per_sec_str[512];
+       snprintf(cpu_ms_per_sec_str, sizeof(cpu_ms_per_sec_str), "0x%x",
+           nsjconf->cgroup_cpu_ms_per_sec * 1000U);
+       snprintf(fname, sizeof(fname), "%s/cpu.cfs_quota_us", cpu_cgroup_path);
+       LOG_D("Setting '%s' to '%s'", fname, cpu_ms_per_sec_str);
+       if (!utilWriteBufToFile(
+               fname, cpu_ms_per_sec_str, strlen(cpu_ms_per_sec_str), O_WRONLY | O_CLOEXEC)) {
+               LOG_E("Could not update cpu quota");
+               return false;
+       }
+
+       char pid_str[512];
+       snprintf(pid_str, sizeof(pid_str), "%d", (int)pid);
+       snprintf(fname, sizeof(fname), "%s/tasks", cpu_cgroup_path);
+       LOG_D("Adding PID='%s' to '%s'", pid_str, fname);
+       if (!utilWriteBufToFile(fname, pid_str, strlen(pid_str), O_WRONLY | O_CLOEXEC)) {
+               LOG_E("Could not update cpu cgroup task list");
+               return false;
+       }
+
+       return true;
+}
+
 bool cgroupInitNsFromParent(struct nsjconf_t* nsjconf, pid_t pid) {
        if (!cgroupInitNsFromParentMem(nsjconf, pid)) {
                return false;
@@ -163,6 +201,9 @@ bool cgroupInitNsFromParent(struct nsjconf_t* nsjconf, pid_t pid) {
        if (!cgroupInitNsFromParentNetCls(nsjconf, pid)) {
                return false;
        }
+       if (!cgroupInitNsFromParentCpu(nsjconf, pid)) {
+               return false;
+       }
        return true;
 }
 
@@ -194,6 +235,20 @@ void cgroupFinishFromParentPids(struct nsjconf_t* nsjconf, pid_t pid) {
        return;
 }
 
+void cgroupFinishFromParentCpu(struct nsjconf_t* nsjconf, pid_t pid) {
+       if (nsjconf->cgroup_cpu_ms_per_sec == 0U) {
+               return;
+       }
+       char cpu_cgroup_path[PATH_MAX];
+       snprintf(cpu_cgroup_path, sizeof(cpu_cgroup_path), "%s/%s/NSJAIL.%d",
+           nsjconf->cgroup_cpu_mount, nsjconf->cgroup_cpu_parent, (int)pid);
+       LOG_D("Remove '%s'", cpu_cgroup_path);
+       if (rmdir(cpu_cgroup_path) == -1) {
+               PLOG_W("rmdir('%s') failed", cpu_cgroup_path);
+       }
+       return;
+}
+
 void cgroupFinishFromParentNetCls(struct nsjconf_t* nsjconf, pid_t pid) {
        if (nsjconf->cgroup_net_cls_classid == 0U) {
                return;
@@ -212,6 +267,7 @@ void cgroupFinishFromParent(struct nsjconf_t* nsjconf, pid_t pid) {
        cgroupFinishFromParentMem(nsjconf, pid);
        cgroupFinishFromParentPids(nsjconf, pid);
        cgroupFinishFromParentNetCls(nsjconf, pid);
+       cgroupFinishFromParentCpu(nsjconf, pid);
 }
 
 bool cgroupInitNs(void) { return true; }
index 26df84f95af6d2b2ec2ba08d87494901fa29d560..f4258745f7032caddc1151de3cb75c78f4e1fa39 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -131,6 +131,9 @@ struct custom_option custom_opts[] = {
     { { "cgroup_net_cls_classid", required_argument, NULL, 0x0821 }, "Class identifier of network packets in the group (default: '0' - disabled)" },
     { { "cgroup_net_cls_mount", required_argument, NULL, 0x0822 }, "Location of net_cls cgroup FS (default: '/sys/fs/cgroup/net_cls')" },
     { { "cgroup_net_cls_parent", required_argument, NULL, 0x0823 }, "Which pre-existing net_cls cgroup to use as a parent (default: 'NSJAIL')" },
+    { { "cgroup_cpu_ms_per_sec", required_argument, NULL, 0x0831 }, "Number of us that the process group can use per second (default: '0' - disabled)" },
+    { { "cgroup_cpu_mount", required_argument, NULL, 0x0822 }, "Location of cpu cgroup FS (default: '/sys/fs/cgroup/net_cls')" },
+    { { "cgroup_cpu_parent", required_argument, NULL, 0x0833 }, "Which pre-existing cpu cgroup to use as a parent (default: 'NSJAIL')" },
     { { "iface_no_lo", no_argument, NULL, 0x700 }, "Don't bring the 'lo' interface up" },
     { { "macvlan_iface", required_argument, NULL, 'I' }, "Interface which will be cloned (MACVLAN) and put inside the subprocess' namespace as 'vs'" },
     { { "macvlan_vs_ip", required_argument, NULL, 0x701 }, "IP of the 'vs' interface (e.g. \"192.168.0.1\")" },
@@ -364,6 +367,9 @@ bool cmdlineParse(int argc, char* argv[], struct nsjconf_t* nsjconf) {
            .cgroup_net_cls_mount = "/sys/fs/cgroup/net_cls",
            .cgroup_net_cls_parent = "NSJAIL",
            .cgroup_net_cls_classid = (unsigned int)0,
+           .cgroup_cpu_mount = "/sys/fs/cgroup/cpu",
+           .cgroup_cpu_parent = "NSJAIL",
+           .cgroup_cpu_ms_per_sec = (unsigned int)0,
            .iface_no_lo = false,
            .iface_vs = NULL,
            .iface_vs_ip = "0.0.0.0",
@@ -740,6 +746,15 @@ bool cmdlineParse(int argc, char* argv[], struct nsjconf_t* nsjconf) {
                case 0x823:
                        nsjconf->cgroup_net_cls_parent = optarg;
                        break;
+               case 0x831:
+                       nsjconf->cgroup_cpu_ms_per_sec = (unsigned int)strtoul(optarg, NULL, 0);
+                       break;
+               case 0x832:
+                       nsjconf->cgroup_cpu_mount = optarg;
+                       break;
+               case 0x833:
+                       nsjconf->cgroup_cpu_parent = optarg;
+                       break;
                case 'P':
                        nsjconf->kafel_file_path = optarg;
                        if (access(nsjconf->kafel_file_path, R_OK) == -1) {
index 02ed8a944d02f3afc12b089ab91c690d3803a9d6..4a97674fe2a39878a51149a3be6add3d5c675587 100644 (file)
@@ -205,16 +205,24 @@ message NsJailConfig {
     optional string cgroup_net_cls_mount = 66 [default = "/sys/fs/cgroup/net_cls"];
     /* Writeable directory (for the nsjail user) under cgroup_net_mount */
     optional string cgroup_net_cls_parent = 67 [default = "NSJAIL"];
+
+    /* If > 0 number of milliseconds of CPU that jail processes can use per each second */
+    optional uint32 cgroup_cpu_ms_per_sec = 68 [default = 0];
+    /* Mount point for cgroups-cpu in your system */
+    optional string cgroup_cpu_mount = 69 [default = "/sys/fs/cgroup/cpu"];
+    /* Writeable directory (for the nsjail user) under cgroup_cpu_mount */
+    optional string cgroup_cpu_parent = 70 [default = "NSJAIL"];
+
     /* Should the 'lo' interface be brought up (active) inside this jail? */
-    optional bool iface_no_lo = 68 [default = false];
+    optional bool iface_no_lo = 71 [default = false];
 
     /* Parameters for the cloned MACVLAN interface inside jail */
-    optional string macvlan_iface = 69; /* Interface to be cloned, eg 'eth0' */
-    optional string macvlan_vs_ip = 70 [default = "192.168.0.2"];
-    optional string macvlan_vs_nm = 71 [default = "255.255.255.0"];
-    optional string macvlan_vs_gw = 72 [default = "192.168.0.1"];
+    optional string macvlan_iface = 72; /* Interface to be cloned, eg 'eth0' */
+    optional string macvlan_vs_ip = 73 [default = "192.168.0.2"];
+    optional string macvlan_vs_nm = 74 [default = "255.255.255.0"];
+    optional string macvlan_vs_gw = 75 [default = "192.168.0.1"];
 
     /* Binary path (with arguments) to be executed. If not specified here, it
        can be specified with cmd-line as "-- /path/to/command arg1 arg2" */
-    optional Exe exec_bin = 73;
+    optional Exe exec_bin = 76;
 }
index bc8c25fe9e30e3ad6813bdf5d708b07b6848989e..2b7161d6465880e1928c5f97c61d2c5ad6158a05 100644 (file)
--- a/nsjail.h
+++ b/nsjail.h
@@ -174,6 +174,9 @@ struct nsjconf_t {
        const char* cgroup_net_cls_mount;
        const char* cgroup_net_cls_parent;
        unsigned int cgroup_net_cls_classid;
+       const char* cgroup_cpu_mount;
+       const char* cgroup_cpu_parent;
+       unsigned int cgroup_cpu_ms_per_sec;
        const char* kafel_file_path;
        const char* kafel_string;
        struct sock_fprog seccomp_fprog;