<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/


#include "condor_common.h"
#include "proc_family_monitor.h"
#include "pid_tracker.h"
#include "login_tracker.h"
#include "environment_tracker.h"
#include "parent_tracker.h"

#if defined(LINUX)
#include "group_tracker.h"
#endif

ProcFamilyMonitor::ProcFamilyMonitor(pid_t pid,
                                     birthday_t birthday,
                                     int snapshot_interval) :
	m_everybody_else(NULL),
	m_family_table(11, pidHashFunc, rejectDuplicateKeys),
	m_member_table(PHBUCKETS, pidHashFunc, rejectDuplicateKeys)
{
	// the snapshot interval must either be non-negative or -1, which
	// means infinite (higher layers should enforce this)
	//
	ASSERT(snapshot_interval >= -1);

	// create our "tracker" objects that provide the various methods
	// for tracking process families
	//
	m_pid_tracker = new PIDTracker(this);
	ASSERT(m_pid_tracker != NULL);
#if defined(LINUX)
	m_group_tracker = NULL;
#endif
	m_login_tracker = new LoginTracker(this);
	ASSERT(m_login_tracker != NULL);
	m_environment_tracker = new EnvironmentTracker(this);
	ASSERT(m_environment_tracker != NULL);
	m_parent_tracker = new ParentTracker(this);
	ASSERT(m_parent_tracker != NULL);

	// create the "family" that will serve as a container for all processes
	// that are not in the family that we are tracking
	//
	// TODO: we currently use the ProcFamily object for this, but we
	// don't need much of what's in there. we should pull out the logic
	// that we need for this object into some kind of "ProcGroup" base
	// class
	//
	m_everybody_else = new ProcFamily(this);
	ASSERT(m_everybody_else != NULL);

	// create the "root" family; set the watcher pid to be the same as
	// the root pid: there's some special logic in delete_unwatched_families
	// to EXCEPT if we see that the root process has exited
	//
	ProcFamily* family = new ProcFamily(this,
	                                    pid,
	                                    birthday,
	                                    pid,
	                                    snapshot_interval);
	ASSERT(family != NULL);

	// make sure we're tracking this family by its root pid/birthday
	//
	m_pid_tracker->add_mapping(family, pid, birthday);

	// make this family the root-level tree node
	//
	m_tree = new Tree<ProcFamily*>(family);
	ASSERT(m_tree != NULL);

	// and insert the tree into our hash table for families
	//
	int ret = m_family_table.insert(pid, m_tree);
	ASSERT(ret != -1);

	// take an initial snapshot
	//
	snapshot();
}

ProcFamilyMonitor::~ProcFamilyMonitor()
{
	delete_all_families(m_tree);
	delete m_tree;
	delete m_everybody_else;
	delete m_parent_tracker;
	delete m_environment_tracker;
	delete m_login_tracker;
#if defined(LINUX)
	if (m_group_tracker != NULL) {
		delete m_group_tracker;
	}
#endif
	delete m_pid_tracker;
}

#if defined(LINUX)
void
ProcFamilyMonitor::enable_group_tracking(gid_t min_tracking_gid,
                                         gid_t max_tracking_gid)
{
	ASSERT(m_group_tracker == NULL);
	m_group_tracker = new GroupTracker(this,
	                                   min_tracking_gid,
	                                   max_tracking_gid);
	ASSERT(m_group_tracker != NULL);
}
#endif

proc_family_error_t
ProcFamilyMonitor::register_subfamily(pid_t root_pid,
                                      pid_t watcher_pid,
                                      int max_snapshot_interval)
{
	int ret;

	// root pid must be positive
	//
	if (root_pid <= 0) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure: bad root pid: %u\n",
		        root_pid);
		return PROC_FAMILY_ERROR_BAD_ROOT_PID;
	}

	// watcher pid must be non-negative; zero indicates the subfamily
	// is unwatched (which just means there will be no automatic
	// garbage collection for this family)
	//
	if (watcher_pid < 0) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure; bad watcher pid: %u\n",
		        watcher_pid);
		return PROC_FAMILY_ERROR_BAD_WATCHER_PID;
	}

	// max_snapshot interval must either be non-negative, or -1
	// for infinite
	//
	if (max_snapshot_interval < -1) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure: bad max snapshot interval: %d\n",
		        max_snapshot_interval);
		return PROC_FAMILY_ERROR_BAD_SNAPSHOT_INTERVAL;
	}

	// get our family tree state as up to date as possible
	//
	snapshot();

	// find the root process of the (potential) new subfamily
	// in our snapshot. we require that the process of any newly
	// created subfamily is in a family we are already tracking
	//
	ProcFamilyMember* member;
	ret = m_member_table.lookup(root_pid, member);
	if ((ret == -1) || (member->get_proc_family() == m_everybody_else)) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure: pid %u not in process tree\n",
		        root_pid);
		return PROC_FAMILY_ERROR_PROCESS_NOT_FAMILY;
	}

	// make sure this process isn't already the root of a family
	//
	Tree<ProcFamily*>* tree;
	ret = m_family_table.lookup(root_pid, tree);
	if (ret != -1) {
		dprintf(D_ALWAYS,
		        "register_subfamily: pid %u already registered\n",
		        root_pid);
		return PROC_FAMILY_ERROR_ALREADY_REGISTERED;
	}

	// finally create the new family
	//
	ProcFamily* family = new ProcFamily(this,
	                                    root_pid,
	                                    member->get_proc_info()->birthday,
	                                    watcher_pid,
	                                    max_snapshot_interval);

	// register this family with the PID-based tracker object
	//
	m_pid_tracker->add_mapping(family,
	                           root_pid,
	                           member->get_proc_info()->birthday);

	// find the family that will be this new subfamily's parent and create
	// the parent-child link
	//
	pid_t parent_root = member->get_proc_family()->get_root_pid();
	Tree<ProcFamily*>* parent_tree_node;
	ret = m_family_table.lookup(parent_root, parent_tree_node);
	ASSERT(ret != -1);
	Tree<ProcFamily*>* child_tree_node = parent_tree_node->add_child(family);
	ASSERT(child_tree_node != NULL);

	// move the new family's root process into the correct family
	//
	dprintf(D_ALWAYS,
	        "moving process %u into new subfamily %u\n",
	        root_pid,
	        root_pid);
	member->move_to_subfamily(family);

	// and insert the tree into our hash table for families
	//
	ret = m_family_table.insert(root_pid, child_tree_node);
	ASSERT(ret != -1);

	dprintf(D_ALWAYS,
	        "new subfamily registered: root = %u, watcher = %u\n",
	        root_pid,
	        watcher_pid);

	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::track_family_via_environment(pid_t pid, PidEnvID* penvid)
{
	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "track_family_via_environment failure: "
				    "family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// add this association to the tracker
	//
	m_environment_tracker->add_mapping(tree->get_data(), penvid);
	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::track_family_via_login(pid_t pid, char* login)
{
	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "track_family_via_login failure: "
				    "family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// add this association to the tracker
	//
	m_login_tracker->add_mapping(tree->get_data(), login);
	return PROC_FAMILY_ERROR_SUCCESS;
}

#if defined(LINUX)
proc_family_error_t
ProcFamilyMonitor::track_family_via_supplementary_group(pid_t pid, gid_t& gid)
{
	// check if group tracking is enabled
	//
	if (m_group_tracker == NULL) {
		return PROC_FAMILY_ERROR_NO_GROUP_ID_AVAILABLE;
	}

	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "track_family_via_supplementary_group failure: "
				    "family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	bool ok = m_group_tracker->add_mapping(tree->get_data(), gid);
	if (!ok) {
		return PROC_FAMILY_ERROR_NO_GROUP_ID_AVAILABLE;
	}

	return PROC_FAMILY_ERROR_SUCCESS;
}
#endif

proc_family_error_t
ProcFamilyMonitor::unregister_subfamily(pid_t pid)
{
	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "unregister_subfamily failure: family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// make sure its not the "root family" (which can't be unregistered)
	//
	if (tree->get_parent() == NULL) {
		dprintf(D_ALWAYS,
		        "unregister_subfamily failure: cannot unregister root family\n");
		return PROC_FAMILY_ERROR_UNREGISTER_ROOT;
	}

	// do the deed
	//
	dprintf(D_ALWAYS,
	        "unregistering family with root pid %u\n",
	        pid);
	unregister_subfamily(tree);

	return PROC_FAMILY_ERROR_SUCCESS;
}

int
ProcFamilyMonitor::get_snapshot_interval()
{
	return get_snapshot_interval(m_tree);
}

proc_family_error_t
ProcFamilyMonitor::signal_process(pid_t pid, int sig)
{
	// make sure signals are only sent to subtree roots
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "signal_process failure: family with root %u not found\n",
				pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// look up the Member so we can get at the procInfo struct
	//
	ProcFamilyMember* pm;
	ret = m_member_table.lookup(pid, pm);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "signal_process failure: family root pid %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_PROCESS_NOT_FOUND;
	}
	procInfo* pi = pm->get_proc_info();
	ASSERT(pi);

	dprintf(D_ALWAYS, "sending signal %d to process %u\n", sig, pid);
	send_signal(pi, sig);

	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::signal_family(pid_t pid, int sig)
{
	// find the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "signal_family error: family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// get as up to date as possible
	//
	snapshot();
	
	// now send the signal and return
	//
	dprintf(D_ALWAYS, "sending signal %d to family with root %u\n", sig, pid);
	signal_family(tree, sig);

	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::get_family_usage(pid_t pid, ProcFamilyUsage* usage)
{
	// find the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "get_family_usage failure: family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// get as up to date as possible
	//
	snapshot();

	// get usage from the requested family and all subfamilies
	//
	dprintf(D_ALWAYS,
	        "gathering usage data for family with root pid %u\n",
	        pid);
	ASSERT(usage != NULL);
	usage->user_cpu_time = 0;
	usage->sys_cpu_time = 0;
	usage->percent_cpu = 0.0;
	usage->max_image_size = 0;
	usage->total_image_size = 0;
	usage->num_procs = 0;
	get_family_usage(tree, usage);

	return PROC_FAMILY_ERROR_SUCCESS;
}

void
ProcFamilyMonitor::snapshot()<commit_msg>ProcD no longer does a snapshot on each GET_USAGE call<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/


#include "condor_common.h"
#include "proc_family_monitor.h"
#include "pid_tracker.h"
#include "login_tracker.h"
#include "environment_tracker.h"
#include "parent_tracker.h"

#if defined(LINUX)
#include "group_tracker.h"
#endif

ProcFamilyMonitor::ProcFamilyMonitor(pid_t pid,
                                     birthday_t birthday,
                                     int snapshot_interval) :
	m_everybody_else(NULL),
	m_family_table(11, pidHashFunc, rejectDuplicateKeys),
	m_member_table(PHBUCKETS, pidHashFunc, rejectDuplicateKeys)
{
	// the snapshot interval must either be non-negative or -1, which
	// means infinite (higher layers should enforce this)
	//
	ASSERT(snapshot_interval >= -1);

	// create our "tracker" objects that provide the various methods
	// for tracking process families
	//
	m_pid_tracker = new PIDTracker(this);
	ASSERT(m_pid_tracker != NULL);
#if defined(LINUX)
	m_group_tracker = NULL;
#endif
	m_login_tracker = new LoginTracker(this);
	ASSERT(m_login_tracker != NULL);
	m_environment_tracker = new EnvironmentTracker(this);
	ASSERT(m_environment_tracker != NULL);
	m_parent_tracker = new ParentTracker(this);
	ASSERT(m_parent_tracker != NULL);

	// create the "family" that will serve as a container for all processes
	// that are not in the family that we are tracking
	//
	// TODO: we currently use the ProcFamily object for this, but we
	// don't need much of what's in there. we should pull out the logic
	// that we need for this object into some kind of "ProcGroup" base
	// class
	//
	m_everybody_else = new ProcFamily(this);
	ASSERT(m_everybody_else != NULL);

	// create the "root" family; set the watcher pid to be the same as
	// the root pid: there's some special logic in delete_unwatched_families
	// to EXCEPT if we see that the root process has exited
	//
	ProcFamily* family = new ProcFamily(this,
	                                    pid,
	                                    birthday,
	                                    pid,
	                                    snapshot_interval);
	ASSERT(family != NULL);

	// make sure we're tracking this family by its root pid/birthday
	//
	m_pid_tracker->add_mapping(family, pid, birthday);

	// make this family the root-level tree node
	//
	m_tree = new Tree<ProcFamily*>(family);
	ASSERT(m_tree != NULL);

	// and insert the tree into our hash table for families
	//
	int ret = m_family_table.insert(pid, m_tree);
	ASSERT(ret != -1);

	// take an initial snapshot
	//
	snapshot();
}

ProcFamilyMonitor::~ProcFamilyMonitor()
{
	delete_all_families(m_tree);
	delete m_tree;
	delete m_everybody_else;
	delete m_parent_tracker;
	delete m_environment_tracker;
	delete m_login_tracker;
#if defined(LINUX)
	if (m_group_tracker != NULL) {
		delete m_group_tracker;
	}
#endif
	delete m_pid_tracker;
}

#if defined(LINUX)
void
ProcFamilyMonitor::enable_group_tracking(gid_t min_tracking_gid,
                                         gid_t max_tracking_gid)
{
	ASSERT(m_group_tracker == NULL);
	m_group_tracker = new GroupTracker(this,
	                                   min_tracking_gid,
	                                   max_tracking_gid);
	ASSERT(m_group_tracker != NULL);
}
#endif

proc_family_error_t
ProcFamilyMonitor::register_subfamily(pid_t root_pid,
                                      pid_t watcher_pid,
                                      int max_snapshot_interval)
{
	int ret;

	// root pid must be positive
	//
	if (root_pid <= 0) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure: bad root pid: %u\n",
		        root_pid);
		return PROC_FAMILY_ERROR_BAD_ROOT_PID;
	}

	// watcher pid must be non-negative; zero indicates the subfamily
	// is unwatched (which just means there will be no automatic
	// garbage collection for this family)
	//
	if (watcher_pid < 0) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure; bad watcher pid: %u\n",
		        watcher_pid);
		return PROC_FAMILY_ERROR_BAD_WATCHER_PID;
	}

	// max_snapshot interval must either be non-negative, or -1
	// for infinite
	//
	if (max_snapshot_interval < -1) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure: bad max snapshot interval: %d\n",
		        max_snapshot_interval);
		return PROC_FAMILY_ERROR_BAD_SNAPSHOT_INTERVAL;
	}

	// get our family tree state as up to date as possible
	//
	snapshot();

	// find the root process of the (potential) new subfamily
	// in our snapshot. we require that the process of any newly
	// created subfamily is in a family we are already tracking
	//
	ProcFamilyMember* member;
	ret = m_member_table.lookup(root_pid, member);
	if ((ret == -1) || (member->get_proc_family() == m_everybody_else)) {
		dprintf(D_ALWAYS,
		        "register_subfamily failure: pid %u not in process tree\n",
		        root_pid);
		return PROC_FAMILY_ERROR_PROCESS_NOT_FAMILY;
	}

	// make sure this process isn't already the root of a family
	//
	Tree<ProcFamily*>* tree;
	ret = m_family_table.lookup(root_pid, tree);
	if (ret != -1) {
		dprintf(D_ALWAYS,
		        "register_subfamily: pid %u already registered\n",
		        root_pid);
		return PROC_FAMILY_ERROR_ALREADY_REGISTERED;
	}

	// finally create the new family
	//
	ProcFamily* family = new ProcFamily(this,
	                                    root_pid,
	                                    member->get_proc_info()->birthday,
	                                    watcher_pid,
	                                    max_snapshot_interval);

	// register this family with the PID-based tracker object
	//
	m_pid_tracker->add_mapping(family,
	                           root_pid,
	                           member->get_proc_info()->birthday);

	// find the family that will be this new subfamily's parent and create
	// the parent-child link
	//
	pid_t parent_root = member->get_proc_family()->get_root_pid();
	Tree<ProcFamily*>* parent_tree_node;
	ret = m_family_table.lookup(parent_root, parent_tree_node);
	ASSERT(ret != -1);
	Tree<ProcFamily*>* child_tree_node = parent_tree_node->add_child(family);
	ASSERT(child_tree_node != NULL);

	// move the new family's root process into the correct family
	//
	dprintf(D_ALWAYS,
	        "moving process %u into new subfamily %u\n",
	        root_pid,
	        root_pid);
	member->move_to_subfamily(family);

	// and insert the tree into our hash table for families
	//
	ret = m_family_table.insert(root_pid, child_tree_node);
	ASSERT(ret != -1);

	dprintf(D_ALWAYS,
	        "new subfamily registered: root = %u, watcher = %u\n",
	        root_pid,
	        watcher_pid);

	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::track_family_via_environment(pid_t pid, PidEnvID* penvid)
{
	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "track_family_via_environment failure: "
				    "family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// add this association to the tracker
	//
	m_environment_tracker->add_mapping(tree->get_data(), penvid);
	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::track_family_via_login(pid_t pid, char* login)
{
	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "track_family_via_login failure: "
				    "family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// add this association to the tracker
	//
	m_login_tracker->add_mapping(tree->get_data(), login);
	return PROC_FAMILY_ERROR_SUCCESS;
}

#if defined(LINUX)
proc_family_error_t
ProcFamilyMonitor::track_family_via_supplementary_group(pid_t pid, gid_t& gid)
{
	// check if group tracking is enabled
	//
	if (m_group_tracker == NULL) {
		return PROC_FAMILY_ERROR_NO_GROUP_ID_AVAILABLE;
	}

	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "track_family_via_supplementary_group failure: "
				    "family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	bool ok = m_group_tracker->add_mapping(tree->get_data(), gid);
	if (!ok) {
		return PROC_FAMILY_ERROR_NO_GROUP_ID_AVAILABLE;
	}

	return PROC_FAMILY_ERROR_SUCCESS;
}
#endif

proc_family_error_t
ProcFamilyMonitor::unregister_subfamily(pid_t pid)
{
	// lookup the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "unregister_subfamily failure: family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// make sure its not the "root family" (which can't be unregistered)
	//
	if (tree->get_parent() == NULL) {
		dprintf(D_ALWAYS,
		        "unregister_subfamily failure: cannot unregister root family\n");
		return PROC_FAMILY_ERROR_UNREGISTER_ROOT;
	}

	// do the deed
	//
	dprintf(D_ALWAYS,
	        "unregistering family with root pid %u\n",
	        pid);
	unregister_subfamily(tree);

	return PROC_FAMILY_ERROR_SUCCESS;
}

int
ProcFamilyMonitor::get_snapshot_interval()
{
	return get_snapshot_interval(m_tree);
}

proc_family_error_t
ProcFamilyMonitor::signal_process(pid_t pid, int sig)
{
	// make sure signals are only sent to subtree roots
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "signal_process failure: family with root %u not found\n",
				pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// look up the Member so we can get at the procInfo struct
	//
	ProcFamilyMember* pm;
	ret = m_member_table.lookup(pid, pm);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "signal_process failure: family root pid %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_PROCESS_NOT_FOUND;
	}
	procInfo* pi = pm->get_proc_info();
	ASSERT(pi);

	dprintf(D_ALWAYS, "sending signal %d to process %u\n", sig, pid);
	send_signal(pi, sig);

	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::signal_family(pid_t pid, int sig)
{
	// find the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "signal_family error: family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// get as up to date as possible
	//
	snapshot();
	
	// now send the signal and return
	//
	dprintf(D_ALWAYS, "sending signal %d to family with root %u\n", sig, pid);
	signal_family(tree, sig);

	return PROC_FAMILY_ERROR_SUCCESS;
}

proc_family_error_t
ProcFamilyMonitor::get_family_usage(pid_t pid, ProcFamilyUsage* usage)
{
	// find the family
	//
	Tree<ProcFamily*>* tree;
	int ret = m_family_table.lookup(pid, tree);
	if (ret == -1) {
		dprintf(D_ALWAYS,
		        "get_family_usage failure: family with root %u not found\n",
		        pid);
		return PROC_FAMILY_ERROR_FAMILY_NOT_FOUND;
	}

	// get usage from the requested family and all subfamilies
	//
	dprintf(D_ALWAYS,
	        "gathering usage data for family with root pid %u\n",
	        pid);
	ASSERT(usage != NULL);
	usage->user_cpu_time = 0;
	usage->sys_cpu_time = 0;
	usage->percent_cpu = 0.0;
	usage->max_image_size = 0;
	usage->total_image_size = 0;
	usage->num_procs = 0;
	get_family_usage(tree, usage);

	return PROC_FAMILY_ERROR_SUCCESS;
}

void
ProcFamilyMonitor::snapshot()
{'fraction_non_alphanumeric': 0.1080674659622028, 'fraction_numerical': 0.003251371672424304, 'mean_word_length': 3.1861177271180674, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5511301', 'n_tokens_mistral': 8690, 'n_tokens_neox': 8091, 'n_words': 2142}