/******************************************************************************
 * Copyright (c) 2005 The Regents of the University of California.
 * This material was produced under U.S. Government contract W-7405-ENG-36
 * for Los Alamos National Laboratory, which is operated by the University
 * of California for the U.S. Department of Energy. The U.S. Government has
 * rights to use, reproduce, and distribute this software. NEITHER THE
 * GOVERNMENT NOR THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
 * ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified
 * to produce derivative works, such modified software should be clearly
 * marked, so as not to confuse it with the version available from LANL.
 *
 * Additionally, this program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * LA-CC 04-115
 ******************************************************************************/

#ifdef __gnu_linux__
#define _GNU_SOURCE
#endif /* __gnu_linux__ */

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>

#include "compat.h"
#include "dbg.h"
#include "dbg_proxy.h"
#include "dbg_master.h"
#include "proxy_msg.h"
#include "bitset.h"
#include "handler.h"
#include "list.h"
#include "sdm.h"

#define RUNNING				0
#define SHUTDOWN_STARTED	1
#define SHUTDOWN_COMPLETED	2

static int				dbg_state;
static sdm_idset		dbg_procs;
static sdm_idset		tmp_idset;
static bitset *			tmp_bitset;
static proxy_svr *		dbg_proxy;

/*
 * The CLIENT_TIMEOUT determines how often the debugger polls for
 * communication with the front-end. The SERVER_TIMEOUT determines
 * how often the debug servers are polled.
 *
 * Too often and it uses too much CPU, too few and things will slow down...
 *
 * Values are in microseconds.
 */
#define CLIENT_TIMEOUT	10000
#define SERVER_TIMEOUT	10000

sdm_idset
bitset_to_idset(bitset *b)
{
	int bit;

	sdm_set_clear(tmp_idset);

	for (bit = 0; bit < bitset_size(b); bit++) {
		if (bitset_test(b, bit)) {
			sdm_set_add_element(tmp_idset, bit);
		}
	}

	return tmp_idset;
}

bitset *
idset_to_bitset(sdm_idset ids)
{
	sdm_id		id;

	bitset_clear(tmp_bitset);

	for (id = sdm_set_first(ids); !sdm_set_done(ids); id = sdm_set_next(ids)) {
		bitset_set(tmp_bitset, id);
	}

	return tmp_bitset;
}

/**
 * A send command is completed. Process the result and
 * call all registered event handlers.
 */
static int
dbg_master_cmd_completed(sdm_message msg, void *data)
{
	int				len;
	unsigned char *	buf;
	proxy_msg *		m;

	sdm_message_get_payload(msg, &buf, &len);

 	if (proxy_deserialize_msg(buf, len, &m) < 0)
 		fprintf(stderr, "bad conversion to proxy event");
 	else {
 		/*
 		 * Insert actual bitset as first arg. The debug events sent internally
 		 * don't include the bitset, it is only added prior to sending back
 		 * to the client.
 		 */
 		DEBUG_PRINTF(DEBUG_LEVEL_MASTER, "dbg_master_cmd_completed src=%s\n",
 				_set_to_str(sdm_message_get_source(msg)));
		proxy_msg_insert_bitset(m, idset_to_bitset(sdm_message_get_source(msg)), 0);
 		proxy_svr_queue_msg(dbg_proxy, m);
	}

 	sdm_message_free(msg);

 	return 0;
}

static int
send_command(sdm_idset dest, int timeout, unsigned char *cmd, int len, void *cbdata)
{
	sdm_message 	msg;

	msg = sdm_message_new(cmd, len);
	sdm_aggregate_set_value(sdm_message_get_aggregate(msg), SDM_AGGREGATE_TIMEOUT, timeout);
	sdm_set_union(sdm_message_get_destination(msg), dest);

	sdm_aggregate_message(msg, SDM_AGGREGATE_DOWNSTREAM);

	sdm_message_set_send_callback(msg, sdm_message_free);
	sdm_message_send(msg);

	return 0;
}

int
DbgMasterInit(int num_svrs, int my_id, char *name, proxy_svr_helper_funcs *funcs, proxy_commands *cmds)
{
	struct timeval	tv = { 0, CLIENT_TIMEOUT };

	DEBUG_PRINTF(DEBUG_LEVEL_MASTER, "DbgMasterInit num_svrs=%d\n", num_svrs);

	tmp_idset = sdm_set_new();
	tmp_bitset = bitset_new(num_svrs);

	/*
	 * Register callback
	 */
	sdm_aggregate_set_completion_callback(dbg_master_cmd_completed, NULL);

	/*
	 * Create a bitset containing all processes
	 */
	dbg_procs = sdm_set_new();
	sdm_set_add_all(dbg_procs, num_svrs-1);

	/*
	 * Reset state
	 */
	dbg_state = RUNNING;

	/*
	 * Initialize proxy
	 */
	if (proxy_svr_init(name, &tv, funcs, cmds, &dbg_proxy) != PTP_PROXY_RES_OK) {
		DbgSetError(DBGERR_DEBUGGER, proxy_get_error_str());
		return DBGRES_ERR;
	}

	return DBGRES_OK;
}

int
DbgMasterCreateSession(int num, char *host, int port)
{
	int			res;

	DEBUG_PRINTF(DEBUG_LEVEL_MASTER, "DbgMasterCreateSession host=%s port=%d\n",
			host != NULL ? host : "null",
			port);

	if (host != NULL)
		res = proxy_svr_connect(dbg_proxy, host, port);
	else
		res = proxy_svr_create(dbg_proxy, port);

	if (res == PTP_PROXY_RES_ERR) {
		DbgSetError(DBGERR_DEBUGGER, proxy_get_error_str());
		return DBGRES_ERR;
	}

	return DBGRES_OK;
}

void
DbgMasterFinish(void)
{
	DEBUG_PRINTS(DEBUG_LEVEL_MASTER, "DbgMasterFinish\n");
	proxy_svr_finish(dbg_proxy);
}

int
DbgMasterIsShutdown(void)
{

	/*
	 * The next event received after a quit command
	 * must be the server shutting down.
	 */
	if (dbg_state == SHUTDOWN_STARTED) {
		DEBUG_PRINTS(DEBUG_LEVEL_MASTER, "shutdown completed\n");
		dbg_state = SHUTDOWN_COMPLETED;
		return 0;
	}

	return (dbg_state == SHUTDOWN_COMPLETED) ? 1 : 0;
}

int
DbgMasterStartSession(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg = new_proxy_msg(DBG_STARTSESSION_CMD, tid);

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterStartSession", nargs, args);

	proxy_msg_add_int(msg, SERVER_TIMEOUT);
	proxy_msg_add_args(msg, nargs, args);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(dbg_procs, DBG_EV_WAITALL, buf, len, NULL);

	free_proxy_msg(msg);

	return res;
}

/*
 * Breakpoint operations
 */
int
DbgMasterSetLineBreakpoint(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	bitset *		set;
	unsigned char *	buf;
	proxy_msg *		msg;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterSetLineBreakpoint", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_SETLINEBREAKPOINT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterSetFuncBreakpoint(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterSetFuncBreakpoint", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_SETFUNCBREAKPOINT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterDeleteBreakpoint(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterDeleteBreakpoint", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_DELETEBREAKPOINT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterEnableBreakpoint(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterEnableBreakpoint", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_ENABLEBREAKPOINT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterDisableBreakpoint(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterDisableBreakpoint", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_DISABLEBREAKPOINT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterConditionBreakpoint(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterConditionBreakpoint", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_CONDITIONBREAKPOINT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterBreakpointAfter(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterBreakpointAfter", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_BREAKPOINTAFTER_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterSetWatchpoint(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterSetWatchpoint", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_SETWATCHPOINT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

/*
 * Process control operations
 */
int
DbgMasterGo(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterGo", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_GO_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITSOME, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterStep(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterStep", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_STEP_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITSOME, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterTerminate(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterTerminate", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_TERMINATE_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterSuspend(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterSuspend", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_SUSPEND_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

/*
 * Stack frame operations
 */
int
DbgMasterListStackframes(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterListStackframes", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_LISTSTACKFRAMES_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterSetCurrentStackframe(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterSetCurrentStackframe", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_SETCURRENTSTACKFRAME_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

/*
 * Expression/variable operations
 */
int
DbgMasterEvaluateExpression(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterEvaluateExpression", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_EVALUATEEXPRESSION_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterGetType(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterGetType", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_GETTYPE_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterListLocalVariables(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterListLocalVariables", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_LISTLOCALVARIABLES_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterListArguments(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterListArguments", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_LISTARGUMENTS_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterListGlobalVariables(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterListGlobalVariables", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_LISTGLOBALVARIABLES_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterListInfoThreads(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterListInfoThreads", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_LISTINFOTHREADS_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterSetThreadSelect(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterSetThreadSelect", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_SETTHREADSELECT_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterStackInfoDepth(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterStackInfoDepth", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_STACKINFODEPTH_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterDataReadMemory(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterDataReadMemory", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_DATAREADMEMORY_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterDataWriteMemory(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterDataWriteMemory", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_DATAWRITEMEMORY_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterListSignals(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterListSignals", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_LISTSIGNALS_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterSignalInfo(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterSignalInfo", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_SIGNALINFO_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterCLIHandle(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterCLIHandle", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_CLIHANDLE_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}

int
DbgMasterQuit(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterQuit", nargs, args);

	dbg_state = SHUTDOWN_STARTED;

	msg = new_proxy_msg(DBG_QUIT_CMD, tid);
	proxy_msg_add_args(msg, nargs, args);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(dbg_procs, DBG_EV_WAITALL, buf, len, NULL);

	free_proxy_msg(msg);

	return res;
}

/*
 * Event handling
 */
int
DbgMasterProgress(void)
{
	/*************************************
	 * Check for any server events
	 */

	sdm_progress();

	/**************************************
	 * Check for proxy events
	 */

	return proxy_svr_progress(dbg_proxy) == PTP_PROXY_RES_OK ? DBGRES_OK : DBGRES_ERR;
}

#if 0
void
DbgMasterRegisterEventHandler(void (*event_callback)(dbg_event *, void *), void *data)
{
	RegisterEventHandler(event_callback, data);
}

/**
 * Unregister file descriptor handler
 */
void
DbgMasterUnregisterEventHandler(void (*event_callback)(dbg_event *, void *))
{
	UnregisterEventHandler(event_callback);
}
#endif

/**
 * Register a handler for file descriptor events.
 *
 * TODO: Should the handler return a value?
 */
void
DbgMasterRegisterReadFileHandler(int fd, int (*file_handler)(int, void *), void *data)
{
	RegisterFileHandler(fd, READ_FILE_HANDLER, file_handler, data);
}

void
DbgMasterRegisterWriteFileHandler(int fd, int (*file_handler)(int, void *), void *data)
{
	RegisterFileHandler(fd, WRITE_FILE_HANDLER, file_handler, data);
}

void
DbgMasterRegisterExceptFileHandler(int fd, int (*file_handler)(int, void *), void *data)
{
	RegisterFileHandler(fd, EXCEPT_FILE_HANDLER, file_handler, data);
}

/**
 * Unregister file descriptor handler
 */
void
DbgMasterUnregisterFileHandler(int fd)
{
	UnregisterFileHandler(fd);
}

int
DbgMasterDataEvaluateExpression(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterDataEvaluateExpression", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_EVALUATEEXPRESSION_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}
int
DbgMasterEvaluatePartialExpression(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterEvaluatePartialExpression", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_EVALUATEPARTIALEXPRESSION_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}
int
DbgMasterDeletePartialExpression(int tid, int nargs, char **args)
{
	int				res;
	int				len;
	unsigned char *	buf;
	proxy_msg *		msg;
	bitset *		set;

	DEBUG_PRINTA(DEBUG_LEVEL_MASTER, "DbgMasterDeletePartialExpression", nargs, args);

	set = bitset_decode((unsigned char *)args[0], NULL);
	if (set == NULL) {
		DbgSetError(DBGERR_PROCSET, NULL);
		return DBGRES_ERR;
	}

	msg = new_proxy_msg(DBG_DELETEPARTIALEXPRESSION_CMD, tid);
	proxy_msg_add_args(msg, --nargs, &args[1]);
	proxy_serialize_msg(msg, &buf, &len);

	res = send_command(bitset_to_idset(set), DBG_EV_WAITALL, buf, len, NULL);

	bitset_free(set);
	free_proxy_msg(msg);

	return res;
}
