aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llplugin/llpluginprocessparent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llplugin/llpluginprocessparent.cpp')
-rwxr-xr-xlinden/indra/llplugin/llpluginprocessparent.cpp1120
1 files changed, 1120 insertions, 0 deletions
diff --git a/linden/indra/llplugin/llpluginprocessparent.cpp b/linden/indra/llplugin/llpluginprocessparent.cpp
new file mode 100755
index 0000000..8db6046
--- /dev/null
+++ b/linden/indra/llplugin/llpluginprocessparent.cpp
@@ -0,0 +1,1120 @@
1/**
2 * @file llpluginprocessparent.cpp
3 * @brief LLPluginProcessParent handles the parent side of the external-process plugin API.
4 *
5 * @cond
6 * $LicenseInfo:firstyear=2008&license=viewergpl$
7 *
8 * Copyright (c) 2008-2010, Linden Research, Inc.
9 *
10 * Second Life Viewer Source Code
11 * The source code in this file ("Source Code") is provided by Linden Lab
12 * to you under the terms of the GNU General Public License, version 2.0
13 * ("GPL"), unless you have obtained a separate licensing agreement
14 * ("Other License"), formally executed by you and Linden Lab. Terms of
15 * the GPL can be found in doc/GPL-license.txt in this distribution, or
16 * online at http://secondlife.com/developers/opensource/gplv2
17 *
18 * There are special exceptions to the terms and conditions of the GPL as
19 * it is applied to this Source Code. View the full text of the exception
20 * in the file doc/FLOSS-exception.txt in this software distribution, or
21 * online at
22 * http://secondlife.com/developers/opensource/flossexception
23 *
24 * By copying, modifying or distributing this software, you acknowledge
25 * that you have read and understood your obligations described above,
26 * and agree to abide by those obligations.
27 *
28 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
29 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
30 * COMPLETENESS OR PERFORMANCE.
31 * $/LicenseInfo$
32 *
33 * @endcond
34 */
35
36#include "linden_common.h"
37
38#include "llpluginprocessparent.h"
39#include "llpluginmessagepipe.h"
40#include "llpluginmessageclasses.h"
41
42#include "llapr.h"
43
44//virtual
45LLPluginProcessParentOwner::~LLPluginProcessParentOwner()
46{
47
48}
49
50bool LLPluginProcessParent::sUseReadThread = false;
51apr_pollset_t *LLPluginProcessParent::sPollSet = NULL;
52bool LLPluginProcessParent::sPollsetNeedsRebuild = false;
53LLMutex *LLPluginProcessParent::sInstancesMutex;
54std::list<LLPluginProcessParent*> LLPluginProcessParent::sInstances;
55LLThread *LLPluginProcessParent::sReadThread = NULL;
56
57
58class LLPluginProcessParentPollThread: public LLThread
59{
60public:
61 LLPluginProcessParentPollThread() :
62 LLThread("LLPluginProcessParentPollThread", gAPRPoolp)
63 {
64 }
65protected:
66 // Inherited from LLThread
67 /*virtual*/ void run(void)
68 {
69 while(!isQuitting() && LLPluginProcessParent::getUseReadThread())
70 {
71 LLPluginProcessParent::poll(0.1f);
72 checkPause();
73 }
74
75 // Final poll to clean up the pollset, etc.
76 LLPluginProcessParent::poll(0.0f);
77 }
78
79 // Inherited from LLThread
80 /*virtual*/ bool runCondition(void)
81 {
82 return(LLPluginProcessParent::canPollThreadRun());
83 }
84
85};
86
87LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
88 mIncomingQueueMutex(gAPRPoolp)
89{
90 if(!sInstancesMutex)
91 {
92 sInstancesMutex = new LLMutex(gAPRPoolp);
93 }
94
95 mOwner = owner;
96 mBoundPort = 0;
97 mState = STATE_UNINITIALIZED;
98 mSleepTime = 0.0;
99 mCPUUsage = 0.0;
100 mDisableTimeout = false;
101 mDebug = false;
102 mBlocked = false;
103 mPolledInput = false;
104 mPollFD.client_data = NULL;
105
106 mPluginLaunchTimeout = 60.0f;
107 mPluginLockupTimeout = 15.0f;
108
109 // Don't start the timer here -- start it when we actually launch the plugin process.
110 mHeartbeat.stop();
111
112
113 // Don't add to the global list until fully constructed.
114 {
115 LLMutexLock lock(sInstancesMutex);
116 sInstances.push_back(this);
117 }
118}
119
120LLPluginProcessParent::~LLPluginProcessParent()
121{
122 LL_DEBUGS("Plugin") << "destructor" << LL_ENDL;
123
124 // Remove from the global list before beginning destruction.
125 {
126 // Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll()
127 LLMutexLock lock(sInstancesMutex);
128 {
129 LLMutexLock lock2(&mIncomingQueueMutex);
130 sInstances.remove(this);
131 }
132 }
133
134 // Destroy any remaining shared memory regions
135 sharedMemoryRegionsType::iterator iter;
136 while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end())
137 {
138 // destroy the shared memory region
139 iter->second->destroy();
140
141 // and remove it from our map
142 mSharedMemoryRegions.erase(iter);
143 }
144
145 mProcess.kill();
146 killSockets();
147}
148
149void LLPluginProcessParent::killSockets(void)
150{
151 {
152 LLMutexLock lock(&mIncomingQueueMutex);
153 killMessagePipe();
154 }
155
156 mListenSocket.reset();
157 mSocket.reset();
158}
159
160void LLPluginProcessParent::errorState(void)
161{
162 if(mState < STATE_RUNNING)
163 setState(STATE_LAUNCH_FAILURE);
164 else
165 setState(STATE_ERROR);
166}
167
168void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug)
169{
170 mProcess.setExecutable(launcher_filename);
171 mPluginFile = plugin_filename;
172 mCPUUsage = 0.0f;
173 mDebug = debug;
174 setState(STATE_INITIALIZED);
175}
176
177bool LLPluginProcessParent::accept()
178{
179 bool result = false;
180
181 apr_status_t status = APR_EGENERAL;
182 apr_socket_t *new_socket = NULL;
183
184 status = apr_socket_accept(
185 &new_socket,
186 mListenSocket->getSocket(),
187 gAPRPoolp);
188
189
190 if(status == APR_SUCCESS)
191 {
192// llinfos << "SUCCESS" << llendl;
193 // Success. Create a message pipe on the new socket
194
195 // we MUST create a new pool for the LLSocket, since it will take ownership of it and delete it in its destructor!
196 apr_pool_t* new_pool = NULL;
197 status = apr_pool_create(&new_pool, gAPRPoolp);
198
199 mSocket = LLSocket::create(new_socket, new_pool);
200 new LLPluginMessagePipe(this, mSocket);
201
202 result = true;
203 }
204 else if(APR_STATUS_IS_EAGAIN(status))
205 {
206// llinfos << "EAGAIN" << llendl;
207
208 // No incoming connections. This is not an error.
209 status = APR_SUCCESS;
210 }
211 else
212 {
213// llinfos << "Error:" << llendl;
214 ll_apr_warn_status(status);
215
216 // Some other error.
217 errorState();
218 }
219
220 return result;
221}
222
223void LLPluginProcessParent::idle(void)
224{
225 bool idle_again;
226
227 do
228 {
229 // process queued messages
230 mIncomingQueueMutex.lock();
231 while(!mIncomingQueue.empty())
232 {
233 LLPluginMessage message = mIncomingQueue.front();
234 mIncomingQueue.pop();
235 mIncomingQueueMutex.unlock();
236
237 receiveMessage(message);
238
239 mIncomingQueueMutex.lock();
240 }
241
242 mIncomingQueueMutex.unlock();
243
244 // Give time to network processing
245 if(mMessagePipe)
246 {
247 // Drain any queued outgoing messages
248 mMessagePipe->pumpOutput();
249
250 // Only do input processing here if this instance isn't in a pollset.
251 if(!mPolledInput)
252 {
253 mMessagePipe->pumpInput();
254 }
255 }
256
257 if(mState <= STATE_RUNNING)
258 {
259 if(APR_STATUS_IS_EOF(mSocketError))
260 {
261 // Plugin socket was closed. This covers both normal plugin termination and plugin crashes.
262 errorState();
263 }
264 else if(mSocketError != APR_SUCCESS)
265 {
266 // The socket is in an error state -- the plugin is gone.
267 LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL;
268 errorState();
269 }
270 }
271
272 // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState().
273 // USE THIS CAREFULLY, since it can starve other code. Specifically make sure there's no way to get into a closed cycle and never return.
274 // When in doubt, don't do it.
275 idle_again = false;
276 switch(mState)
277 {
278 case STATE_UNINITIALIZED:
279 break;
280
281 case STATE_INITIALIZED:
282 {
283
284 apr_status_t status = APR_SUCCESS;
285 apr_sockaddr_t* addr = NULL;
286 mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
287 mBoundPort = 0;
288
289 // This code is based on parts of LLSocket::create() in lliosocket.cpp.
290
291 status = apr_sockaddr_info_get(
292 &addr,
293 "127.0.0.1",
294 APR_INET,
295 0, // port 0 = ephemeral ("find me a port")
296 0,
297 gAPRPoolp);
298
299 if(ll_apr_warn_status(status))
300 {
301 killSockets();
302 errorState();
303 break;
304 }
305
306 // This allows us to reuse the address on quick down/up. This is unlikely to create problems.
307 ll_apr_warn_status(apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_REUSEADDR, 1));
308
309 status = apr_socket_bind(mListenSocket->getSocket(), addr);
310 if(ll_apr_warn_status(status))
311 {
312 killSockets();
313 errorState();
314 break;
315 }
316
317 // Get the actual port the socket was bound to
318 {
319 apr_sockaddr_t* bound_addr = NULL;
320 if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket())))
321 {
322 killSockets();
323 errorState();
324 break;
325 }
326 mBoundPort = bound_addr->port;
327
328 if(mBoundPort == 0)
329 {
330 LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL;
331
332 killSockets();
333 errorState();
334 break;
335 }
336 }
337
338 LL_DEBUGS("Plugin") << "Bound tcp socket to port: " << addr->port << LL_ENDL;
339
340 // Make the listen socket non-blocking
341 status = apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_NONBLOCK, 1);
342 if(ll_apr_warn_status(status))
343 {
344 killSockets();
345 errorState();
346 break;
347 }
348
349 apr_socket_timeout_set(mListenSocket->getSocket(), 0);
350 if(ll_apr_warn_status(status))
351 {
352 killSockets();
353 errorState();
354 break;
355 }
356
357 // If it's a stream based socket, we need to tell the OS
358 // to keep a queue of incoming connections for ACCEPT.
359 status = apr_socket_listen(
360 mListenSocket->getSocket(),
361 10); // FIXME: Magic number for queue size
362
363 if(ll_apr_warn_status(status))
364 {
365 killSockets();
366 errorState();
367 break;
368 }
369
370 // If we got here, we're listening.
371 setState(STATE_LISTENING);
372 }
373 break;
374
375 case STATE_LISTENING:
376 {
377 // Launch the plugin process.
378
379 // Only argument to the launcher is the port number we're listening on
380 std::stringstream stream;
381 stream << mBoundPort;
382 mProcess.addArgument(stream.str());
383 if(mProcess.launch() != 0)
384 {
385 errorState();
386 }
387 else
388 {
389 if(mDebug)
390 {
391 #if LL_DARWIN
392 // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue.
393
394 // The command we're constructing would look like this on the command line:
395 // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell'
396
397 std::stringstream cmd;
398
399 mDebugger.setExecutable("/usr/bin/osascript");
400 mDebugger.addArgument("-e");
401 mDebugger.addArgument("tell application \"Terminal\"");
402 mDebugger.addArgument("-e");
403 cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\"";
404 mDebugger.addArgument(cmd.str());
405 mDebugger.addArgument("-e");
406 mDebugger.addArgument("do script \"continue\" in win");
407 mDebugger.addArgument("-e");
408 mDebugger.addArgument("end tell");
409 mDebugger.launch();
410
411 #elif LL_LINUX
412
413 std::stringstream cmd;
414
415 mDebugger.setExecutable("/usr/bin/gnome-terminal");
416 mDebugger.addArgument("--geometry=165x24-0+0");
417 mDebugger.addArgument("-e");
418 cmd << "/usr/bin/gdb -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID();
419 mDebugger.addArgument(cmd.str());
420 mDebugger.launch();
421
422 #endif
423 }
424
425 // This will allow us to time out if the process never starts.
426 mHeartbeat.start();
427 mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout);
428 setState(STATE_LAUNCHED);
429 }
430 }
431 break;
432
433 case STATE_LAUNCHED:
434 // waiting for the plugin to connect
435 if(pluginLockedUpOrQuit())
436 {
437 errorState();
438 }
439 else
440 {
441 // Check for the incoming connection.
442 if(accept())
443 {
444 // Stop listening on the server port
445 mListenSocket.reset();
446 setState(STATE_CONNECTED);
447 }
448 }
449 break;
450
451 case STATE_CONNECTED:
452 // waiting for hello message from the plugin
453
454 if(pluginLockedUpOrQuit())
455 {
456 errorState();
457 }
458 break;
459
460 case STATE_HELLO:
461 LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL;
462
463 // Send the message to load the plugin
464 {
465 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin");
466 message.setValue("file", mPluginFile);
467 sendMessage(message);
468 }
469
470 setState(STATE_LOADING);
471 break;
472
473 case STATE_LOADING:
474 // The load_plugin_response message will kick us from here into STATE_RUNNING
475 if(pluginLockedUpOrQuit())
476 {
477 errorState();
478 }
479 break;
480
481 case STATE_RUNNING:
482 if(pluginLockedUpOrQuit())
483 {
484 errorState();
485 }
486 break;
487
488 case STATE_EXITING:
489 if(!mProcess.isRunning())
490 {
491 setState(STATE_CLEANUP);
492 }
493 else if(pluginLockedUp())
494 {
495 LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL;
496 errorState();
497 }
498 break;
499
500 case STATE_LAUNCH_FAILURE:
501 if(mOwner != NULL)
502 {
503 mOwner->pluginLaunchFailed();
504 }
505 setState(STATE_CLEANUP);
506 break;
507
508 case STATE_ERROR:
509 if(mOwner != NULL)
510 {
511 mOwner->pluginDied();
512 }
513 setState(STATE_CLEANUP);
514 break;
515
516 case STATE_CLEANUP:
517 mProcess.kill();
518 killSockets();
519 setState(STATE_DONE);
520 break;
521
522
523 case STATE_DONE:
524 // just sit here.
525 break;
526
527 }
528
529 } while (idle_again);
530}
531
532bool LLPluginProcessParent::isLoading(void)
533{
534 bool result = false;
535
536 if(mState <= STATE_LOADING)
537 result = true;
538
539 return result;
540}
541
542bool LLPluginProcessParent::isRunning(void)
543{
544 bool result = false;
545
546 if(mState == STATE_RUNNING)
547 result = true;
548
549 return result;
550}
551
552bool LLPluginProcessParent::isDone(void)
553{
554 bool result = false;
555
556 if(mState == STATE_DONE)
557 result = true;
558
559 return result;
560}
561
562void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send)
563{
564 if(force_send || (sleep_time != mSleepTime))
565 {
566 // Cache the time locally
567 mSleepTime = sleep_time;
568
569 if(canSendMessage())
570 {
571 // and send to the plugin.
572 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "sleep_time");
573 message.setValueReal("time", mSleepTime);
574 sendMessage(message);
575 }
576 else
577 {
578 // Too early to send -- the load_plugin_response message will trigger us to send mSleepTime later.
579 }
580 }
581}
582
583void LLPluginProcessParent::sendMessage(const LLPluginMessage &message)
584{
585 if(message.hasValue("blocking_response"))
586 {
587 mBlocked = false;
588
589 // reset the heartbeat timer, since there will have been no heartbeats while the plugin was blocked.
590 mHeartbeat.setTimerExpirySec(mPluginLockupTimeout);
591 }
592
593 std::string buffer = message.generate();
594 LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL;
595 writeMessageRaw(buffer);
596
597 // Try to send message immediately.
598 if(mMessagePipe)
599 {
600 mMessagePipe->pumpOutput();
601 }
602}
603
604//virtual
605void LLPluginProcessParent::setMessagePipe(LLPluginMessagePipe *message_pipe)
606{
607 bool update_pollset = false;
608
609 if(mMessagePipe)
610 {
611 // Unsetting an existing message pipe -- remove from the pollset
612 mPollFD.client_data = NULL;
613
614 // pollset needs an update
615 update_pollset = true;
616 }
617 if(message_pipe != NULL)
618 {
619 // Set up the apr_pollfd_t
620 mPollFD.p = gAPRPoolp;
621 mPollFD.desc_type = APR_POLL_SOCKET;
622 mPollFD.reqevents = APR_POLLIN|APR_POLLERR|APR_POLLHUP;
623 mPollFD.rtnevents = 0;
624 mPollFD.desc.s = mSocket->getSocket();
625 mPollFD.client_data = (void*)this;
626
627 // pollset needs an update
628 update_pollset = true;
629 }
630
631 mMessagePipe = message_pipe;
632
633 if(update_pollset)
634 {
635 dirtyPollSet();
636 }
637}
638
639//static
640void LLPluginProcessParent::dirtyPollSet()
641{
642 sPollsetNeedsRebuild = true;
643
644 if(sReadThread)
645 {
646 LL_DEBUGS("PluginPoll") << "unpausing read thread " << LL_ENDL;
647 sReadThread->unpause();
648 }
649}
650
651void LLPluginProcessParent::updatePollset()
652{
653 if(!sInstancesMutex)
654 {
655 // No instances have been created yet. There's no work to do.
656 return;
657 }
658
659 LLMutexLock lock(sInstancesMutex);
660
661 if(sPollSet)
662 {
663 LL_DEBUGS("PluginPoll") << "destroying pollset " << sPollSet << LL_ENDL;
664 // delete the existing pollset.
665 apr_pollset_destroy(sPollSet);
666 sPollSet = NULL;
667 }
668
669 std::list<LLPluginProcessParent*>::iterator iter;
670 int count = 0;
671
672 // Count the number of instances that want to be in the pollset
673 for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
674 {
675 (*iter)->mPolledInput = false;
676 if((*iter)->mPollFD.client_data)
677 {
678 // This instance has a socket that needs to be polled.
679 ++count;
680 }
681 }
682
683 if(sUseReadThread && sReadThread && !sReadThread->isQuitting())
684 {
685 if(!sPollSet && (count > 0))
686 {
687#ifdef APR_POLLSET_NOCOPY
688 // The pollset doesn't exist yet. Create it now.
689 apr_status_t status = apr_pollset_create(&sPollSet, count, gAPRPoolp, APR_POLLSET_NOCOPY);
690 if(status != APR_SUCCESS)
691 {
692#endif // APR_POLLSET_NOCOPY
693 LL_WARNS("PluginPoll") << "Couldn't create pollset. Falling back to non-pollset mode." << LL_ENDL;
694 sPollSet = NULL;
695#ifdef APR_POLLSET_NOCOPY
696 }
697 else
698 {
699 LL_DEBUGS("PluginPoll") << "created pollset " << sPollSet << LL_ENDL;
700
701 // Pollset was created, add all instances to it.
702 for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
703 {
704 if((*iter)->mPollFD.client_data)
705 {
706 status = apr_pollset_add(sPollSet, &((*iter)->mPollFD));
707 if(status == APR_SUCCESS)
708 {
709 (*iter)->mPolledInput = true;
710 }
711 else
712 {
713 LL_WARNS("PluginPoll") << "apr_pollset_add failed with status " << status << LL_ENDL;
714 }
715 }
716 }
717 }
718#endif // APR_POLLSET_NOCOPY
719 }
720 }
721}
722
723void LLPluginProcessParent::setUseReadThread(bool use_read_thread)
724{
725 if(sUseReadThread != use_read_thread)
726 {
727 sUseReadThread = use_read_thread;
728
729 if(sUseReadThread)
730 {
731 if(!sReadThread)
732 {
733 // start up the read thread
734 LL_INFOS("PluginPoll") << "creating read thread " << LL_ENDL;
735
736 // make sure the pollset gets rebuilt.
737 sPollsetNeedsRebuild = true;
738
739 sReadThread = new LLPluginProcessParentPollThread;
740 sReadThread->start();
741 }
742 }
743 else
744 {
745 if(sReadThread)
746 {
747 // shut down the read thread
748 LL_INFOS("PluginPoll") << "destroying read thread " << LL_ENDL;
749 delete sReadThread;
750 sReadThread = NULL;
751 }
752 }
753
754 }
755}
756
757void LLPluginProcessParent::poll(F64 timeout)
758{
759 if(sPollsetNeedsRebuild || !sUseReadThread)
760 {
761 sPollsetNeedsRebuild = false;
762 updatePollset();
763 }
764
765 if(sPollSet)
766 {
767 apr_status_t status;
768 apr_int32_t count;
769 const apr_pollfd_t *descriptors;
770 status = apr_pollset_poll(sPollSet, (apr_interval_time_t)(timeout * 1000000), &count, &descriptors);
771 if(status == APR_SUCCESS)
772 {
773 // One or more of the descriptors signalled. Call them.
774 for(int i = 0; i < count; i++)
775 {
776 LLPluginProcessParent *self = (LLPluginProcessParent *)(descriptors[i].client_data);
777 // NOTE: the descriptor returned here is actually a COPY of the original (even though we create the pollset with APR_POLLSET_NOCOPY).
778 // This means that even if the parent has set its mPollFD.client_data to NULL, the old pointer may still there in this descriptor.
779 // It's even possible that the old pointer no longer points to a valid LLPluginProcessParent.
780 // This means that we can't safely dereference the 'self' pointer here without some extra steps...
781 if(self)
782 {
783 // Make sure this pointer is still in the instances list
784 bool valid = false;
785 {
786 LLMutexLock lock(sInstancesMutex);
787 for(std::list<LLPluginProcessParent*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
788 {
789 if(*iter == self)
790 {
791 // Lock the instance's mutex before unlocking the global mutex.
792 // This avoids a possible race condition where the instance gets deleted between this check and the servicePoll() call.
793 self->mIncomingQueueMutex.lock();
794 valid = true;
795 break;
796 }
797 }
798 }
799
800 if(valid)
801 {
802 // The instance is still valid.
803 // Pull incoming messages off the socket
804 self->servicePoll();
805 self->mIncomingQueueMutex.unlock();
806 }
807 else
808 {
809 LL_DEBUGS("PluginPoll") << "detected deleted instance " << self << LL_ENDL;
810 }
811
812 }
813 }
814 }
815 else if(APR_STATUS_IS_TIMEUP(status))
816 {
817 // timed out with no incoming data. Just return.
818 }
819 else if(status == EBADF)
820 {
821 // This happens when one of the file descriptors in the pollset is destroyed, which happens whenever a plugin's socket is closed.
822 // The pollset has been or will be recreated, so just return.
823 LL_DEBUGS("PluginPoll") << "apr_pollset_poll returned EBADF" << LL_ENDL;
824 }
825 else if(status != APR_SUCCESS)
826 {
827 LL_WARNS("PluginPoll") << "apr_pollset_poll failed with status " << status << LL_ENDL;
828 }
829 }
830}
831
832void LLPluginProcessParent::servicePoll()
833{
834 bool result = true;
835
836 // poll signalled on this object's socket. Try to process incoming messages.
837 if(mMessagePipe)
838 {
839 result = mMessagePipe->pumpInput(0.0f);
840 }
841
842 if(!result)
843 {
844 // If we got a read error on input, remove this pipe from the pollset
845 apr_pollset_remove(sPollSet, &mPollFD);
846
847 // and tell the code not to re-add it
848 mPollFD.client_data = NULL;
849 }
850}
851
852void LLPluginProcessParent::receiveMessageRaw(const std::string &message)
853{
854 LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL;
855
856 LLPluginMessage parsed;
857 if(parsed.parse(message) != -1)
858 {
859 if(parsed.hasValue("blocking_request"))
860 {
861 mBlocked = true;
862 }
863
864 if(mPolledInput)
865 {
866 // This is being called on the polling thread -- only do minimal processing/queueing.
867 receiveMessageEarly(parsed);
868 }
869 else
870 {
871 // This is not being called on the polling thread -- do full message processing at this time.
872 receiveMessage(parsed);
873 }
874 }
875}
876
877void LLPluginProcessParent::receiveMessageEarly(const LLPluginMessage &message)
878{
879 // NOTE: this function will be called from the polling thread. It will be called with mIncomingQueueMutex _already locked_.
880
881 bool handled = false;
882
883 std::string message_class = message.getClass();
884 if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
885 {
886 // no internal messages need to be handled early.
887 }
888 else
889 {
890 // Call out to the owner and see if they to reply
891 // TODO: Should this only happen when blocked?
892 if(mOwner != NULL)
893 {
894 handled = mOwner->receivePluginMessageEarly(message);
895 }
896 }
897
898 if(!handled)
899 {
900 // any message that wasn't handled early needs to be queued.
901 mIncomingQueue.push(message);
902 }
903}
904
905void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message)
906{
907 std::string message_class = message.getClass();
908 if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
909 {
910 // internal messages should be handled here
911 std::string message_name = message.getName();
912 if(message_name == "hello")
913 {
914 if(mState == STATE_CONNECTED)
915 {
916 // Plugin host has launched. Tell it which plugin to load.
917 setState(STATE_HELLO);
918 }
919 else
920 {
921 LL_WARNS("Plugin") << "received hello message in wrong state -- bailing out" << LL_ENDL;
922 errorState();
923 }
924
925 }
926 else if(message_name == "load_plugin_response")
927 {
928 if(mState == STATE_LOADING)
929 {
930 // Plugin has been loaded.
931
932 mPluginVersionString = message.getValue("plugin_version");
933 LL_INFOS("Plugin") << "plugin version string: " << mPluginVersionString << LL_ENDL;
934
935 // Check which message classes/versions the plugin supports.
936 // TODO: check against current versions
937 // TODO: kill plugin on major mismatches?
938 mMessageClassVersions = message.getValueLLSD("versions");
939 LLSD::map_iterator iter;
940 for(iter = mMessageClassVersions.beginMap(); iter != mMessageClassVersions.endMap(); iter++)
941 {
942 LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL;
943 }
944
945 // Send initial sleep time
946 setSleepTime(mSleepTime, true);
947
948 setState(STATE_RUNNING);
949 }
950 else
951 {
952 LL_WARNS("Plugin") << "received load_plugin_response message in wrong state -- bailing out" << LL_ENDL;
953 errorState();
954 }
955 }
956 else if(message_name == "heartbeat")
957 {
958 // this resets our timer.
959 mHeartbeat.setTimerExpirySec(mPluginLockupTimeout);
960
961 mCPUUsage = message.getValueReal("cpu_usage");
962
963 LL_DEBUGS("Plugin") << "cpu usage reported as " << mCPUUsage << LL_ENDL;
964
965 }
966 else if(message_name == "shm_add_response")
967 {
968 // Nothing to do here.
969 }
970 else if(message_name == "shm_remove_response")
971 {
972 std::string name = message.getValue("name");
973 sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
974
975 if(iter != mSharedMemoryRegions.end())
976 {
977 // destroy the shared memory region
978 iter->second->destroy();
979
980 // and remove it from our map
981 mSharedMemoryRegions.erase(iter);
982 }
983 }
984 else
985 {
986 LL_WARNS("Plugin") << "Unknown internal message from child: " << message_name << LL_ENDL;
987 }
988 }
989 else
990 {
991 if(mOwner != NULL)
992 {
993 mOwner->receivePluginMessage(message);
994 }
995 }
996}
997
998std::string LLPluginProcessParent::addSharedMemory(size_t size)
999{
1000 std::string name;
1001
1002 LLPluginSharedMemory *region = new LLPluginSharedMemory;
1003
1004 // This is a new region
1005 if(region->create(size))
1006 {
1007 name = region->getName();
1008
1009 mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region));
1010
1011 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add");
1012 message.setValue("name", name);
1013 message.setValueS32("size", (S32)size);
1014 sendMessage(message);
1015 }
1016 else
1017 {
1018 LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL;
1019
1020 // Don't leak
1021 delete region;
1022 }
1023
1024 return name;
1025}
1026
1027void LLPluginProcessParent::removeSharedMemory(const std::string &name)
1028{
1029 sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
1030
1031 if(iter != mSharedMemoryRegions.end())
1032 {
1033 // This segment exists. Send the message to the child to unmap it. The response will cause the parent to unmap our end.
1034 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove");
1035 message.setValue("name", name);
1036 sendMessage(message);
1037 }
1038 else
1039 {
1040 LL_WARNS("Plugin") << "Request to remove an unknown shared memory segment." << LL_ENDL;
1041 }
1042}
1043size_t LLPluginProcessParent::getSharedMemorySize(const std::string &name)
1044{
1045 size_t result = 0;
1046
1047 sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
1048 if(iter != mSharedMemoryRegions.end())
1049 {
1050 result = iter->second->getSize();
1051 }
1052
1053 return result;
1054}
1055void *LLPluginProcessParent::getSharedMemoryAddress(const std::string &name)
1056{
1057 void *result = NULL;
1058
1059 sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
1060 if(iter != mSharedMemoryRegions.end())
1061 {
1062 result = iter->second->getMappedAddress();
1063 }
1064
1065 return result;
1066}
1067
1068std::string LLPluginProcessParent::getMessageClassVersion(const std::string &message_class)
1069{
1070 std::string result;
1071
1072 if(mMessageClassVersions.has(message_class))
1073 {
1074 result = mMessageClassVersions[message_class].asString();
1075 }
1076
1077 return result;
1078}
1079
1080std::string LLPluginProcessParent::getPluginVersion(void)
1081{
1082 return mPluginVersionString;
1083}
1084
1085void LLPluginProcessParent::setState(EState state)
1086{
1087 LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
1088 mState = state;
1089};
1090
1091bool LLPluginProcessParent::pluginLockedUpOrQuit()
1092{
1093 bool result = false;
1094
1095 if(!mProcess.isRunning())
1096 {
1097 LL_WARNS("Plugin") << "child exited" << LL_ENDL;
1098 result = true;
1099 }
1100 else if(pluginLockedUp())
1101 {
1102 LL_WARNS("Plugin") << "timeout" << LL_ENDL;
1103 result = true;
1104 }
1105
1106 return result;
1107}
1108
1109bool LLPluginProcessParent::pluginLockedUp()
1110{
1111 if(mDisableTimeout || mDebug || mBlocked)
1112 {
1113 // Never time out a plugin process in these cases.
1114 return false;
1115 }
1116
1117 // If the timer is running and has expired, the plugin has locked up.
1118 return (mHeartbeat.getStarted() && mHeartbeat.hasExpired());
1119}
1120