forked from OSchip/llvm-project
325 lines
8.5 KiB
C++
325 lines
8.5 KiB
C++
//===-- ConnectionMachPort.cpp ----------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#if defined(__APPLE__)
|
|
|
|
#include "lldb/Core/ConnectionMachPort.h"
|
|
|
|
// C Includes
|
|
#include <mach/mach.h>
|
|
#include <servers/bootstrap.h>
|
|
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
// Project includes
|
|
#include "lldb/lldb-private-log.h"
|
|
#include "lldb/Core/Communication.h"
|
|
#include "lldb/Core/Log.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
struct MessageType
|
|
{
|
|
mach_msg_header_t head;
|
|
ConnectionMachPort::PayloadType payload;
|
|
};
|
|
|
|
|
|
|
|
ConnectionMachPort::ConnectionMachPort () :
|
|
Connection(),
|
|
m_task(mach_task_self()),
|
|
m_port(MACH_PORT_TYPE_NONE)
|
|
{
|
|
}
|
|
|
|
ConnectionMachPort::~ConnectionMachPort ()
|
|
{
|
|
Disconnect (NULL);
|
|
}
|
|
|
|
bool
|
|
ConnectionMachPort::IsConnected () const
|
|
{
|
|
return m_port != MACH_PORT_TYPE_NONE;
|
|
}
|
|
|
|
ConnectionStatus
|
|
ConnectionMachPort::Connect (const char *s, Error *error_ptr)
|
|
{
|
|
if (IsConnected())
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetErrorString ("already connected");
|
|
return eConnectionStatusError;
|
|
}
|
|
|
|
if (s == NULL || s[0] == '\0')
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetErrorString ("empty connect URL");
|
|
return eConnectionStatusError;
|
|
}
|
|
|
|
ConnectionStatus status = eConnectionStatusError;
|
|
|
|
if (strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://")))
|
|
{
|
|
s += strlen("bootstrap-checkin://");
|
|
|
|
if (*s)
|
|
{
|
|
status = BootstrapCheckIn (s, error_ptr);
|
|
}
|
|
else
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetErrorString ("bootstrap port name is empty");
|
|
}
|
|
}
|
|
else if (strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://")))
|
|
{
|
|
s += strlen("bootstrap-lookup://");
|
|
if (*s)
|
|
{
|
|
status = BootstrapLookup (s, error_ptr);
|
|
}
|
|
else
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetErrorString ("bootstrap port name is empty");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s);
|
|
}
|
|
|
|
|
|
if (status == eConnectionStatusSuccess)
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->Clear();
|
|
}
|
|
else
|
|
{
|
|
Disconnect(NULL);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
ConnectionStatus
|
|
ConnectionMachPort::BootstrapCheckIn (const char *port, Error *error_ptr)
|
|
{
|
|
mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE;
|
|
|
|
/* Getting bootstrap server port */
|
|
kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
|
|
if (kret == KERN_SUCCESS)
|
|
{
|
|
name_t port_name;
|
|
int len = snprintf(port_name, sizeof(port_name), "%s", port);
|
|
if (len < sizeof(port_name))
|
|
{
|
|
kret = ::bootstrap_check_in (bootstrap_port,
|
|
port_name,
|
|
&m_port);
|
|
}
|
|
else
|
|
{
|
|
Disconnect(NULL);
|
|
if (error_ptr)
|
|
error_ptr->SetErrorString ("bootstrap is too long");
|
|
return eConnectionStatusError;
|
|
}
|
|
}
|
|
|
|
if (kret != KERN_SUCCESS)
|
|
{
|
|
Disconnect(NULL);
|
|
if (error_ptr)
|
|
error_ptr->SetError (kret, eErrorTypeMachKernel);
|
|
return eConnectionStatusError;
|
|
}
|
|
return eConnectionStatusSuccess;
|
|
}
|
|
|
|
lldb::ConnectionStatus
|
|
ConnectionMachPort::BootstrapLookup (const char *port,
|
|
Error *error_ptr)
|
|
{
|
|
name_t port_name;
|
|
|
|
if (port && port[0])
|
|
{
|
|
if (::snprintf (port_name, sizeof (port_name), "%s", port) >= sizeof (port_name))
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetErrorString ("port netname is too long");
|
|
return eConnectionStatusError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetErrorString ("empty port netname");
|
|
return eConnectionStatusError;
|
|
}
|
|
|
|
mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE;
|
|
|
|
/* Getting bootstrap server port */
|
|
kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
|
|
if (kret == KERN_SUCCESS)
|
|
{
|
|
kret = ::bootstrap_look_up (bootstrap_port,
|
|
port_name,
|
|
&m_port);
|
|
}
|
|
|
|
if (kret != KERN_SUCCESS)
|
|
{
|
|
if (error_ptr)
|
|
error_ptr->SetError (kret, eErrorTypeMachKernel);
|
|
return eConnectionStatusError;
|
|
}
|
|
|
|
return eConnectionStatusSuccess;
|
|
}
|
|
|
|
ConnectionStatus
|
|
ConnectionMachPort::Disconnect (Error *error_ptr)
|
|
{
|
|
kern_return_t kret;
|
|
|
|
// TODO: verify if we need to netname_check_out for
|
|
// either or both
|
|
if (m_port != MACH_PORT_TYPE_NONE)
|
|
{
|
|
kret = ::mach_port_deallocate (m_task, m_port);
|
|
if (error_ptr)
|
|
error_ptr->SetError (kret, eErrorTypeMachKernel);
|
|
m_port = MACH_PORT_TYPE_NONE;
|
|
}
|
|
|
|
return eConnectionStatusSuccess;
|
|
}
|
|
|
|
size_t
|
|
ConnectionMachPort::Read (void *dst,
|
|
size_t dst_len,
|
|
uint32_t timeout_usec,
|
|
ConnectionStatus &status,
|
|
Error *error_ptr)
|
|
{
|
|
PayloadType payload;
|
|
|
|
kern_return_t kret = Receive (payload);
|
|
if (kret == KERN_SUCCESS)
|
|
{
|
|
memcpy (dst, payload.data, payload.data_length);
|
|
status = eConnectionStatusSuccess;
|
|
return payload.data_length;
|
|
}
|
|
|
|
if (error_ptr)
|
|
error_ptr->SetError (kret, eErrorTypeMachKernel);
|
|
status = eConnectionStatusError;
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
ConnectionMachPort::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr)
|
|
{
|
|
PayloadType payload;
|
|
payload.command = 0;
|
|
payload.data_length = src_len;
|
|
const size_t max_payload_size = sizeof(payload.data);
|
|
if (src_len > max_payload_size)
|
|
payload.data_length = max_payload_size;
|
|
memcpy (payload.data, src, payload.data_length);
|
|
|
|
if (Send (payload) == KERN_SUCCESS)
|
|
{
|
|
status = eConnectionStatusSuccess;
|
|
return payload.data_length;
|
|
}
|
|
status = eConnectionStatusError;
|
|
return 0;
|
|
}
|
|
|
|
ConnectionStatus
|
|
ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr)
|
|
{
|
|
return eConnectionStatusLostConnection;
|
|
}
|
|
|
|
kern_return_t
|
|
ConnectionMachPort::Send (const PayloadType &payload)
|
|
{
|
|
struct MessageType message;
|
|
|
|
/* (i) Form the message : */
|
|
|
|
/* (i.a) Fill the header fields : */
|
|
message.head.msgh_bits = MACH_MSGH_BITS_REMOTE (MACH_MSG_TYPE_MAKE_SEND) |
|
|
MACH_MSGH_BITS_OTHER (MACH_MSGH_BITS_COMPLEX);
|
|
message.head.msgh_size = sizeof(MessageType);
|
|
message.head.msgh_local_port = MACH_PORT_NULL;
|
|
message.head.msgh_remote_port = m_port;
|
|
|
|
/* (i.b) Explain the message type ( an integer ) */
|
|
// message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32;
|
|
// message.type.msgt_size = 32;
|
|
// message.type.msgt_number = 1;
|
|
// message.type.msgt_inline = TRUE;
|
|
// message.type.msgt_longform = FALSE;
|
|
// message.type.msgt_deallocate = FALSE;
|
|
/* message.type.msgt_unused = 0; */ /* not needed, I think */
|
|
|
|
/* (i.c) Fill the message with the given integer : */
|
|
message.payload = payload;
|
|
|
|
/* (ii) Send the message : */
|
|
kern_return_t kret = ::mach_msg (&message.head,
|
|
MACH_SEND_MSG,
|
|
message.head.msgh_size,
|
|
0,
|
|
MACH_PORT_NULL,
|
|
MACH_MSG_TIMEOUT_NONE,
|
|
MACH_PORT_NULL);
|
|
|
|
return kret;
|
|
}
|
|
|
|
kern_return_t
|
|
ConnectionMachPort::Receive (PayloadType &payload)
|
|
{
|
|
MessageType message;
|
|
message.head.msgh_size = sizeof(MessageType);
|
|
|
|
kern_return_t kret = ::mach_msg (&message.head,
|
|
MACH_RCV_MSG,
|
|
0,
|
|
sizeof(MessageType),
|
|
m_port,
|
|
MACH_MSG_TIMEOUT_NONE,
|
|
MACH_PORT_NULL);
|
|
|
|
if (kret == KERN_SUCCESS)
|
|
payload = message.payload;
|
|
|
|
return kret;
|
|
}
|
|
|
|
|
|
#endif // #if defined(__APPLE__)
|