.. | .. |
---|
15 | 15 | #include <unistd.h> |
---|
16 | 16 | |
---|
17 | 17 | #include "cgroup_util.h" |
---|
| 18 | +#include "../clone3/clone3_selftests.h" |
---|
18 | 19 | |
---|
19 | 20 | static ssize_t read_text(const char *path, char *buf, size_t max_len) |
---|
20 | 21 | { |
---|
.. | .. |
---|
70 | 71 | char *ret = malloc(len); |
---|
71 | 72 | |
---|
72 | 73 | snprintf(ret, len, "%s/%s_%d", root, name, index); |
---|
| 74 | + |
---|
| 75 | + return ret; |
---|
| 76 | +} |
---|
| 77 | + |
---|
| 78 | +char *cg_control(const char *cgroup, const char *control) |
---|
| 79 | +{ |
---|
| 80 | + size_t len = strlen(cgroup) + strlen(control) + 2; |
---|
| 81 | + char *ret = malloc(len); |
---|
| 82 | + |
---|
| 83 | + snprintf(ret, len, "%s/%s", cgroup, control); |
---|
73 | 84 | |
---|
74 | 85 | return ret; |
---|
75 | 86 | } |
---|
.. | .. |
---|
148 | 159 | return atol(ptr + strlen(key)); |
---|
149 | 160 | } |
---|
150 | 161 | |
---|
| 162 | +long cg_read_lc(const char *cgroup, const char *control) |
---|
| 163 | +{ |
---|
| 164 | + char buf[PAGE_SIZE]; |
---|
| 165 | + const char delim[] = "\n"; |
---|
| 166 | + char *line; |
---|
| 167 | + long cnt = 0; |
---|
| 168 | + |
---|
| 169 | + if (cg_read(cgroup, control, buf, sizeof(buf))) |
---|
| 170 | + return -1; |
---|
| 171 | + |
---|
| 172 | + for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) |
---|
| 173 | + cnt++; |
---|
| 174 | + |
---|
| 175 | + return cnt; |
---|
| 176 | +} |
---|
| 177 | + |
---|
151 | 178 | int cg_write(const char *cgroup, const char *control, char *buf) |
---|
152 | 179 | { |
---|
153 | 180 | char path[PATH_MAX]; |
---|
.. | .. |
---|
192 | 219 | |
---|
193 | 220 | int cg_create(const char *cgroup) |
---|
194 | 221 | { |
---|
195 | | - return mkdir(cgroup, 0644); |
---|
| 222 | + return mkdir(cgroup, 0755); |
---|
196 | 223 | } |
---|
197 | 224 | |
---|
198 | | -static int cg_killall(const char *cgroup) |
---|
| 225 | +int cg_wait_for_proc_count(const char *cgroup, int count) |
---|
| 226 | +{ |
---|
| 227 | + char buf[10 * PAGE_SIZE] = {0}; |
---|
| 228 | + int attempts; |
---|
| 229 | + char *ptr; |
---|
| 230 | + |
---|
| 231 | + for (attempts = 10; attempts >= 0; attempts--) { |
---|
| 232 | + int nr = 0; |
---|
| 233 | + |
---|
| 234 | + if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) |
---|
| 235 | + break; |
---|
| 236 | + |
---|
| 237 | + for (ptr = buf; *ptr; ptr++) |
---|
| 238 | + if (*ptr == '\n') |
---|
| 239 | + nr++; |
---|
| 240 | + |
---|
| 241 | + if (nr >= count) |
---|
| 242 | + return 0; |
---|
| 243 | + |
---|
| 244 | + usleep(100000); |
---|
| 245 | + } |
---|
| 246 | + |
---|
| 247 | + return -1; |
---|
| 248 | +} |
---|
| 249 | + |
---|
| 250 | +int cg_killall(const char *cgroup) |
---|
199 | 251 | { |
---|
200 | 252 | char buf[PAGE_SIZE]; |
---|
201 | 253 | char *ptr = buf; |
---|
.. | .. |
---|
226 | 278 | retry: |
---|
227 | 279 | ret = rmdir(cgroup); |
---|
228 | 280 | if (ret && errno == EBUSY) { |
---|
229 | | - ret = cg_killall(cgroup); |
---|
230 | | - if (ret) |
---|
231 | | - return ret; |
---|
| 281 | + cg_killall(cgroup); |
---|
232 | 282 | usleep(100); |
---|
233 | 283 | goto retry; |
---|
234 | 284 | } |
---|
.. | .. |
---|
239 | 289 | return ret; |
---|
240 | 290 | } |
---|
241 | 291 | |
---|
242 | | -int cg_enter_current(const char *cgroup) |
---|
| 292 | +int cg_enter(const char *cgroup, int pid) |
---|
243 | 293 | { |
---|
244 | 294 | char pidbuf[64]; |
---|
245 | 295 | |
---|
246 | | - snprintf(pidbuf, sizeof(pidbuf), "%d", getpid()); |
---|
| 296 | + snprintf(pidbuf, sizeof(pidbuf), "%d", pid); |
---|
247 | 297 | return cg_write(cgroup, "cgroup.procs", pidbuf); |
---|
| 298 | +} |
---|
| 299 | + |
---|
| 300 | +int cg_enter_current(const char *cgroup) |
---|
| 301 | +{ |
---|
| 302 | + return cg_write(cgroup, "cgroup.procs", "0"); |
---|
| 303 | +} |
---|
| 304 | + |
---|
| 305 | +int cg_enter_current_thread(const char *cgroup) |
---|
| 306 | +{ |
---|
| 307 | + return cg_write(cgroup, "cgroup.threads", "0"); |
---|
248 | 308 | } |
---|
249 | 309 | |
---|
250 | 310 | int cg_run(const char *cgroup, |
---|
.. | .. |
---|
272 | 332 | } |
---|
273 | 333 | } |
---|
274 | 334 | |
---|
| 335 | +pid_t clone_into_cgroup(int cgroup_fd) |
---|
| 336 | +{ |
---|
| 337 | +#ifdef CLONE_ARGS_SIZE_VER2 |
---|
| 338 | + pid_t pid; |
---|
| 339 | + |
---|
| 340 | + struct __clone_args args = { |
---|
| 341 | + .flags = CLONE_INTO_CGROUP, |
---|
| 342 | + .exit_signal = SIGCHLD, |
---|
| 343 | + .cgroup = cgroup_fd, |
---|
| 344 | + }; |
---|
| 345 | + |
---|
| 346 | + pid = sys_clone3(&args, sizeof(struct __clone_args)); |
---|
| 347 | + /* |
---|
| 348 | + * Verify that this is a genuine test failure: |
---|
| 349 | + * ENOSYS -> clone3() not available |
---|
| 350 | + * E2BIG -> CLONE_INTO_CGROUP not available |
---|
| 351 | + */ |
---|
| 352 | + if (pid < 0 && (errno == ENOSYS || errno == E2BIG)) |
---|
| 353 | + goto pretend_enosys; |
---|
| 354 | + |
---|
| 355 | + return pid; |
---|
| 356 | + |
---|
| 357 | +pretend_enosys: |
---|
| 358 | +#endif |
---|
| 359 | + errno = ENOSYS; |
---|
| 360 | + return -ENOSYS; |
---|
| 361 | +} |
---|
| 362 | + |
---|
| 363 | +int clone_reap(pid_t pid, int options) |
---|
| 364 | +{ |
---|
| 365 | + int ret; |
---|
| 366 | + siginfo_t info = { |
---|
| 367 | + .si_signo = 0, |
---|
| 368 | + }; |
---|
| 369 | + |
---|
| 370 | +again: |
---|
| 371 | + ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD); |
---|
| 372 | + if (ret < 0) { |
---|
| 373 | + if (errno == EINTR) |
---|
| 374 | + goto again; |
---|
| 375 | + return -1; |
---|
| 376 | + } |
---|
| 377 | + |
---|
| 378 | + if (options & WEXITED) { |
---|
| 379 | + if (WIFEXITED(info.si_status)) |
---|
| 380 | + return WEXITSTATUS(info.si_status); |
---|
| 381 | + } |
---|
| 382 | + |
---|
| 383 | + if (options & WSTOPPED) { |
---|
| 384 | + if (WIFSTOPPED(info.si_status)) |
---|
| 385 | + return WSTOPSIG(info.si_status); |
---|
| 386 | + } |
---|
| 387 | + |
---|
| 388 | + if (options & WCONTINUED) { |
---|
| 389 | + if (WIFCONTINUED(info.si_status)) |
---|
| 390 | + return 0; |
---|
| 391 | + } |
---|
| 392 | + |
---|
| 393 | + return -1; |
---|
| 394 | +} |
---|
| 395 | + |
---|
| 396 | +int dirfd_open_opath(const char *dir) |
---|
| 397 | +{ |
---|
| 398 | + return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH); |
---|
| 399 | +} |
---|
| 400 | + |
---|
| 401 | +#define close_prot_errno(fd) \ |
---|
| 402 | + if (fd >= 0) { \ |
---|
| 403 | + int _e_ = errno; \ |
---|
| 404 | + close(fd); \ |
---|
| 405 | + errno = _e_; \ |
---|
| 406 | + } |
---|
| 407 | + |
---|
| 408 | +static int clone_into_cgroup_run_nowait(const char *cgroup, |
---|
| 409 | + int (*fn)(const char *cgroup, void *arg), |
---|
| 410 | + void *arg) |
---|
| 411 | +{ |
---|
| 412 | + int cgroup_fd; |
---|
| 413 | + pid_t pid; |
---|
| 414 | + |
---|
| 415 | + cgroup_fd = dirfd_open_opath(cgroup); |
---|
| 416 | + if (cgroup_fd < 0) |
---|
| 417 | + return -1; |
---|
| 418 | + |
---|
| 419 | + pid = clone_into_cgroup(cgroup_fd); |
---|
| 420 | + close_prot_errno(cgroup_fd); |
---|
| 421 | + if (pid == 0) |
---|
| 422 | + exit(fn(cgroup, arg)); |
---|
| 423 | + |
---|
| 424 | + return pid; |
---|
| 425 | +} |
---|
| 426 | + |
---|
275 | 427 | int cg_run_nowait(const char *cgroup, |
---|
276 | 428 | int (*fn)(const char *cgroup, void *arg), |
---|
277 | 429 | void *arg) |
---|
278 | 430 | { |
---|
279 | 431 | int pid; |
---|
| 432 | + |
---|
| 433 | + pid = clone_into_cgroup_run_nowait(cgroup, fn, arg); |
---|
| 434 | + if (pid > 0) |
---|
| 435 | + return pid; |
---|
| 436 | + |
---|
| 437 | + /* Genuine test failure. */ |
---|
| 438 | + if (pid < 0 && errno != ENOSYS) |
---|
| 439 | + return -1; |
---|
280 | 440 | |
---|
281 | 441 | pid = fork(); |
---|
282 | 442 | if (pid == 0) { |
---|
.. | .. |
---|
368 | 528 | close(fd); |
---|
369 | 529 | return 0; |
---|
370 | 530 | } |
---|
| 531 | + |
---|
| 532 | +ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size) |
---|
| 533 | +{ |
---|
| 534 | + char path[PATH_MAX]; |
---|
| 535 | + |
---|
| 536 | + if (!pid) |
---|
| 537 | + snprintf(path, sizeof(path), "/proc/%s/%s", |
---|
| 538 | + thread ? "thread-self" : "self", item); |
---|
| 539 | + else |
---|
| 540 | + snprintf(path, sizeof(path), "/proc/%d/%s", pid, item); |
---|
| 541 | + |
---|
| 542 | + return read_text(path, buf, size); |
---|
| 543 | +} |
---|
| 544 | + |
---|
| 545 | +int proc_read_strstr(int pid, bool thread, const char *item, const char *needle) |
---|
| 546 | +{ |
---|
| 547 | + char buf[PAGE_SIZE]; |
---|
| 548 | + |
---|
| 549 | + if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0) |
---|
| 550 | + return -1; |
---|
| 551 | + |
---|
| 552 | + return strstr(buf, needle) ? 0 : -1; |
---|
| 553 | +} |
---|
| 554 | + |
---|
| 555 | +int clone_into_cgroup_run_wait(const char *cgroup) |
---|
| 556 | +{ |
---|
| 557 | + int cgroup_fd; |
---|
| 558 | + pid_t pid; |
---|
| 559 | + |
---|
| 560 | + cgroup_fd = dirfd_open_opath(cgroup); |
---|
| 561 | + if (cgroup_fd < 0) |
---|
| 562 | + return -1; |
---|
| 563 | + |
---|
| 564 | + pid = clone_into_cgroup(cgroup_fd); |
---|
| 565 | + close_prot_errno(cgroup_fd); |
---|
| 566 | + if (pid < 0) |
---|
| 567 | + return -1; |
---|
| 568 | + |
---|
| 569 | + if (pid == 0) |
---|
| 570 | + exit(EXIT_SUCCESS); |
---|
| 571 | + |
---|
| 572 | + /* |
---|
| 573 | + * We don't care whether this fails. We only care whether the initial |
---|
| 574 | + * clone succeeded. |
---|
| 575 | + */ |
---|
| 576 | + (void)clone_reap(pid, WEXITED); |
---|
| 577 | + return 0; |
---|
| 578 | +} |
---|