summaryrefslogtreecommitdiffabout
path: root/microkde/oprocess.cpp
Side-by-side diff
Diffstat (limited to 'microkde/oprocess.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--microkde/oprocess.cpp951
1 files changed, 951 insertions, 0 deletions
diff --git a/microkde/oprocess.cpp b/microkde/oprocess.cpp
new file mode 100644
index 0000000..95e3e4b
--- a/dev/null
+++ b/microkde/oprocess.cpp
@@ -0,0 +1,951 @@
+/*
+                This file is part of the Opie Project
+             Copyright (C) 2002-2004 Holger Freyther <zecke@handhelds.org>
+ and The Opie Team <opie-devel@handhelds.org>
+ =. Based on KProcess (C) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at)
+ .=l.
+          .>+-=
+_;:,     .>    :=|. This program is free software; you can
+.> <`_,   >  .   <= redistribute it and/or modify it under
+:`=1 )Y*s>-.--   : the terms of the GNU Library General Public
+.="- .-=="i,     .._ License as published by the Free Software
+- .   .-<_>     .<> Foundation; either version 2 of the License,
+    ._= =}       : or (at your option) any later version.
+   .%`+i>       _;_.
+   .i_,=:_.      -<s. 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
+..}^=.=       =       ; Library General Public License for more
+++=   -.     .`     .: details.
+:     =  ...= . :.=-
+-.   .:....=;==+<; You should have received a copy of the GNU
+ -_. . .   )=.  = Library General Public License along with
+   --        :-=` this library; see the file COPYING.LIB.
+ If not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "oprocctrl.h"
+
+/* OPIE */
+#include <oprocess.h>
+
+/* QT */
+
+#include <qapplication.h>
+#include <qdir.h>
+#include <qmap.h>
+#include <qsocketnotifier.h>
+#include <qtextstream.h>
+
+/* STD */
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_INITGROUPS
+#include <grp.h>
+#endif
+
+using namespace Opie::Core::Internal;
+
+namespace Opie {
+namespace Core {
+namespace Internal {
+class OProcessPrivate
+{
+public:
+ OProcessPrivate() : useShell( false )
+ { }
+
+ bool useShell;
+ QMap<QString, QString> env;
+ QString wd;
+ QCString shell;
+};
+}
+
+OProcess::OProcess( QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ init ( );
+}
+
+OProcess::OProcess( const QString &arg0, QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ init ( );
+ *this << arg0;
+}
+
+OProcess::OProcess( const QStringList &args, QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ init ( );
+ *this << args;
+}
+
+void OProcess::init ( )
+{
+ run_mode = NotifyOnExit;
+ runs = false;
+ pid_ = 0;
+ status = 0;
+ keepPrivs = false;
+ innot = 0;
+ outnot = 0;
+ errnot = 0;
+ communication = NoCommunication;
+ input_data = 0;
+ input_sent = 0;
+ input_total = 0;
+ d = 0;
+
+ if ( 0 == OProcessController::theOProcessController )
+ {
+ ( void ) new OProcessController();
+ CHECK_PTR( OProcessController::theOProcessController );
+ }
+
+ OProcessController::theOProcessController->addOProcess( this );
+ out[ 0 ] = out[ 1 ] = -1;
+ in[ 0 ] = in[ 1 ] = -1;
+ err[ 0 ] = err[ 1 ] = -1;
+}
+
+void OProcess::setEnvironment( const QString &name, const QString &value )
+{
+ if ( !d )
+ d = new OProcessPrivate;
+ d->env.insert( name, value );
+}
+
+void OProcess::setWorkingDirectory( const QString &dir )
+{
+ if ( !d )
+ d = new OProcessPrivate;
+ d->wd = dir;
+}
+
+void OProcess::setupEnvironment()
+{
+ if ( d )
+ {
+ QMap<QString, QString>::Iterator it;
+ for ( it = d->env.begin(); it != d->env.end(); ++it )
+ setenv( QFile::encodeName( it.key() ).data(),
+ QFile::encodeName( it.data() ).data(), 1 );
+ if ( !d->wd.isEmpty() )
+ chdir( QFile::encodeName( d->wd ).data() );
+ }
+}
+
+void OProcess::setRunPrivileged( bool keepPrivileges )
+{
+ keepPrivs = keepPrivileges;
+}
+
+bool OProcess::runPrivileged() const
+{
+ return keepPrivs;
+}
+
+OProcess::~OProcess()
+{
+ // destroying the OProcess instance sends a SIGKILL to the
+ // child process (if it is running) after removing it from the
+ // list of valid processes (if the process is not started as
+ // "DontCare")
+
+ OProcessController::theOProcessController->removeOProcess( this );
+ // this must happen before we kill the child
+ // TODO: block the signal while removing the current process from the process list
+
+ if ( runs && ( run_mode != DontCare ) )
+ kill( SIGKILL );
+
+ // Clean up open fd's and socket notifiers.
+ closeStdin();
+ closeStdout();
+ closeStderr();
+
+ // TODO: restore SIGCHLD and SIGPIPE handler if this is the last OProcess
+ delete d;
+}
+
+void OProcess::detach()
+{
+ OProcessController::theOProcessController->removeOProcess( this );
+
+ runs = false;
+ pid_ = 0;
+
+ // Clean up open fd's and socket notifiers.
+ closeStdin();
+ closeStdout();
+ closeStderr();
+}
+
+bool OProcess::setExecutable( const QString& proc )
+{
+ if ( runs )
+ return false;
+
+ if ( proc.isEmpty() )
+ return false;
+
+ if ( !arguments.isEmpty() )
+ arguments.remove( arguments.begin() );
+ arguments.prepend( QFile::encodeName( proc ) );
+
+ return true;
+}
+
+OProcess &OProcess::operator<<( const QStringList& args )
+{
+ QStringList::ConstIterator it = args.begin();
+ for ( ; it != args.end() ; ++it )
+ arguments.append( QFile::encodeName( *it ) );
+ return *this;
+}
+
+OProcess &OProcess::operator<<( const QCString& arg )
+{
+ return operator<< ( arg.data() );
+}
+
+OProcess &OProcess::operator<<( const char* arg )
+{
+ arguments.append( arg );
+ return *this;
+}
+
+OProcess &OProcess::operator<<( const QString& arg )
+{
+ arguments.append( QFile::encodeName( arg ) );
+ return *this;
+}
+
+void OProcess::clearArguments()
+{
+ arguments.clear();
+}
+
+bool OProcess::start( RunMode runmode, Communication comm )
+{
+ uint i;
+ uint n = arguments.count();
+ char **arglist;
+
+ if ( runs || ( 0 == n ) )
+ {
+ return false; // cannot start a process that is already running
+ // or if no executable has been assigned
+ }
+ run_mode = runmode;
+ status = 0;
+
+ QCString shellCmd;
+ if ( d && d->useShell )
+ {
+ if ( d->shell.isEmpty() )
+ {
+ qWarning( "Could not find a valid shell" );
+ return false;
+ }
+
+ arglist = static_cast<char **>( malloc( ( 4 ) * sizeof( char * ) ) );
+ for ( i = 0; i < n; i++ )
+ {
+ shellCmd += arguments[ i ];
+ shellCmd += " "; // CC: to separate the arguments
+ }
+
+ arglist[ 0 ] = d->shell.data();
+ arglist[ 1 ] = ( char * ) "-c";
+ arglist[ 2 ] = shellCmd.data();
+ arglist[ 3 ] = 0;
+ }
+ else
+ {
+ arglist = static_cast<char **>( malloc( ( n + 1 ) * sizeof( char * ) ) );
+ for ( i = 0; i < n; i++ )
+ arglist[ i ] = arguments[ i ].data();
+ arglist[ n ] = 0;
+ }
+
+ if ( !setupCommunication( comm ) )
+ qWarning( "Could not setup Communication!" );
+
+ // We do this in the parent because if we do it in the child process
+ // gdb gets confused when the application runs from gdb.
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+#ifdef HAVE_INITGROUPS
+
+ struct passwd *pw = getpwuid( uid );
+#endif
+
+ int fd[ 2 ];
+ if ( 0 > pipe( fd ) )
+ {
+ fd[ 0 ] = fd[ 1 ] = 0; // Pipe failed.. continue
+ }
+
+ runs = true;
+
+ QApplication::flushX();
+
+ // WABA: Note that we use fork() and not vfork() because
+ // vfork() has unclear semantics and is not standardized.
+ pid_ = fork();
+
+ if ( 0 == pid_ )
+ {
+ if ( fd[ 0 ] )
+ close( fd[ 0 ] );
+ if ( !runPrivileged() )
+ {
+ setgid( gid );
+#if defined( HAVE_INITGROUPS)
+
+ if ( pw )
+ initgroups( pw->pw_name, pw->pw_gid );
+#endif
+
+ setuid( uid );
+ }
+ // The child process
+ if ( !commSetupDoneC() )
+ qWarning( "Could not finish comm setup in child!" );
+
+ setupEnvironment();
+
+ // Matthias
+ if ( run_mode == DontCare )
+ setpgid( 0, 0 );
+ // restore default SIGPIPE handler (Harri)
+ struct sigaction act;
+ sigemptyset( &( act.sa_mask ) );
+ sigaddset( &( act.sa_mask ), SIGPIPE );
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ sigaction( SIGPIPE, &act, 0L );
+
+ // We set the close on exec flag.
+ // Closing of fd[1] indicates that the execvp succeeded!
+ if ( fd[ 1 ] )
+ fcntl( fd[ 1 ], F_SETFD, FD_CLOEXEC );
+ execvp( arglist[ 0 ], arglist );
+ char resultByte = 1;
+ if ( fd[ 1 ] )
+ write( fd[ 1 ], &resultByte, 1 );
+ _exit( -1 );
+ }
+ else if ( -1 == pid_ )
+ {
+ // forking failed
+
+ runs = false;
+ free( arglist );
+ return false;
+ }
+ else
+ {
+ if ( fd[ 1 ] )
+ close( fd[ 1 ] );
+ // the parent continues here
+
+ // Discard any data for stdin that might still be there
+ input_data = 0;
+
+ // Check whether client could be started.
+ if ( fd[ 0 ] )
+ for ( ;; )
+ {
+ char resultByte;
+ int n = ::read( fd[ 0 ], &resultByte, 1 );
+ if ( n == 1 )
+ {
+ // Error
+ runs = false;
+ close( fd[ 0 ] );
+ free( arglist );
+ pid_ = 0;
+ return false;
+ }
+ if ( n == -1 )
+ {
+ if ( ( errno == ECHILD ) || ( errno == EINTR ) )
+ continue; // Ignore
+ }
+ break; // success
+ }
+ if ( fd[ 0 ] )
+ close( fd[ 0 ] );
+
+ if ( !commSetupDoneP() ) // finish communication socket setup for the parent
+ qWarning( "Could not finish comm setup in parent!" );
+
+ if ( run_mode == Block )
+ {
+ commClose();
+
+ // The SIGCHLD handler of the process controller will catch
+ // the exit and set the status
+ while ( runs )
+ {
+ OProcessController::theOProcessController->
+ slotDoHousekeeping( 0 );
+ }
+ runs = FALSE;
+ emit processExited( this );
+ }
+ }
+ free( arglist );
+ return true;
+}
+
+
+
+bool OProcess::kill( int signo )
+{
+ bool rv = false;
+
+ if ( 0 != pid_ )
+ rv = ( -1 != ::kill( pid_, signo ) );
+ // probably store errno somewhere...
+ return rv;
+}
+
+bool OProcess::isRunning() const
+{
+ return runs;
+}
+
+pid_t OProcess::pid() const
+{
+ return pid_;
+}
+
+bool OProcess::normalExit() const
+{
+ int _status = status;
+ return ( pid_ != 0 ) && ( !runs ) && ( WIFEXITED( ( _status ) ) );
+}
+
+int OProcess::exitStatus() const
+{
+ int _status = status;
+ return WEXITSTATUS( ( _status ) );
+}
+
+bool OProcess::writeStdin( const char *buffer, int buflen )
+{
+ bool rv;
+
+ // if there is still data pending, writing new data
+ // to stdout is not allowed (since it could also confuse
+ // kprocess...
+ if ( 0 != input_data )
+ return false;
+
+ if ( runs && ( communication & Stdin ) )
+ {
+ input_data = buffer;
+ input_sent = 0;
+ input_total = buflen;
+ slotSendData( 0 );
+ innot->setEnabled( true );
+ rv = true;
+ }
+ else
+ rv = false;
+ return rv;
+}
+
+void OProcess::flushStdin ( )
+{
+ if ( !input_data || ( input_sent == input_total ) )
+ return ;
+
+ int d1, d2;
+
+ do
+ {
+ d1 = input_total - input_sent;
+ slotSendData ( 0 );
+ d2 = input_total - input_sent;
+ }
+ while ( d2 <= d1 );
+}
+
+void OProcess::suspend()
+{
+ if ( ( communication & Stdout ) && outnot )
+ outnot->setEnabled( false );
+}
+
+void OProcess::resume()
+{
+ if ( ( communication & Stdout ) && outnot )
+ outnot->setEnabled( true );
+}
+
+bool OProcess::closeStdin()
+{
+ bool rv;
+
+ if ( communication & Stdin )
+ {
+ communication = ( Communication ) ( communication & ~Stdin );
+ delete innot;
+ innot = 0;
+ close( in[ 1 ] );
+ rv = true;
+ }
+ else
+ rv = false;
+ return rv;
+}
+
+bool OProcess::closeStdout()
+{
+ bool rv;
+
+ if ( communication & Stdout )
+ {
+ communication = ( Communication ) ( communication & ~Stdout );
+ delete outnot;
+ outnot = 0;
+ close( out[ 0 ] );
+ rv = true;
+ }
+ else
+ rv = false;
+ return rv;
+}
+
+bool OProcess::closeStderr()
+{
+ bool rv;
+
+ if ( communication & Stderr )
+ {
+ communication = static_cast<Communication>( communication & ~Stderr );
+ delete errnot;
+ errnot = 0;
+ close( err[ 0 ] );
+ rv = true;
+ }
+ else
+ rv = false;
+ return rv;
+}
+
+void OProcess::slotChildOutput( int fdno )
+{
+ if ( !childOutput( fdno ) )
+ closeStdout();
+}
+
+void OProcess::slotChildError( int fdno )
+{
+ if ( !childError( fdno ) )
+ closeStderr();
+}
+
+void OProcess::slotSendData( int )
+{
+ if ( input_sent == input_total )
+ {
+ innot->setEnabled( false );
+ input_data = 0;
+ emit wroteStdin( this );
+ }
+ else
+ input_sent += ::write( in[ 1 ], input_data + input_sent, input_total - input_sent );
+}
+
+void OProcess::processHasExited( int state )
+{
+ if ( runs )
+ {
+ runs = false;
+ status = state;
+
+ commClose(); // cleanup communication sockets
+
+ // also emit a signal if the process was run Blocking
+ if ( DontCare != run_mode )
+ {
+ emit processExited( this );
+ }
+ }
+}
+
+int OProcess::childOutput( int fdno )
+{
+ if ( communication & NoRead )
+ {
+ int len = -1;
+ emit receivedStdout( fdno, len );
+ errno = 0; // Make sure errno doesn't read "EAGAIN"
+ return len;
+ }
+ else
+ {
+ char buffer[ 1024 ];
+ int len;
+
+ len = ::read( fdno, buffer, 1024 );
+
+ if ( 0 < len )
+ {
+ emit receivedStdout( this, buffer, len );
+ }
+ return len;
+ }
+}
+
+int OProcess::childError( int fdno )
+{
+ char buffer[ 1024 ];
+ int len;
+
+ len = ::read( fdno, buffer, 1024 );
+
+ if ( 0 < len )
+ emit receivedStderr( this, buffer, len );
+ return len;
+}
+
+int OProcess::setupCommunication( Communication comm )
+{
+ int ok;
+
+ communication = comm;
+
+ ok = 1;
+ if ( comm & Stdin )
+ ok &= socketpair( AF_UNIX, SOCK_STREAM, 0, in ) >= 0;
+
+ if ( comm & Stdout )
+ ok &= socketpair( AF_UNIX, SOCK_STREAM, 0, out ) >= 0;
+
+ if ( comm & Stderr )
+ ok &= socketpair( AF_UNIX, SOCK_STREAM, 0, err ) >= 0;
+
+ return ok;
+}
+
+int OProcess::commSetupDoneP()
+{
+ int ok = 1;
+
+ if ( communication != NoCommunication )
+ {
+ if ( communication & Stdin )
+ close( in[ 0 ] );
+ if ( communication & Stdout )
+ close( out[ 1 ] );
+ if ( communication & Stderr )
+ close( err[ 1 ] );
+
+ // Don't create socket notifiers and set the sockets non-blocking if
+ // blocking is requested.
+ if ( run_mode == Block )
+ return ok;
+
+ if ( communication & Stdin )
+ {
+ // ok &= (-1 != fcntl(in[1], F_SETFL, O_NONBLOCK));
+ innot = new QSocketNotifier( in[ 1 ], QSocketNotifier::Write, this );
+ CHECK_PTR( innot );
+ innot->setEnabled( false ); // will be enabled when data has to be sent
+ QObject::connect( innot, SIGNAL( activated(int) ),
+ this, SLOT( slotSendData(int) ) );
+ }
+
+ if ( communication & Stdout )
+ {
+ // ok &= (-1 != fcntl(out[0], F_SETFL, O_NONBLOCK));
+ outnot = new QSocketNotifier( out[ 0 ], QSocketNotifier::Read, this );
+ CHECK_PTR( outnot );
+ QObject::connect( outnot, SIGNAL( activated(int) ),
+ this, SLOT( slotChildOutput(int) ) );
+ if ( communication & NoRead )
+ suspend();
+ }
+
+ if ( communication & Stderr )
+ {
+ // ok &= (-1 != fcntl(err[0], F_SETFL, O_NONBLOCK));
+ errnot = new QSocketNotifier( err[ 0 ], QSocketNotifier::Read, this );
+ CHECK_PTR( errnot );
+ QObject::connect( errnot, SIGNAL( activated(int) ),
+ this, SLOT( slotChildError(int) ) );
+ }
+ }
+ return ok;
+}
+
+int OProcess::commSetupDoneC()
+{
+ int ok = 1;
+ struct linger so;
+ memset( &so, 0, sizeof( so ) );
+
+ if ( communication & Stdin )
+ close( in[ 1 ] );
+ if ( communication & Stdout )
+ close( out[ 0 ] );
+ if ( communication & Stderr )
+ close( err[ 0 ] );
+
+ if ( communication & Stdin )
+ ok &= dup2( in[ 0 ], STDIN_FILENO ) != -1;
+ else
+ {
+ int null_fd = open( "/dev/null", O_RDONLY );
+ ok &= dup2( null_fd, STDIN_FILENO ) != -1;
+ close( null_fd );
+ }
+ if ( communication & Stdout )
+ {
+ ok &= dup2( out[ 1 ], STDOUT_FILENO ) != -1;
+ ok &= !setsockopt( out[ 1 ], SOL_SOCKET, SO_LINGER, ( char* ) & so, sizeof( so ) );
+ }
+ else
+ {
+ int null_fd = open( "/dev/null", O_WRONLY );
+ ok &= dup2( null_fd, STDOUT_FILENO ) != -1;
+ close( null_fd );
+ }
+ if ( communication & Stderr )
+ {
+ ok &= dup2( err[ 1 ], STDERR_FILENO ) != -1;
+ ok &= !setsockopt( err[ 1 ], SOL_SOCKET, SO_LINGER, reinterpret_cast<char *>( &so ), sizeof( so ) );
+ }
+ else
+ {
+ int null_fd = open( "/dev/null", O_WRONLY );
+ ok &= dup2( null_fd, STDERR_FILENO ) != -1;
+ close( null_fd );
+ }
+ return ok;
+}
+
+void OProcess::commClose()
+{
+ if ( NoCommunication != communication )
+ {
+ bool b_in = ( communication & Stdin );
+ bool b_out = ( communication & Stdout );
+ bool b_err = ( communication & Stderr );
+ if ( b_in )
+ delete innot;
+
+ if ( b_out || b_err )
+ {
+ // If both channels are being read we need to make sure that one socket buffer
+ // doesn't fill up whilst we are waiting for data on the other (causing a deadlock).
+ // Hence we need to use select.
+
+ // Once one or other of the channels has reached EOF (or given an error) go back
+ // to the usual mechanism.
+
+ int fds_ready = 1;
+ fd_set rfds;
+
+ int max_fd = 0;
+ if ( b_out )
+ {
+ fcntl( out[ 0 ], F_SETFL, O_NONBLOCK );
+ if ( out[ 0 ] > max_fd )
+ max_fd = out[ 0 ];
+ delete outnot;
+ outnot = 0;
+ }
+ if ( b_err )
+ {
+ fcntl( err[ 0 ], F_SETFL, O_NONBLOCK );
+ if ( err[ 0 ] > max_fd )
+ max_fd = err[ 0 ];
+ delete errnot;
+ errnot = 0;
+ }
+
+
+ while ( b_out || b_err )
+ {
+ // * If the process is still running we block until we
+ // receive data. (p_timeout = 0, no timeout)
+ // * If the process has already exited, we only check
+ // the available data, we don't wait for more.
+ // (p_timeout = &timeout, timeout immediately)
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ struct timeval *p_timeout = runs ? 0 : &timeout;
+
+ FD_ZERO( &rfds );
+ if ( b_out )
+ FD_SET( out[ 0 ], &rfds );
+
+ if ( b_err )
+ FD_SET( err[ 0 ], &rfds );
+
+ fds_ready = select( max_fd + 1, &rfds, 0, 0, p_timeout );
+ if ( fds_ready <= 0 )
+ break;
+
+ if ( b_out && FD_ISSET( out[ 0 ], &rfds ) )
+ {
+ int ret = 1;
+ while ( ret > 0 )
+ ret = childOutput( out[ 0 ] );
+ if ( ( ret == -1 && errno != EAGAIN ) || ret == 0 )
+ b_out = false;
+ }
+
+ if ( b_err && FD_ISSET( err[ 0 ], &rfds ) )
+ {
+ int ret = 1;
+ while ( ret > 0 )
+ ret = childError( err[ 0 ] );
+ if ( ( ret == -1 && errno != EAGAIN ) || ret == 0 )
+ b_err = false;
+ }
+ }
+ }
+
+ if ( b_in )
+ {
+ communication = ( Communication ) ( communication & ~Stdin );
+ close( in[ 1 ] );
+ }
+ if ( b_out )
+ {
+ communication = ( Communication ) ( communication & ~Stdout );
+ close( out[ 0 ] );
+ }
+ if ( b_err )
+ {
+ communication = ( Communication ) ( communication & ~Stderr );
+ close( err[ 0 ] );
+ }
+ }
+}
+
+void OProcess::setUseShell( bool useShell, const char *shell )
+{
+ if ( !d )
+ d = new OProcessPrivate;
+ d->useShell = useShell;
+ d->shell = shell;
+ if ( d->shell.isEmpty() )
+ d->shell = searchShell();
+}
+
+QString OProcess::quote( const QString &arg )
+{
+ QString res = arg;
+ res.replace( QRegExp( QString::fromLatin1( "\'" ) ),
+ QString::fromLatin1( "'\"'\"'" ) );
+ res.prepend( '\'' );
+ res.append( '\'' );
+ return res;
+}
+
+QCString OProcess::searchShell()
+{
+ QCString tmpShell = QCString( getenv( "SHELL" ) ).stripWhiteSpace();
+ if ( !isExecutable( tmpShell ) )
+ {
+ tmpShell = "/bin/sh";
+ }
+
+ return tmpShell;
+}
+
+bool OProcess::isExecutable( const QCString &filename )
+{
+ struct stat fileinfo;
+
+ if ( filename.isEmpty() )
+ return false;
+
+ // CC: we've got a valid filename, now let's see whether we can execute that file
+
+ if ( -1 == stat( filename.data(), &fileinfo ) )
+ return false;
+ // CC: return false if the file does not exist
+
+ // CC: anyway, we cannot execute directories, block/character devices, fifos or sockets
+ if ( ( S_ISDIR( fileinfo.st_mode ) ) ||
+ ( S_ISCHR( fileinfo.st_mode ) ) ||
+ ( S_ISBLK( fileinfo.st_mode ) ) ||
+#ifdef S_ISSOCK
+ // CC: SYSVR4 systems don't have that macro
+ ( S_ISSOCK( fileinfo.st_mode ) ) ||
+#endif
+ ( S_ISFIFO( fileinfo.st_mode ) ) ||
+ ( S_ISDIR( fileinfo.st_mode ) ) )
+ {
+ return false;
+ }
+
+ // CC: now check for permission to execute the file
+ if ( access( filename.data(), X_OK ) != 0 )
+ return false;
+
+ // CC: we've passed all the tests...
+ return true;
+}
+
+int OProcess::processPID( const QString& process )
+{
+ QString line;
+ QDir d = QDir( "/proc" );
+ QStringList dirs = d.entryList( QDir::Dirs );
+ QStringList::Iterator it;
+ for ( it = dirs.begin(); it != dirs.end(); ++it )
+ {
+ //qDebug( "next entry: %s", (const char*) *it );
+ QFile file( "/proc/"+*it+"/cmdline" );
+ file.open( IO_ReadOnly );
+ if ( !file.isOpen() ) continue;
+ QTextStream t( &file );
+ line = t.readLine();
+ //qDebug( "cmdline = %s", (const char*) line );
+ if ( line.contains( process ) ) break; //FIXME: That may find also other process, if the name is not long enough ;)
+ }
+ if ( line.contains( process ) )
+ {
+ //qDebug( "found process id #%d", (*it).toInt() );
+ return (*it).toInt();
+ }
+ else
+ {
+ //qDebug( "process '%s' not found", (const char*) process );
+ return 0;
+ }
+}
+
+}
+}