add eConsoleAppContainer to execute shell scripts or applications from python without...
authorAndreas Monzner <andreas.monzner@multimedia-labs.de>
Tue, 21 Feb 2006 16:01:16 +0000 (16:01 +0000)
committerAndreas Monzner <andreas.monzner@multimedia-labs.de>
Tue, 21 Feb 2006 16:01:16 +0000 (16:01 +0000)
lib/base/Makefile.am
lib/base/console.cpp [new file with mode: 0644]
lib/base/console.h [new file with mode: 0644]
lib/python/connections.h
lib/python/enigma_python.i

index d667296af47e9bb8656bd27e9469add7bbac0818..877de00d6ca2adc8dfeeddac4e9feeba4df5893a 100644 (file)
@@ -7,4 +7,4 @@ libenigma_base_a_SOURCES = \
        buffer.cpp ebase.cpp eerror.cpp elock.cpp \
        init.cpp message.cpp thread.cpp \
        smartptr.cpp estring.cpp connection.cpp \
        buffer.cpp ebase.cpp eerror.cpp elock.cpp \
        init.cpp message.cpp thread.cpp \
        smartptr.cpp estring.cpp connection.cpp \
-       filepush.cpp encoding.cpp
+       filepush.cpp encoding.cpp console.cpp
diff --git a/lib/base/console.cpp b/lib/base/console.cpp
new file mode 100644 (file)
index 0000000..b3fbbf7
--- /dev/null
@@ -0,0 +1,339 @@
+#include <lib/base/console.h>
+#include <lib/base/eerror.h>
+#include <sys/vfs.h> // for statfs
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <poll.h>
+
+int bidirpipe(int pfd[], char *cmd , char *argv[])
+{
+       int pfdin[2];  /* from child to parent */
+       int pfdout[2]; /* from parent to child */
+       int pfderr[2]; /* stderr from child to parent */
+       int pid;       /* child's pid */
+
+       if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1)
+               return(-1);
+
+       if ( ( pid = fork() ) == -1 )
+               return(-1);
+       else if (pid == 0) /* child process */
+       {
+               setsid();
+               if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
+                       _exit(0);
+
+               if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 )
+                       _exit(0);
+
+               if (close(pfdout[0]) == -1 || close(pfdout[1]) == -1 ||
+                               close(pfdin[0]) == -1 || close(pfdin[1]) == -1 ||
+                               close(pfderr[0]) == -1 || close(pfderr[1]) == -1 )
+                       _exit(0);
+
+               for (unsigned int i=3; i < 90; ++i )
+                       close(i);
+
+               execvp(cmd,argv);
+               _exit(0);
+       }
+       if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1)
+                       return(-1);
+
+       pfd[0] = pfdin[0];
+       pfd[1] = pfdout[1];
+       pfd[2] = pfderr[0];
+
+       return(pid);
+}
+
+eConsoleAppContainer::eConsoleAppContainer()
+:pid(-1), killstate(0)
+{
+       for (int i=0; i < 3; ++i)
+               fd[i]=-1;
+}
+
+int eConsoleAppContainer::execute( const std::string &cmd )
+{
+       if (running())
+               return -1;
+       pid=-1;
+       killstate=0;
+//     eDebug("cmd = %s", cmd.c_str() );
+       int cnt=2; // path to app + terminated 0
+       std::string str(cmd.length()?cmd:"");
+
+       // kill spaces at beginning
+       unsigned int pos = str.find_first_not_of(' ');
+       if (pos != std::string::npos && pos)
+               str = str.substr(pos);
+
+       // kill spaces at the end
+       pos = str.find_last_not_of(' ');
+       if (pos != std::string::npos && (pos+1) < str.length())
+               str = str.erase(pos+1);
+
+       unsigned int slen=str.length();
+       if (!slen)
+               return -2;
+
+       std::map<char,char> brackets;
+       brackets.insert(std::pair<char,char>('\'','\''));
+       brackets.insert(std::pair<char,char>('"','"'));
+       brackets.insert(std::pair<char,char>('`','`'));
+       brackets.insert(std::pair<char,char>('(',')'));
+       brackets.insert(std::pair<char,char>('{','}'));
+       brackets.insert(std::pair<char,char>('[',']'));
+       brackets.insert(std::pair<char,char>('<','>'));
+
+       unsigned int idx=str.find(' ');
+       std::string path = str.substr(0, idx != std::string::npos ? idx : slen );
+//     eDebug("path = %s", path.c_str() );
+       unsigned int plen = path.length();
+
+       std::string cmds = slen > plen ? str.substr( plen+1 ) : "";
+       unsigned int clen = cmds.length();
+//     eDebug("cmds = %s", cmds.c_str() );
+
+       idx = 0;
+       std::map<char,char>::iterator it = brackets.find(cmds[idx]);
+       while ( (idx = cmds.find(' ',idx) ) != std::string::npos )  // count args
+       {
+               if (it != brackets.end())
+               {
+                       if (cmds[idx-1] == it->second)
+                               it = brackets.end();
+               }
+               if (it == brackets.end())
+               {
+                       cnt++;
+                       it = brackets.find(cmds[idx+1]);
+               }
+               idx++;
+       }
+
+//     eDebug("idx = %d, %d counted spaces", idx, cnt-2);
+
+       if ( clen )
+       {
+               cnt++;
+//             eDebug("increase cnt");
+       }
+
+//     eDebug("%d args", cnt-2);
+       char **argv = new char*[cnt];  // min two args... path and terminating 0
+       argv[0] = new char[ plen ];
+       strcpy( argv[0], path.c_str() );
+       argv[cnt-1] = 0;               // set terminating null
+
+       if ( cnt > 2 )  // more then default args?
+       {
+               cnt=1;  // do not overwrite path in argv[0]
+
+               it = brackets.find(cmds[0]);
+               idx=0;
+               while ( (idx = cmds.find(' ',idx)) != std::string::npos )  // parse all args..
+               {
+                       bool bracketClosed=false;
+                       if ( it != brackets.end() )
+                       {
+                               if (cmds[idx-1]==it->second)
+                               {
+                                       it = brackets.end();
+                                       bracketClosed=true;
+                               }
+                       }
+                       if ( it == brackets.end() )
+                       {
+                               std::string tmp = cmds.substr(0, idx);
+                               if (bracketClosed)
+                               {
+                                       tmp.erase(0,1);
+                                       tmp.erase(tmp.length()-1, 1);
+                                       bracketClosed=false;
+                               }
+                               argv[cnt] = new char[ tmp.length()+1 ];
+//                             eDebug("idx=%d, arg = %s", idx, tmp.c_str() );
+                               strcpy( argv[cnt++], tmp.c_str() );
+                               cmds.erase(0, idx+1);
+//                             eDebug("str = %s", cmds.c_str() );
+                               it = brackets.find(cmds[0]);
+                               idx=0;
+                       }
+                       else
+                               idx++;
+               }
+               if ( it != brackets.end() )
+               {
+                       cmds.erase(0,1);
+                       cmds.erase(cmds.length()-1, 1);
+               }
+               // store the last arg
+               argv[cnt] = new char[ cmds.length() ];
+               strcpy( argv[cnt], cmds.c_str() );
+       }
+       else
+               cnt=1;
+
+  // get one read ,one write and the err pipe to the prog..
+
+//     int tmp=0;
+//     while(argv[tmp])
+//             eDebug("%d is %s", tmp, argv[tmp++]);
+  
+       pid = bidirpipe(fd, argv[0], argv);
+
+       while ( cnt >= 0 )  // release heap memory
+       {
+//             eDebug("delete argv[%d]", cnt);
+               delete [] argv[cnt--];
+       }
+//     eDebug("delete argv");
+       delete [] argv;
+       
+       if ( pid == -1 )
+               return -3;
+
+       eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
+
+       in = new eSocketNotifier(eApp, fd[0], POLLIN|POLLPRI|POLLHUP );
+       out = new eSocketNotifier(eApp, fd[1], POLLOUT, false);  
+       err = new eSocketNotifier(eApp, fd[2], POLLIN|POLLPRI );
+       CONNECT(in->activated, eConsoleAppContainer::readyRead);
+       CONNECT(out->activated, eConsoleAppContainer::readyWrite);
+       CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
+       return 0;
+}
+
+eConsoleAppContainer::~eConsoleAppContainer()
+{
+       kill();
+}
+
+void eConsoleAppContainer::kill()
+{
+       if ( killstate != -1 )
+       {
+               eDebug("user kill(SIGKILL) console App");
+               killstate=-1;
+               ::kill(pid, SIGKILL);
+               closePipes();
+       }
+       while( outbuf.size() ) // cleanup out buffer
+       {
+               queue_data d = outbuf.front();
+               outbuf.pop();
+               delete [] d.data;
+       }
+       delete in;
+       delete out;
+       delete err;
+       in=out=err=0;
+}
+
+void eConsoleAppContainer::sendCtrlC()
+{
+       if ( killstate != -1 )
+       {
+               eDebug("user send SIGINT(Ctrl-C) to console App");
+               ::kill(pid, SIGINT);
+       }
+}
+
+void eConsoleAppContainer::closePipes()
+{
+       in->stop();
+       out->stop();
+       err->stop();
+       ::close(fd[0]);
+       fd[0]=-1;
+       ::close(fd[1]);
+       fd[1]=-1;
+       ::close(fd[2]);
+       fd[2]=-1;
+       eDebug("pipes closed");
+       while( outbuf.size() ) // cleanup out buffer
+       {
+               queue_data d = outbuf.front();
+               outbuf.pop();
+               delete [] d.data;
+       }
+}
+
+void eConsoleAppContainer::readyRead(int what)
+{
+       if (what & POLLPRI|POLLIN)
+       {
+//             eDebug("what = %d");
+               char buf[2048];
+               int readed = read(fd[0], buf, 2047);
+//             eDebug("%d bytes read", readed);
+               if ( readed != -1 && readed )
+               {
+/*                     for ( int i = 0; i < readed; i++ )
+                               eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
+                       buf[readed]=0;
+                       /*emit*/ dataAvail(buf);
+               }
+               else if (readed == -1)
+                       eDebug("readerror %d", errno);
+       }
+       if (what & eSocketNotifier::Hungup)
+       {
+               eDebug("child has terminated");
+               closePipes();
+               /*emit*/ appClosed(killstate);
+       }
+}
+
+void eConsoleAppContainer::readyErrRead(int what)
+{
+       if (what & POLLPRI|POLLIN)
+       {
+//             eDebug("what = %d");
+               char buf[2048];
+               int readed = read(fd[2], buf, 2047);
+//             eDebug("%d bytes read", readed);
+               if ( readed != -1 && readed )
+               {
+/*                     for ( int i = 0; i < readed; i++ )
+                               eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
+                       buf[readed]=0;
+                       /*emit*/ dataAvail(buf);
+               }
+               else if (readed == -1)
+                       eDebug("readerror %d", errno);
+       }
+}
+
+void eConsoleAppContainer::write( const char *data, int len )
+{
+       char *tmp = new char[len];
+       memcpy(tmp, data, len);
+       outbuf.push(queue_data(tmp,len));
+       out->start();
+}
+
+void eConsoleAppContainer::readyWrite(int what)
+{
+       if (what&POLLOUT && outbuf.size() )
+       {
+               queue_data d = outbuf.front();
+               outbuf.pop();
+               if ( ::write( fd[1], d.data, d.len ) != d.len )
+               {
+                       /* emit */ dataSent(-1);
+//                     eDebug("writeError");
+               }
+               else
+               {
+                       /* emit */ dataSent(0);
+//                     eDebug("write ok");
+               }
+               delete [] d.data;
+       }
+       if ( !outbuf.size() )
+               out->stop();
+}
diff --git a/lib/base/console.h b/lib/base/console.h
new file mode 100644 (file)
index 0000000..bfa0240
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef __LIB_BASE_CONSOLE_H__
+#define __LIB_BASE_CONSOLE_H__
+
+#include <string>
+#include <lib/base/ebase.h>
+#include <lib/python/connections.h>
+#include <queue>
+
+#ifndef SWIG
+struct queue_data
+{
+       queue_data( char *data, int len )
+               :data(data), len(len)
+       {
+       }
+       char *data;
+       int len;
+};
+#endif
+
+class eConsoleAppContainer: public Object
+{
+#ifndef SWIG
+       int fd[3];
+       int pid;
+       int killstate;
+       std::queue<struct queue_data> outbuf;
+       eSocketNotifier *in, *out, *err;
+       void readyRead(int what);
+       void readyErrRead(int what);
+       void readyWrite(int what);
+       void closePipes();
+#endif
+public:
+       eConsoleAppContainer();
+       int execute( const std::string &str );
+       ~eConsoleAppContainer();
+       int getPID() { return pid; }
+       void kill();
+       void sendCtrlC();
+       void write( const char *data, int len );
+       bool running() { return (fd[0]!=-1) && (fd[1]!=-1) && (fd[2]!=-1); }
+       PSignal1Str<void, const char*> dataAvail;
+       PSignal1<void,int> dataSent;
+       PSignal1<void,int> appClosed;
+};
+
+#endif // __LIB_BASE_CONSOLE_H__
index 3df7b565cc7643e8f98d2eabb0bc038d46c564af..ac5865e51f73f8a395a7e1d8dd1ce102bf73fb94 100644 (file)
@@ -40,11 +40,6 @@ public:
        PyObject *get() { Py_INCREF(m_list); return m_list; }
 };
 
        PyObject *get() { Py_INCREF(m_list); return m_list; }
 };
 
-inline PyObject *PyFrom(int v)
-{
-       return PyInt_FromLong(v);
-}
-
 template <class R>
 class PSignal0: public PSignal, public Signal0<R>
 {
 template <class R>
 class PSignal0: public PSignal, public Signal0<R>
 {
@@ -65,7 +60,21 @@ public:
        R operator()(V0 a0)
        {
                PyObject *pArgs = PyTuple_New(1);
        R operator()(V0 a0)
        {
                PyObject *pArgs = PyTuple_New(1);
-               PyTuple_SET_ITEM(pArgs, 0, PyFrom(a0));
+               PyTuple_SET_ITEM(pArgs, 0, PyInt_FromLong(a0));
+               callPython(pArgs);
+               Py_DECREF(pArgs);
+               return Signal1<R,V0>::operator()(a0);
+       }
+};
+
+template <class R, class V0>
+class PSignal1Str: public PSignal, public Signal1<R,V0>
+{
+public:
+       R operator()(V0 a0)
+       {
+               PyObject *pArgs = PyTuple_New(1);
+               PyTuple_SET_ITEM(pArgs, 0, PyString_FromString(a0));
                callPython(pArgs);
                Py_DECREF(pArgs);
                return Signal1<R,V0>::operator()(a0);
                callPython(pArgs);
                Py_DECREF(pArgs);
                return Signal1<R,V0>::operator()(a0);
@@ -79,8 +88,8 @@ public:
        R operator()(V0 a0, V1 a1)
        {
                PyObject *pArgs = PyTuple_New(2);
        R operator()(V0 a0, V1 a1)
        {
                PyObject *pArgs = PyTuple_New(2);
-               PyTuple_SET_ITEM(pArgs, 0, PyFrom(a0));
-               PyTuple_SET_ITEM(pArgs, 1, PyFrom(a1));
+               PyTuple_SET_ITEM(pArgs, 0, PyInt_FromLong(a0));
+               PyTuple_SET_ITEM(pArgs, 1, PyInt_FromLong(a1));
                callPython(pArgs);
                Py_DECREF(pArgs);
                return Signal2<R,V0,V1>::operator()(a0, a1);
                callPython(pArgs);
                Py_DECREF(pArgs);
                return Signal2<R,V0,V1>::operator()(a0, a1);
index 8261385d444359fbccabb65c385e5b846bd739f5..42315ce78663de05314dbd6f87786cae79664e59 100644 (file)
@@ -39,6 +39,7 @@ is usually caused by not marking PSignals as immutable.
 #include <lib/base/ebase.h>
 #include <lib/base/smartptr.h>
 #include <lib/base/eerror.h>
 #include <lib/base/ebase.h>
 #include <lib/base/smartptr.h>
 #include <lib/base/eerror.h>
+#include <lib/base/console.h>
 #include <lib/service/iservice.h>
 #include <lib/service/service.h>
 #include <lib/service/event.h>
 #include <lib/service/iservice.h>
 #include <lib/service/service.h>
 #include <lib/service/event.h>
@@ -138,6 +139,9 @@ typedef long time_t;
 
 
 // TODO: embed these...
 
 
 // TODO: embed these...
+%immutable eConsoleAppContainer::appClosed;
+%immutable eConsoleAppContainer::dataAvail;
+%immutable eConsoleAppContainer::dataSent;
 %immutable eButton::selected;
 %immutable eInput::changed;
 %immutable eComponentScan::statusChanged;
 %immutable eButton::selected;
 %immutable eInput::changed;
 %immutable eComponentScan::statusChanged;
@@ -145,6 +149,7 @@ typedef long time_t;
 %immutable pNavigation::m_event;
 %immutable eListbox::selectionChanged;
 
 %immutable pNavigation::m_event;
 %immutable eListbox::selectionChanged;
 
+%include <lib/base/console.h>
 %include <lib/gdi/font.h>
 %include <lib/gdi/gpixmap.h>
 %include <lib/gdi/epoint.h>
 %include <lib/gdi/font.h>
 %include <lib/gdi/gpixmap.h>
 %include <lib/gdi/epoint.h>
@@ -198,13 +203,13 @@ public:
        PyObject *get();
 };
 
        PyObject *get();
 };
 
-template<class R, class P0> class PSignal1
-{
-public:
-       PyObject *get();
-};
+%template(PSignal0V) PSignal0<void>;
 
 
-template<class R, class P0, class P1> class PSignal2
+%typemap(out) PSignal0V {
+       $1 = $input->get();
+}
+
+template<class R, class P0> class PSignal1
 {
 public:
        PyObject *get();
 {
 public:
        PyObject *get();
@@ -216,12 +221,23 @@ public:
        $1 = $input->get();
 }
 
        $1 = $input->get();
 }
 
-%template(PSignal0V) PSignal0<void>;
+template<class R, class P0> class PSignal1Str
+{
+public:
+       PyObject *get();
+};
 
 
-%typemap(out) PSignal0V {
+%template(PSignal1VS) PSignal1Str<void,const char*>;
+
+%typemap(out) PSignal1VS {
        $1 = $input->get();
 }
 
        $1 = $input->get();
 }
 
+template<class R, class P0, class P1> class PSignal2
+{
+public:
+       PyObject *get();
+};
 
 /**************  debug  **************/
 
 
 /**************  debug  **************/