llpluginprocesschild.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /**
  2. * @file llpluginprocesschild.cpp
  3. * @brief LLPluginProcessChild handles the child side of the external-process plugin API.
  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 "llpluginprocesschild.h"
  34. #include "llplugininstance.h"
  35. #include "llpluginmessagepipe.h"
  36. #include "llpluginmessageclasses.h"
  37. constexpr F32 HEARTBEAT_SECONDS = 1.f;
  38. // Each call to idle will give the plugin this much time.
  39. constexpr F32 PLUGIN_IDLE_SECONDS = 0.01f;
  40. // Do not set it to be bigger than mPluginLockupTimeout or parent will kill
  41. // LLPluginProcessChild
  42. constexpr F32 GOODBYE_SECONDS = 5.f;
  43. LLPluginProcessChild::LLPluginProcessChild()
  44. : mInstance(NULL),
  45. mState(STATE_UNINITIALIZED),
  46. mSleepTime(PLUGIN_IDLE_SECONDS), // default: send idle messages at 100Hz
  47. mCPUElapsed(0.f),
  48. mBlockingRequest(false),
  49. mBlockingResponseReceived(false)
  50. {
  51. mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
  52. }
  53. LLPluginProcessChild::~LLPluginProcessChild()
  54. {
  55. if (mInstance)
  56. {
  57. sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
  58. // IMPORTANT: under some (unknown) circumstances the apr_dso_unload()
  59. // triggered when mInstance is deleted appears to fail and lock up
  60. // which means that a given instance of the slplugin process never
  61. // exits.
  62. #if 0
  63. delete mInstance;
  64. mInstance = NULL;
  65. #else
  66. exit(0);
  67. #endif
  68. }
  69. }
  70. void LLPluginProcessChild::killSockets()
  71. {
  72. killMessagePipe();
  73. mSocket.reset();
  74. }
  75. void LLPluginProcessChild::init(U32 launcher_port)
  76. {
  77. mLauncherHost = LLHost("127.0.0.1", launcher_port);
  78. setState(STATE_INITIALIZED);
  79. }
  80. void LLPluginProcessChild::idle()
  81. {
  82. bool idle_again;
  83. do
  84. {
  85. // Once we have hit the shutdown request state checking for errors
  86. // might put us in a spurious error state... Do not do that.
  87. if (mState < STATE_SHUTDOWNREQ)
  88. {
  89. if (APR_STATUS_IS_EOF(mSocketError))
  90. {
  91. // Plugin socket was closed. This covers both normal plugin
  92. // termination and host crashes.
  93. setState(STATE_ERROR);
  94. }
  95. else if (mSocketError != APR_SUCCESS)
  96. {
  97. llwarns << "Message pipe is in error state (" << mSocketError
  98. << "), moving to STATE_ERROR"<< llendl;
  99. setState(STATE_ERROR);
  100. }
  101. if (mState > STATE_INITIALIZED && !mMessagePipe)
  102. {
  103. // The pipe has been closed, we are done. *TODO: This could be
  104. // slightly more subtle, but not sure it needs to be.
  105. llwarns << "Message pipe went away, moving to STATE_ERROR"
  106. << llendl;
  107. setState(STATE_ERROR);
  108. }
  109. }
  110. // If a state needs to go directly to another state (as a performance
  111. // enhancement), it can set idle_again to true after calling
  112. // setState(). USE THIS CAREFULLY, since it can starve other code.
  113. // Specifically make sure there is no way to get into a closed cycle
  114. // and never return. When in doubt, do not do it.
  115. idle_again = false;
  116. switch (mState)
  117. {
  118. case STATE_UNINITIALIZED:
  119. break;
  120. case STATE_INITIALIZED:
  121. if (mSocket->blockingConnect(mLauncherHost))
  122. {
  123. // This automatically sets mMessagePipe
  124. new LLPluginMessagePipe(this, mSocket);
  125. setState(STATE_CONNECTED);
  126. }
  127. else
  128. {
  129. // Connect failed
  130. setState(STATE_ERROR);
  131. }
  132. break;
  133. case STATE_CONNECTED:
  134. sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL,
  135. "hello"));
  136. setState(STATE_PLUGIN_LOADING);
  137. break;
  138. case STATE_PLUGIN_LOADING:
  139. if (!mPluginFile.empty())
  140. {
  141. mInstance = new LLPluginInstance(this);
  142. if (mInstance->load(mPluginDir, mPluginFile) == 0)
  143. {
  144. mHeartbeat.start();
  145. mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
  146. mCPUElapsed = 0.f;
  147. setState(STATE_PLUGIN_LOADED);
  148. }
  149. else
  150. {
  151. setState(STATE_ERROR);
  152. }
  153. }
  154. break;
  155. case STATE_PLUGIN_LOADED:
  156. {
  157. setState(STATE_PLUGIN_INITIALIZING);
  158. LLPluginMessage message("base", "init");
  159. sendMessageToPlugin(message);
  160. break;
  161. }
  162. case STATE_PLUGIN_INITIALIZING:
  163. // Waiting for init_response...
  164. break;
  165. case STATE_RUNNING:
  166. if (mInstance)
  167. {
  168. // Provide some time to the plugin
  169. LLPluginMessage message("base", "idle");
  170. message.setValueReal("time", PLUGIN_IDLE_SECONDS);
  171. sendMessageToPlugin(message);
  172. if (mHeartbeat.hasExpired())
  173. {
  174. // This just proves that we are not stuck down inside
  175. // the plugin code.
  176. LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL,
  177. "heartbeat");
  178. // Calculate the approximage CPU usage fraction
  179. // (floating point value between 0 and 1) used by the
  180. // plugin this heartbeat cycle. Note that this will not
  181. // take into account any threads or additional
  182. // processes the plugin spawns, but it's a first
  183. // approximation. If we could write OS-specific
  184. // functions to query the actual CPU usage of this
  185. // process, that would be a better approximation.
  186. heartbeat.setValueReal("cpu_usage",
  187. mCPUElapsed /
  188. mHeartbeat.getElapsedTimeF64());
  189. sendMessageToParent(heartbeat);
  190. mHeartbeat.reset();
  191. mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
  192. mCPUElapsed = 0.f;
  193. }
  194. }
  195. // receivePluginMessage will transition to STATE_UNLOADING
  196. break;
  197. case STATE_SHUTDOWNREQ:
  198. // Set next state first thing in case "cleanup" message advances
  199. // state.
  200. setState(STATE_UNLOADING);
  201. mWaitGoodbye.setTimerExpirySec(GOODBYE_SECONDS);
  202. if (mInstance)
  203. {
  204. sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
  205. }
  206. break;
  207. case STATE_UNLOADING:
  208. // Waiting for goodbye from plugin.
  209. if (mWaitGoodbye.hasExpired())
  210. {
  211. llwarns << "Wait for goodbye expired. Advancing to UNLOADED"
  212. << llendl;
  213. if (mInstance)
  214. {
  215. // Something went wrong, at least make sure plugin will
  216. // terminate
  217. sendMessageToPlugin(LLPluginMessage("base",
  218. "force_exit"));
  219. }
  220. setState(STATE_UNLOADED);
  221. }
  222. if (mInstance)
  223. {
  224. // Provide some time to the plugin. E.g. CEF on "cleanup"
  225. // sets shutdown request, but it still needs idle loop to
  226. // actually shutdown.
  227. LLPluginMessage message("base", "idle");
  228. message.setValueReal("time", PLUGIN_IDLE_SECONDS);
  229. sendMessageToPlugin(message);
  230. }
  231. break;
  232. case STATE_UNLOADED:
  233. killSockets();
  234. if (mInstance)
  235. {
  236. delete mInstance;
  237. mInstance = NULL;
  238. }
  239. setState(STATE_DONE);
  240. break;
  241. case STATE_ERROR:
  242. // Close the socket to the launcher
  243. killSockets();
  244. // TODO: Where do we go from here? Just exit()?
  245. setState(STATE_DONE);
  246. break;
  247. case STATE_DONE:
  248. // Just sit here.
  249. break;
  250. }
  251. }
  252. while (idle_again);
  253. }
  254. void LLPluginProcessChild::sleep(F64 seconds)
  255. {
  256. deliverQueuedMessages();
  257. if (mMessagePipe)
  258. {
  259. mMessagePipe->pump(seconds);
  260. }
  261. else
  262. {
  263. ms_sleep((int)(seconds * 1000.f));
  264. }
  265. }
  266. void LLPluginProcessChild::pump()
  267. {
  268. deliverQueuedMessages();
  269. if (mMessagePipe)
  270. {
  271. mMessagePipe->pump(0.f);
  272. }
  273. #if 0 // Should we warn here ?
  274. else
  275. {
  276. }
  277. #endif
  278. }
  279. void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage& message)
  280. {
  281. if (mInstance)
  282. {
  283. std::string buffer = message.generate();
  284. LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
  285. LLTimer elapsed;
  286. mInstance->sendMessage(buffer);
  287. mCPUElapsed += elapsed.getElapsedTimeF64();
  288. }
  289. else
  290. {
  291. llwarns_sparse << "Instance is NULL !" << llendl;
  292. }
  293. }
  294. void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage& message)
  295. {
  296. std::string buffer = message.generate();
  297. LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL;
  298. writeMessageRaw(buffer);
  299. }
  300. void LLPluginProcessChild::receiveMessageRaw(const std::string& message)
  301. {
  302. // Incoming message from the TCP Socket
  303. LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL;
  304. // Decode this message
  305. LLPluginMessage parsed;
  306. parsed.parse(message);
  307. if (mBlockingRequest)
  308. {
  309. // We are blocking the plugin waiting for a response.
  310. if (parsed.hasValue("blocking_response"))
  311. {
  312. // This is the message we've been waiting for: fall through and
  313. // send it immediately.
  314. mBlockingResponseReceived = true;
  315. }
  316. else
  317. {
  318. // Still waiting. Queue this message and don't process it yet.
  319. mMessageQueue.emplace(message);
  320. return;
  321. }
  322. }
  323. bool pass_message = true;
  324. // FIXME: how should we handle queueing here ?
  325. {
  326. std::string message_class = parsed.getClass();
  327. if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
  328. {
  329. pass_message = false;
  330. std::string message_name = parsed.getName();
  331. if (message_name == "load_plugin")
  332. {
  333. mPluginFile = parsed.getValue("file");
  334. mPluginDir = parsed.getValue("dir");
  335. }
  336. else if (message_name == "shutdown_plugin")
  337. {
  338. setState(STATE_SHUTDOWNREQ);
  339. }
  340. else if (message_name == "shm_add")
  341. {
  342. std::string name = parsed.getValue("name");
  343. size_t size = (size_t)parsed.getValueS32("size");
  344. shared_mem_regions_t::iterator iter =
  345. mSharedMemoryRegions.find(name);
  346. if (iter != mSharedMemoryRegions.end())
  347. {
  348. // Need to remove the old region first
  349. llwarns << "Adding a duplicate shared memory segment !"
  350. << llendl;
  351. }
  352. else
  353. {
  354. // This is a new region
  355. LLPluginSharedMemory* region = new LLPluginSharedMemory;
  356. if (region->attach(name, size))
  357. {
  358. mSharedMemoryRegions.emplace(name, region);
  359. std::stringstream addr;
  360. addr << region->getMappedAddress();
  361. // Send the add notification to the plugin...
  362. LLPluginMessage message("base", "shm_added");
  363. message.setValue("name", name);
  364. message.setValueS32("size", (S32)size);
  365. message.setValuePointer("address",
  366. region->getMappedAddress());
  367. sendMessageToPlugin(message);
  368. // And send the response to the parent.
  369. message.setMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL,
  370. "shm_add_response");
  371. message.setValue("name", name);
  372. sendMessageToParent(message);
  373. }
  374. else
  375. {
  376. llwarns << "Could not create a shared memory segment !"
  377. << llendl;
  378. delete region;
  379. }
  380. }
  381. }
  382. else if (message_name == "shm_remove")
  383. {
  384. std::string name = parsed.getValue("name");
  385. shared_mem_regions_t::iterator iter =
  386. mSharedMemoryRegions.find(name);
  387. if (iter != mSharedMemoryRegions.end())
  388. {
  389. // Forward the remove request to the plugin; its response
  390. // will trigger us to detach the segment.
  391. LLPluginMessage message("base", "shm_remove");
  392. message.setValue("name", name);
  393. sendMessageToPlugin(message);
  394. }
  395. else
  396. {
  397. llwarns << "shm_remove for unknown memory segment !"
  398. << llendl;
  399. }
  400. }
  401. else if (message_name == "sleep_time")
  402. {
  403. mSleepTime = llmax(parsed.getValueReal("time"),
  404. (F64)0.01f); // clamp to maximum of 100Hz
  405. }
  406. #if LL_DEBUG
  407. else if (message_name == "crash")
  408. {
  409. // Crash the plugin
  410. llerrs << "Plugin crash requested." << llendl;
  411. }
  412. else if (message_name == "hang")
  413. {
  414. // Hang the plugin
  415. llwarns << "Plugin hang requested." << llendl;
  416. while (true) ;
  417. }
  418. #endif
  419. else
  420. {
  421. llwarns << "Unknown internal message from parent: "
  422. << message_name << llendl;
  423. }
  424. }
  425. }
  426. if (pass_message && mInstance)
  427. {
  428. LLTimer elapsed;
  429. mInstance->sendMessage(message);
  430. mCPUElapsed += elapsed.getElapsedTimeF64();
  431. }
  432. }
  433. //virtual
  434. void LLPluginProcessChild::receivePluginMessage(const std::string& message)
  435. {
  436. LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL;
  437. if (mBlockingRequest)
  438. {
  439. llwarns << "Cannot send a message while already waiting on a blocking request; aborting"
  440. << llendl;
  441. return;
  442. }
  443. // Incoming message from the plugin instance
  444. bool pass_message = true;
  445. // *FIXME: how should we handle queueing here?
  446. // Intercept certain base messages (responses to ones sent by this class)
  447. {
  448. // Decode this message
  449. LLPluginMessage parsed;
  450. parsed.parse(message);
  451. if (parsed.hasValue("blocking_request"))
  452. {
  453. mBlockingRequest = true;
  454. }
  455. std::string message_class = parsed.getClass();
  456. if (message_class == "base")
  457. {
  458. std::string message_name = parsed.getName();
  459. if (message_name == "init_response")
  460. {
  461. // The plugin has finished initializing.
  462. setState(STATE_RUNNING);
  463. // Do not pass this message up to the parent
  464. pass_message = false;
  465. LLPluginMessage new_message(LLPLUGIN_MESSAGE_CLASS_INTERNAL,
  466. "load_plugin_response");
  467. LLSD versions = parsed.getValueLLSD("versions");
  468. new_message.setValueLLSD("versions", versions);
  469. if (parsed.hasValue("plugin_version"))
  470. {
  471. std::string version = parsed.getValue("plugin_version");
  472. new_message.setValueLLSD("plugin_version", version);
  473. }
  474. // Let the parent know it is loaded and initialized.
  475. sendMessageToParent(new_message);
  476. }
  477. else if (message_name == "goodbye")
  478. {
  479. setState(STATE_UNLOADED);
  480. }
  481. else if (message_name == "shm_remove_response")
  482. {
  483. // Do not pass this message up to the parent
  484. pass_message = false;
  485. std::string name = parsed.getValue("name");
  486. shared_mem_regions_t::iterator iter =
  487. mSharedMemoryRegions.find(name);
  488. if (iter != mSharedMemoryRegions.end())
  489. {
  490. // Detach the shared memory region
  491. iter->second->detach();
  492. // Remove it from our map
  493. mSharedMemoryRegions.erase(iter);
  494. // Finally, send the response to the parent.
  495. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL,
  496. "shm_remove_response");
  497. message.setValue("name", name);
  498. sendMessageToParent(message);
  499. }
  500. else
  501. {
  502. llwarns << "shm_remove_response for unknown memory segment!"
  503. << llendl;
  504. }
  505. }
  506. }
  507. }
  508. if (pass_message)
  509. {
  510. LL_DEBUGS("Plugin") << "Passing through to parent: " << message
  511. << LL_ENDL;
  512. writeMessageRaw(message);
  513. }
  514. while (mBlockingRequest)
  515. {
  516. // The plugin wants to block and wait for a response to this message.
  517. sleep(mSleepTime); // Pumps the message pipe and processes messages
  518. if (mBlockingResponseReceived || mSocketError != APR_SUCCESS ||
  519. !mMessagePipe)
  520. {
  521. // Response has been received, or we've hit an error state. Stop
  522. // waiting.
  523. mBlockingRequest = false;
  524. mBlockingResponseReceived = false;
  525. }
  526. }
  527. }
  528. void LLPluginProcessChild::setState(EState state)
  529. {
  530. LL_DEBUGS("Plugin") << "Setting state to " << state << LL_ENDL;
  531. mState = state;
  532. }
  533. void LLPluginProcessChild::deliverQueuedMessages()
  534. {
  535. if (!mBlockingRequest)
  536. {
  537. while (!mMessageQueue.empty())
  538. {
  539. receiveMessageRaw(mMessageQueue.front());
  540. mMessageQueue.pop();
  541. }
  542. }
  543. }