fixed: pass string by reference
[vuplus_xbmc] / xbmc / interfaces / python / PythonInvoker.cpp
1 /*
2  *      Copyright (C) 2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
22   #include "config.h"
23 #endif
24
25 // python.h should always be included first before any other includes
26 #include <Python.h>
27 #include <osdefs.h>
28
29 #include "system.h"
30 #include "PythonInvoker.h"
31 #include "Application.h"
32 #include "ApplicationMessenger.h"
33 #include "addons/AddonManager.h"
34 #include "dialogs/GUIDialogKaiToast.h"
35 #include "filesystem/File.h"
36 #include "filesystem/SpecialProtocol.h"
37 #include "guilib/GraphicContext.h"
38 #include "guilib/GUIWindowManager.h"
39 #include "interfaces/legacy/Addon.h"
40 #include "interfaces/python/LanguageHook.h"
41 #include "interfaces/python/PyContext.h"
42 #include "interfaces/python/pythreadstate.h"
43 #include "interfaces/python/swig.h"
44 #include "interfaces/python/XBPython.h"
45 #include "threads/SingleLock.h"
46 #if defined(TARGET_WINDOWS)
47 #include "utils/CharsetConverter.h"
48 #endif // defined(TARGET_WINDOWS)
49 #include "utils/log.h"
50 #include "utils/StringUtils.h"
51 #include "utils/URIUtils.h"
52
53 #ifdef TARGET_WINDOWS
54 extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
55 #else
56 #define fopen_utf8 fopen
57 #endif
58
59 #define GC_SCRIPT \
60   "import gc\n" \
61   "gc.collect(2)\n"
62
63 #define PY_PATH_SEP DELIM
64
65 // Time before ill-behaved scripts are terminated
66 #define PYTHON_SCRIPT_TIMEOUT 5000 // ms
67
68 using namespace std;
69 using namespace XFILE;
70
71 extern "C"
72 {
73   int xbp_chdir(const char *dirname);
74   char* dll_getenv(const char* szKey);
75 }
76
77 #define PythonModulesSize sizeof(PythonModules) / sizeof(PythonModule)
78
79 CCriticalSection CPythonInvoker::s_critical;
80
81 static const CStdString getListOfAddonClassesAsString(XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook>& languageHook)
82 {
83   CStdString message;
84   XBMCAddon::AddonClass::Synchronize l(*(languageHook.get()));
85   std::set<XBMCAddon::AddonClass*>& acs = languageHook->GetRegisteredAddonClasses();
86   bool firstTime = true;
87   for (std::set<XBMCAddon::AddonClass*>::iterator iter = acs.begin(); iter != acs.end(); ++iter)
88   {
89     if (!firstTime)
90       message += ",";
91     else
92       firstTime = false;
93     message += (*iter)->GetClassname().c_str();
94   }
95
96   return message;
97 }
98
99 CPythonInvoker::CPythonInvoker(ILanguageInvocationHandler *invocationHandler)
100   : ILanguageInvoker(invocationHandler),
101     m_source(NULL), m_argc(0), m_argv(NULL),
102     m_threadState(NULL), m_stop(false)
103 { }
104
105 CPythonInvoker::~CPythonInvoker()
106 {
107   // nothing to do for the default invoker used for registration with the
108   // CScriptInvocationManager
109   if (GetId() < 0)
110     return;
111
112   if (GetState() < InvokerStateDone)
113     CLog::Log(LOGDEBUG, "CPythonInvoker(%d): waiting for python thread \"%s\" to stop",
114       GetId(), (m_source != NULL ? m_source : "unknown script"));
115   Stop(true);
116   g_pythonParser.PulseGlobalEvent();
117
118   delete [] m_source;
119   if (m_argv != NULL)
120   {
121     for (unsigned int i = 0; i < m_argc; i++)
122       delete [] m_argv[i];
123     delete [] m_argv;
124   }
125   g_pythonParser.FinalizeScript();
126 }
127
128 bool CPythonInvoker::Execute(const std::string &script, const std::vector<std::string> &arguments /* = std::vector<std::string>() */)
129 {
130   if (script.empty())
131     return false;
132
133   if (!CFile::Exists(script))
134   {
135     CLog::Log(LOGERROR, "CPythonInvoker(%d): python script \"%s\" does not exist", GetId(), CSpecialProtocol::TranslatePath(script).c_str());
136     return false;
137   }
138
139   if (!g_pythonParser.InitializeEngine())
140     return false;
141
142   return ILanguageInvoker::Execute(script, arguments);
143 }
144
145 bool CPythonInvoker::execute(const std::string &script, const std::vector<std::string> &arguments)
146 {
147   // copy the code/script into a local string buffer
148 #ifdef TARGET_WINDOWS
149   CStdString strsrc = script;
150   g_charsetConverter.utf8ToSystem(strsrc);
151   m_source = new char[strsrc.length() + 1];
152   strcpy(m_source, strsrc);
153 #else
154   m_source = new char[script.length() + 1];
155   strcpy(m_source, script.c_str());
156 #endif
157
158   // copy the arguments into a local buffer
159   m_argc = arguments.size();
160   m_argv = new char*[m_argc];
161   for (unsigned int i = 0; i < m_argc; i++)
162   {
163     m_argv[i] = new char[arguments.at(i).length() + 1];
164     strcpy(m_argv[i], arguments.at(i).c_str());
165   }
166
167   CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): start processing", GetId(), m_source);
168   int m_Py_file_input = Py_file_input;
169
170   // get the global lock
171   PyEval_AcquireLock();
172   PyThreadState* state = Py_NewInterpreter();
173   if (state == NULL)
174   {
175     PyEval_ReleaseLock();
176     CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread state!", GetId(), m_source);
177     return false;
178   }
179   // swap in my thread state
180   PyThreadState_Swap(state);
181
182   XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> languageHook(new XBMCAddon::Python::LanguageHook(state->interp));
183   languageHook->RegisterMe();
184
185   onInitialization();
186   setState(InvokerStateInitialized);
187
188   CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is %s", GetId(), m_source, m_source);
189
190   // get path from script file name and add python path's
191   // this is used for python so it will search modules from script path first
192   CStdString scriptDir = URIUtils::GetDirectory(CSpecialProtocol::TranslatePath(m_source));
193   URIUtils::RemoveSlashAtEnd(scriptDir);
194   addPath(scriptDir);
195
196   // add on any addon modules the user has installed
197   ADDON::VECADDONS addons;
198   ADDON::CAddonMgr::Get().GetAddons(ADDON::ADDON_SCRIPT_MODULE, addons);
199   for (unsigned int i = 0; i < addons.size(); ++i)
200     addPath(CSpecialProtocol::TranslatePath(addons[i]->LibPath()));
201
202   // we want to use sys.path so it includes site-packages
203   // if this fails, default to using Py_GetPath
204   PyObject *sysMod(PyImport_ImportModule((char*)"sys")); // must call Py_DECREF when finished
205   PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete
206   PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete
207
208   if (pathObj != NULL && PyList_Check(pathObj))
209   {
210     for (int i = 0; i < PyList_Size(pathObj); i++)
211     {
212       PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete
213       if (e != NULL && PyString_Check(e))
214         addPath(PyString_AsString(e)); // returns internal data, don't delete or modify
215     }
216   }
217   else
218     addPath(Py_GetPath());
219
220   Py_DECREF(sysMod); // release ref to sysMod
221
222   // set current directory and python's path.
223   if (m_argv != NULL)
224     PySys_SetArgv(m_argc, m_argv);
225
226   CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_source, m_pythonPath.c_str());
227   PySys_SetPath((char *)m_pythonPath.c_str());
228
229   CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s", GetId(), m_source, scriptDir.c_str());
230   PyObject* module = PyImport_AddModule((char*)"__main__");
231   PyObject* moduleDict = PyModule_GetDict(module);
232
233   // when we are done initing we store thread state so we can be aborted
234   PyThreadState_Swap(NULL);
235   PyEval_ReleaseLock();
236
237   // we need to check if we was asked to abort before we had inited
238   bool stopping = false;
239   { CSingleLock lock(m_critical);
240     m_threadState = state;
241     stopping = m_stop;
242   }
243
244   PyEval_AcquireLock();
245   PyThreadState_Swap(state);
246
247   bool failed = false;
248   if (!stopping)
249   {
250     try
251     {
252       // run script from file
253       // We need to have python open the file because on Windows the DLL that python
254       //  is linked against may not be the DLL that xbmc is linked against so
255       //  passing a FILE* to python from an fopen has the potential to crash.
256       PyObject* file = PyFile_FromString((char *) CSpecialProtocol::TranslatePath(m_source).c_str(), (char*)"r");
257       FILE *fp = PyFile_AsFile(file);
258
259       if (fp != NULL)
260       {
261         PyObject *f = PyString_FromString(CSpecialProtocol::TranslatePath(m_source).c_str());
262         PyDict_SetItemString(moduleDict, "__file__", f);
263
264         onPythonModuleInitialization(moduleDict);
265
266         Py_DECREF(f);
267         setState(InvokerStateRunning);
268         XBMCAddon::Python::PyContext pycontext; // this is a guard class that marks this callstack as being in a python context
269         PyRun_FileExFlags(fp, CSpecialProtocol::TranslatePath(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict,1,NULL);
270       }
271       else
272         CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): %s not found!", GetId(), m_source, m_source);
273     }
274     catch (const XbmcCommons::Exception& e)
275     {
276       setState(InvokerStateFailed);
277       e.LogThrowMessage();
278       failed = true;
279     }
280     catch (...)
281     {
282       setState(InvokerStateFailed);
283       CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failure in script", GetId(), m_source);
284       failed = true;
285     }
286   }
287
288   bool systemExitThrown = false;
289   if (!failed && !PyErr_Occurred())
290   {
291     CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run", GetId(), m_source);
292     setState(InvokerStateDone);
293     onSuccess();
294   }
295   else if (PyErr_ExceptionMatches(PyExc_SystemExit))
296   {
297     systemExitThrown = true;
298     CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script aborted", GetId(), m_source);
299     setState(InvokerStateFailed);
300     onAbort();
301   }
302   else
303   {
304     setState(InvokerStateFailed);
305
306     // if it failed with an exception we already logged the details
307     if (!failed)
308     {
309       PythonBindings::PythonToCppException e;
310       e.LogThrowMessage();
311     }
312
313     onError();
314   }
315
316   // no need to do anything else because the script has already stopped
317   if (failed)
318     return true;
319
320   PyObject *m = PyImport_AddModule((char*)"xbmc");
321   if (m == NULL || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
322     CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_source);
323
324   // make sure all sub threads have finished
325   for (PyThreadState* s = state->interp->tstate_head, *old = NULL; s;)
326   {
327     if (s == state)
328     {
329       s = s->next;
330       continue;
331     }
332     if (old != s)
333     {
334       CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %"PRIu64, GetId(), m_source, (uint64_t)s->thread_id);
335       old = s;
336     }
337
338     CPyThreadState pyState;
339     Sleep(100);
340     pyState.Restore();
341
342     s = state->interp->tstate_head;
343   }
344
345   // pending calls must be cleared out
346   XBMCAddon::RetardedAsynchCallbackHandler::clearPendingCalls(state);
347
348   PyThreadState_Swap(NULL);
349   PyEval_ReleaseLock();
350
351   // set stopped event - this allows ::stop to run and kill remaining threads
352   // this event has to be fired without holding m_critical
353   // also the GIL (PyEval_AcquireLock) must not be held
354   // if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!)
355   m_stoppedEvent.Set();
356
357   { CSingleLock lock(m_critical);
358     m_threadState = NULL;
359   }
360
361   PyEval_AcquireLock();
362   PyThreadState_Swap(state);
363
364   onDeinitialization();
365
366   // run the gc before finishing
367   //
368   // if the script exited by throwing a SystemExit excepton then going back
369   // into the interpreter causes this python bug to get hit:
370   //    http://bugs.python.org/issue10582
371   // and that causes major failures. So we are not going to go back in
372   // to run the GC if that's the case.
373   if (!m_stop && languageHook->HasRegisteredAddonClasses() && !systemExitThrown &&
374       PyRun_SimpleString(GC_SCRIPT) == -1)
375     CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to run the gc to clean up after running prior to shutting down the Interpreter", GetId(), m_source);
376
377   Py_EndInterpreter(state);
378
379   // If we still have objects left around, produce an error message detailing what's been left behind
380   if (languageHook->HasRegisteredAddonClasses())
381     CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): the python script \"%s\" has left several "
382       "classes in memory that we couldn't clean up. The classes include: %s",
383       GetId(), m_source, m_source, getListOfAddonClassesAsString(languageHook).c_str());
384
385   // unregister the language hook
386   languageHook->UnregisterMe();
387
388   PyEval_ReleaseLock();
389
390   return true;
391 }
392
393 bool CPythonInvoker::stop(bool abort)
394 {
395   CSingleLock lock(m_critical);
396   m_stop = true;
397
398   if (!IsRunning())
399     return false;
400
401   setState(InvokerStateStopping);
402
403   if (m_threadState != NULL)
404   {
405     PyEval_AcquireLock();
406     PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState);
407
408     //tell xbmc.Monitor to call onAbortRequested()
409     if (m_addon != NULL)
410       g_pythonParser.OnAbortRequested(m_addon->ID());
411
412     PyObject *m;
413     m = PyImport_AddModule((char*)"xbmc");
414     if (m == NULL || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
415       CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_source);
416
417     PyThreadState_Swap(old);
418     old = NULL;
419     PyEval_ReleaseLock();
420
421     XbmcThreads::EndTime timeout(PYTHON_SCRIPT_TIMEOUT);
422     while (!m_stoppedEvent.WaitMSec(15))
423     {
424       if (timeout.IsTimePast())
425       {
426         CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): script didn't stop in %d seconds - let's kill it", GetId(), m_source, PYTHON_SCRIPT_TIMEOUT / 1000);
427         break;
428       }
429
430       // We can't empty-spin in the main thread and expect scripts to be able to
431       // dismantle themselves. Python dialogs aren't normal XBMC dialogs, they rely
432       // on TMSG_GUI_PYTHON_DIALOG messages, so pump the message loop.
433       if (g_application.IsCurrentThread())
434       {
435         CSingleExit ex(g_graphicsContext);
436         CApplicationMessenger::Get().ProcessMessages();
437       }
438     }
439
440     // Useful for add-on performance metrics
441     if (!timeout.IsTimePast())
442       CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): script termination took %dms", GetId(), m_source, PYTHON_SCRIPT_TIMEOUT - timeout.MillisLeft());
443
444     // everything which didn't exit by now gets killed
445     {
446       // grabbing the PyLock while holding the m_critical is asking for a deadlock
447       CSingleExit ex2(m_critical);
448       PyEval_AcquireLock();
449     }
450
451     // Since we released the m_critical it's possible that the state is cleaned up
452     // so we need to recheck for m_threadState == NULL
453     if (m_threadState != NULL)
454     {
455       old = PyThreadState_Swap((PyThreadState*)m_threadState);
456       for (PyThreadState* state = ((PyThreadState*)m_threadState)->interp->tstate_head; state; state = state->next)
457       {
458         // Raise a SystemExit exception in python threads
459         Py_XDECREF(state->async_exc);
460         state->async_exc = PyExc_SystemExit;
461         Py_XINCREF(state->async_exc);
462       }
463
464       // If a dialog entered its doModal(), we need to wake it to see the exception
465       g_pythonParser.PulseGlobalEvent();
466     }
467
468     if (old != NULL)
469       PyThreadState_Swap(old);
470
471     lock.Leave();
472     PyEval_ReleaseLock();
473   }
474
475   return true;
476 }
477
478 void CPythonInvoker::onExecutionFailed()
479 {
480   PyThreadState_Swap(NULL);
481   PyEval_ReleaseLock();
482
483   setState(InvokerStateFailed);
484   CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): abnormally terminating python thread", GetId(), m_source);
485
486   CSingleLock lock(m_critical);
487   m_threadState = NULL;
488
489   ILanguageInvoker::onExecutionFailed();
490 }
491
492 std::map<std::string, CPythonInvoker::PythonModuleInitialization> CPythonInvoker::getModules() const
493 {
494   static std::map<std::string, PythonModuleInitialization> modules;
495   return modules;
496 }
497
498 void CPythonInvoker::onInitialization()
499 {
500   TRACE;
501   {
502     GilSafeSingleLock lock(s_critical);
503     initializeModules(getModules());
504   }
505
506   // get a possible initialization script
507   const char* runscript = getInitializationScript();
508   if (runscript!= NULL && strlen(runscript) > 0)
509   {
510     // redirecting default output to debug console
511     if (PyRun_SimpleString(runscript) == -1)
512       CLog::Log(LOGFATAL, "CPythonInvoker(%d, %s): initialize error", GetId(), m_source);
513   }
514 }
515
516 void CPythonInvoker::onPythonModuleInitialization(void* moduleDict)
517 {
518   if (m_addon.get() == NULL || moduleDict == NULL)
519     return;
520
521   PyObject *moduleDictionary = (PyObject *)moduleDict;
522
523   PyObject *pyaddonid = PyString_FromString(m_addon->ID().c_str());
524   PyDict_SetItemString(moduleDictionary, "__xbmcaddonid__", pyaddonid);
525
526   CStdString version = ADDON::GetXbmcApiVersionDependency(m_addon);
527   PyObject *pyxbmcapiversion = PyString_FromString(version.c_str());
528   PyDict_SetItemString(moduleDictionary, "__xbmcapiversion__", pyxbmcapiversion);
529
530   CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): instantiating addon using automatically obtained id of \"%s\" dependent on version %s of the xbmc.python api",
531             GetId(), m_source, m_addon->ID().c_str(), version.c_str());
532 }
533
534 void CPythonInvoker::onDeinitialization()
535 {
536   TRACE;
537 }
538
539 void CPythonInvoker::onError()
540 {
541   CPyThreadState releaseGil;
542   CSingleLock gc(g_graphicsContext);
543
544   CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
545   if (pDlgToast != NULL)
546   {
547     CStdString desc;
548     CStdString script;
549     if (m_addon.get() != NULL)
550       script = m_addon->Name();
551     else
552     {
553       CStdString path;
554       URIUtils::Split(m_source, path, script);
555       if (script.Equals("default.py"))
556       {
557         CStdString path2;
558         URIUtils::RemoveSlashAtEnd(path);
559         URIUtils::Split(path, path2, script);
560       }
561     }
562
563     desc.Format(g_localizeStrings.Get(2100), script);
564     pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc);
565   }
566 }
567
568 const char* CPythonInvoker::getInitializationScript() const
569 {
570   return NULL;
571 }
572
573 void CPythonInvoker::initializeModules(const std::map<std::string, PythonModuleInitialization> &modules)
574 {
575   for (std::map<std::string, PythonModuleInitialization>::const_iterator module = modules.begin(); module != modules.end(); ++module)
576   {
577     if (!initializeModule(module->second))
578       CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): unable to initialize python module \"%s\"", GetId(), m_source, module->first.c_str());
579   }
580 }
581
582 bool CPythonInvoker::initializeModule(PythonModuleInitialization module)
583 {
584   if (module == NULL)
585     return false;
586
587   module();
588   return true;
589 }
590
591 void CPythonInvoker::addPath(const std::string& path)
592 {
593   if (path.empty())
594     return;
595
596   if (!m_pythonPath.empty())
597     m_pythonPath += PY_PATH_SEP;
598
599 #if defined(TARGET_WINDOWS)
600   CStdString tmp(path);
601   g_charsetConverter.utf8ToSystem(tmp);
602   m_pythonPath += tmp;
603 #else
604   m_pythonPath += path;
605 #endif // defined(TARGET_WINDOWS)
606 }