fixed: missed something in r23359
[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   CStdString strAutoExecPy;
434
435   if (m_bStartup)
436   {
437     m_bStartup = false;
438
439     // autoexec.py - userdata
440     strAutoExecPy = "special://home/scripts/autoexec.py";
441
442     if (XFILE::CFile::Exists(strAutoExecPy))
443       evalFile(strAutoExecPy);
444     else
445       CLog::Log(LOGDEBUG, "%s - no user autoexec.py (%s) found, skipping", __FUNCTION__, CSpecialProtocol::TranslatePath(strAutoExecPy).c_str());
446
447     // autoexec.py - system
448     strAutoExecPy = "special://xbmc/scripts/autoexec.py";
449
450     if (XFILE::CFile::Exists(strAutoExecPy))
451       evalFile(strAutoExecPy);
452     else
453       CLog::Log(LOGDEBUG, "%s - no system autoexec.py (%s) found, skipping", __FUNCTION__, CSpecialProtocol::TranslatePath(strAutoExecPy).c_str());
454   }
455
456   if (m_bLogin)
457   {
458     m_bLogin = false;
459
460     // autoexec.py - profile
461     strAutoExecPy = "special://profile/scripts/autoexec.py";
462
463     if (XFILE::CFile::Exists(strAutoExecPy))
464       evalFile(strAutoExecPy);
465     else
466       CLog::Log(LOGDEBUG, "%s - no profile autoexec.py (%s) found, skipping", __FUNCTION__, CSpecialProtocol::TranslatePath(strAutoExecPy).c_str());
467   }
468
469   CSingleLock lock(m_critSection);
470
471   if (m_bInitialized)
472   {
473     PyList::iterator it = m_vecPyList.begin();
474     while (it != m_vecPyList.end())
475     {
476       //delete scripts which are done
477       if (it->bDone)
478       {
479         delete it->pyThread;
480         it = m_vecPyList.erase(it);
481         Finalize();
482       }
483       else ++it;
484     }
485   }
486 }
487
488 int XBPython::evalFile(const char *src) { return evalFile(src, 0, NULL); }
489 // execute script, returns -1 if script doesn't exist
490 int XBPython::evalFile(const char *src, const unsigned int argc, const char ** argv)
491 {
492   CSingleLock lock(m_critSection);
493   // return if file doesn't exist
494   if (!XFILE::CFile::Exists(src))
495   {
496     CLog::Log(LOGERROR, "Python script \"%s\" does not exist", CSpecialProtocol::TranslatePath(src).c_str());
497     return -1;
498   }
499
500   // check if locked
501   int profile = g_settings.m_iLastLoadedProfileIndex;
502   if (g_settings.m_vecProfiles[profile].programsLocked() &&
503       g_settings.m_vecProfiles[0].getLockMode() != LOCK_MODE_EVERYONE)
504     if (!g_passwordManager.IsMasterLockUnlocked(true))
505       return -1;
506
507   Initialize();
508
509   if (!m_bInitialized) return -1;
510
511   m_nextid++;
512   XBPyThread *pyThread = new XBPyThread(this, m_nextid);
513   if (argv != NULL)
514     pyThread->setArgv(argc, argv);
515   pyThread->evalFile(src);
516   PyElem inf;
517   inf.id        = m_nextid;
518   inf.bDone     = false;
519   inf.strFile   = src;
520   inf.pyThread  = pyThread;
521
522   m_vecPyList.push_back(inf);
523
524   return m_nextid;
525 }
526
527 void XBPython::setDone(int id)
528 {
529   CSingleLock lock(m_critSection);
530   PyList::iterator it = m_vecPyList.begin();
531   while (it != m_vecPyList.end())
532   {
533     if (it->id == id)
534     {
535       if (it->pyThread->isStopping())
536         CLog::Log(LOGINFO, "Python script interrupted by user");
537       else
538         CLog::Log(LOGINFO, "Python script stopped");
539       it->bDone = true;
540     }
541     ++it;
542   }
543 }
544
545 void XBPython::stopScript(int id)
546 {
547   CSingleLock lock(m_critSection);
548   PyList::iterator it = m_vecPyList.begin();
549   while (it != m_vecPyList.end())
550   {
551     if (it->id == id) {
552       CLog::Log(LOGINFO, "Stopping script with id: %i", id);
553       it->pyThread->stop();
554       return;
555     }
556     ++it;
557   }
558 }
559
560 PyThreadState *XBPython::getMainThreadState()
561 {
562   CSingleLock lock(m_critSection);
563   return m_mainThreadState;
564 }
565
566 int XBPython::ScriptsSize()
567 {
568   CSingleLock lock(m_critSection);
569   return m_vecPyList.size();
570 }
571
572 const char* XBPython::getFileName(int scriptId)
573 {
574   const char* cFileName = NULL;
575  
576   CSingleLock lock(m_critSection);
577   PyList::iterator it = m_vecPyList.begin();
578   while (it != m_vecPyList.end())
579   {
580     if (it->id == scriptId)
581       cFileName = it->strFile.c_str();
582     ++it;
583   }
584
585   return cFileName;
586 }
587
588 int XBPython::getScriptId(const char* strFile)
589 {
590   int iId = -1;
591   
592   CSingleLock lock(m_critSection);
593
594   PyList::iterator it = m_vecPyList.begin();
595   while (it != m_vecPyList.end())
596   {
597     if (!stricmp(it->strFile.c_str(), strFile))
598       iId = it->id;
599     ++it;
600   }
601   
602   return iId;
603 }
604
605 bool XBPython::isRunning(int scriptId)
606 {
607   bool bRunning = false;
608   CSingleLock lock(m_critSection); 
609
610   PyList::iterator it = m_vecPyList.begin();
611   while (it != m_vecPyList.end())
612   {
613     if (it->id == scriptId)
614       bRunning = true;
615     ++it;
616   }
617   
618   return bRunning;
619 }
620
621 bool XBPython::isStopping(int scriptId)
622 {
623   bool bStopping = false;
624   
625   CSingleLock lock(m_critSection);
626   PyList::iterator it = m_vecPyList.begin();
627   while (it != m_vecPyList.end())
628   {
629     if (it->id == scriptId)
630       bStopping = it->pyThread->isStopping();
631     ++it;
632   }
633   
634   return bStopping;
635 }
636
637 int XBPython::GetPythonScriptId(int scriptPosition)
638 {
639   CSingleLock lock(m_critSection);
640   return (int)m_vecPyList[scriptPosition].id;
641 }
642
643 void XBPython::PulseGlobalEvent()
644 {
645   SetEvent(m_globalEvent);
646 }
647
648 void XBPython::WaitForEvent(HANDLE hEvent, DWORD timeout)
649 {
650   // wait for either this event our our global event
651   HANDLE handles[2] = { hEvent, m_globalEvent };
652   WaitForMultipleObjects(2, handles, FALSE, timeout);
653   ResetEvent(m_globalEvent);
654 }
655
656 // execute script, returns -1 if script doesn't exist
657 int XBPython::evalString(const char *src, const unsigned int argc, const char ** argv)
658 {
659   CLog::Log(LOGDEBUG, "XBPython::evalString (python)");
660   CSingleLock lock(m_critSection);
661   
662   Initialize();
663
664   if (!m_bInitialized) 
665   {
666     CLog::Log(LOGERROR, "XBPython::evalString, python not initialized (python)");
667     return -1;
668   }
669
670   // Previous implementation would create a new thread for every script
671   m_nextid++;
672   XBPyThread *pyThread = new XBPyThread(this, m_nextid);
673   if (argv != NULL)
674     pyThread->setArgv(argc, argv);
675   pyThread->evalString(src);
676   
677   PyElem inf;
678   inf.id        = m_nextid;
679   inf.bDone     = false;
680   inf.strFile   = "<string>";
681   inf.pyThread  = pyThread;
682
683   m_vecPyList.push_back(inf);
684
685   return m_nextid;
686 }