/*++ Copyright (c) Microsoft. All rights reserved. Module Name: cgroup.c Abstract: This file contains unit tests for cgroup support. N.B. This test depends on libmount, which is part of the libmount-dev apt package. N.B. To pass on native Linux this test requires cgroups to not be managed by an OS daemon. cgclear can be used to remove some subsystems. --*/ #include "lxtcommon.h" #include "unittests.h" #include #include #include #include #include #include #include #include "lxtmount.h" #include #include #include #define LXT_NAME "cgroup" #define CGROUP_TEST_PATH "/data" #define CGROUP_TEST_MOUNT_NAME "cgroup" #define CGROUP_TEST_MOUNT_POINT_NAME "cgroup_mount_test" #define CGROUP_TEST_MOUNT_POINT CGROUP_TEST_PATH "/" CGROUP_TEST_MOUNT_POINT_NAME #define CGROUP_TEST_MOUNT_POINT2 "/sys/fs/cgroup" #define CGROUP_TEST_MOUNT_POINT_DIR1_NAME "dir1" #define CGROUP_TEST_MOUNT_POINT_DIR1 CGROUP_TEST_MOUNT_POINT "/" CGROUP_TEST_MOUNT_POINT_DIR1_NAME #define CGROUP_TEST_MOUNT_POINT2_DIR1 CGROUP_TEST_MOUNT_POINT2 "/" CGROUP_TEST_MOUNT_POINT_DIR1_NAME #define CGROUP_TEST_MOUNT_POINT_DIR1_CHILD_NAME "child" #define CGROUP_TEST_MOUNT_POINT_DIR1_CHILD CGROUP_TEST_MOUNT_POINT_DIR1 "/" CGROUP_TEST_MOUNT_POINT_DIR1_CHILD_NAME #define CGROUP_TEST_DEFAULT_BUFFER_SIZE 128 #define CGROUP_TEST_MAX_CGROUPS 12 #define CGROUP_TEST_MAX_NAME_LENGTH 32 #define CGROUP_TEST_MAX_PIDS 2048 #define CGROUP_TEST_DEVICES_DEFAULT_LIST "a *:* rwm\n" LXT_VARIATION_HANDLER CgroupTestBasicMount; LXT_VARIATION_HANDLER CgroupTestMkdir; LXT_VARIATION_HANDLER CgroupTestThreads; LXT_VARIATION_HANDLER CgroupTestProcfs; LXT_VARIATION_HANDLER CgroupTestProcsFile; LXT_VARIATION_HANDLER CgroupTestMountReuse; LXT_VARIATION_HANDLER CgroupTestDevices; // // TODO_LX: Enable all files when supported. // static const LXT_CHILD_INFO g_CgroupRootChildren[] = { {"cgroup.sane_behavior", DT_REG}, /* {"cgroup.clone_children", DT_REG}, {"cgroup.event_control", DT_REG}, */ {"cgroup.procs", DT_REG}}; /* {"notify_on_release", DT_REG}, {"release_agent", DT_REG}, {"tasks", DT_REG}};*/ static const LXT_CHILD_INFO g_CgroupDefaultChildren[] = {/* {"cgroup.clone_children", DT_REG}, {"cgroup.event_control", DT_REG}, */ {"cgroup.procs", DT_REG}}; /* {"notify_on_release", DT_REG}, {"release_agent", DT_REG}, {"tasks", DT_REG}};*/ static const LXT_CHILD_INFO g_CgroupDevicesChildren[] = {{"devices.allow", DT_REG}, {"devices.deny", DT_REG}, {"devices.list", DT_REG}}; // // Global constants // static const LXT_VARIATION g_LxtVariations[] = { {"cgroup - basic mount", CgroupTestBasicMount}, {"cgroup - mkdir", CgroupTestMkdir}, {"cgroup - threads", CgroupTestThreads}, {"cgroup - procfs", CgroupTestProcfs}, {"cgroup - cgroup.procs file", CgroupTestProcsFile}, {"cgroup - mount reuse", CgroupTestMountReuse}, {"cgroup - devices subsystem", CgroupTestDevices}}; static int g_TestPathMountId; int CgroupTestEntry(int Argc, char* Argv[]) /*++ --*/ { LXT_ARGS Args; int Result; // // Clean-up from previous iterations. // rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD); rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); umount(CGROUP_TEST_MOUNT_POINT2); umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT2); // // Run the test variations. // LxtCheckResult(g_TestPathMountId = MountGetMountId(CGROUP_TEST_PATH)); LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME)); LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); // // Mount cgroup with a folder to test the instance uninitialize path. // LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices")); LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777)); ErrorExit: LxtUninitialize(); return !LXT_SUCCESS(Result); } int CgroupTestBasicMount(PLXT_ARGS Args) /*++ Description: This routine tests the mount and umount system calls for cgroups. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { int MountId; int Result; // // Create the directory and ensure it's not a mount point yet. // LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); // // Mount a cgroup instance and check it was mounted. // LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, NULL)); LxtCheckResult( MountId = MountCheckIsMount( CGROUP_TEST_MOUNT_POINT, g_TestPathMountId, "mycgroupnew", CGROUP_TEST_MOUNT_NAME, "/", "rw,relatime", "rw,devices", "rw,relatime,devices", 0)); // // Mounting again should fail. // LxtCheckErrnoFailure(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, NULL), EBUSY); LxtCheckErrnoFailure(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices"), EBUSY); // // Unmount and check it was unmounted. // LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); ErrorExit: umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); return Result; } int CgroupTestMkdir(PLXT_ARGS Args) /*++ Description: This routine tests the mkdir and rmdir system calls for cgroups. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { LXT_CHILD_INFO ChildInfo; char ChildPidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; int ChildPidBufferLength; char Path[512]; int ProcsFd; int MountId; int Result; ProcsFd = -1; // // Mount cgroup. // LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices")); // // Removing the mount point root directory should fail. // LxtCheckErrnoFailure(rmdir(CGROUP_TEST_MOUNT_POINT), EBUSY); // // Create two subdirectories. // LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, g_CgroupRootChildren, LXT_COUNT_OF(g_CgroupRootChildren))); LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777)); ChildInfo.FileType = DT_DIR; ChildInfo.Name = CGROUP_TEST_MOUNT_POINT_DIR1_NAME; LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, &ChildInfo, 1)); LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD, 0777)); ChildInfo.Name = CGROUP_TEST_MOUNT_POINT_DIR1_CHILD_NAME; LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT_DIR1, &ChildInfo, 1)); // // Removing the first directory should fail if the second one still exists, // otherwise it succeeds. // LxtCheckErrnoFailure(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1), EBUSY); LxtCheckErrno(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD)); LxtCheckErrno(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1)); // // Check that removing the first directory fails if a thread is still // associated, otherwise it succeeds. // LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777)); sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT_DIR1, "cgroup.procs"); LxtCheckErrno(ProcsFd = open(Path, O_WRONLY)); ChildPidBufferLength = sprintf(ChildPidBuffer, "%d\n", getpid()); LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength)); LxtClose(ProcsFd); ProcsFd = -1; LxtCheckErrnoFailure(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1), EBUSY); sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT, "cgroup.procs"); LxtCheckErrno(ProcsFd = open(Path, O_WRONLY)); LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength)); LxtClose(ProcsFd); ProcsFd = -1; LxtCheckErrno(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1)); // // Unmount cgroup. // LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); ErrorExit: if (ProcsFd != -1) { LxtClose(ProcsFd); } rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD); rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); return Result; } int CgroupTestThreads(PLXT_ARGS Args) /*++ Description: This routine tests thread behavior with cgroups mounts. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { pid_t ChildPid; int MountId; LXT_PIPE Pipe = {-1, -1}; int Result; // // Create a thread, mount cgroup, and signal the thread to exit to test // cgroup assignment during mount. // LxtCheckResult(LxtCreatePipe(&Pipe)); LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { read(Pipe.Read, &Result, sizeof(Result)); _exit(Result); } LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, NULL)); write(Pipe.Write, &Result, sizeof(Result)); LxtWaitPidPoll(ChildPid, 0); // // Create a thread to test cgroup inheritance. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { _exit(Result); } LxtWaitPidPoll(ChildPid, 0); // // Unmount and exit. // LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); ErrorExit: write(Pipe.Write, &Result, sizeof(Result)); LxtClosePipe(&Pipe); umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); return Result; } typedef struct _CGROUP_TEST_PROCFS_ENTRY { char Name[CGROUP_TEST_MAX_NAME_LENGTH]; int Hierarcy; int NumCgroups; int Enabled; } CGROUP_TEST_PROCFS_ENTRY, *PCGROUP_TEST_PROCFS_ENTRY; typedef struct _CGROUP_TEST_PROCFS { int EntryCount; CGROUP_TEST_PROCFS_ENTRY Entries[CGROUP_TEST_MAX_CGROUPS]; } CGROUP_TEST_PROCFS, *PCGROUP_TEST_PROCFS; int CgroupTestReadProcfs(PCGROUP_TEST_PROCFS Data) /*++ Description: This routine parses /proc/cgroups. Arguments: Data - Supplies a buffer to store the data Return Value: Returns 0 on success, -1 on failure. --*/ { char Line[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; FILE* CgroupFile; int NumCgroups; int Result; memset(Data, 0, sizeof(*Data)); CgroupFile = fopen("/proc/cgroups", "r"); LxtCheckNotEqual(CgroupFile, NULL, "%p"); if (fgets(Line, LXT_COUNT_OF(Line), CgroupFile) == NULL) { Result = LXT_RESULT_FAILURE; LxtLogError("Failed to read header"); goto ErrorExit; } LxtCheckStringEqual(Line, "#subsys_name\thierarchy\tnum_cgroups\tenabled\n"); NumCgroups = 0; while (fgets(Line, LXT_COUNT_OF(Line), CgroupFile) != NULL) { sscanf( Line, "%s\t%d\t%d\t%d", Data->Entries[NumCgroups].Name, &Data->Entries[NumCgroups].Hierarcy, &Data->Entries[NumCgroups].NumCgroups, &Data->Entries[NumCgroups].Enabled); NumCgroups += 1; } Data->EntryCount = NumCgroups; ErrorExit: if (CgroupFile != NULL) { fclose(CgroupFile); } return Result; } typedef struct _CGROUP_TEST_PROCFS_PID_ENTRY { int Hierarcy; char Subsystems[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; char CgroupPath[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; } CGROUP_TEST_PROCFS_PID_ENTRY, *PCGROUP_TEST_PROCFS_PID_ENTRY; typedef struct _CGROUP_TEST_PROCFS_PID { int EntryCount; CGROUP_TEST_PROCFS_PID_ENTRY Entries[CGROUP_TEST_MAX_CGROUPS]; } CGROUP_TEST_PROCFS_PID, *PCGROUP_TEST_PROCFS_PID; int CgroupTestReadProcfsPid(PCGROUP_TEST_PROCFS_PID Data) /*++ Description: This routine parses /proc/self/cgroup. Arguments: Data - Supplies a buffer to store the data Return Value: Returns 0 on success, -1 on failure. --*/ { char Line[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; FILE* CgroupFile; int NumCgroups; int Result; memset(Data, 0, sizeof(*Data)); CgroupFile = fopen("/proc/self/cgroup", "r"); LxtCheckNotEqual(CgroupFile, NULL, "%p"); NumCgroups = 0; while (fgets(Line, LXT_COUNT_OF(Line), CgroupFile) != NULL) { sscanf( Line, "%d:%[^:]:%[^:\n]", &Data->Entries[NumCgroups].Hierarcy, Data->Entries[NumCgroups].Subsystems, Data->Entries[NumCgroups].CgroupPath); NumCgroups += 1; } Data->EntryCount = NumCgroups; ErrorExit: if (CgroupFile != NULL) { fclose(CgroupFile); } return Result; } int CgroupTestProcfs(PLXT_ARGS Args) /*++ Description: This routine tests the cgroup procfs files. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { int BytesRead; int Found; int Index; int MountId; CGROUP_TEST_PROCFS ProcfsNew; CGROUP_TEST_PROCFS ProcfsOrig; CGROUP_TEST_PROCFS_PID ProcfsPidNew; CGROUP_TEST_PROCFS_PID ProcfsPidOrig; int Result; // // Create the cgroup mount. // LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices")); // // Read the procfs files before cgroups are mounted, staring // with /proc/cgroup. // LxtCheckResult(CgroupTestReadProcfs(&ProcfsNew)); LxtCheckNotEqual(ProcfsNew.EntryCount, 0, "%d"); Found = 0; for (Index = 0; Index < ProcfsNew.EntryCount; ++Index) { LxtCheckNotEqual(ProcfsNew.Entries[Index].NumCgroups, 0, "%d"); LxtCheckEqual(ProcfsNew.Entries[Index].Enabled, 1, "%d"); if (strcmp(ProcfsNew.Entries[Index].Name, "devices") == 0) { LxtCheckNotEqual(Found, 1, "%d"); LxtCheckNotEqual(ProcfsNew.Entries[Index].Hierarcy, 0, "%d"); Found = 1; } } LxtCheckEqual(Found, 1, "%d"); // // Now /proc/self/cgroup. // LxtCheckResult(CgroupTestReadProcfsPid(&ProcfsPidNew)); Found = 0; for (Index = 0; Index < ProcfsPidNew.EntryCount; ++Index) { if (strstr(ProcfsPidNew.Entries[Index].Subsystems, "devices") != NULL) { LxtCheckNotEqual(Found, 1, "%d"); LxtCheckNotEqual(ProcfsPidNew.Entries[Index].Hierarcy, 0, "%d"); LxtCheckStringEqual(ProcfsPidNew.Entries[Index].CgroupPath, "/"); Found = 1; } } LxtCheckEqual(Found, 1, "%d"); // // Unmount and recheck the original. // LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); ErrorExit: umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); return Result; } int CgroupTestGetProcsFileIds(char* CgroupPath, pid_t* IdArray[], int* IdArrayCount) /*++ Description: This routine tests the behavior of the cgroup.procs file. Arguments: Path - Supplies the path to query. IdArray - Supplies a buffer to store the array of ids. IdArrayCount - Supplies a buffer to store the number of ids. Return Value: Returns 0 on success, -1 on failure. --*/ { pid_t* Array; int Count; int Index; FILE* File; char Line[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; char Path[512]; int PathLength; int Result; Array = NULL; File = NULL; sprintf(Path, "%s/%s", CgroupPath, "cgroup.procs"); File = fopen(Path, "r"); LxtCheckNotEqual(File, NULL, "%p"); Array = LxtAlloc(sizeof(*Array) * CGROUP_TEST_MAX_PIDS); if (Array == NULL) { Result = LXT_RESULT_FAILURE; goto ErrorExit; } Count = 0; while (fgets(Line, LXT_COUNT_OF(Line), File) != NULL) { if (Count > CGROUP_TEST_MAX_PIDS) { LxtLogError("Unexpected count"); Result = LXT_RESULT_FAILURE; goto ErrorExit; } sscanf(Line, "%d\n", &Array[Count]); Count += 1; } for (Index = 1; Index < Count; ++Index) { if (Array[Index - 1] >= Array[Index]) { LxtLogError("Unexpected value %d, %d", Array[Index - 1], Array[Index]); Result = LXT_RESULT_FAILURE; goto ErrorExit; } } *IdArray = Array; Array = NULL; *IdArrayCount = Count; ErrorExit: if (File != NULL) { fclose(File); } if (Array != NULL) { LxtFree(Array); } return Result; } int CgroupTestProcsFile(PLXT_ARGS Args) /*++ Description: This routine tests the behavior of the cgroup.procs file. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { pid_t* Array; pid_t ChildPid; char ChildPidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; int ChildPidBufferLength; int Count; int Index; int MountId; char Path[512]; int ProcsFd; LXT_PIPE Pipe = {-1, -1}; int Result; Array = NULL; ProcsFd = -1; LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices")); // // Create a threadgroup and check that it is in the root folder. // LxtCheckResult(LxtCreatePipe(&Pipe)); LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { read(Pipe.Read, &Result, sizeof(Result)); _exit(0); } LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT, &Array, &Count)); for (Index = 0; Index < Count; ++Index) { if (Array[Index] == ChildPid) { break; } } LxtCheckNotEqual(Index, Count, "%d"); LxtFree(Array); Array = NULL; // // Create a folder and check that it is empty. // rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777)); LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT_DIR1, &Array, &Count)); LxtCheckEqual(0, Count, "%d"); LxtFree(Array); Array = NULL; // // Move the thread to the folder and check that the thread was moved. // sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT_DIR1, "cgroup.procs"); LxtCheckErrno(ProcsFd = open(Path, O_WRONLY)); ChildPidBufferLength = sprintf(ChildPidBuffer, "%d\n", ChildPid); LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength)); LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT_DIR1, &Array, &Count)); for (Index = 0; Index < Count; ++Index) { if (Array[Index] == ChildPid) { break; } } LxtCheckNotEqual(Index, Count, "%d"); LxtFree(Array); Array = NULL; LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT, &Array, &Count)); for (Index = 0; Index < Count; ++Index) { if (Array[Index] == ChildPid) { break; } } LxtCheckEqual(Index, Count, "%d"); LxtFree(Array); Array = NULL; LxtClose(ProcsFd); ProcsFd = -1; // // Move the thread to the root and check that the thread was moved. // sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT, "cgroup.procs"); LxtCheckErrno(ProcsFd = open(Path, O_WRONLY)); ChildPidBufferLength = sprintf(ChildPidBuffer, "%d\n", ChildPid); LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength)); LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT_DIR1, &Array, &Count)); LxtCheckEqual(0, Count, "%d"); LxtFree(Array); Array = NULL; LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT, &Array, &Count)); for (Index = 0; Index < Count; ++Index) { if (Array[Index] == ChildPid) { break; } } LxtCheckNotEqual(Index, Count, "%d"); LxtFree(Array); Array = NULL; LxtClose(ProcsFd); ProcsFd = -1; // // Unmount and exit. // rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); ErrorExit: write(Pipe.Write, &Result, sizeof(Result)); LxtClosePipe(&Pipe); if (Array != NULL) { LxtFree(Array); } if (ProcsFd != -1) { LxtClose(ProcsFd); } umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); return Result; } int CgroupTestMountReuse(PLXT_ARGS Args) /*++ Description: This routine tests the behavior of reusing cgroup hierarchy mounts. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { LXT_CHILD_INFO ChildInfo; char ChildPidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; int ChildPidBufferLength; int Found; int Index; char Path[512]; int ProcsFd; CGROUP_TEST_PROCFS ProcfsNew; int MountId; int Result; ProcsFd = -1; rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD); rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); // // Mount cgroup. // LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices")); // // A cgroup with a directory should be reported as active when unmounted // LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777)); LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT_DIR1, F_OK)); LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckErrnoFailure(access(CGROUP_TEST_MOUNT_POINT_DIR1, F_OK), ENOENT); LxtCheckResult(CgroupTestReadProcfs(&ProcfsNew)); LxtCheckNotEqual(ProcfsNew.EntryCount, 0, "%d"); Found = 0; for (Index = 0; Index < ProcfsNew.EntryCount; ++Index) { LxtCheckNotEqual(ProcfsNew.Entries[Index].NumCgroups, 0, "%d"); LxtCheckEqual(ProcfsNew.Entries[Index].Enabled, 1, "%d"); if (strcmp(ProcfsNew.Entries[Index].Name, "devices") == 0) { LxtCheckNotEqual(Found, 1, "%d"); LxtCheckNotEqual(ProcfsNew.Entries[Index].Hierarcy, 0, "%d"); Found = 1; } } LxtCheckEqual(Found, 1, "%d"); // // When remounted the directory is present // LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices")); LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT_DIR1, F_OK)); // // When that cgroup is mounted again, the directory should be present // LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT2, CGROUP_TEST_MOUNT_NAME, 0, "devices")); LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT2_DIR1, F_OK)); umount(CGROUP_TEST_MOUNT_POINT2); // // Failing variation to check the mount all case. // // TODO_LX: This variation needs to be updated once multiple subsystems are // supported. // LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT2, CGROUP_TEST_MOUNT_NAME, 0, NULL)); LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT2_DIR1, F_OK)); // // Unmount and exit. // rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT2)); LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); ErrorExit: if (ProcsFd != -1) { LxtClose(ProcsFd); } rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD); rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); umount(CGROUP_TEST_MOUNT_POINT2); umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT2); return Result; } int CgroupTestDevices(PLXT_ARGS Args) /*++ Description: This routine tests the files for the devices subsystem. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { int BytesRead; int CgroupFd; char DevicesListBuffer[32]; char Path[512]; char PidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE]; int PidBufferLength; int Result; CgroupFd = -1; PidBufferLength = sprintf(PidBuffer, "%d\n", getpid()); // // Mount cgroup. // LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777)); LxtCheckErrnoZeroSuccess(mount("mycgroupnew", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, "devices")); // // Check for the expected default files and devices files in the root. // LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, g_CgroupRootChildren, LXT_COUNT_OF(g_CgroupRootChildren))); LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, g_CgroupDevicesChildren, LXT_COUNT_OF(g_CgroupDevicesChildren))); // // Check for the expected default files and devices files in a subdirectory. // // N.B. A thread has to exist in the cgroup for some files to successfully // be read. // LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777)); sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT_DIR1, "cgroup.procs"); LxtCheckErrno(CgroupFd = open(Path, O_WRONLY)); LxtCheckErrno(write(CgroupFd, PidBuffer, PidBufferLength)); LxtClose(CgroupFd); CgroupFd = -1; LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT_DIR1, g_CgroupDefaultChildren, LXT_COUNT_OF(g_CgroupDefaultChildren))); LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT_DIR1, g_CgroupDevicesChildren, LXT_COUNT_OF(g_CgroupDevicesChildren))); // // Check for the expected value of the devices.list file in both folders. // sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT, "devices.list"); LxtCheckErrno(CgroupFd = open(Path, O_RDONLY)); LxtCheckErrno(BytesRead = read(CgroupFd, DevicesListBuffer, sizeof(DevicesListBuffer) - 1)); DevicesListBuffer[BytesRead] = 0; LxtClose(CgroupFd); CgroupFd = -1; LxtCheckStringEqual(DevicesListBuffer, CGROUP_TEST_DEVICES_DEFAULT_LIST); sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT_DIR1, "devices.list"); LxtCheckErrno(CgroupFd = open(Path, O_RDONLY)); LxtCheckErrno(BytesRead = read(CgroupFd, DevicesListBuffer, sizeof(DevicesListBuffer) - 1)); DevicesListBuffer[BytesRead] = 0; LxtClose(CgroupFd); CgroupFd = -1; LxtCheckStringEqual(DevicesListBuffer, CGROUP_TEST_DEVICES_DEFAULT_LIST); // // Unmount cgroup. // sprintf(Path, "%s/%s", CGROUP_TEST_MOUNT_POINT, "cgroup.procs"); LxtCheckErrno(CgroupFd = open(Path, O_WRONLY)); LxtCheckErrno(write(CgroupFd, PidBuffer, PidBufferLength)); LxtClose(CgroupFd); CgroupFd = -1; rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT)); LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT)); ErrorExit: if (CgroupFd != -1) { LxtClose(CgroupFd); } rmdir(CGROUP_TEST_MOUNT_POINT_DIR1); umount(CGROUP_TEST_MOUNT_POINT); rmdir(CGROUP_TEST_MOUNT_POINT); return Result; }