X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/a1664e81dd83e11828909d10b629ed04ea7d3467..29ccc62c5ad37c01717d98dadea70b85dc3bd4a4:/lib/base/console.cpp diff --git a/lib/base/console.cpp b/lib/base/console.cpp index b3fbbf73..c310bc9b 100644 --- a/lib/base/console.cpp +++ b/lib/base/console.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include int bidirpipe(int pfd[], char *cmd , char *argv[]) { @@ -16,7 +18,7 @@ int bidirpipe(int pfd[], char *cmd , char *argv[]) if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1) return(-1); - if ( ( pid = fork() ) == -1 ) + if ( ( pid = vfork() ) == -1 ) return(-1); else if (pid == 0) /* child process */ { @@ -49,161 +51,113 @@ int bidirpipe(int pfd[], char *cmd , char *argv[]) } eConsoleAppContainer::eConsoleAppContainer() -:pid(-1), killstate(0) +:pid(-1), killstate(0), in(0), out(0), err(0) { for (int i=0; i < 3; ++i) fd[i]=-1; } -int eConsoleAppContainer::execute( const std::string &cmd ) +static char brakets[][2] = { + { '\'','\'' }, + {'"','"'}, + {'`','`'}, + {'(',')'}, + {'{','}'}, + {'[',']'}, + {'<','>'} +}; + +static char *find_bracket(char ch) +{ + size_t idx=0; + while (idx < sizeof(brakets)/2) { + if (brakets[idx][0] == ch) + return &brakets[idx][0]; + ++idx; + } + return NULL; +} + +int eConsoleAppContainer::execute( const char *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:""); + + int cnt=0, slen=strlen(cmd); + char buf[slen+1]; + char *tmp=0, *argv[64], *path=buf, *cmds = buf; + memcpy(buf, cmd, slen+1); + +// printf("cmd = %s, len %d\n", cmd, slen); // kill spaces at beginning - unsigned int pos = str.find_first_not_of(' '); - if (pos != std::string::npos && pos) - str = str.substr(pos); + while(path[0] == ' ') { + ++path; + ++cmds; + --slen; + } // 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); + while(slen && path[slen-1] == ' ') { + path[slen-1] = 0; + --slen; + } - unsigned int slen=str.length(); if (!slen) return -2; - std::map brackets; - brackets.insert(std::pair('\'','\'')); - brackets.insert(std::pair('"','"')); - brackets.insert(std::pair('`','`')); - brackets.insert(std::pair('(',')')); - brackets.insert(std::pair('{','}')); - brackets.insert(std::pair('[',']')); - brackets.insert(std::pair('<','>')); - - 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::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"); - } + tmp = strchr(path, ' '); + if (!tmp) + return -3; -// 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 + *tmp = 0; + cmds = tmp+1; - if ( cnt > 2 ) // more then default args? - { - cnt=1; // do not overwrite path in argv[0] + memset(argv, 0, sizeof(argv)); + argv[cnt++] = path; - 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 (slen) { + char *argb=NULL; + char *it = find_bracket(*cmds); + while ( (tmp = strchr(cmds, ' ')) ) { + if (!argb) // not arg begin + argb = cmds; + if (it && *(tmp-1) == it[1]) { + it = 0; } - 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; + if (!it) { // end of arg + argv[cnt++] = argb; + argb=0; // reset arg begin + it = find_bracket(*(tmp+1)); + *tmp = 0; } - else - idx++; - } - if ( it != brackets.end() ) - { - cmds.erase(0,1); - cmds.erase(cmds.length()-1, 1); + cmds = tmp+1; } - // store the last arg - argv[cnt] = new char[ cmds.length() ]; - strcpy( argv[cnt], cmds.c_str() ); + argv[cnt++] = argb ? argb : cmds; } - else - cnt=1; - // get one read ,one write and the err pipe to the prog.. + // 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]); +// 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 ); + in = new eSocketNotifier(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup ); + out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write, false); + err = new eSocketNotifier(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority ); CONNECT(in->activated, eConsoleAppContainer::readyRead); CONNECT(out->activated, eConsoleAppContainer::readyWrite); CONNECT(err->activated, eConsoleAppContainer::readyErrRead); + return 0; } @@ -214,11 +168,15 @@ eConsoleAppContainer::~eConsoleAppContainer() void eConsoleAppContainer::kill() { - if ( killstate != -1 ) + if ( killstate != -1 && pid != -1 ) { eDebug("user kill(SIGKILL) console App"); killstate=-1; - ::kill(pid, SIGKILL); + /* + * Use a negative pid value, to signal the whole process group + * ('pid' might not even be running anymore at this point) + */ + ::kill(-pid, SIGKILL); closePipes(); } while( outbuf.size() ) // cleanup out buffer @@ -235,24 +193,40 @@ void eConsoleAppContainer::kill() void eConsoleAppContainer::sendCtrlC() { - if ( killstate != -1 ) + if ( killstate != -1 && pid != -1 ) { eDebug("user send SIGINT(Ctrl-C) to console App"); - ::kill(pid, SIGINT); + /* + * Use a negative pid value, to signal the whole process group + * ('pid' might not even be running anymore at this point) + */ + ::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; + if (in) + in->stop(); + if (out) + out->stop(); + if (err) + err->stop(); + if (fd[0] != -1) + { + ::close(fd[0]); + fd[0]=-1; + } + if (fd[1] != -1) + { + ::close(fd[1]); + fd[1]=-1; + } + if (fd[2] != -1) + { + ::close(fd[2]); + fd[2]=-1; + } eDebug("pipes closed"); while( outbuf.size() ) // cleanup out buffer { @@ -260,51 +234,62 @@ void eConsoleAppContainer::closePipes() outbuf.pop(); delete [] d.data; } + pid = -1; } void eConsoleAppContainer::readyRead(int what) { - if (what & POLLPRI|POLLIN) + bool hungup = what & eSocketNotifier::Hungup; + if (what & (eSocketNotifier::Priority|eSocketNotifier::Read)) { // eDebug("what = %d"); char buf[2048]; - int readed = read(fd[0], buf, 2047); -// eDebug("%d bytes read", readed); - if ( readed != -1 && readed ) + int rd; + while((rd = read(fd[0], buf, 2047)) > 0) { -/* for ( int i = 0; i < readed; i++ ) +/* for ( int i = 0; i < rd; i++ ) eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/ - buf[readed]=0; + buf[rd]=0; /*emit*/ dataAvail(buf); + if (!hungup) + break; } - else if (readed == -1) - eDebug("readerror %d", errno); } - if (what & eSocketNotifier::Hungup) + if (hungup) { eDebug("child has terminated"); closePipes(); - /*emit*/ appClosed(killstate); + int childstatus; + int retval = killstate; + /* + * We have to call 'wait' on the child process, in order to avoid zombies. + * Also, this gives us the chance to provide better exit status info to appClosed. + */ + if (::waitpid(pid, &childstatus, 0) > 0) + { + if (WIFEXITED(childstatus)) + { + retval = WEXITSTATUS(childstatus); + } + } + /*emit*/ appClosed(retval); } } void eConsoleAppContainer::readyErrRead(int what) { - if (what & POLLPRI|POLLIN) + if (what & (eSocketNotifier::Priority|eSocketNotifier::Read)) { // eDebug("what = %d"); char buf[2048]; - int readed = read(fd[2], buf, 2047); -// eDebug("%d bytes read", readed); - if ( readed != -1 && readed ) + int rd; + while((rd = read(fd[2], buf, 2047)) > 0) { -/* for ( int i = 0; i < readed; i++ ) +/* for ( int i = 0; i < rd; i++ ) eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/ - buf[readed]=0; + buf[rd]=0; /*emit*/ dataAvail(buf); } - else if (readed == -1) - eDebug("readerror %d", errno); } } @@ -318,7 +303,7 @@ void eConsoleAppContainer::write( const char *data, int len ) void eConsoleAppContainer::readyWrite(int what) { - if (what&POLLOUT && outbuf.size() ) + if (what&eSocketNotifier::Write && outbuf.size() ) { queue_data d = outbuf.front(); outbuf.pop();