author | sandman <sandman> | 2002-12-17 00:29:22 (UTC) |
---|---|---|
committer | sandman <sandman> | 2002-12-17 00:29:22 (UTC) |
commit | 2d39ff65b27c1a680a56c1b70b534e3cb767e08f (patch) (side-by-side diff) | |
tree | 09e5cc3f7157681fe51fe83bb54ebbaa548d8c64 /library/global.cpp | |
parent | 70090722d240bed8c390281e072c9bcfc5ba7782 (diff) | |
download | opie-2d39ff65b27c1a680a56c1b70b534e3cb767e08f.zip opie-2d39ff65b27c1a680a56c1b70b534e3cb767e08f.tar.gz opie-2d39ff65b27c1a680a56c1b70b534e3cb767e08f.tar.bz2 |
replaced the simple fork and execv method of starting applications with a
more sophisticated method (mostly taken from K/OProcess, because we can't
use libopie in libqpe).
We can detect now, if apps can not be started.
-rw-r--r-- | library/global.cpp | 49 |
1 files changed, 46 insertions, 3 deletions
diff --git a/library/global.cpp b/library/global.cpp index d6ba84f..68a3a75 100644 --- a/library/global.cpp +++ b/library/global.cpp @@ -1,172 +1,173 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the Qtopia Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #define QTOPIA_INTERNAL_LANGLIST #include <qpe/qpedebug.h> #include <qpe/global.h> #include <qpe/qdawg.h> #include <qpe/qpeapplication.h> #include <qpe/resource.h> #include <qpe/storage.h> #include <qpe/applnk.h> #include <qpe/qcopenvelope_qws.h> #include <qfile.h> #include <qlabel.h> #include <qtimer.h> #include <qmap.h> #include <qdict.h> #include <qdir.h> #include <qmessagebox.h> #include <qregexp.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> +#include <errno.h> #include <qwindowsystem_qws.h> // for qwsServer #include <qdatetime.h> #include <qfile.h> namespace { // checks if the storage should be searched bool checkStorage(const QString &path ){ // this is a small Config replacement cause config is too limited -zecke QFile file(path ); if(!file.open(IO_ReadOnly ) ) return true; QByteArray array = file.readAll(); QStringList list = QStringList::split('\n', QString( array ) ); for(QStringList::Iterator it = list.begin(); it != list.end(); ++it ){ if( (*it).startsWith("autocheck = 0" ) ){ return false; }else if( (*it).startsWith("autocheck = 1" ) ){ return true; } } return true; } } //#include "quickexec_p.h" class Emitter : public QObject { Q_OBJECT public: Emitter( QWidget* receiver, const QString& document ) { connect(this, SIGNAL(setDocument(const QString&)), receiver, SLOT(setDocument(const QString&))); emit setDocument(document); disconnect(this, SIGNAL(setDocument(const QString&)), receiver, SLOT(setDocument(const QString&))); } signals: void setDocument(const QString&); }; class StartingAppList : public QObject { Q_OBJECT public: static void add( const QString& name ); static bool isStarting( const QString name ); private slots: void handleNewChannel( const QString &); private: StartingAppList( QObject *parent=0, const char* name=0 ) ; QDict<QTime> dict; static StartingAppList *appl; }; StartingAppList* StartingAppList::appl = 0; StartingAppList::StartingAppList( QObject *parent, const char* name ) :QObject( parent, name ) { #if QT_VERSION >= 232 && defined(QWS) connect( qwsServer, SIGNAL( newChannel(const QString&)), this, SLOT( handleNewChannel(const QString&)) ); #endif dict.setAutoDelete( TRUE ); } void StartingAppList::add( const QString& name ) { #if QT_VERSION >= 232 && !defined(QT_NO_COP) if ( !appl ) appl = new StartingAppList; QTime *t = new QTime; t->start(); appl->dict.insert( "QPE/Application/" + name, t ); #endif } bool StartingAppList::isStarting( const QString name ) { #if QT_VERSION >= 232 && !defined(QT_NO_COP) if ( appl ) { QTime *t = appl->dict.find( "QPE/Application/" + name ); if ( !t ) return FALSE; if ( t->elapsed() > 10000 ) { // timeout in case of crash or something appl->dict.remove( "QPE/Application/" + name ); return FALSE; } return TRUE; } #endif return FALSE; } void StartingAppList::handleNewChannel( const QString & name ) { #if QT_VERSION >= 232 && !defined(QT_NO_COP) dict.remove( name ); #endif } static bool docDirCreated = FALSE; static QDawg* fixed_dawg = 0; static QDict<QDawg> *named_dawg = 0; static QString qpeDir() { QString dir = getenv("OPIEDIR"); if ( dir.isEmpty() ) dir = ".."; return dir; } static QString dictDir() { return qpeDir() + "/etc/dict"; } /*! \class Global global.h \brief The Global class provides application-wide global functions. The Global functions are grouped as follows: @@ -479,267 +480,309 @@ Global::Command* Global::builtin=0; QGuardedPtr<QWidget> *Global::running=0; /*! \class Global::Command \brief The Global::Command class is internal. \internal */ /*! \internal */ void Global::setBuiltinCommands( Command* list ) { if ( running ) delete [] running; builtin = list; int count = 0; if (!builtin) return; while ( builtin[count].file ) count++; running = new QGuardedPtr<QWidget> [ count ]; } /*! \internal */ void Global::setDocument( QWidget* receiver, const QString& document ) { Emitter emitter(receiver,document); } /*! \internal */ bool Global::terminateBuiltin( const QString& n ) { if (!builtin) return FALSE; for (int i = 0; builtin[i].file; i++) { if ( builtin[i].file == n ) { delete running[i]; return TRUE; } } return FALSE; } /*! \internal */ void Global::terminate( const AppLnk* app ) { //if ( terminateBuiltin(app->exec()) ) return; // maybe? haven't tried this #ifndef QT_NO_COP QCString channel = "QPE/Application/" + app->exec().utf8(); if ( QCopChannel::isRegistered(channel) ) { QCopEnvelope e(channel, "quit()"); } #endif } /*! Low-level function to run command \a c. \warning Do not use this function. Use execute instead. \sa execute() */ void Global::invoke(const QString &c) { // Convert the command line in to a list of arguments QStringList list = QStringList::split(QRegExp(" *"),c); #if !defined(QT_NO_COP) QString ap=list[0]; // see if the application is already running // XXX should lock file /tmp/qcop-msg-ap if ( QCopChannel::isRegistered( ("QPE/Application/" + ap).latin1() ) ) { // If the channel is already register, the app is already running, so show it. { QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "raise()" ); } QCopEnvelope e("QPE/System", "notBusy(QString)" ); e << ap; return; } // XXX should unlock file /tmp/qcop-msg-ap //see if it is being started if ( StartingAppList::isStarting( ap ) ) { QCopEnvelope e("QPE/System", "notBusy(QString)" ); e << ap; return; } #endif #ifdef QT_NO_QWS_MULTIPROCESS QMessageBox::warning( 0, "Error", "Could not find the application " + c, "Ok", 0, 0, 0, 1 ); #else QStrList slist; unsigned int j; for ( j = 0; j < list.count(); j++ ) slist.append( list[j].utf8() ); const char **args = new (const char *)[slist.count() + 1]; for ( j = 0; j < slist.count(); j++ ) args[j] = slist.at(j); args[j] = NULL; #if !defined(QT_NO_COP) // an attempt to show a wait... // more logic should be used, but this will be fine for the moment... QCopEnvelope ( "QPE/System", "busy()" ); #endif #ifdef HAVE_QUICKEXEC QString libexe = qpeDir()+"/binlib/lib"+args[0] + ".so"; qDebug("libfile = %s", libexe.latin1() ); if ( QFile::exists( libexe ) ) { qDebug("calling quickexec %s", libexe.latin1() ); quickexecv( libexe.utf8().data(), (const char **)args ); } else #endif { - if ( !::vfork() ) { - for ( int fd = 3; fd < 100; fd++ ) + bool success = false; + int pfd [2]; + if ( ::pipe ( pfd ) < 0 ) + pfd [0] = pfd [1] = -1; + + pid_t pid = ::fork ( ); + + if ( pid == 0 ) { // child + for ( int fd = 3; fd < 100; fd++ ) { + if ( fd != pfd [1] ) ::close( fd ); + } ::setpgid( ::getpid(), ::getppid() ); + + // Closing of fd[1] indicates that the execvp succeeded! + if ( pfd [1] >= 0 ) + ::fcntl ( pfd [1], F_SETFD, FD_CLOEXEC ); + // Try bindir first, so that foo/bar works too ::execv( qpeDir()+"/bin/"+args[0], (char * const *)args ); ::execvp( args[0], (char * const *)args ); - _exit( -1 ); + + char resultByte = 1; + if ( pfd [1] >= 0 ) + ::write ( pfd [1], &resultByte, 1 ); + ::_exit ( -1 ); + } + else if ( pid > 0 ) { + success = true; + + if ( pfd [1] >= 0 ) + ::close ( pfd [1] ); + if ( pfd [0] >= 0 ) { + while ( true ) { + char resultByte; + int n = ::read ( pfd [0], &resultByte, 1 ); + if ( n == 1 ) { + success = false; + break; + } + if (( n == -1 ) && (( errno == ECHILD ) || ( errno == EINTR ))) + continue; + + break; // success + } + ::close ( pfd [0] ); } } + if ( success ) StartingAppList::add( list[0] ); + else + QMessageBox::warning( 0, "Error", "Could not start the application " + c, "Ok", 0, 0, 0, 1 ); + } #endif //QT_NO_QWS_MULTIPROCESS } /*! Executes the application identfied by \a c, passing \a document if it isn't null. Note that a better approach might be to send a QCop message to the application's QPE/Application/\e{appname} channel. */ void Global::execute( const QString &c, const QString& document ) { if ( qApp->type() != QApplication::GuiServer ) { // ask the server to do the work #if !defined(QT_NO_COP) if ( document.isNull() ) { QCopEnvelope e( "QPE/System", "execute(QString)" ); e << c; } else { QCopEnvelope e( "QPE/System", "execute(QString,QString)" ); e << c << document; } #endif return; } // Attempt to execute the app using a builtin class for the app first // else try and find it in the bin directory if (builtin) { for (int i = 0; builtin[i].file; i++) { if ( builtin[i].file == c ) { if ( running[i] ) { if ( !document.isNull() && builtin[i].documentary ) setDocument(running[i], document); running[i]->raise(); running[i]->show(); running[i]->setActiveWindow(); } else { running[i] = builtin[i].func( builtin[i].maximized ); } #ifndef QT_NO_COP QCopEnvelope e("QPE/System", "notBusy(QString)" ); e << c; // that was quick ;-) #endif return; } } } //Global::invoke(c, document); // Convert the command line in to a list of arguments QStringList list = QStringList::split(QRegExp(" *"),c); #if !defined(QT_NO_COP) QString ap=list[0]; qDebug("executing %s", ap.latin1() ); /* if need be, sending a qcop message will result in an invoke, see preceeding function */ invoke( ap ); //{ QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "raise()" ); } if ( !document.isEmpty() ) { QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "setDocument(QString)" ); env << document; } #endif } /*! Returns the string \a s with the characters '\', '"', and '$' quoted by a preceeding '\'. \sa stringQuote() */ QString Global::shellQuote(const QString& s) { QString r="\""; for (int i=0; i<(int)s.length(); i++) { char c = s[i].latin1(); switch (c) { case '\\': case '"': case '$': r+="\\"; } r += s[i]; } r += "\""; return r; } /*! Returns the string \a s with the characters '\' and '"' quoted by a preceeding '\'. \sa shellQuote() */ QString Global::stringQuote(const QString& s) { QString r="\""; for (int i=0; i<(int)s.length(); i++) { char c = s[i].latin1(); switch (c) { case '\\': case '"': r+="\\"; } r += s[i]; } r += "\""; return r; } /*! Finds all documents on the system's document directories which match the filter \a mimefilter, and appends the resulting DocLnk objects to \a folder. */ void Global::findDocuments(DocLnkSet* folder, const QString &mimefilter) { QString homedocs = QString(getenv("HOME")) + "/Documents"; DocLnkSet d(homedocs,mimefilter); folder->appendFrom(d); /** let's do intellegint way of searching these files * a) the user don't want to check mediums global * b) the user wants to check but use the global options for it * c) the user wants to check it but not this medium * d) the user wants to check and this medium as well |