signon  8.58
remotepluginprocess.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of signon
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22 #include <QProcess>
23 #include <QUrl>
24 #include <QTimer>
25 #include <QBuffer>
26 #include <QDataStream>
27 #include <unistd.h>
28 
29 #include "debug.h"
30 #ifdef HAVE_LIBPROXY
32 #endif
33 #include "remotepluginprocess.h"
34 
35 // signon-plugins-common
36 #include "SignOn/blobiohandler.h"
37 #include "SignOn/ipc.h"
38 
39 using namespace SignOn;
40 
42 
43 static CancelEventThread *cancelThread = NULL;
44 
45 /* ---------------------- RemotePluginProcess ---------------------- */
46 
47 RemotePluginProcess::RemotePluginProcess(QObject *parent):
48  QObject(parent)
49 {
50  m_plugin = NULL;
51  m_readnotifier = NULL;
52  m_errnotifier = NULL;
53 
54  qRegisterMetaType<SignOn::SessionData>("SignOn::SessionData");
55  qRegisterMetaType<QString>("QString");
56 }
57 
59 {
60  delete m_plugin;
61  delete m_readnotifier;
62  delete m_errnotifier;
63 
64  if (cancelThread) {
65  cancelThread->quit();
66  cancelThread->wait();
67  delete cancelThread;
68  }
69 }
70 
72 RemotePluginProcess::createRemotePluginProcess(QString &type, QObject *parent)
73 {
74  RemotePluginProcess *rpp = new RemotePluginProcess(parent);
75 
76  //this is needed before plugin is initialized
77  rpp->setupProxySettings();
78 
79  if (!rpp->loadPlugin(type) ||
80  !rpp->setupDataStreams() ||
81  rpp->m_plugin->type() != type) {
82  delete rpp;
83  return NULL;
84  }
85  return rpp;
86 }
87 
89 {
90  TRACE() << " loading auth library for " << type;
91 
92  QLibrary lib(getPluginName(type));
93 
94  if (!lib.load()) {
95  qCritical() << QString("Failed to load %1 (reason: %2)")
96  .arg(getPluginName(type)).arg(lib.errorString());
97  return false;
98  }
99 
100  TRACE() << "library loaded";
101 
102  typedef AuthPluginInterface* (*SsoAuthPluginInstanceF)();
103  SsoAuthPluginInstanceF instance =
104  (SsoAuthPluginInstanceF)lib.resolve("auth_plugin_instance");
105  if (!instance) {
106  qCritical() << QString("Failed to resolve init function in %1 "
107  "(reason: %2)")
108  .arg(getPluginName(type)).arg(lib.errorString());
109  return false;
110  }
111 
112  TRACE() << "constructor resolved";
113 
114  m_plugin = qobject_cast<AuthPluginInterface *>(instance());
115 
116  if (!m_plugin) {
117  qCritical() << QString("Failed to cast object for %1 type")
118  .arg(type);
119  return false;
120  }
121 
122  connect(m_plugin, SIGNAL(result(const SignOn::SessionData&)),
123  this, SLOT(result(const SignOn::SessionData&)));
124 
125  connect(m_plugin, SIGNAL(store(const SignOn::SessionData&)),
126  this, SLOT(store(const SignOn::SessionData&)));
127 
128  connect(m_plugin, SIGNAL(error(const SignOn::Error &)),
129  this, SLOT(error(const SignOn::Error &)));
130 
131  connect(m_plugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
132  this, SLOT(userActionRequired(const SignOn::UiSessionData&)));
133 
134  connect(m_plugin, SIGNAL(refreshed(const SignOn::UiSessionData&)),
135  this, SLOT(refreshed(const SignOn::UiSessionData&)));
136 
137  connect(m_plugin,
138  SIGNAL(statusChanged(const AuthPluginState, const QString&)),
139  this, SLOT(statusChanged(const AuthPluginState, const QString&)));
140 
141  m_plugin->setParent(this);
142 
143  TRACE() << "plugin is fully initialized";
144  return true;
145 }
146 
148 {
149  TRACE();
150 
151  m_inFile.open(STDIN_FILENO, QIODevice::ReadOnly);
152  m_outFile.open(STDOUT_FILENO, QIODevice::WriteOnly);
153 
154  m_readnotifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read);
155  m_errnotifier = new QSocketNotifier(STDIN_FILENO,
156  QSocketNotifier::Exception);
157 
158  connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
159  connect(m_errnotifier, SIGNAL(activated(int)),
160  this, SIGNAL(processStopped()));
161 
162  if (!cancelThread)
163  cancelThread = new CancelEventThread(m_plugin);
164 
165  TRACE() << "cancel thread created";
166 
167  m_blobIOHandler = new BlobIOHandler(&m_inFile, &m_outFile, this);
168 
169  connect(m_blobIOHandler,
170  SIGNAL(dataReceived(const QVariantMap &)),
171  this,
172  SLOT(sessionDataReceived(const QVariantMap &)));
173 
174  connect(m_blobIOHandler,
175  SIGNAL(error()),
176  this,
177  SLOT(blobIOError()));
178 
179  m_blobIOHandler->setReadChannelSocketNotifier(m_readnotifier);
180 
181  return true;
182 }
183 
185 {
186  TRACE();
187 
188 #ifdef HAVE_LIBPROXY
189  /* Use a libproxy-based proxy factory; this code will no longer be
190  * needed when https://bugreports.qt-project.org/browse/QTBUG-26295
191  * is fixed. */
192  MyNetworkProxyFactory *proxyFactory = new MyNetworkProxyFactory();
193  QNetworkProxyFactory::setApplicationProxyFactory(proxyFactory);
194 #endif
195 
196  return true;
197 }
198 
199 void RemotePluginProcess::blobIOError()
200 {
201  error(
202  Error(Error::InternalServer,
203  QLatin1String("Failed to I/O session data to/from the signon daemon.")));
204  connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
205 }
206 
207 void RemotePluginProcess::result(const SignOn::SessionData &data)
208 {
209  disableCancelThread();
210  QDataStream out(&m_outFile);
211  QVariantMap resultDataMap;
212 
213  foreach(QString key, data.propertyNames())
214  resultDataMap[key] = data.getProperty(key);
215 
216  out << (quint32)PLUGIN_RESPONSE_RESULT;
217 
218  m_blobIOHandler->sendData(resultDataMap);
219 
220  m_outFile.flush();
221 }
222 
223 void RemotePluginProcess::store(const SignOn::SessionData &data)
224 {
225  QDataStream out(&m_outFile);
226  QVariantMap storeDataMap;
227 
228  foreach(QString key, data.propertyNames())
229  storeDataMap[key] = data.getProperty(key);
230 
231  out << (quint32)PLUGIN_RESPONSE_STORE;
232 
233  m_blobIOHandler->sendData(storeDataMap);
234 
235  m_outFile.flush();
236 }
237 
238 void RemotePluginProcess::error(const SignOn::Error &err)
239 {
240  disableCancelThread();
241 
242  QDataStream out(&m_outFile);
243 
244  out << (quint32)PLUGIN_RESPONSE_ERROR;
245  out << (quint32)err.type();
246  out << err.message();
247  m_outFile.flush();
248 
249  TRACE() << "error is sent" << err.type() << " " << err.message();
250 }
251 
252 void RemotePluginProcess::userActionRequired(const SignOn::UiSessionData &data)
253 {
254  TRACE();
255  disableCancelThread();
256 
257  QDataStream out(&m_outFile);
258  QVariantMap resultDataMap;
259 
260  foreach(QString key, data.propertyNames())
261  resultDataMap[key] = data.getProperty(key);
262 
263  out << (quint32)PLUGIN_RESPONSE_UI;
264  m_blobIOHandler->sendData(resultDataMap);
265  m_outFile.flush();
266 }
267 
268 void RemotePluginProcess::refreshed(const SignOn::UiSessionData &data)
269 {
270  TRACE();
271  disableCancelThread();
272 
273  QDataStream out(&m_outFile);
274  QVariantMap resultDataMap;
275 
276  foreach(QString key, data.propertyNames())
277  resultDataMap[key] = data.getProperty(key);
278 
279  m_readnotifier->setEnabled(true);
280 
281  out << (quint32)PLUGIN_RESPONSE_REFRESHED;
282 
283  m_blobIOHandler->sendData(resultDataMap);
284 
285  m_outFile.flush();
286 }
287 
288 void RemotePluginProcess::statusChanged(const AuthPluginState state,
289  const QString &message)
290 {
291  TRACE();
292  QDataStream out(&m_outFile);
293 
294  out << (quint32)PLUGIN_RESPONSE_SIGNAL;
295  out << (quint32)state;
296  out << message;
297 
298  m_outFile.flush();
299 }
300 
301 QString RemotePluginProcess::getPluginName(const QString &type)
302 {
303  QString dirName = qgetenv("SSO_PLUGINS_DIR");
304  if (dirName.isEmpty())
305  dirName = QDir::cleanPath(SIGNOND_PLUGINS_DIR);
306  QString fileName = dirName +
307  QDir::separator() +
308  QString(SIGNON_PLUGIN_PREFIX) +
309  type +
310  QString(SIGNON_PLUGIN_SUFFIX);
311 
312  return fileName;
313 }
314 
315 void RemotePluginProcess::type()
316 {
317  QDataStream out(&m_outFile);
318  out << m_plugin->type();
319 }
320 
321 void RemotePluginProcess::mechanisms()
322 {
323  QDataStream out(&m_outFile);
324  QStringList mechanisms = m_plugin->mechanisms();
325  QVariant mechsVar = mechanisms;
326  out << mechsVar;
327 }
328 
329 void RemotePluginProcess::process()
330 {
331  QDataStream in(&m_inFile);
332 
333 
334  in >> m_currentMechanism;
335 
336  int processBlobSize = -1;
337  in >> processBlobSize;
338 
339  disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
340 
341  m_currentOperation = PLUGIN_OP_PROCESS;
342  m_blobIOHandler->receiveData(processBlobSize);
343 }
344 
345 void RemotePluginProcess::userActionFinished()
346 {
347  QDataStream in(&m_inFile);
348  int processBlobSize = -1;
349  in >> processBlobSize;
350 
351  disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
352 
353  m_currentOperation = PLUGIN_OP_PROCESS_UI;
354  m_blobIOHandler->receiveData(processBlobSize);
355 }
356 
357 void RemotePluginProcess::refresh()
358 {
359  QDataStream in(&m_inFile);
360  int processBlobSize = -1;
361  in >> processBlobSize;
362 
363  disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
364 
365  m_currentOperation = PLUGIN_OP_REFRESH;
366  m_blobIOHandler->receiveData(processBlobSize);
367 }
368 
369 void RemotePluginProcess::sessionDataReceived(const QVariantMap &sessionDataMap)
370 {
371  enableCancelThread();
372  TRACE() << "The cancel thread is started";
373 
374  if (m_currentOperation == PLUGIN_OP_PROCESS) {
375  SessionData inData(sessionDataMap);
376  m_plugin->process(inData, m_currentMechanism);
377  m_currentMechanism.clear();
378 
379  } else if(m_currentOperation == PLUGIN_OP_PROCESS_UI) {
380  UiSessionData inData(sessionDataMap);
381  m_plugin->userActionFinished(inData);
382 
383  } else if(m_currentOperation == PLUGIN_OP_REFRESH) {
384  UiSessionData inData(sessionDataMap);
385  m_plugin->refresh(inData);
386 
387  } else {
388  TRACE() << "Wrong operation code.";
389  error(Error(Error::InternalServer,
390  QLatin1String("Plugin process - invalid operation code.")));
391  }
392 
393  m_currentOperation = PLUGIN_OP_STOP;
394  connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
395 }
396 
397 void RemotePluginProcess::enableCancelThread()
398 {
399  QEventLoop loop;
400  connect(cancelThread,
401  SIGNAL(started()),
402  &loop,
403  SLOT(quit()));
404 
405  m_readnotifier->setEnabled(false);
406  QTimer::singleShot(0.5*1000, &loop, SLOT(quit()));
407  cancelThread->start();
408  loop.exec();
409  QThread::yieldCurrentThread();
410 }
411 
412 void RemotePluginProcess::disableCancelThread()
413 {
414  if (!cancelThread->isRunning())
415  return;
416 
423  cancelThread->quit();
424 
425  TRACE() << "Before the isFinished loop ";
426 
427  int i = 0;
428  while (!cancelThread->isFinished()) {
429  cancelThread->quit();
430  TRACE() << "Internal iteration " << i++;
431  usleep(0.005 * 1000000);
432  }
433 
434  if (!cancelThread->wait(500)) {
435  BLAME() << "Cannot disable cancel thread";
436  int i;
437  for (i = 0; i < 5; i++) {
438  usleep(0.01 * 1000000);
439  if (cancelThread->wait(500))
440  break;
441  }
442 
443  if (i == 5) {
444  BLAME() << "Cannot do anything with cancel thread";
445  cancelThread->terminate();
446  cancelThread->wait();
447  }
448  }
449 
450  m_readnotifier->setEnabled(true);
451 }
452 
454 {
455  quint32 opcode = PLUGIN_OP_STOP;
456  bool is_stopped = false;
457 
458  QDataStream in(&m_inFile);
459  in >> opcode;
460 
461  switch (opcode) {
462  case PLUGIN_OP_CANCEL:
463  {
464  m_plugin->cancel(); break;
465  //still do not have clear understanding
466  //of the cancelation-stop mechanism
467  //is_stopped = true;
468  }
469  break;
470  case PLUGIN_OP_TYPE:
471  type();
472  break;
473  case PLUGIN_OP_MECHANISMS:
474  mechanisms();
475  break;
476  case PLUGIN_OP_PROCESS:
477  process();
478  break;
479  case PLUGIN_OP_PROCESS_UI:
480  userActionFinished();
481  break;
482  case PLUGIN_OP_REFRESH:
483  refresh();
484  break;
485  case PLUGIN_OP_STOP:
486  is_stopped = true;
487  break;
488  default:
489  {
490  qCritical() << " unknown operation code: " << opcode;
491  is_stopped = true;
492  }
493  break;
494  };
495 
496  TRACE() << "operation is completed";
497 
498  if (!is_stopped) {
499  if (!m_outFile.flush())
500  is_stopped = true;
501  }
502 
503  if (is_stopped)
504  {
505  m_plugin->abort();
506  emit processStopped();
507  }
508 }
509 
510 CancelEventThread::CancelEventThread(AuthPluginInterface *plugin)
511 {
512  m_plugin = plugin;
513  m_cancelNotifier = 0;
514 }
515 
517 {
518  delete m_cancelNotifier;
519 }
520 
522 {
523  if (!m_cancelNotifier) {
524  m_cancelNotifier = new QSocketNotifier(STDIN_FILENO,
525  QSocketNotifier::Read);
526  connect(m_cancelNotifier, SIGNAL(activated(int)),
527  this, SLOT(cancel()), Qt::DirectConnection);
528  }
529 
530  m_cancelNotifier->setEnabled(true);
531  exec();
532  m_cancelNotifier->setEnabled(false);
533 }
534 
536 {
537  char buf[4];
538  memset(buf, 0, 4);
539  int n = 0;
540 
541  if (!(n = read(STDIN_FILENO, buf, 4))) {
542  qCritical() << "Cannot read from cancel socket";
543  return;
544  }
545 
546  /*
547  * Read the actual value of
548  * */
549  QByteArray ba(buf, 4);
550  quint32 opcode;
551  QDataStream ds(ba);
552  ds >> opcode;
553 
554  if (opcode != PLUGIN_OP_CANCEL)
555  qCritical() << "wrong operation code: breakage of remotepluginprocess "
556  "threads synchronization: " << opcode;
557 
558  m_plugin->cancel();
559 }
560 
561 } //namespace RemotePluginProcessNS
562 
#define SIGNON_PLUGIN_SUFFIX
#define BLAME()
Definition: debug.h:32
void sessionDataReceived(const QVariantMap &sessionDataMap)
static RemotePluginProcess * createRemotePluginProcess(QString &type, QObject *parent)
Thread to enable cancel functionality.
Class to execute plugin process.
#define SIGNON_PLUGIN_PREFIX
#define TRACE()
Definition: debug.h:28
#define SIGNOND_PLUGINS_DIR
CancelEventThread(AuthPluginInterface *plugin)