⛏️ index : haiku.git

/*
 * Copyright 2007, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 * 		Hugo Santos <hugosantos@gmail.com>
 * 		Ingo Weinhold <bonefish@cs.tu-berlin.de>
 */

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "Context.h"
#include "MemoryReader.h"
#include "TypeHandler.h"


template<typename Type>
static bool
obtain_pointer_data(Context &context, Type *data, void *address, uint32 what)
{
	if (address == NULL || !context.GetContents(what))
		return false;

	int32 bytesRead;

	status_t err = context.Reader().Read(address, data, sizeof(Type), bytesRead);
	if (err != B_OK || bytesRead < (int32)sizeof(Type))
		return false;

	return true;
}


static string
format_number(uint32 value)
{
	char tmp[32];
	snprintf(tmp, sizeof(tmp), "%u", (unsigned int)value);
	return tmp;
}


static string
format_iovecs(Context &context, const iovec *iov, int iovlen)
{
	if (iov == NULL && iovlen == 0)
		return "(empty)";

	iovec vecs[iovlen];
	int32 bytesRead;

	string r = "[";
	status_t err = context.Reader().Read((void*)iov, vecs, sizeof(vecs), bytesRead);
	if (err != B_OK) {
		r += context.FormatPointer(iov);
		r += ", " + context.FormatSigned(iovlen);
	} else {
		for (int i = 0; i < iovlen; i++) {
			if (i > 0)
				r += ", ";
			if (i >= 8) {
				r += "...";
				break;
			}
			r += "{iov_base=" + context.FormatPointer(vecs[i].iov_base);
			r += ", iov_len=" + context.FormatUnsigned(vecs[i].iov_len);
			r += "}";
		}
	}
	return r + "]";
}


template<>
string
TypeHandlerImpl<iovec *>::GetParameterValue(Context &context, Parameter *param,
	const void *address)
{
	Parameter *size = context.GetNextSibling(param);
	return format_iovecs(context, (const iovec*)*(void **)address,
		context.ReadValue<size_t>(size));
}


template<>
string
TypeHandlerImpl<iovec *>::GetReturnValue(Context &context, uint64 value)
{
	return context.FormatPointer((void *)value);
}


static string
format_ltype(Context &context, int ltype)
{
	if (context.GetContents(Context::ENUMERATIONS)) {
#define LTYPE(type) \
		case type: \
			return #type

		switch (ltype) {
			LTYPE(F_RDLCK);
			LTYPE(F_UNLCK);
			LTYPE(F_WRLCK);
		}
	}

	return context.FormatSigned(ltype);
}


static string
format_lwhence(Context &context, int lwhence)
{
	if (context.GetContents(Context::ENUMERATIONS)) {
#define LWHENCE(whence) \
		case whence: \
			return #whence

		switch (lwhence) {
			LWHENCE(SEEK_SET);
			LWHENCE(SEEK_CUR);
			LWHENCE(SEEK_END);
			LWHENCE(SEEK_DATA);
			LWHENCE(SEEK_HOLE);
		}
	}

	return context.FormatSigned(lwhence);
}



static string
format_pointer(Context &context, flock *lock)
{
	string r;

	r = "l_type=" + format_ltype(context, lock->l_type) + ", ";
	r += "l_whence=" + format_lwhence(context, lock->l_whence) + ", ";
	r += "l_start=" + context.FormatSigned(lock->l_start) + ", ";
	r += "l_len=" + context.FormatSigned(lock->l_len);

	return r;
}



template<typename Type>
static string
format_pointer_value(Context &context, void *address)
{
	Type data;

	if (obtain_pointer_data(context, &data, address, Context::COMPLEX_STRUCTS))
		return "{" + format_pointer(context, &data) + "}";

	return context.FormatPointer(address);
}


static string
get_ipv4_address(in_addr *addr)
{
	char tmp[32];
	snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
		 (unsigned int)(htonl(addr->s_addr) >> 24) & 0xff,
		 (unsigned int)(htonl(addr->s_addr) >> 16) & 0xff,
		 (unsigned int)(htonl(addr->s_addr) >>  8) & 0xff,
		 (unsigned int)(htonl(addr->s_addr) >>  0) & 0xff);
	return tmp;
}


static string
format_socket_family(Context &context, int family)
{
	if (context.GetContents(Context::ENUMERATIONS)) {
#define SOCKET_FAMILY(family) \
		case family: \
			return #family

		switch (family) {
			SOCKET_FAMILY(AF_UNSPEC);
			SOCKET_FAMILY(AF_INET);
			SOCKET_FAMILY(AF_APPLETALK);
			SOCKET_FAMILY(AF_ROUTE);
			SOCKET_FAMILY(AF_LINK);
			SOCKET_FAMILY(AF_INET6);
			SOCKET_FAMILY(AF_LOCAL);
		}
	}

	return "family = " + context.FormatSigned(family);
}


#if 0
static string
format_socket_type(Context &context, int type)
{
	if (context.GetContents(Context::ENUMERATIONS)) {
		switch (type) {
			case SOCK_RAW:
				return "SOCK_RAW";
			case SOCK_DGRAM:
				return "SOCK_DGRAM";
			case SOCK_STREAM:
				return "SOCK_STREAM";
		}
	}

	return "type = " + context.FormatSigned(type);
}


static string
format_socket_protocol(Context &context, int protocol)
{
	if (context.GetContents(Context::ENUMERATIONS)) {
		switch (protocol) {
			case IPPROTO_IP:
				return "IPPROTO_IP";
			case IPPROTO_RAW:
				return "IPPROTO_RAW";
			case IPPROTO_ICMP:
				return "IPPROTO_ICMP";
			case IPPROTO_UDP:
				return "IPPROTO_UDP";
			case IPPROTO_TCP:
				return "IPPROTO_TCP";
		}
	}

	return "protocol = " + context.FormatSigned(protocol);
}
#endif


static string
format_pointer(Context &context, sockaddr *saddr)
{
	string r;

	r = format_socket_family(context, saddr->sa_family) + ", ";

	switch (saddr->sa_family) {
		case AF_INET:
		{
			sockaddr_in *sin = (sockaddr_in *)saddr;
			r += get_ipv4_address(&sin->sin_addr);
			r += "/";
			r += format_number(ntohs(sin->sin_port));
			break;
		}
		case AF_UNIX:
		{
			sockaddr_un *sun = (sockaddr_un *)saddr;
			r += "path = \"" + string(sun->sun_path) + "\"";
			break;
		}
		default:
			r += "...";
			break;
	}

	return r;
}


static string
read_sockaddr(Context &context, Parameter *param, void *address)
{
	param = context.GetNextSibling(param);
	if (param == NULL)
		return context.FormatPointer(address);

	socklen_t addrlen = context.ReadValue<socklen_t>(param);

	sockaddr_storage data;

	if (addrlen > sizeof(data))
		return context.FormatPointer(address);

	int32 bytesRead;
	status_t err = context.Reader().Read(address, &data, addrlen, bytesRead);
	if (err != B_OK)
		return context.FormatPointer(address);

	return "{" + format_pointer(context, (sockaddr *)&data) + "}";
}


template<>
string
TypeHandlerImpl<sockaddr *>::GetParameterValue(Context &context,
	Parameter *param, const void *address)
{
	void *data = *(void **)address;
	if (data != NULL && context.GetContents(Context::SIMPLE_STRUCTS))
		return read_sockaddr(context, param, data);
	return context.FormatPointer(data);
}


template<>
string
TypeHandlerImpl<sockaddr *>::GetReturnValue(Context &context, uint64 value)
{
	return context.FormatPointer((void *)value);
}


static string
format_pointer(Context &context, msghdr *h)
{
	string r;

	r  =   "name = " + format_pointer_value<sockaddr>(context, h->msg_name);
	r += ", name_len = " + context.FormatUnsigned(h->msg_namelen);
	r += ", iov = " + format_iovecs(context, h->msg_iov, h->msg_iovlen);
	if (h->msg_control != NULL || h->msg_controllen != 0) {
		r += ", control = " + context.FormatPointer(h->msg_control);
		r += ", control_len = " + context.FormatUnsigned(h->msg_controllen);
	}
	r += ", flags = " + context.FormatFlags(h->msg_flags);

	return r;
}


static string
format_pointer(Context &context, ifreq *ifr)
{
	return string(ifr->ifr_name) + ", ...";
}


static string
format_pointer(Context &context, ifconf *conf)
{
	unsigned char buffer[sizeof(ifreq) * 8];
	int maxData = sizeof(buffer);
	int32 bytesRead;

	if (conf->ifc_len < maxData)
		maxData = conf->ifc_len;

	string r = "len = " + context.FormatSigned(conf->ifc_len);

	if (conf->ifc_len == 0)
		return r;

	status_t err = context.Reader().Read(conf->ifc_buf, buffer,
					     maxData, bytesRead);
	if (err != B_OK)
		return r + ", buf = " + context.FormatPointer(conf->ifc_buf);

	r += ", [";

	uint8 *current = buffer, *bufferEnd = buffer + bytesRead;
	for (int i = 0; i < 8; i++) {
		if ((bufferEnd - current) < (IF_NAMESIZE + 1))
			break;

		ifreq *ifr = (ifreq *)current;
		int size = IF_NAMESIZE + ifr->ifr_addr.sa_len;

		if ((bufferEnd - current) < size)
			break;

		if (i > 0)
			r += ", ";

		r += "{" + string(ifr->ifr_name) + ", {"
			 + format_pointer(context, &ifr->ifr_addr) + "}}";

		current += size;
	}

	if (current < bufferEnd)
		r += ", ...";

	return r + "]";
}


static string
format_pointer(Context &context, siginfo_t *info)
{
	string r;

	switch (info->si_code) {
		case CLD_EXITED:
			r = "WIFEXITED(s) && WEXITSTATUS(s) == " + context.FormatUnsigned(info->si_status & 0xff);
			break;
	}
	return r;
}



template<typename Type>
class SpecializedPointerTypeHandler : public TypeHandler {
	string GetParameterValue(Context &context, Parameter *,
		const void *address)
	{
		return format_pointer_value<Type>(context, *(void **)address);
	}

	string GetReturnValue(Context &context, uint64 value)
	{
		return format_pointer_value<Type>(context, (void *)value);
	}
};

#define POINTER_TYPE(name, type) \
	TypeHandler *create_##name##_type_handler() \
	{ \
		return new SpecializedPointerTypeHandler<type>(); \
	}

DEFINE_TYPE(iovec_ptr, iovec *);
POINTER_TYPE(flock_ptr, flock);
POINTER_TYPE(ifconf_ptr, ifconf);
POINTER_TYPE(ifreq_ptr, ifreq);
POINTER_TYPE(siginfo_t_ptr, siginfo_t);
POINTER_TYPE(msghdr_ptr, msghdr);
DEFINE_TYPE(sockaddr_ptr, sockaddr *);