Home | History | Annotate | Download | only in Core
      1 //===-- ConnectionMachPort.cpp ----------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 #if defined(__APPLE__)
     10 
     11 #include "lldb/Core/ConnectionMachPort.h"
     12 
     13 // C Includes
     14 #include <servers/bootstrap.h>
     15 
     16 // C++ Includes
     17 // Other libraries and framework includes
     18 // Project includes
     19 #include "lldb/lldb-private-log.h"
     20 #include "lldb/Core/Communication.h"
     21 #include "lldb/Core/Log.h"
     22 
     23 using namespace lldb;
     24 using namespace lldb_private;
     25 
     26 struct MessageType
     27 {
     28     mach_msg_header_t head;
     29     ConnectionMachPort::PayloadType payload;
     30 };
     31 
     32 
     33 
     34 ConnectionMachPort::ConnectionMachPort () :
     35     Connection(),
     36     m_task(mach_task_self()),
     37     m_port(MACH_PORT_TYPE_NONE)
     38 {
     39 }
     40 
     41 ConnectionMachPort::~ConnectionMachPort ()
     42 {
     43     Disconnect (NULL);
     44 }
     45 
     46 bool
     47 ConnectionMachPort::IsConnected () const
     48 {
     49     return  m_port != MACH_PORT_TYPE_NONE;
     50 }
     51 
     52 ConnectionStatus
     53 ConnectionMachPort::Connect (const char *s, Error *error_ptr)
     54 {
     55     if (IsConnected())
     56     {
     57         if (error_ptr)
     58             error_ptr->SetErrorString ("already connected");
     59         return eConnectionStatusError;
     60     }
     61 
     62     if (s == NULL || s[0] == '\0')
     63     {
     64         if (error_ptr)
     65             error_ptr->SetErrorString ("empty connect URL");
     66         return eConnectionStatusError;
     67     }
     68 
     69     ConnectionStatus status = eConnectionStatusError;
     70 
     71     if (strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://")))
     72     {
     73         s += strlen("bootstrap-checkin://");
     74 
     75         if (*s)
     76         {
     77             status = BootstrapCheckIn (s, error_ptr);
     78         }
     79         else
     80         {
     81             if (error_ptr)
     82                 error_ptr->SetErrorString ("bootstrap port name is empty");
     83         }
     84     }
     85     else if (strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://")))
     86     {
     87         s += strlen("bootstrap-lookup://");
     88         if (*s)
     89         {
     90             status = BootstrapLookup (s, error_ptr);
     91         }
     92         else
     93         {
     94             if (error_ptr)
     95                 error_ptr->SetErrorString ("bootstrap port name is empty");
     96         }
     97     }
     98     else
     99     {
    100         if (error_ptr)
    101             error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s);
    102     }
    103 
    104 
    105     if (status == eConnectionStatusSuccess)
    106     {
    107         if (error_ptr)
    108             error_ptr->Clear();
    109     }
    110     else
    111     {
    112         Disconnect(NULL);
    113     }
    114 
    115     return status;
    116 }
    117 
    118 ConnectionStatus
    119 ConnectionMachPort::BootstrapCheckIn (const char *port, Error *error_ptr)
    120 {
    121     mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE;
    122 
    123     /* Getting bootstrap server port */
    124     kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
    125     if (kret == KERN_SUCCESS)
    126     {
    127         name_t port_name;
    128         int len = snprintf(port_name, sizeof(port_name), "%s", port);
    129         if (len < sizeof(port_name))
    130         {
    131             kret = ::bootstrap_check_in (bootstrap_port,
    132                                          port_name,
    133                                          &m_port);
    134         }
    135         else
    136         {
    137             Disconnect(NULL);
    138             if (error_ptr)
    139                 error_ptr->SetErrorString ("bootstrap is too long");
    140             return eConnectionStatusError;
    141         }
    142     }
    143 
    144     if (kret != KERN_SUCCESS)
    145     {
    146         Disconnect(NULL);
    147         if (error_ptr)
    148             error_ptr->SetError (kret, eErrorTypeMachKernel);
    149         return eConnectionStatusError;
    150     }
    151     return eConnectionStatusSuccess;
    152 }
    153 
    154 lldb::ConnectionStatus
    155 ConnectionMachPort::BootstrapLookup (const char *port,
    156                                      Error *error_ptr)
    157 {
    158     name_t port_name;
    159 
    160     if (port && port[0])
    161     {
    162         if (::snprintf (port_name, sizeof (port_name), "%s", port) >= sizeof (port_name))
    163         {
    164             if (error_ptr)
    165                 error_ptr->SetErrorString ("port netname is too long");
    166             return eConnectionStatusError;
    167         }
    168     }
    169     else
    170     {
    171         if (error_ptr)
    172             error_ptr->SetErrorString ("empty port netname");
    173         return eConnectionStatusError;
    174     }
    175 
    176     mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE;
    177 
    178     /* Getting bootstrap server port */
    179     kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
    180     if (kret == KERN_SUCCESS)
    181     {
    182         kret = ::bootstrap_look_up (bootstrap_port,
    183                                     port_name,
    184                                     &m_port);
    185     }
    186 
    187     if (kret != KERN_SUCCESS)
    188     {
    189         if (error_ptr)
    190             error_ptr->SetError (kret, eErrorTypeMachKernel);
    191         return eConnectionStatusError;
    192     }
    193 
    194     return eConnectionStatusSuccess;
    195 }
    196 
    197 ConnectionStatus
    198 ConnectionMachPort::Disconnect (Error *error_ptr)
    199 {
    200     kern_return_t kret;
    201 
    202     // TODO: verify if we need to netname_check_out for
    203     // either or both
    204     if (m_port != MACH_PORT_TYPE_NONE)
    205     {
    206         kret = ::mach_port_deallocate (m_task, m_port);
    207         if (error_ptr)
    208             error_ptr->SetError (kret, eErrorTypeMachKernel);
    209         m_port = MACH_PORT_TYPE_NONE;
    210     }
    211 
    212     return eConnectionStatusSuccess;
    213 }
    214 
    215 size_t
    216 ConnectionMachPort::Read (void *dst,
    217                           size_t dst_len,
    218                           uint32_t timeout_usec,
    219                           ConnectionStatus &status,
    220                           Error *error_ptr)
    221 {
    222     PayloadType payload;
    223 
    224     kern_return_t kret = Receive (payload);
    225     if (kret == KERN_SUCCESS)
    226     {
    227         memcpy (dst, payload.data, payload.data_length);
    228         status = eConnectionStatusSuccess;
    229         return payload.data_length;
    230     }
    231 
    232     if (error_ptr)
    233         error_ptr->SetError (kret, eErrorTypeMachKernel);
    234     status = eConnectionStatusError;
    235     return 0;
    236 }
    237 
    238 size_t
    239 ConnectionMachPort::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr)
    240 {
    241     PayloadType payload;
    242     payload.command = 0;
    243     payload.data_length = src_len;
    244     const size_t max_payload_size = sizeof(payload.data);
    245     if (src_len > max_payload_size)
    246         payload.data_length = max_payload_size;
    247     memcpy (payload.data, src, payload.data_length);
    248 
    249     if (Send (payload) == KERN_SUCCESS)
    250     {
    251         status = eConnectionStatusSuccess;
    252         return payload.data_length;
    253     }
    254     status = eConnectionStatusError;
    255     return 0;
    256 }
    257 
    258 ConnectionStatus
    259 ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr)
    260 {
    261     return eConnectionStatusLostConnection;
    262 }
    263 
    264 kern_return_t
    265 ConnectionMachPort::Send (const PayloadType &payload)
    266 {
    267 	struct MessageType message;
    268 
    269 	/* (i) Form the message : */
    270 
    271 	/* (i.a) Fill the header fields : */
    272 	message.head.msgh_bits = MACH_MSGH_BITS_REMOTE (MACH_MSG_TYPE_MAKE_SEND) |
    273                              MACH_MSGH_BITS_OTHER (MACH_MSGH_BITS_COMPLEX);
    274 	message.head.msgh_size = sizeof(MessageType);
    275 	message.head.msgh_local_port = MACH_PORT_NULL;
    276 	message.head.msgh_remote_port = m_port;
    277 
    278 	/* (i.b) Explain the message type ( an integer ) */
    279     //	message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32;
    280     //	message.type.msgt_size = 32;
    281     //	message.type.msgt_number = 1;
    282     //	message.type.msgt_inline = TRUE;
    283     //	message.type.msgt_longform = FALSE;
    284     //	message.type.msgt_deallocate = FALSE;
    285 	/* message.type.msgt_unused = 0; */ /* not needed, I think */
    286 
    287 	/* (i.c) Fill the message with the given integer : */
    288 	message.payload = payload;
    289 
    290 	/* (ii) Send the message : */
    291 	kern_return_t kret = ::mach_msg (&message.head,
    292                                      MACH_SEND_MSG,
    293                                      message.head.msgh_size,
    294                                      0,
    295                                      MACH_PORT_NULL,
    296                                      MACH_MSG_TIMEOUT_NONE,
    297                                      MACH_PORT_NULL);
    298 
    299 	return kret;
    300 }
    301 
    302 kern_return_t
    303 ConnectionMachPort::Receive (PayloadType &payload)
    304 {
    305 	MessageType message;
    306 	message.head.msgh_size = sizeof(MessageType);
    307 
    308 	kern_return_t kret = ::mach_msg (&message.head,
    309                                      MACH_RCV_MSG,
    310                                      0,
    311                                      sizeof(MessageType),
    312                                      m_port,
    313                                      MACH_MSG_TIMEOUT_NONE,
    314                                      MACH_PORT_NULL);
    315 
    316     if (kret == KERN_SUCCESS)
    317         payload = message.payload;
    318 
    319 	return kret;
    320 }
    321 
    322 
    323 #endif // #if defined(__APPLE__)
    324