signon  8.58
remotepluginprocess.cpp
Go to the documentation of this file.
00001 /*
00002  * This file is part of signon
00003  *
00004  * Copyright (C) 2009-2010 Nokia Corporation.
00005  *
00006  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public License
00010  * version 2.1 as published by the Free Software Foundation.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00020  * 02110-1301 USA
00021  */
00022 #include <QProcess>
00023 #include <QUrl>
00024 #include <QTimer>
00025 #include <QBuffer>
00026 #include <QDataStream>
00027 #include <unistd.h>
00028 
00029 #include "debug.h"
00030 #ifdef HAVE_LIBPROXY
00031 #include "my-network-proxy-factory.h"
00032 #endif
00033 #include "remotepluginprocess.h"
00034 
00035 // signon-plugins-common
00036 #include "SignOn/blobiohandler.h"
00037 #include "SignOn/ipc.h"
00038 
00039 using namespace SignOn;
00040 
00041 namespace RemotePluginProcessNS {
00042 
00043 static CancelEventThread *cancelThread = NULL;
00044 
00045 /* ---------------------- RemotePluginProcess ---------------------- */
00046 
00047 RemotePluginProcess::RemotePluginProcess(QObject *parent):
00048     QObject(parent)
00049 {
00050     m_plugin = NULL;
00051     m_readnotifier = NULL;
00052     m_errnotifier = NULL;
00053 
00054     qRegisterMetaType<SignOn::SessionData>("SignOn::SessionData");
00055     qRegisterMetaType<QString>("QString");
00056 }
00057 
00058 RemotePluginProcess::~RemotePluginProcess()
00059 {
00060     delete m_plugin;
00061     delete m_readnotifier;
00062     delete m_errnotifier;
00063 
00064     if (cancelThread) {
00065         cancelThread->quit();
00066         cancelThread->wait();
00067         delete cancelThread;
00068     }
00069 }
00070 
00071 RemotePluginProcess *
00072 RemotePluginProcess::createRemotePluginProcess(QString &type, QObject *parent)
00073 {
00074     RemotePluginProcess *rpp = new RemotePluginProcess(parent);
00075 
00076     //this is needed before plugin is initialized
00077     rpp->setupProxySettings();
00078 
00079     if (!rpp->loadPlugin(type) ||
00080        !rpp->setupDataStreams() ||
00081        rpp->m_plugin->type() != type) {
00082         delete rpp;
00083         return NULL;
00084     }
00085     return rpp;
00086 }
00087 
00088 bool RemotePluginProcess::loadPlugin(QString &type)
00089 {
00090     TRACE() << " loading auth library for " << type;
00091 
00092     QLibrary lib(getPluginName(type));
00093 
00094     if (!lib.load()) {
00095         qCritical() << QString("Failed to load %1 (reason: %2)")
00096             .arg(getPluginName(type)).arg(lib.errorString());
00097         return false;
00098     }
00099 
00100     TRACE() << "library loaded";
00101 
00102     typedef AuthPluginInterface* (*SsoAuthPluginInstanceF)();
00103     SsoAuthPluginInstanceF instance =
00104         (SsoAuthPluginInstanceF)lib.resolve("auth_plugin_instance");
00105     if (!instance) {
00106         qCritical() << QString("Failed to resolve init function in %1 "
00107                                "(reason: %2)")
00108             .arg(getPluginName(type)).arg(lib.errorString());
00109         return false;
00110     }
00111 
00112     TRACE() << "constructor resolved";
00113 
00114     m_plugin = qobject_cast<AuthPluginInterface *>(instance());
00115 
00116     if (!m_plugin) {
00117         qCritical() << QString("Failed to cast object for %1 type")
00118             .arg(type);
00119         return false;
00120     }
00121 
00122     connect(m_plugin, SIGNAL(result(const SignOn::SessionData&)),
00123             this, SLOT(result(const SignOn::SessionData&)));
00124 
00125     connect(m_plugin, SIGNAL(store(const SignOn::SessionData&)),
00126             this, SLOT(store(const SignOn::SessionData&)));
00127 
00128     connect(m_plugin, SIGNAL(error(const SignOn::Error &)),
00129             this, SLOT(error(const SignOn::Error &)));
00130 
00131     connect(m_plugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
00132             this, SLOT(userActionRequired(const SignOn::UiSessionData&)));
00133 
00134     connect(m_plugin, SIGNAL(refreshed(const SignOn::UiSessionData&)),
00135             this, SLOT(refreshed(const SignOn::UiSessionData&)));
00136 
00137     connect(m_plugin,
00138             SIGNAL(statusChanged(const AuthPluginState, const QString&)),
00139             this, SLOT(statusChanged(const AuthPluginState, const QString&)));
00140 
00141     m_plugin->setParent(this);
00142 
00143     TRACE() << "plugin is fully initialized";
00144     return true;
00145 }
00146 
00147 bool RemotePluginProcess::setupDataStreams()
00148 {
00149     TRACE();
00150 
00151     m_inFile.open(STDIN_FILENO, QIODevice::ReadOnly);
00152     m_outFile.open(STDOUT_FILENO, QIODevice::WriteOnly);
00153 
00154     m_readnotifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read);
00155     m_errnotifier = new QSocketNotifier(STDIN_FILENO,
00156                                         QSocketNotifier::Exception);
00157 
00158     connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
00159     connect(m_errnotifier, SIGNAL(activated(int)),
00160             this, SIGNAL(processStopped()));
00161 
00162     if (!cancelThread)
00163         cancelThread = new CancelEventThread(m_plugin);
00164 
00165     TRACE() << "cancel thread created";
00166 
00167     m_blobIOHandler = new BlobIOHandler(&m_inFile, &m_outFile, this);
00168 
00169     connect(m_blobIOHandler,
00170             SIGNAL(dataReceived(const QVariantMap &)),
00171             this,
00172             SLOT(sessionDataReceived(const QVariantMap &)));
00173 
00174     connect(m_blobIOHandler,
00175             SIGNAL(error()),
00176             this,
00177             SLOT(blobIOError()));
00178 
00179     m_blobIOHandler->setReadChannelSocketNotifier(m_readnotifier);
00180 
00181     return true;
00182 }
00183 
00184 bool RemotePluginProcess::setupProxySettings()
00185 {
00186     TRACE();
00187 
00188 #ifdef HAVE_LIBPROXY
00189     /* Use a libproxy-based proxy factory; this code will no longer be
00190      * needed when https://bugreports.qt-project.org/browse/QTBUG-26295
00191      * is fixed. */
00192     MyNetworkProxyFactory *proxyFactory = new MyNetworkProxyFactory();
00193     QNetworkProxyFactory::setApplicationProxyFactory(proxyFactory);
00194 #endif
00195 
00196     return true;
00197 }
00198 
00199 void RemotePluginProcess::blobIOError()
00200 {
00201     error(
00202         Error(Error::InternalServer,
00203         QLatin1String("Failed to I/O session data to/from the signon daemon.")));
00204     connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
00205 }
00206 
00207 void RemotePluginProcess::result(const SignOn::SessionData &data)
00208 {
00209     disableCancelThread();
00210     QDataStream out(&m_outFile);
00211     QVariantMap resultDataMap;
00212 
00213     foreach(QString key, data.propertyNames())
00214         resultDataMap[key] = data.getProperty(key);
00215 
00216     out << (quint32)PLUGIN_RESPONSE_RESULT;
00217 
00218     m_blobIOHandler->sendData(resultDataMap);
00219 
00220     m_outFile.flush();
00221 }
00222 
00223 void RemotePluginProcess::store(const SignOn::SessionData &data)
00224 {
00225     QDataStream out(&m_outFile);
00226     QVariantMap storeDataMap;
00227 
00228     foreach(QString key, data.propertyNames())
00229         storeDataMap[key] = data.getProperty(key);
00230 
00231     out << (quint32)PLUGIN_RESPONSE_STORE;
00232 
00233     m_blobIOHandler->sendData(storeDataMap);
00234 
00235     m_outFile.flush();
00236 }
00237 
00238 void RemotePluginProcess::error(const SignOn::Error &err)
00239 {
00240     disableCancelThread();
00241 
00242     QDataStream out(&m_outFile);
00243 
00244     out << (quint32)PLUGIN_RESPONSE_ERROR;
00245     out << (quint32)err.type();
00246     out << err.message();
00247     m_outFile.flush();
00248 
00249     TRACE() << "error is sent" << err.type() << " " << err.message();
00250 }
00251 
00252 void RemotePluginProcess::userActionRequired(const SignOn::UiSessionData &data)
00253 {
00254     TRACE();
00255     disableCancelThread();
00256 
00257     QDataStream out(&m_outFile);
00258     QVariantMap resultDataMap;
00259 
00260     foreach(QString key, data.propertyNames())
00261         resultDataMap[key] = data.getProperty(key);
00262 
00263     out << (quint32)PLUGIN_RESPONSE_UI;
00264     m_blobIOHandler->sendData(resultDataMap);
00265     m_outFile.flush();
00266 }
00267 
00268 void RemotePluginProcess::refreshed(const SignOn::UiSessionData &data)
00269 {
00270     TRACE();
00271     disableCancelThread();
00272 
00273     QDataStream out(&m_outFile);
00274     QVariantMap resultDataMap;
00275 
00276     foreach(QString key, data.propertyNames())
00277         resultDataMap[key] = data.getProperty(key);
00278 
00279     m_readnotifier->setEnabled(true);
00280 
00281     out << (quint32)PLUGIN_RESPONSE_REFRESHED;
00282 
00283     m_blobIOHandler->sendData(resultDataMap);
00284 
00285     m_outFile.flush();
00286 }
00287 
00288 void RemotePluginProcess::statusChanged(const AuthPluginState state,
00289                                         const QString &message)
00290 {
00291     TRACE();
00292     QDataStream out(&m_outFile);
00293 
00294     out << (quint32)PLUGIN_RESPONSE_SIGNAL;
00295     out << (quint32)state;
00296     out << message;
00297 
00298     m_outFile.flush();
00299 }
00300 
00301 QString RemotePluginProcess::getPluginName(const QString &type)
00302 {
00303     QString dirName = qgetenv("SSO_PLUGINS_DIR");
00304     if (dirName.isEmpty())
00305         dirName = QDir::cleanPath(SIGNOND_PLUGINS_DIR);
00306     QString fileName = dirName +
00307                        QDir::separator() +
00308                        QString(SIGNON_PLUGIN_PREFIX) +
00309                        type +
00310                        QString(SIGNON_PLUGIN_SUFFIX);
00311 
00312     return fileName;
00313 }
00314 
00315 void RemotePluginProcess::type()
00316 {
00317     QDataStream out(&m_outFile);
00318     out << m_plugin->type();
00319 }
00320 
00321 void RemotePluginProcess::mechanisms()
00322 {
00323     QDataStream out(&m_outFile);
00324     QStringList mechanisms = m_plugin->mechanisms();
00325     QVariant mechsVar = mechanisms;
00326     out << mechsVar;
00327 }
00328 
00329 void RemotePluginProcess::process()
00330 {
00331     QDataStream in(&m_inFile);
00332 
00333 
00334     in >> m_currentMechanism;
00335 
00336     int processBlobSize = -1;
00337     in >> processBlobSize;
00338 
00339     disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
00340 
00341     m_currentOperation = PLUGIN_OP_PROCESS;
00342     m_blobIOHandler->receiveData(processBlobSize);
00343 }
00344 
00345 void RemotePluginProcess::userActionFinished()
00346 {
00347     QDataStream in(&m_inFile);
00348     int processBlobSize = -1;
00349     in >> processBlobSize;
00350 
00351     disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
00352 
00353     m_currentOperation = PLUGIN_OP_PROCESS_UI;
00354     m_blobIOHandler->receiveData(processBlobSize);
00355 }
00356 
00357 void RemotePluginProcess::refresh()
00358 {
00359     QDataStream in(&m_inFile);
00360     int processBlobSize = -1;
00361     in >> processBlobSize;
00362 
00363     disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
00364 
00365     m_currentOperation = PLUGIN_OP_REFRESH;
00366     m_blobIOHandler->receiveData(processBlobSize);
00367 }
00368 
00369 void RemotePluginProcess::sessionDataReceived(const QVariantMap &sessionDataMap)
00370 {
00371     enableCancelThread();
00372     TRACE() << "The cancel thread is started";
00373 
00374     if (m_currentOperation == PLUGIN_OP_PROCESS) {
00375         SessionData inData(sessionDataMap);
00376         m_plugin->process(inData, m_currentMechanism);
00377         m_currentMechanism.clear();
00378 
00379     } else if(m_currentOperation == PLUGIN_OP_PROCESS_UI) {
00380         UiSessionData inData(sessionDataMap);
00381         m_plugin->userActionFinished(inData);
00382 
00383     } else if(m_currentOperation == PLUGIN_OP_REFRESH) {
00384         UiSessionData inData(sessionDataMap);
00385         m_plugin->refresh(inData);
00386 
00387     } else {
00388         TRACE() << "Wrong operation code.";
00389         error(Error(Error::InternalServer,
00390                     QLatin1String("Plugin process - invalid operation code.")));
00391     }
00392 
00393     m_currentOperation = PLUGIN_OP_STOP;
00394     connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
00395 }
00396 
00397 void RemotePluginProcess::enableCancelThread()
00398 {
00399     QEventLoop loop;
00400     connect(cancelThread,
00401             SIGNAL(started()),
00402             &loop,
00403             SLOT(quit()));
00404 
00405     m_readnotifier->setEnabled(false);
00406     QTimer::singleShot(0.5*1000, &loop, SLOT(quit()));
00407     cancelThread->start();
00408     loop.exec();
00409     QThread::yieldCurrentThread();
00410 }
00411 
00412 void RemotePluginProcess::disableCancelThread()
00413 {
00414     if (!cancelThread->isRunning())
00415         return;
00416 
00423     cancelThread->quit();
00424 
00425     TRACE() << "Before the isFinished loop ";
00426 
00427     int i = 0;
00428     while (!cancelThread->isFinished()) {
00429         cancelThread->quit();
00430         TRACE() << "Internal iteration " << i++;
00431         usleep(0.005 * 1000000);
00432     }
00433 
00434     if (!cancelThread->wait(500)) {
00435         BLAME() << "Cannot disable cancel thread";
00436         int i;
00437         for (i = 0; i < 5; i++) {
00438             usleep(0.01 * 1000000);
00439             if (cancelThread->wait(500))
00440                 break;
00441         }
00442 
00443         if (i == 5) {
00444             BLAME() << "Cannot do anything with cancel thread";
00445             cancelThread->terminate();
00446             cancelThread->wait();
00447         }
00448     }
00449 
00450     m_readnotifier->setEnabled(true);
00451 }
00452 
00453 void RemotePluginProcess::startTask()
00454 {
00455     quint32 opcode = PLUGIN_OP_STOP;
00456     bool is_stopped = false;
00457 
00458     QDataStream in(&m_inFile);
00459     in >> opcode;
00460 
00461     switch (opcode) {
00462     case PLUGIN_OP_CANCEL:
00463         {
00464             m_plugin->cancel(); break;
00465             //still do not have clear understanding
00466             //of the cancelation-stop mechanism
00467             //is_stopped = true;
00468         }
00469         break;
00470     case PLUGIN_OP_TYPE:
00471         type();
00472         break;
00473     case PLUGIN_OP_MECHANISMS:
00474         mechanisms();
00475         break;
00476     case PLUGIN_OP_PROCESS:
00477         process();
00478         break;
00479     case PLUGIN_OP_PROCESS_UI:
00480         userActionFinished();
00481         break;
00482     case PLUGIN_OP_REFRESH:
00483         refresh();
00484         break;
00485     case PLUGIN_OP_STOP:
00486         is_stopped = true;
00487         break;
00488     default:
00489         {
00490             qCritical() << " unknown operation code: " << opcode;
00491             is_stopped = true;
00492         }
00493         break;
00494     };
00495 
00496     TRACE() << "operation is completed";
00497 
00498     if (!is_stopped) {
00499         if (!m_outFile.flush())
00500             is_stopped = true;
00501     }
00502 
00503     if (is_stopped)
00504     {
00505         m_plugin->abort();
00506         emit processStopped();
00507     }
00508 }
00509 
00510 CancelEventThread::CancelEventThread(AuthPluginInterface *plugin)
00511 {
00512     m_plugin = plugin;
00513     m_cancelNotifier = 0;
00514 }
00515 
00516 CancelEventThread::~CancelEventThread()
00517 {
00518     delete m_cancelNotifier;
00519 }
00520 
00521 void CancelEventThread::run()
00522 {
00523     if (!m_cancelNotifier) {
00524         m_cancelNotifier = new QSocketNotifier(STDIN_FILENO,
00525                                                QSocketNotifier::Read);
00526         connect(m_cancelNotifier, SIGNAL(activated(int)),
00527                 this, SLOT(cancel()), Qt::DirectConnection);
00528     }
00529 
00530     m_cancelNotifier->setEnabled(true);
00531     exec();
00532     m_cancelNotifier->setEnabled(false);
00533 }
00534 
00535 void CancelEventThread::cancel()
00536 {
00537     char buf[4];
00538     memset(buf, 0, 4);
00539     int n = 0;
00540 
00541     if (!(n = read(STDIN_FILENO, buf, 4))) {
00542         qCritical() << "Cannot read from cancel socket";
00543         return;
00544     }
00545 
00546     /*
00547      * Read the actual value of
00548      * */
00549     QByteArray ba(buf, 4);
00550     quint32 opcode;
00551     QDataStream ds(ba);
00552     ds >> opcode;
00553 
00554     if (opcode != PLUGIN_OP_CANCEL)
00555         qCritical() << "wrong operation code: breakage of remotepluginprocess "
00556             "threads synchronization: " << opcode;
00557 
00558     m_plugin->cancel();
00559 }
00560 
00561 } //namespace RemotePluginProcessNS
00562