/* * This function assumes that all pids inside a cgroup * belong to the same namespace, that is the container namespace. * Therefore, from the host box, any of them will do. */ pid_t get_pid_from_container(envid_t veid) { char cgrp[CT_MAX_STR_SIZE]; struct cgroup *ct; void *task_handle; void *cont_handle; struct cgroup_mount_point mnt; pid_t pid = -1; int ret; veid_to_name(cgrp, veid); ct = cgroup_new_cgroup(cgrp); ret = cgroup_get_cgroup(ct); if (ret == ECGROUPNOTEXIST) goto out_free; ret = cgroup_get_controller_begin(&cont_handle, &mnt); if (ret != 0) /* no controllers, something is wrong */ goto out_free; ret = cgroup_get_task_begin(cgrp, mnt.name, &task_handle, &pid); if (ret != 0) /* no tasks, something is also wrong */ goto out_end_cont; cgroup_get_task_end(&task_handle); out_end_cont: cgroup_get_controller_end(&cont_handle); out_free: cgroup_free(&ct); return pid; }
int ProcFamily::spree_cgroup(int sig) { // The general idea here is we freeze the cgroup, give the signal, // then thaw everything out. This way, signals are given in an atomic manner. // // Note that if the FREEZE call could be attempted, but not 100% completed, we // proceed anyway. bool use_freezer = !m_last_signal_was_sigstop; m_last_signal_was_sigstop = sig == SIGSTOP ? true : false; if (!use_freezer) { dprintf(D_ALWAYS, "Not using freezer controller to send signal; last " "signal was SIGSTOP.\n"); } else { dprintf(D_FULLDEBUG, "Using freezer controller to send signal to process family.\n"); } int err = use_freezer ? freezer_cgroup(FROZEN) : 0; if ((err != 0) && (err != -EBUSY)) { return err; } ASSERT (m_cgroup.isValid()); cgroup_get_cgroup(&const_cast<struct cgroup&>(m_cgroup.getCgroup())); void **handle = (void **)malloc(sizeof(void*)); ASSERT (handle != NULL); pid_t pid; err = cgroup_get_task_begin(m_cgroup_string.c_str(), FREEZE_CONTROLLER_STR, handle, &pid); if ((err > 0) && (err != ECGEOF)) handle = NULL; while (err != ECGEOF) { if (err > 0) { dprintf(D_ALWAYS, "Unable to iterate through cgroup %s (ProcFamily %u): %u %s\n", m_cgroup_string.c_str(), m_root_pid, err, cgroup_strerror(err)); goto release; } send_signal(pid, sig); err = cgroup_get_task_next(handle, &pid); } err = 0; release: if (handle != NULL) { cgroup_get_task_end(handle); free(handle); } if (use_freezer) freezer_cgroup(THAWED); return err; }
/* libcgroup is lame. This should be done with the cgroup structure, not the * cgroup name */ static int controller_has_tasks(const char *cgrp, const char *name) { int ret; pid_t pid; void *handle; ret = cgroup_get_task_begin(cgrp, name, &handle, &pid); ret = (ret != ECGEOF); cgroup_get_task_end(&handle); return ret; }
/* * We send a kill signal to all processes. This is racy in theory, since they * could spawn new processes faster than we kill. But since one of them is the * init process, (we don't really know which), then eventually the init process * will die taking away all the others, so this is fine. * * This is a big hack, and only exists because we have no way to enter a PID * namespace from the outside (yet). From there, we could just issue a normal * reboot. */ int hackish_empty_container(envid_t veid) { char cgrp[CT_MAX_STR_SIZE]; struct cgroup *ct; int ret = 0; void *task_handle; pid_t pid; int i; veid_to_name(cgrp, veid); ct = cgroup_new_cgroup(cgrp); ret = cgroup_get_cgroup(ct); if (ret == ECGROUPNOTEXIST) { ret = 0; goto out; } /* Any controller will do */ ret = cgroup_get_task_begin(cgrp, "cpu", &task_handle, &pid); while (!ret) { kill(pid, SIGKILL); ret = cgroup_get_task_next(&task_handle, &pid); } cgroup_get_task_end(&task_handle); if (ret != ECGEOF) { logger(-1, 0, "Could not finish all tasks: %s", cgroup_strerror(ret)); goto out; } ret = 0; for (i = 0; i < DEF_STOP_TIMEOUT; i++) { if (!container_is_running(veid)) goto out; usleep(500000); } logger(-1, 0, "Failed to wait for CT tasks to die"); ret = VZ_STOP_ERROR; out: cgroup_free(&ct); return ret; }
int ProcFamily::count_tasks_cgroup() { if (!m_cm.isMounted(CgroupManager::CPUACCT_CONTROLLER) || !m_cgroup.isValid()) { return -1; } int tasks = 0, err = 0; pid_t pid; void **handle = (void **)malloc(sizeof(void*)); ASSERT (handle != NULL) *handle = NULL; err = cgroup_get_task_begin(m_cgroup_string.c_str(), CPUACCT_CONTROLLER_STR, handle, &pid); while (err != ECGEOF) { if (err > 0) { dprintf(D_PROCFAMILY, "Unable to read cgroup %s memory stats (ProcFamily %u): %u %s.\n", m_cgroup_string.c_str(), m_root_pid, err, cgroup_strerror(err)); break; } tasks ++; err = cgroup_get_task_next(handle, &pid); } // Reset err to 0 if (err == ECGEOF) { err = 0; } if (*handle) { cgroup_get_task_end(handle); } if (handle) { free(handle); } if (err) { return -err; } return tasks; }