diff options
| author | Andreas Monzner <andreas.monzner@multimedia-labs.de> | 2006-02-21 16:01:16 +0000 |
|---|---|---|
| committer | Andreas Monzner <andreas.monzner@multimedia-labs.de> | 2006-02-21 16:01:16 +0000 |
| commit | a1664e81dd83e11828909d10b629ed04ea7d3467 (patch) | |
| tree | ce8f13091dc7a16cd181315ea956f3f3726ab2d0 /lib/base/console.cpp | |
| parent | 8841c38f30a89f83ab9b89528fb90297591bd21c (diff) | |
| download | enigma2-a1664e81dd83e11828909d10b629ed04ea7d3467.tar.gz enigma2-a1664e81dd83e11828909d10b629ed04ea7d3467.zip | |
add eConsoleAppContainer to execute shell scripts or applications from python without blocking the mainloop
Diffstat (limited to 'lib/base/console.cpp')
| -rw-r--r-- | lib/base/console.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/lib/base/console.cpp b/lib/base/console.cpp new file mode 100644 index 00000000..b3fbbf73 --- /dev/null +++ b/lib/base/console.cpp @@ -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(); +} |
