Below is the file 'atf/procs.cpp' from this revision. You can also download the file.

//
// Automated Testing Framework (atf)
//
// Copyright (c) 2008 The NetBSD Foundation, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. All advertising materials mentioning features or use of this
//    software must display the following acknowledgement:
//        This product includes software developed by the NetBSD
//        Foundation, Inc. and its contributors.
// 4. Neither the name of The NetBSD Foundation nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

#if defined(HAVE_CONFIG_H)
#   include "config.h"
#endif // defined(HAVE_CONFIG_H)

#if defined(HAVE_KVM_GETPROC2)
#   define PID_GRABBER_KVM_GETPROC2
#elif defined(HAVE_SYSCTL_KERN_PROC)
#   define PID_GRABBER_SYSCTL_KERN_PROC
#elif defined(HAVE_LINUX_PROCFS)
#   define PID_GRABBER_LINUX_PROCFS
#else
#   define PID_GRABBER_OTHER
#endif

extern "C" {
#include <signal.h>
#include <unistd.h>
#if defined(PID_GRABBER_KVM_GETPROC2)
#   include <fcntl.h>
#   include <kvm.h>
#   include <sys/sysctl.h>
#endif // defined(PID_GRABBER_KVM_GETPROC2)
#if defined(PID_GRABBER_SYSCTL_KERN_PROC)
#   include <sys/types.h>
#   include <sys/sysctl.h>
#endif // defined(PID_GRABBER_SYSCTL_KERN_PROC)
#if defined(PID_GRABBER_LINUX_PROCFS)
#   include <sys/types.h>
#   include <dirent.h>
#endif // defined(PID_GRABBER_LINUX_PROCFS)
}

#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <fstream>
#include <iostream>

#include "atf/exceptions.hpp"
#include "atf/procs.hpp"
#include "atf/sanity.hpp"
#include "atf/text.hpp"
#include "atf/utils.hpp"

namespace impl = atf::procs;
#define IMPL_NAME "atf::procs"

// ------------------------------------------------------------------------
// Auxiliary functions.
// ------------------------------------------------------------------------

static
bool
kill(pid_t pid, int signo, impl::errors_vector& errors, bool ignore = false)
{
    int ret;

    ret = ::kill(pid, signo);
    if (ret == -1 && !(ignore && errno == ESRCH)) {
        const std::string pidstr = atf::text::to_string(pid);
        const std::string signostr = atf::text::to_string(signo);
        atf::system_error e(IMPL_NAME "::kill",
                            "kill(" + pidstr + ", " +
                            signostr + ") failed",
                            errno);
        errors.push_back(impl::pid_error_pair(pid, e.what()));
    }
    return ret != -1;
}

// ------------------------------------------------------------------------
// The "pid_grabber" class for systems with KVM.
// ------------------------------------------------------------------------

#if defined(PID_GRABBER_KVM_GETPROC2)

impl::pid_grabber::pid_grabber(void)
{
    m_cookie = ::kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
    if (m_cookie == NULL)
        throw std::runtime_error("Failed to initialize the KVM library");
}

impl::pid_grabber::~pid_grabber(void)
{
    ::kvm_close(reinterpret_cast< kvm_t * >(m_cookie));
}

bool
impl::pid_grabber::can_get_children_of(void)
    const
{
    return true;
}

impl::pid_set
impl::pid_grabber::get_children_of(pid_t pid)
{
    kvm_t* kd = reinterpret_cast< kvm_t * >(m_cookie);

    int cnt;
    struct kinfo_proc2 *procs =
        ::kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2),
                       &cnt);
    if (procs == NULL) {
        const char* err = ::kvm_geterr(kd);
        throw std::runtime_error(std::string("kvm_getprocs failed: ") + err);
    }

    pid_set children;
    for (int i = 0; i < cnt; i++) {
        if (procs[i].p_ppid == pid)
            children.insert(procs[i].p_pid);
    }
    return children;
}

#endif // defined(PID_GRABBER_KVM_GETPROC2)

// ------------------------------------------------------------------------
// The "pid_grabber" class for systems with kern.proc sysctl nodes.
// ------------------------------------------------------------------------

#if defined(PID_GRABBER_SYSCTL_KERN_PROC)

impl::pid_grabber::pid_grabber(void) :
    m_cookie(NULL)
{
}

impl::pid_grabber::~pid_grabber(void)
{
}

bool
impl::pid_grabber::can_get_children_of(void)
    const
{
    return true;
}

impl::pid_set
impl::pid_grabber::get_children_of(pid_t pid)
{
    static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };

    atf::utils::auto_array< struct kinfo_proc > result(NULL);
    size_t nprocs = 0;
    do {
        int err;
        size_t length;

        INV(result.get() == NULL);

        length = 0;
        err = ::sysctl(static_cast< int * >(name),
                       (sizeof(name) / sizeof(*name)) - 1,
                       NULL, &length, NULL, 0);
        if (err == -1)
            throw atf::system_error("get_children_of", "sysctl failed",
                                    errno);
        nprocs = length / sizeof(struct kinfo_proc);

        result.reset(new struct kinfo_proc[nprocs]);

        err = ::sysctl(static_cast< int * >(name),
                       (sizeof(name) / sizeof(*name)) - 1,
                       result.get(), &length, NULL, 0);
        if (err == -1) {
            if (errno == ENOMEM)
                result.reset(NULL);
            else
                throw atf::system_error("get_children_of", "sysctl failed",
                                        errno);
        }
    } while (result.get() == NULL);

    pid_set children;
    for (int i = 0; i < nprocs; i++)
        if (result[i].kp_eproc.e_ppid == pid)
            children.insert(result[i].kp_proc.p_pid);
    return children;
}

#endif // defined(PID_GRABBER_SYSCTL_KERN_PROC)

// ------------------------------------------------------------------------
// The "pid_grabber" class for systems with the Linux proc file system.
// ------------------------------------------------------------------------

#if defined(PID_GRABBER_LINUX_PROCFS)

impl::pid_grabber::pid_grabber(void) :
    m_cookie(NULL)
{
}

impl::pid_grabber::~pid_grabber(void)
{
}

bool
impl::pid_grabber::can_get_children_of(void)
    const
{
    return true;
}

static
pid_t
get_parent_pid_of(const std::string& pidstr)
{
    std::ifstream is(("/proc/" + pidstr + "/stat").c_str());
    if (!is)
        throw std::runtime_error("Cannot determine parent pid of " +
                                 pidstr);

    std::string pid;
    std::string name;
    std::string status;
    pid_t ppid;
    is >> pid >> name >> status >> ppid;

    return ppid;
}

impl::pid_set
impl::pid_grabber::get_children_of(pid_t pid)
{
    pid_set children;

    DIR* dir = ::opendir("/proc");
    try {
        struct dirent* de;
        while ((de = readdir(dir)) != NULL) {
            if (de->d_type == DT_DIR && std::isdigit(de->d_name[0])) {
                if (get_parent_pid_of(de->d_name) == pid)
                    children.insert
                        (atf::text::to_type< pid_t >(de->d_name));
            }
        }
    } catch (...) {
        ::closedir(dir);
        throw;
    }
    ::closedir(dir);

    return children;
}

#endif // defined(PID_GRABBER_LINUX_PROCFS)

// ------------------------------------------------------------------------
// The "pid_grabber" class for unsupported systems.
// ------------------------------------------------------------------------

#if defined(PID_GRABBER_OTHER)

impl::pid_grabber::pid_grabber(void) :
    m_cookie(NULL)
{
}

impl::pid_grabber::~pid_grabber(void)
{
}

bool
impl::pid_grabber::can_get_children_of(void)
    const
{
    return false;
}

impl::pid_set
impl::pid_grabber::get_children_of(pid_t pid)
{
    UNREACHABLE;
    return pid_set();
}

#endif // defined(PID_GRABBER_OTHER)

// ------------------------------------------------------------------------
// Free functions.
// ------------------------------------------------------------------------

impl::errors_vector
impl::kill_tree(pid_t pid, int signo, pid_grabber& pg)
{
    errors_vector errors;
    if (!pg.can_get_children_of()) {
        errors.push_back
            (pid_error_pair(pid,
                            "Only killing this process because this "
                            "platform is currently unsupported"));
        (void)kill(pid, signo, errors);
    } else {
        if (kill(pid, SIGSTOP, errors) == -1)
            errors.push_back
                (pid_error_pair(pid, "Some children may not be killed"));

        pid_set children = pg.get_children_of(pid);

        for (pid_set::const_iterator iter = children.begin();
             iter != children.end(); iter++) {
            errors_vector aux = kill_tree(*iter, signo, pg);
            errors.insert(errors.end(), aux.begin(), aux.end());
        }

        if (signo == SIGKILL)
            (void)kill(pid, signo, errors);
        else {
#if defined(SUPPORT_SIGNAL_WHILE_STOPPED)
            (void)kill(pid, signo, errors, false);
#else // !defined(SUPPORT_SIGNAL_WHILE_STOPPED)
            (void)kill(pid, SIGCONT, errors);
            (void)kill(pid, signo, errors, true);
#endif // defined(SUPPORT_SIGNAL_WHILE_STOPPED)
        }
    }
    return errors;
}