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
23using namespace lldb;
24using namespace lldb_private;
25
26struct MessageType
27{
28    mach_msg_header_t head;
29    ConnectionMachPort::PayloadType payload;
30};
31
32
33
34ConnectionMachPort::ConnectionMachPort () :
35    Connection(),
36    m_task(mach_task_self()),
37    m_port(MACH_PORT_TYPE_NONE)
38{
39}
40
41ConnectionMachPort::~ConnectionMachPort ()
42{
43    Disconnect (NULL);
44}
45
46bool
47ConnectionMachPort::IsConnected () const
48{
49    return  m_port != MACH_PORT_TYPE_NONE;
50}
51
52ConnectionStatus
53ConnectionMachPort::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
118ConnectionStatus
119ConnectionMachPort::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
154lldb::ConnectionStatus
155ConnectionMachPort::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
197ConnectionStatus
198ConnectionMachPort::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
215size_t
216ConnectionMachPort::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
238size_t
239ConnectionMachPort::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
258ConnectionStatus
259ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr)
260{
261    return eConnectionStatusLostConnection;
262}
263
264kern_return_t
265ConnectionMachPort::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
302kern_return_t
303ConnectionMachPort::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