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