llpluginmessagepipe.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /**
  2. * @file llpluginmessagepipe.cpp
  3. * @brief Classes that implement connections from the plugin system to pipes/pumps.
  4. *
  5. * $LicenseInfo:firstyear=2008&license=viewergpl$
  6. *
  7. * Copyright (c) 2008-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include "llpluginmessagepipe.h"
  34. #include "llapr.h"
  35. #include "llbufferstream.h"
  36. #include "lltimer.h" // For ms_sleep()
  37. static const char MESSAGE_DELIMITER = '\0';
  38. LLPluginMessagePipeOwner::LLPluginMessagePipeOwner()
  39. : mMessagePipe(NULL),
  40. mSocketError(APR_SUCCESS)
  41. {
  42. }
  43. //virtual
  44. LLPluginMessagePipeOwner::~LLPluginMessagePipeOwner()
  45. {
  46. killMessagePipe();
  47. }
  48. //virtual
  49. apr_status_t LLPluginMessagePipeOwner::socketError(apr_status_t error)
  50. {
  51. mSocketError = error;
  52. return error;
  53. }
  54. //virtual
  55. void LLPluginMessagePipeOwner::setMessagePipe(LLPluginMessagePipe* read_pipe)
  56. {
  57. // Save a reference to this pipe
  58. mMessagePipe = read_pipe;
  59. }
  60. bool LLPluginMessagePipeOwner::writeMessageRaw(const std::string& message)
  61. {
  62. if (mMessagePipe)
  63. {
  64. return mMessagePipe->addMessage(message);
  65. }
  66. // It is "normal" to see a "shutdown_plugin" message dropped after a plugin
  67. // has been killed by other means... So do not log a warning for this. HB
  68. if (message.find("<string>shutdown_plugin</string>") == std::string::npos)
  69. {
  70. llwarns << "Dropping message: " << message << llendl;
  71. }
  72. return false;
  73. }
  74. void LLPluginMessagePipeOwner::killMessagePipe()
  75. {
  76. if (mMessagePipe)
  77. {
  78. delete mMessagePipe;
  79. mMessagePipe = NULL;
  80. }
  81. }
  82. LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner* owner,
  83. LLSocket::ptr_t socket)
  84. : mOutputStartIndex(0),
  85. mOwner(owner),
  86. mSocket(socket)
  87. {
  88. mOwner->setMessagePipe(this);
  89. }
  90. LLPluginMessagePipe::~LLPluginMessagePipe()
  91. {
  92. if (mOwner)
  93. {
  94. mOwner->setMessagePipe(NULL);
  95. }
  96. }
  97. // Queues the message for later output
  98. bool LLPluginMessagePipe::addMessage(const std::string& message)
  99. {
  100. mOutputMutex.lock();
  101. // If we are starting to use up too much memory, clear
  102. if (mOutputStartIndex > 1024 * 1024)
  103. {
  104. mOutput = mOutput.substr(mOutputStartIndex);
  105. mOutputStartIndex = 0;
  106. }
  107. mOutput += message;
  108. mOutput += MESSAGE_DELIMITER; // message separator
  109. mOutputMutex.unlock();
  110. return true;
  111. }
  112. void LLPluginMessagePipe::setSocketTimeout(apr_interval_time_t timeout_usec)
  113. {
  114. // We never want to sleep forever, so force negative timeouts to become
  115. // non-blocking. According to this page:
  116. // http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-13.html
  117. // blocking/non-blocking with apr sockets is somewhat non-portable.
  118. apr_socket_opt_set(mSocket->getSocket(), APR_SO_NONBLOCK, 1);
  119. if (timeout_usec <= 0)
  120. {
  121. // Make the socket non-blocking
  122. apr_socket_timeout_set(mSocket->getSocket(), 0);
  123. }
  124. else
  125. {
  126. // Make the socket blocking-with-timeout
  127. apr_socket_timeout_set(mSocket->getSocket(), timeout_usec);
  128. }
  129. }
  130. bool LLPluginMessagePipe::pump(F64 timeout)
  131. {
  132. bool result = pumpOutput();
  133. if (result)
  134. {
  135. result = pumpInput(timeout);
  136. }
  137. return result;
  138. }
  139. bool LLPluginMessagePipe::pumpOutput()
  140. {
  141. bool result = true;
  142. if (mSocket)
  143. {
  144. mOutputMutex.lock();
  145. const char* output_data = &(mOutput.data()[mOutputStartIndex]);
  146. if (*output_data != '\0')
  147. {
  148. // Write any outgoing messages
  149. apr_size_t in_size = (apr_size_t)(mOutput.size() -
  150. mOutputStartIndex);
  151. apr_size_t out_size = in_size;
  152. setSocketTimeout(0);
  153. apr_status_t status = apr_socket_send(mSocket->getSocket(),
  154. output_data, &out_size);
  155. if (status == APR_SUCCESS || APR_STATUS_IS_EAGAIN(status))
  156. {
  157. // Success or Socket buffer is full...
  158. // If we have pumped the entire string, clear it
  159. if (out_size == in_size)
  160. {
  161. mOutputStartIndex = 0;
  162. mOutput.clear();
  163. }
  164. else
  165. {
  166. llassert(in_size > out_size);
  167. // Remove the written part from the buffer and try again
  168. // later.
  169. mOutputStartIndex += out_size;
  170. }
  171. }
  172. else if (APR_STATUS_IS_EOF(status))
  173. {
  174. // This is what we normally expect when a plugin exits.
  175. llinfos << "Got EOF from plugin socket. " << llendl;
  176. if (mOwner)
  177. {
  178. mOwner->socketError(status);
  179. }
  180. result = false;
  181. }
  182. else
  183. {
  184. // Some other error. Treat this as fatal.
  185. ll_apr_warn_status(status);
  186. if (mOwner)
  187. {
  188. mOwner->socketError(status);
  189. }
  190. result = false;
  191. }
  192. }
  193. mOutputMutex.unlock();
  194. }
  195. return result;
  196. }
  197. bool LLPluginMessagePipe::pumpInput(F64 timeout)
  198. {
  199. bool result = true;
  200. if (mSocket)
  201. {
  202. // FIXME: For some reason, the apr timeout stuff is not working
  203. // properly on windows. Until such time as we figure out why, do not
  204. // try to use the socket timeout; just sleep here instead.
  205. #if LL_WINDOWS
  206. if (timeout != 0.f)
  207. {
  208. ms_sleep((int)(timeout * 1000.f));
  209. timeout = 0.f;
  210. }
  211. #endif
  212. char input_buf[1024];
  213. apr_size_t request_size;
  214. if (timeout == 0.f)
  215. {
  216. // If we have no timeout, start out with a full read.
  217. request_size = sizeof(input_buf);
  218. }
  219. else
  220. {
  221. // Start out by reading one byte, so that any data received will
  222. // wake us up.
  223. request_size = 1;
  224. }
  225. // Use the timeout so we will sleep if no data is available.
  226. setSocketTimeout((apr_interval_time_t)(timeout * 1000000));
  227. while (true)
  228. {
  229. apr_size_t size = request_size;
  230. apr_status_t status = apr_socket_recv(mSocket->getSocket(),
  231. input_buf, &size);
  232. if (size > 0)
  233. {
  234. mInputMutex.lock();
  235. mInput.append(input_buf, size);
  236. mInputMutex.unlock();
  237. }
  238. if (status == APR_SUCCESS)
  239. {
  240. LL_DEBUGS("PluginSocket") << "success, read " << size
  241. << LL_ENDL;
  242. if (size != request_size)
  243. {
  244. // This was a short read, so we are done.
  245. break;
  246. }
  247. }
  248. else if (APR_STATUS_IS_TIMEUP(status))
  249. {
  250. LL_DEBUGS("PluginSocket") << "TIMEUP, read " << size
  251. << LL_ENDL;
  252. // Timeout was hit. Since the initial read is 1 byte, this
  253. // should never be a partial read.
  254. break;
  255. }
  256. else if (APR_STATUS_IS_EAGAIN(status))
  257. {
  258. LL_DEBUGS("PluginSocket") << "EAGAIN, read " << size
  259. << LL_ENDL;
  260. // Non-blocking read returned immediately.
  261. break;
  262. }
  263. else if (APR_STATUS_IS_EOF(status))
  264. {
  265. // This is what we normally expect when a plugin exits.
  266. llinfos << "Got EOF from plugin socket. " << llendl;
  267. if (mOwner)
  268. {
  269. mOwner->socketError(status);
  270. }
  271. result = false;
  272. break;
  273. }
  274. else
  275. {
  276. // Some other error; treat this as fatal.
  277. ll_apr_warn_status(status);
  278. if (mOwner)
  279. {
  280. mOwner->socketError(status);
  281. }
  282. result = false;
  283. break;
  284. }
  285. if (timeout != 0.f)
  286. {
  287. // Second and subsequent reads should not use the timeout...
  288. setSocketTimeout(0);
  289. // ... and should try to fill the input buffer
  290. request_size = sizeof(input_buf);
  291. }
  292. }
  293. processInput();
  294. }
  295. return result;
  296. }
  297. void LLPluginMessagePipe::processInput()
  298. {
  299. // Look for input delimiter(s) in the input buffer.
  300. std::string message;
  301. size_t delim;
  302. mInputMutex.lock();
  303. while ((delim = mInput.find(MESSAGE_DELIMITER)) != std::string::npos)
  304. {
  305. // Let the owner process this message
  306. if (mOwner)
  307. {
  308. // Pull the message out of the input buffer before calling
  309. // receiveMessageRaw. It is now possible for this function to get
  310. // called recursively (in the case where the plugin makes a
  311. // blocking request) and this guarantees that the messages will get
  312. // dequeued correctly.
  313. message.assign(mInput, 0, delim);
  314. mInput.erase(0, delim + 1);
  315. mInputMutex.unlock();
  316. mOwner->receiveMessageRaw(message);
  317. mInputMutex.lock();
  318. }
  319. else
  320. {
  321. llwarns << "NULL owner" << llendl;
  322. }
  323. }
  324. mInputMutex.unlock();
  325. }