/thirdparty/breakpad/common/mac/MachIPC.mm

http://github.com/tomahawk-player/tomahawk · Objective C++ · 306 lines · 180 code · 60 blank · 66 comment · 28 complexity · bf520cb0081aa277ac096b18e1130b22 MD5 · raw file

  1. // Copyright (c) 2007, Google Inc.
  2. // All rights reserved.
  3. //
  4. // Redistribution and use in source and binary forms, with or without
  5. // modification, are permitted provided that the following conditions are
  6. // met:
  7. //
  8. // * Redistributions of source code must retain the above copyright
  9. // notice, this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above
  11. // copyright notice, this list of conditions and the following disclaimer
  12. // in the documentation and/or other materials provided with the
  13. // distribution.
  14. // * Neither the name of Google Inc. nor the names of its
  15. // contributors may be used to endorse or promote products derived from
  16. // this software without specific prior written permission.
  17. //
  18. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. //
  30. // MachIPC.mm
  31. // Wrapper for mach IPC calls
  32. #import <stdio.h>
  33. #import "MachIPC.h"
  34. #include "common/mac/bootstrap_compat.h"
  35. namespace google_breakpad {
  36. //==============================================================================
  37. MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
  38. head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
  39. // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
  40. head.msgh_local_port = MACH_PORT_NULL;
  41. head.msgh_reserved = 0;
  42. head.msgh_id = 0;
  43. SetDescriptorCount(0); // start out with no descriptors
  44. SetMessageID(message_id);
  45. SetData(NULL, 0); // client may add data later
  46. }
  47. //==============================================================================
  48. // returns true if successful
  49. bool MachMessage::SetData(void *data,
  50. int32_t data_length) {
  51. // first check to make sure we have enough space
  52. size_t size = CalculateSize();
  53. size_t new_size = size + data_length;
  54. if (new_size > sizeof(MachMessage)) {
  55. return false; // not enough space
  56. }
  57. GetDataPacket()->data_length = EndianU32_NtoL(data_length);
  58. if (data) memcpy(GetDataPacket()->data, data, data_length);
  59. CalculateSize();
  60. return true;
  61. }
  62. //==============================================================================
  63. // calculates and returns the total size of the message
  64. // Currently, the entire message MUST fit inside of the MachMessage
  65. // messsage size <= sizeof(MachMessage)
  66. mach_msg_size_t MachMessage::CalculateSize() {
  67. size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
  68. // add space for MessageDataPacket
  69. int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
  70. size += 2*sizeof(int32_t) + alignedDataLength;
  71. // add space for descriptors
  72. size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
  73. head.msgh_size = static_cast<mach_msg_size_t>(size);
  74. return head.msgh_size;
  75. }
  76. //==============================================================================
  77. MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
  78. size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
  79. MessageDataPacket *packet =
  80. reinterpret_cast<MessageDataPacket*>(padding + desc_size);
  81. return packet;
  82. }
  83. //==============================================================================
  84. void MachMessage::SetDescriptor(int n,
  85. const MachMsgPortDescriptor &desc) {
  86. MachMsgPortDescriptor *desc_array =
  87. reinterpret_cast<MachMsgPortDescriptor*>(padding);
  88. desc_array[n] = desc;
  89. }
  90. //==============================================================================
  91. // returns true if successful otherwise there was not enough space
  92. bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
  93. // first check to make sure we have enough space
  94. int size = CalculateSize();
  95. size_t new_size = size + sizeof(MachMsgPortDescriptor);
  96. if (new_size > sizeof(MachMessage)) {
  97. return false; // not enough space
  98. }
  99. // unfortunately, we need to move the data to allow space for the
  100. // new descriptor
  101. u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
  102. bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
  103. SetDescriptor(GetDescriptorCount(), desc);
  104. SetDescriptorCount(GetDescriptorCount() + 1);
  105. CalculateSize();
  106. return true;
  107. }
  108. //==============================================================================
  109. void MachMessage::SetDescriptorCount(int n) {
  110. body.msgh_descriptor_count = n;
  111. if (n > 0) {
  112. head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
  113. } else {
  114. head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
  115. }
  116. }
  117. //==============================================================================
  118. MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) {
  119. if (n < GetDescriptorCount()) {
  120. MachMsgPortDescriptor *desc =
  121. reinterpret_cast<MachMsgPortDescriptor*>(padding);
  122. return desc + n;
  123. }
  124. return nil;
  125. }
  126. //==============================================================================
  127. mach_port_t MachMessage::GetTranslatedPort(int n) {
  128. if (n < GetDescriptorCount()) {
  129. return GetDescriptor(n)->GetMachPort();
  130. }
  131. return MACH_PORT_NULL;
  132. }
  133. #pragma mark -
  134. //==============================================================================
  135. // create a new mach port for receiving messages and register a name for it
  136. ReceivePort::ReceivePort(const char *receive_port_name) {
  137. mach_port_t current_task = mach_task_self();
  138. init_result_ = mach_port_allocate(current_task,
  139. MACH_PORT_RIGHT_RECEIVE,
  140. &port_);
  141. if (init_result_ != KERN_SUCCESS)
  142. return;
  143. init_result_ = mach_port_insert_right(current_task,
  144. port_,
  145. port_,
  146. MACH_MSG_TYPE_MAKE_SEND);
  147. if (init_result_ != KERN_SUCCESS)
  148. return;
  149. mach_port_t task_bootstrap_port = 0;
  150. init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port);
  151. if (init_result_ != KERN_SUCCESS)
  152. return;
  153. init_result_ = breakpad::BootstrapRegister(
  154. bootstrap_port,
  155. const_cast<char*>(receive_port_name),
  156. port_);
  157. }
  158. //==============================================================================
  159. // create a new mach port for receiving messages
  160. ReceivePort::ReceivePort() {
  161. mach_port_t current_task = mach_task_self();
  162. init_result_ = mach_port_allocate(current_task,
  163. MACH_PORT_RIGHT_RECEIVE,
  164. &port_);
  165. if (init_result_ != KERN_SUCCESS)
  166. return;
  167. init_result_ = mach_port_insert_right(current_task,
  168. port_,
  169. port_,
  170. MACH_MSG_TYPE_MAKE_SEND);
  171. }
  172. //==============================================================================
  173. // Given an already existing mach port, use it. We take ownership of the
  174. // port and deallocate it in our destructor.
  175. ReceivePort::ReceivePort(mach_port_t receive_port)
  176. : port_(receive_port),
  177. init_result_(KERN_SUCCESS) {
  178. }
  179. //==============================================================================
  180. ReceivePort::~ReceivePort() {
  181. if (init_result_ == KERN_SUCCESS)
  182. mach_port_deallocate(mach_task_self(), port_);
  183. }
  184. //==============================================================================
  185. kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
  186. mach_msg_timeout_t timeout) {
  187. if (!out_message) {
  188. return KERN_INVALID_ARGUMENT;
  189. }
  190. // return any error condition encountered in constructor
  191. if (init_result_ != KERN_SUCCESS)
  192. return init_result_;
  193. out_message->head.msgh_bits = 0;
  194. out_message->head.msgh_local_port = port_;
  195. out_message->head.msgh_remote_port = MACH_PORT_NULL;
  196. out_message->head.msgh_reserved = 0;
  197. out_message->head.msgh_id = 0;
  198. mach_msg_option_t options = MACH_RCV_MSG;
  199. if (timeout != MACH_MSG_TIMEOUT_NONE)
  200. options |= MACH_RCV_TIMEOUT;
  201. kern_return_t result = mach_msg(&out_message->head,
  202. options,
  203. 0,
  204. sizeof(MachMessage),
  205. port_,
  206. timeout, // timeout in ms
  207. MACH_PORT_NULL);
  208. return result;
  209. }
  210. #pragma mark -
  211. //==============================================================================
  212. // get a port with send rights corresponding to a named registered service
  213. MachPortSender::MachPortSender(const char *receive_port_name) {
  214. mach_port_t task_bootstrap_port = 0;
  215. init_result_ = task_get_bootstrap_port(mach_task_self(),
  216. &task_bootstrap_port);
  217. if (init_result_ != KERN_SUCCESS)
  218. return;
  219. init_result_ = bootstrap_look_up(task_bootstrap_port,
  220. const_cast<char*>(receive_port_name),
  221. &send_port_);
  222. }
  223. //==============================================================================
  224. MachPortSender::MachPortSender(mach_port_t send_port)
  225. : send_port_(send_port),
  226. init_result_(KERN_SUCCESS) {
  227. }
  228. //==============================================================================
  229. kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
  230. mach_msg_timeout_t timeout) {
  231. if (message.head.msgh_size == 0) {
  232. return KERN_INVALID_VALUE; // just for safety -- never should occur
  233. };
  234. if (init_result_ != KERN_SUCCESS)
  235. return init_result_;
  236. message.head.msgh_remote_port = send_port_;
  237. kern_return_t result = mach_msg(&message.head,
  238. MACH_SEND_MSG | MACH_SEND_TIMEOUT,
  239. message.head.msgh_size,
  240. 0,
  241. MACH_PORT_NULL,
  242. timeout, // timeout in ms
  243. MACH_PORT_NULL);
  244. return result;
  245. }
  246. } // namespace google_breakpad