fixed: python lockup on xbmc start.
[xbmc:xbmc.git] / xbmc / lib / libPython / XBPython.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 // python.h should always be included first before any other includes
23 #if (defined HAVE_CONFIG_H) && (!defined WIN32)
24   #include "config.h"
25 #endif
26 #if (defined USE_EXTERNAL_PYTHON)
27   #if (defined HAVE_LIBPYTHON2_6)
28     #include <python2.6/Python.h>
29   #elif (defined HAVE_LIBPYTHON2_5)
30     #include <python2.5/Python.h>
31   #elif (defined HAVE_LIBPYTHON2_4)
32     #include <python2.4/Python.h>
33   #else
34     #error "Could not determine version of Python to use."
35   #endif
36 #else
37   #include "Python/Include/Python.h"
38 #endif
39 #include "cores/DllLoader/DllLoaderContainer.h"
40 #include "GUIPassword.h"
41
42 #include "XBPython.h"
43 #include "XBPythonDll.h"
44 #include "Settings.h"
45 #include "Profile.h"
46 #include "FileSystem/File.h"
47 #include "FileSystem/SpecialProtocol.h"
48 #include "utils/log.h"
49 #include "utils/SingleLock.h"
50
51 XBPython g_pythonParser;
52
53 #ifndef _LINUX
54 #define PYTHON_DLL "special://xbmc/system/python/python24.dll"
55 #else
56 #if defined(__APPLE__)
57 #if defined(__POWERPC__)
58 #define PYTHON_DLL "special://xbmc/system/python/python24-powerpc-osx.so"
59 #else
60 #define PYTHON_DLL "special://xbmc/system/python/python24-x86-osx.so"
61 #endif
62 #elif defined(__x86_64__)
63 #if (defined HAVE_LIBPYTHON2_6)
64 #define PYTHON_DLL "special://xbmc/system/python/python26-x86_64-linux.so"
65 #elif (defined HAVE_LIBPYTHON2_5)
66 #define PYTHON_DLL "special://xbmc/system/python/python25-x86_64-linux.so"
67 #else
68 #define PYTHON_DLL "special://xbmc/system/python/python24-x86_64-linux.so"
69 #endif
70 #elif defined(__powerpc__)
71 #if (defined HAVE_LIBPYTHON2_6)
72 #define PYTHON_DLL "special://xbmc/system/python/python26-powerpc-linux.so"
73 #elif (defined HAVE_LIBPYTHON2_5)
74 #define PYTHON_DLL "special://xbmc/system/python/python25-powerpc-linux.so"
75 #else
76 #define PYTHON_DLL "special://xbmc/system/python/python24-powerpc-linux.so"
77 #endif
78 #else /* !__x86_64__ && !__powerpc__ */
79 #if (defined HAVE_LIBPYTHON2_6)
80 #define PYTHON_DLL "special://xbmc/system/python/python26-i486-linux.so"
81 #elif (defined HAVE_LIBPYTHON2_5)
82 #define PYTHON_DLL "special://xbmc/system/python/python25-i486-linux.so"
83 #else
84 #define PYTHON_DLL "special://xbmc/system/python/python24-i486-linux.so"
85 #endif
86 #endif /* __x86_64__ */
87 #endif /* _LINUX */
88
89 extern "C" HMODULE __stdcall dllLoadLibraryA(LPCSTR file);
90 extern "C" BOOL __stdcall dllFreeLibrary(HINSTANCE hLibModule);
91
92 extern "C" {
93   void InitXBMCModule(void);
94   void InitXBMCTypes(void);
95   void DeinitXBMCModule(void);
96   void InitPluginModule(void);
97   void InitPluginTypes(void);
98   void DeinitPluginModule(void);
99   void InitGUIModule(void);
100   void InitGUITypes(void);
101   void DeinitGUIModule(void);
102 }
103
104 XBPython::XBPython()
105 {
106   m_bInitialized      = false;
107   m_bStartup          = false;
108   m_bLogin            = false;
109   m_nextid            = 0;
110   m_mainThreadState   = NULL;
111   m_hEvent            = CreateEvent(NULL, false, false, (char*)"pythonEvent");
112   m_globalEvent       = CreateEvent(NULL, false, false, (char*)"pythonGlobalEvent");
113   m_ThreadId          = CThread::GetCurrentThreadId();
114   m_iDllScriptCounter = 0;
115   m_vecPlayerCallbackList.clear();
116 }
117
118 XBPython::~XBPython()
119 {
120   CloseHandle(m_globalEvent);
121 }
122
123 bool XBPython::SendMessage(CGUIMessage& message)
124 {
125   return (evalFile(message.GetStringParam().c_str()) != -1);
126 }
127
128 // message all registered callbacks that xbmc stopped playing
129 void XBPython::OnPlayBackEnded()
130 {
131   if (m_bInitialized)
132   {
133     PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
134     while (it != m_vecPlayerCallbackList.end())
135     {
136       ((IPlayerCallback*)(*it))->OnPlayBackEnded();
137       it++;
138     }
139   }
140 }
141
142 // message all registered callbacks that we started playing
143 void XBPython::OnPlayBackStarted()
144 {
145   if (m_bInitialized)
146   {
147     PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
148     while (it != m_vecPlayerCallbackList.end())
149     {
150       ((IPlayerCallback*)(*it))->OnPlayBackStarted();
151       it++;
152     }
153   }
154 }
155
156 // message all registered callbacks that user stopped playing
157 void XBPython::OnPlayBackStopped()
158 {
159   if (m_bInitialized)
160   {
161     PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
162     while (it != m_vecPlayerCallbackList.end())
163     {
164       ((IPlayerCallback*)(*it))->OnPlayBackStopped();
165       it++;
166     }
167   }
168 }
169
170 void XBPython::RegisterPythonPlayerCallBack(IPlayerCallback* pCallback)
171 {
172   m_vecPlayerCallbackList.push_back(pCallback);
173 }
174
175 void XBPython::UnregisterPythonPlayerCallBack(IPlayerCallback* pCallback)
176 {
177   PlayerCallbackList::iterator it = m_vecPlayerCallbackList.begin();
178   while (it != m_vecPlayerCallbackList.end())
179   {
180     if (*it == pCallback)
181       it = m_vecPlayerCallbackList.erase(it);
182     else
183       it++;
184   }
185 }
186
187 /**
188 * Check for file and print an error if needed
189 */
190 bool XBPython::FileExist(const char* strFile)
191 {
192   if (!strFile)
193     return false;
194
195   if (!XFILE::CFile::Exists(strFile))
196   {
197     CLog::Log(LOGERROR, "Python: Cannot find '%s'", strFile);
198     return false;
199   }
200   return true;
201 }
202
203 void XBPython::RegisterExtensionLib(LibraryLoader *pLib)
204 {
205   if (!pLib) 
206     return;
207
208   CSingleLock lock(m_critSection);
209
210   CLog::Log(LOGDEBUG,"%s, adding %s (%p)", __FUNCTION__, pLib->GetName(), (void*)pLib);
211   m_extensions.push_back(pLib);
212 }
213
214 void XBPython::UnregisterExtensionLib(LibraryLoader *pLib)
215 {
216   if (!pLib) 
217     return;
218
219   CSingleLock lock(m_critSection);
220   CLog::Log(LOGDEBUG,"%s, removing %s (0x%p)", __FUNCTION__, pLib->GetName(), (void *)pLib);
221   PythonExtensionLibraries::iterator iter = m_extensions.begin();
222   while (iter != m_extensions.end())
223   {
224     if (*iter == pLib)
225     {
226       m_extensions.erase(iter);
227       break;
228     }
229     iter++;
230   }
231 }
232
233 void XBPython::UnloadExtensionLibs()
234 {
235   CLog::Log(LOGDEBUG,"%s, clearing python extension libraries", __FUNCTION__);
236   CSingleLock lock(m_critSection);
237   PythonExtensionLibraries::iterator iter = m_extensions.begin();
238   while (iter != m_extensions.end())
239   {
240       DllLoaderContainer::ReleaseModule(*iter);
241       iter++;
242   }
243   m_extensions.clear();
244 }
245
246 void XBPython::InitializeInterpreter()
247 {
248   InitXBMCModule(); // init xbmc modules
249   InitPluginModule(); // init plugin modules
250   InitGUIModule(); // init xbmcgui modules
251
252   // redirecting default output to debug console
253   if (PyRun_SimpleString(""
254         "import xbmc\n"
255         "class xbmcout:\n"
256         "       def write(self, data):\n"
257         "               xbmc.output(data)\n"
258         "       def close(self):\n"
259         "               xbmc.output('.')\n"
260         "       def flush(self):\n"
261         "               xbmc.output('.')\n"
262         "\n"
263         "import sys\n"
264         "sys.stdout = xbmcout()\n"
265         "sys.stderr = xbmcout()\n"
266         "print '-->Python Interpreter Initialized<--'\n"
267         "") == -1)
268   {
269     CLog::Log(LOGFATAL, "Python Initialize Error");
270   }
271 }
272
273 void XBPython::DeInitializeInterpreter()
274 {
275   DeinitXBMCModule(); 
276   DeinitPluginModule(); 
277   DeinitGUIModule(); 
278 }
279
280 /**
281 * Should be called before executing a script
282 */
283 void XBPython::Initialize()
284 {
285   CLog::Log(LOGINFO, "initializing python engine. ");
286   CSingleLock lock(m_critSection);
287   m_iDllScriptCounter++;
288   if (!m_bInitialized)
289   {
290     if (CThread::IsCurrentThread(m_ThreadId))
291     {
292       m_pDll = DllLoaderContainer::LoadModule(PYTHON_DLL, NULL, true);
293
294       if (!m_pDll || !python_load_dll(*m_pDll))
295       {
296         CLog::Log(LOGFATAL, "Python: error loading python24.dll");
297         Finalize();
298         return;
299       }
300
301       // first we check if all necessary files are installed
302 #ifndef _LINUX      
303       if (!FileExist("special://xbmc/system/python/python24.zlib") ||
304         !FileExist("special://xbmc/system/python/DLLs/_socket.pyd") ||
305         !FileExist("special://xbmc/system/python/DLLs/_ssl.pyd") ||
306         !FileExist("special://xbmc/system/python/DLLs/bz2.pyd") ||
307         !FileExist("special://xbmc/system/python/DLLs/pyexpat.pyd") ||
308         !FileExist("special://xbmc/system/python/DLLs/select.pyd") ||
309         !FileExist("special://xbmc/system/python/DLLs/unicodedata.pyd") ||
310         !FileExist("special://xbmc/system/python/DLLs/zlib.pyd"))
311       {
312         CLog::Log(LOGERROR, "Python: Missing files, unable to execute script");
313         Finalize();
314         return;
315       }
316 #endif        
317
318
319       // Info about interesting python envvars available 
320       // at http://docs.python.org/using/cmdline.html#environment-variables
321
322 #if (!defined USE_EXTERNAL_PYTHON)
323 #ifdef _LINUX
324       // Required for python to find optimized code (pyo) files
325       setenv("PYTHONOPTIMIZE", "1", 1);
326       setenv("PYTHONHOME", _P("special://xbmc/system/python").c_str(), 1);
327 #ifdef __APPLE__
328       // OSX uses contents from extracted zip, 3X to 4X times faster during Py_Initialize
329       setenv("PYTHONPATH", _P("special://xbmc/system/python/Lib").c_str(), 1);
330 #else
331       setenv("PYTHONPATH", _P("special://xbmc/system/python/python24.zip").c_str(), 1);
332 #endif /* __APPLE__ */
333       setenv("PYTHONCASEOK", "1", 1);
334       CLog::Log(LOGDEBUG, "Python wrapper library linked with internal Python library");
335 #endif /* _LINUX */
336 #else
337       /* PYTHONOPTIMIZE is set off intentionally when using external Python.
338          Reason for this is because we cannot be sure what version of Python
339          was used to compile the various Python object files (i.e. .pyo,
340          .pyc, etc.). */
341       setenv("PYTHONCASEOK", "1", 1); //This line should really be removed
342       CLog::Log(LOGDEBUG, "Python wrapper library linked with system Python library");
343 #endif /* USE_EXTERNAL_PYTHON */
344
345       Py_Initialize();
346       PyEval_InitThreads();
347
348       char* python_argv[1] = { (char*)"" } ;
349       PySys_SetArgv(1, python_argv);
350
351       InitXBMCTypes();
352       InitGUITypes();
353       InitPluginTypes();
354       
355       if (!(m_mainThreadState = PyThreadState_Get()))
356         CLog::Log(LOGERROR, "Python threadstate is NULL.");
357       PyEval_ReleaseLock();
358
359       m_bInitialized = true;
360       PulseEvent(m_hEvent);
361     }
362     else
363     {
364       // only the main thread should initialize python.
365       m_iDllScriptCounter--;
366
367       lock.Leave();
368       WaitForSingleObject(m_hEvent, INFINITE);
369       lock.Enter();
370     }
371   }
372 }
373
374 /**
375 * Should be called when a script is finished
376 */
377 void XBPython::Finalize()
378 {
379   CSingleLock lock(m_critSection);
380   // for linux - we never release the library. its loaded and stays in memory.
381   if (m_iDllScriptCounter)
382     m_iDllScriptCounter--;
383   else
384     CLog::Log(LOGERROR, "Python script counter attempted to become negative");
385   if (m_iDllScriptCounter == 0 && m_bInitialized)
386   {
387     CLog::Log(LOGINFO, "Python, unloading python24.dll because no scripts are running anymore");
388     
389     PyEval_AcquireLock();
390     PyThreadState_Swap(m_mainThreadState);
391     
392     Py_Finalize();
393     PyEval_ReleaseLock();
394
395     UnloadExtensionLibs();
396
397     // first free all dlls loaded by python, after that python24.dll (this is done by UnloadPythonDlls
398     DllLoaderContainer::UnloadPythonDlls();
399 #ifdef _LINUX
400     // we can't release it on windows, as this is done in UnloadPythonDlls() for win32 (see above).
401     // The implementation for linux and os x needs looking at - UnloadPythonDlls() currently only searches for "python24.dll"
402     DllLoaderContainer::ReleaseModule(m_pDll);
403 #endif
404     m_hModule         = NULL;
405     m_mainThreadState = NULL;
406     m_bInitialized    = false;
407   }
408 }
409
410 void XBPython::FreeResources()
411 {
412   CSingleLock lock(m_critSection);
413   if (m_bInitialized)
414   {
415     // cleanup threads that are still running
416     PyList::iterator it = m_vecPyList.begin();
417     while (it != m_vecPyList.end())
418     { 
419       lock.Leave(); //unlock here because the python thread might lock when it exits
420       delete it->pyThread;
421       lock.Enter();
422       it = m_vecPyList.erase(it);
423       Finalize();
424     }
425   }
426
427   if (m_hEvent)
428     CloseHandle(m_hEvent);
429 }
430
431 void XBPython::Process()
432 {
433   if (m_bStartup)
434   {
435     m_bStartup = false;
436     if (evalFile("special://home/scripts/autoexec.py") < 0)
437       evalFile("special://xbmc/scripts/autoexec.py");
438   }
439
440   if (m_bLogin)
441   {
442     m_bLogin = false;
443     evalFile("special://profile/scripts/autoexec.py");
444   }
445
446   CSingleLock lock(m_critSection);
447
448   if (m_bInitialized)
449   {
450     PyList::iterator it = m_vecPyList.begin();
451     while (it != m_vecPyList.end())
452     {
453       //delete scripts which are done
454       if (it->bDone)
455       {
456         delete it->pyThread;
457         it = m_vecPyList.erase(it);
458         Finalize();
459       }
460       else ++it;
461     }
462   }
463 }
464
465 int XBPython::evalFile(const char *src) { return evalFile(src, 0, NULL); }
466 // execute script, returns -1 if script doesn't exist
467 int XBPython::evalFile(const char *src, const unsigned int argc, const char ** argv)
468 {
469   CSingleLock lock(m_critSection);
470   // return if file doesn't exist
471   if (!XFILE::CFile::Exists(src))
472   {
473     CLog::Log(LOGERROR, "Python script \"%s\" does not exist", src);
474     return -1;
475   }
476
477   // check if locked
478   int profile = g_settings.m_iLastLoadedProfileIndex;
479   if (g_settings.m_vecProfiles[profile].programsLocked() &&
480       g_settings.m_vecProfiles[0].getLockMode() != LOCK_MODE_EVERYONE)
481     if (!g_passwordManager.IsMasterLockUnlocked(true))
482       return -1;
483
484   Initialize();
485
486   if (!m_bInitialized) return -1;
487
488   m_nextid++;
489   XBPyThread *pyThread = new XBPyThread(this, m_nextid);
490   if (argv != NULL)
491     pyThread->setArgv(argc, argv);
492   pyThread->evalFile(src);
493   PyElem inf;
494   inf.id        = m_nextid;
495   inf.bDone     = false;
496   inf.strFile   = src;
497   inf.pyThread  = pyThread;
498
499   m_vecPyList.push_back(inf);
500
501   return m_nextid;
502 }
503
504 void XBPython::setDone(int id)
505 {
506   CSingleLock lock(m_critSection);
507   PyList::iterator it = m_vecPyList.begin();
508   while (it != m_vecPyList.end())
509   {
510     if (it->id == id)
511     {
512       if (it->pyThread->isStopping())
513         CLog::Log(LOGINFO, "Python script interrupted by user");
514       else
515         CLog::Log(LOGINFO, "Python script stopped");
516       it->bDone = true;
517     }
518     ++it;
519   }
520 }
521
522 void XBPython::stopScript(int id)
523 {
524   CSingleLock lock(m_critSection);
525   PyList::iterator it = m_vecPyList.begin();
526   while (it != m_vecPyList.end())
527   {
528     if (it->id == id) {
529       CLog::Log(LOGINFO, "Stopping script with id: %i", id);
530       it->pyThread->stop();
531       return;
532     }
533     ++it;
534   }
535 }
536
537 PyThreadState *XBPython::getMainThreadState()
538 {
539   CSingleLock lock(m_critSection);
540   return m_mainThreadState;
541 }
542
543 int XBPython::ScriptsSize()
544 {
545   CSingleLock lock(m_critSection);
546   return m_vecPyList.size();
547 }
548
549 const char* XBPython::getFileName(int scriptId)
550 {
551   const char* cFileName = NULL;
552  
553   CSingleLock lock(m_critSection);
554   PyList::iterator it = m_vecPyList.begin();
555   while (it != m_vecPyList.end())
556   {
557     if (it->id == scriptId)
558       cFileName = it->strFile.c_str();
559     ++it;
560   }
561
562   return cFileName;
563 }
564
565 int XBPython::getScriptId(const char* strFile)
566 {
567   int iId = -1;
568   
569   CSingleLock lock(m_critSection);
570
571   PyList::iterator it = m_vecPyList.begin();
572   while (it != m_vecPyList.end())
573   {
574     if (!stricmp(it->strFile.c_str(), strFile))
575       iId = it->id;
576     ++it;
577   }
578   
579   return iId;
580 }
581
582 bool XBPython::isRunning(int scriptId)
583 {
584   bool bRunning = false;
585   CSingleLock lock(m_critSection); 
586
587   PyList::iterator it = m_vecPyList.begin();
588   while (it != m_vecPyList.end())
589   {
590     if (it->id == scriptId)
591       bRunning = true;
592     ++it;
593   }
594   
595   return bRunning;
596 }
597
598 bool XBPython::isStopping(int scriptId)
599 {
600   bool bStopping = false;
601   
602   CSingleLock lock(m_critSection);
603   PyList::iterator it = m_vecPyList.begin();
604   while (it != m_vecPyList.end())
605   {
606     if (it->id == scriptId)
607       bStopping = it->pyThread->isStopping();
608     ++it;
609   }
610   
611   return bStopping;
612 }
613
614 int XBPython::GetPythonScriptId(int scriptPosition)
615 {
616   CSingleLock lock(m_critSection);
617   return (int)m_vecPyList[scriptPosition].id;
618 }
619
620 void XBPython::PulseGlobalEvent()
621 {
622   SetEvent(m_globalEvent);
623 }
624
625 void XBPython::WaitForEvent(HANDLE hEvent, DWORD timeout)
626 {
627   // wait for either this event our our global event
628   HANDLE handles[2] = { hEvent, m_globalEvent };
629   WaitForMultipleObjects(2, handles, FALSE, timeout);
630   ResetEvent(m_globalEvent);
631 }
632
633 // execute script, returns -1 if script doesn't exist
634 int XBPython::evalString(const char *src, const unsigned int argc, const char ** argv)
635 {
636   CLog::Log(LOGDEBUG, "XBPython::evalString (python)");
637   CSingleLock lock(m_critSection);
638   
639   Initialize();
640
641   if (!m_bInitialized) 
642   {
643     CLog::Log(LOGERROR, "XBPython::evalString, python not initialized (python)");
644     return -1;
645   }
646
647   // Previous implementation would create a new thread for every script
648   m_nextid++;
649   XBPyThread *pyThread = new XBPyThread(this, m_nextid);
650   if (argv != NULL)
651     pyThread->setArgv(argc, argv);
652   pyThread->evalString(src);
653   
654   PyElem inf;
655   inf.id        = m_nextid;
656   inf.bDone     = false;
657   inf.strFile   = "<string>";
658   inf.pyThread  = pyThread;
659
660   m_vecPyList.push_back(inf);
661
662   return m_nextid;
663 }