/* * console.cpp * * Copyright (C) 2002 Felix Domke * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #include #include #include // for statfs #include #include #include 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 */ { 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); execv(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( const eString &cmd ) :pid(-1), killstate(0), outbuf(0) { // eDebug("cmd = %s", cmd.c_str() ); memset(fd, 0, sizeof(fd) ); int cnt=2; // path to app + terminated 0 eString str(cmd?cmd:""); while( str.length() && str[0] == ' ' ) // kill spaces at beginning str = str.mid(1); while( str.length() && str[str.length()-1] == ' ' ) // kill spaces at the end str = str.left( str.length() - 1 ); if (!str.length()) return; unsigned int idx=0; eString path = str.left( (idx = str.find(' ')) != eString::npos ? idx : str.length() ); // eDebug("path = %s", path.c_str() ); eString cmds = str.mid( path.length()+1 ); // eDebug("cmds = %s", cmds.c_str() ); idx = 0; while ( (idx = cmds.find(' ',idx) ) != eString::npos ) // count args { cnt++; idx++; } // eDebug("idx = %d, %d counted spaces", idx, cnt-2); if ( cmds.length() ) { 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[ path.length() ]; 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] while ( (idx = cmds.find(' ')) != eString::npos ) // parse all args.. { argv[cnt] = new char[ idx ]; // eDebug("idx=%d, arg = %s", idx, cmds.left(idx).c_str() ); strcpy( argv[cnt++], cmds.left( idx ).c_str() ); cmds = cmds.mid(idx+1); // eDebug("str = %s", cmds.c_str() ); } // store the last arg argv[cnt] = new char[ cmds.length() ]; strcpy( argv[cnt], cmds.c_str() ); } // get one read ,one write and the err pipe to the prog.. if ( (pid = bidirpipe(fd, argv[0], argv)) == -1 ) { while ( cnt-- > 0 ) delete [] argv[cnt]; delete [] argv; return; } while ( cnt-- > 0 ) // release heap memory delete [] argv[cnt]; delete [] argv; eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]); in = new eSocketNotifier(eApp, fd[0], 19 ); // 19 = POLLIN, POLLPRI, POLLHUP out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write); // POLLOUT err = new eSocketNotifier(eApp, fd[2], 19 ); // 19 = POLLIN, POLLPRI, POLLHUP CONNECT(in->activated, eConsoleAppContainer::readyRead); CONNECT(out->activated, eConsoleAppContainer::readyWrite); CONNECT(err->activated, eConsoleAppContainer::readyErrRead); signal(SIGCHLD, SIG_IGN); // no zombie when child killed } eConsoleAppContainer::~eConsoleAppContainer() { if ( running() ) { killstate=-1; kill(); } if ( outbuf ) delete [] outbuf; } void eConsoleAppContainer::kill() { killstate=-1; system( eString().sprintf("kill %d", pid).c_str() ); eDebug("user kill console App"); } void eConsoleAppContainer::closePipes() { in->stop(); out->stop(); err->stop(); ::close(fd[0]); fd[0]=0; ::close(fd[1]); fd[1]=0; ::close(fd[2]); fd[2]=0; eDebug("pipes closed"); } void eConsoleAppContainer::readyRead(int what) { if (what & POLLPRI|POLLIN) { eDebug("what = %d"); char buf[2048]; int readed = read(fd[0], buf, 2048); eDebug("%d bytes read", readed); if ( readed != -1 && readed ) /*emit*/ dataAvail( eString( 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, 2048); eDebug("%d bytes read", readed); if ( readed != -1 && readed ) /*emit*/ dataAvail( eString( buf ) ); else if (readed == -1) eDebug("readerror %d", errno); } } void eConsoleAppContainer::write( const eString & str ) { outbuf = new char[ str.length()]; strcpy( outbuf, str.c_str() ); } void eConsoleAppContainer::readyWrite(int what) { if (what == 4 && outbuf) { if ( ::write( fd[1], outbuf, strlen(outbuf) ) != (int) strlen(outbuf) ) { /* emit */ dataSent(-1); eDebug("writeError"); } else { /* emit */ dataSent(0); eDebug("write ok"); } delete outbuf; outbuf=0; } }