signon  8.42
signondaemon.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: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
7  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1 as published by the Free Software Foundation.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23 
24 extern "C" {
25  #include <sys/socket.h>
26  #include <sys/stat.h>
27  #include <sys/types.h>
28 }
29 
30 #include <QtDebug>
31 #include <QDir>
32 #include <QDBusConnection>
33 #include <QDBusMessage>
34 #include <QDBusMetaType>
35 #include <QPluginLoader>
36 #include <QProcessEnvironment>
37 #include <QSocketNotifier>
38 
39 #include "SignOn/misc.h"
40 
41 #include "signondaemon.h"
42 #include "signond-common.h"
43 #include "signontrace.h"
44 #include "signondaemonadaptor.h"
45 #include "signonidentity.h"
46 #include "signonauthsession.h"
48 #include "backupifadaptor.h"
49 
50 #define SIGNON_RETURN_IF_CAM_UNAVAILABLE(_ret_arg_) do { \
51  if (m_pCAMManager && !m_pCAMManager->credentialsSystemOpened()) { \
52  sendErrorReply(internalServerErrName, \
53  internalServerErrStr + \
54  QLatin1String("Could not access Signon " \
55  "Database.")); \
56  return _ret_arg_; \
57  } \
58  } while(0)
59 
60 #define BACKUP_DIR_NAME() \
61  (QDir::separator() + QLatin1String("backup"))
62 
63 using namespace SignOn;
64 
65 namespace SignonDaemonNS {
66 
67 /* ---------------------- SignonDaemonConfiguration ---------------------- */
68 
69 SignonDaemonConfiguration::SignonDaemonConfiguration():
70  m_pluginsDir(QLatin1String(SIGNOND_PLUGINS_DIR)),
71  m_extensionsDir(QLatin1String(SIGNOND_EXTENSIONS_DIR)),
72  m_camConfiguration(),
73  m_daemonTimeout(0), // 0 = no timeout
74  m_identityTimeout(300),//secs
75  m_authSessionTimeout(300)//secs
76 {}
77 
79 {
80  TRACE();
81 }
82 
83 /*
84  --- Configuration file template ---
85 
86  [General]
87  UseSecureStorage=yes
88  StoragePath=~/.signon/
89  ;0 - fatal, 1 - critical(default), 2 - info/debug
90  LoggingLevel=1
91 
92  [SecureStorage]
93  FileSystemName=signonfs
94  Size=8
95  FileSystemType=ext2
96 
97  [ObjectTimeouts]
98  IdentityTimeout=300
99  AuthSessionTimeout=300
100  */
102 {
103  //Daemon configuration file
104 
105  QSettings::setPath(QSettings::NativeFormat, QSettings::SystemScope,
106  QLatin1String("/etc"));
107 
108  QSettings settings(QLatin1String("signond"));
109 
110  int loggingLevel =
111  settings.value(QLatin1String("LoggingLevel"), 1).toInt();
112  setLoggingLevel(loggingLevel);
113 
114  QString storagePath =
115  QDir(settings.value(QLatin1String("StoragePath")).toString()).path();
116  m_camConfiguration.setStoragePath(storagePath);
117 
118  // Secure storage
119 
120  // Support legacy setting "UseSecureStorage"
121  QString useSecureStorage =
122  settings.value(QLatin1String("UseSecureStorage")).toString();
123  if (useSecureStorage == QLatin1String("yes") ||
124  useSecureStorage == QLatin1String("true")) {
125  m_camConfiguration.addSetting(QLatin1String("CryptoManager"),
126  QLatin1String("cryptsetup"));
127  }
128 
129  settings.beginGroup(QLatin1String("SecureStorage"));
130 
131  QVariantMap storageOptions;
132  foreach (const QString &key, settings.childKeys()) {
133  m_camConfiguration.addSetting(key, settings.value(key));
134  }
135 
136  settings.endGroup();
137 
138  //Timeouts
139  settings.beginGroup(QLatin1String("ObjectTimeouts"));
140 
141  bool isOk = false;
142  uint aux = settings.value(QLatin1String("Identity")).toUInt(&isOk);
143  if (isOk)
144  m_identityTimeout = aux;
145 
146  aux = settings.value(QLatin1String("AuthSession")).toUInt(&isOk);
147  if (isOk)
148  m_authSessionTimeout = aux;
149 
150  settings.endGroup();
151 
152  //Environment variables
153 
154  QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
155  int value = 0;
156  if (environment.contains(QLatin1String("SSO_DAEMON_TIMEOUT"))) {
157  value = environment.value(
158  QLatin1String("SSO_DAEMON_TIMEOUT")).toInt(&isOk);
159  if (value > 0 && isOk) m_daemonTimeout = value;
160  }
161 
162  if (environment.contains(QLatin1String("SSO_IDENTITY_TIMEOUT"))) {
163  value = environment.value(
164  QLatin1String("SSO_IDENTITY_TIMEOUT")).toInt(&isOk);
165  if (value > 0 && isOk) m_identityTimeout = value;
166  }
167 
168  if (environment.contains(QLatin1String("SSO_AUTHSESSION_TIMEOUT"))) {
169  value = environment.value(
170  QLatin1String("SSO_AUTHSESSION_TIMEOUT")).toInt(&isOk);
171  if (value > 0 && isOk) m_authSessionTimeout = value;
172  }
173 
174  if (environment.contains(QLatin1String("SSO_LOGGING_LEVEL"))) {
175  value = environment.value(
176  QLatin1String("SSO_LOGGING_LEVEL")).toInt(&isOk);
177  if (isOk)
178  setLoggingLevel(value);
179  }
180 
181  QString logOutput = environment.value(QLatin1String("SSO_LOGGING_OUTPUT"),
182  QLatin1String("syslog"));
183  SignonTrace::initialize(logOutput == QLatin1String("syslog") ?
184  SignonTrace::Syslog : SignonTrace::Stdout);
185 
186  if (environment.contains(QLatin1String("SSO_STORAGE_PATH"))) {
187  m_camConfiguration.setStoragePath(
188  environment.value(QLatin1String("SSO_STORAGE_PATH")));
189  }
190 
191  if (environment.contains(QLatin1String("SSO_PLUGINS_DIR"))) {
192  m_pluginsDir = environment.value(QLatin1String("SSO_PLUGINS_DIR"));
193  }
194 
195  if (environment.contains(QLatin1String("SSO_EXTENSIONS_DIR"))) {
196  m_extensionsDir =
197  environment.value(QLatin1String("SSO_EXTENSIONS_DIR"));
198  }
199 }
200 
201 /* ---------------------- SignonDaemon ---------------------- */
202 
203 const QString internalServerErrName = SIGNOND_INTERNAL_SERVER_ERR_NAME;
204 const QString internalServerErrStr = SIGNOND_INTERNAL_SERVER_ERR_STR;
205 
206 static int sigFd[2];
207 
208 SignonDaemon *SignonDaemon::m_instance = NULL;
209 
210 SignonDaemon::SignonDaemon(QObject *parent) : QObject(parent)
211  , m_configuration(NULL)
212 {
213  // Files created by signond must be unreadable by "other"
214  umask(S_IROTH | S_IWOTH);
215 
216  // Register D-Bus meta types
217  qDBusRegisterMetaType<MethodMap>();
218  qDBusRegisterMetaType<MapList>();
219 }
220 
221 SignonDaemon::~SignonDaemon()
222 {
223  ::close(sigFd[0]);
224  ::close(sigFd[1]);
225 
226  if (m_backup) {
227  exit(0);
228  }
229 
230  SignonAuthSession::stopAllAuthSessions();
231  m_storedIdentities.clear();
232  m_unstoredIdentities.clear();
233 
234  if (m_pCAMManager) {
235  m_pCAMManager->closeCredentialsSystem();
236  delete m_pCAMManager;
237  }
238 
239  QDBusConnection sessionConnection = QDBusConnection::sessionBus();
240 
241  sessionConnection.unregisterObject(SIGNOND_DAEMON_OBJECTPATH
242  + QLatin1String("/Backup"));
243  sessionConnection.unregisterService(SIGNOND_SERVICE
244  + QLatin1String(".Backup"));
245  if (m_backup == false)
246  {
247  sessionConnection.unregisterObject(SIGNOND_DAEMON_OBJECTPATH);
248  sessionConnection.unregisterService(SIGNOND_SERVICE);
249  }
250 
251  delete m_configuration;
252 
253  QMetaObject::invokeMethod(QCoreApplication::instance(),
254  "quit",
255  Qt::QueuedConnection);
256 }
257 
258 void SignonDaemon::setupSignalHandlers()
259 {
260  if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigFd) != 0)
261  BLAME() << "Couldn't create HUP socketpair";
262 
263  m_sigSn = new QSocketNotifier(sigFd[1], QSocketNotifier::Read, this);
264  connect(m_sigSn, SIGNAL(activated(int)),
265  this, SLOT(handleUnixSignal()));
266 }
267 
268 void SignonDaemon::signalHandler(int signal)
269 {
270  int ret = ::write(sigFd[0], &signal, sizeof(signal));
271  Q_UNUSED(ret);
272 }
273 
274 void SignonDaemon::handleUnixSignal()
275 {
276  m_sigSn->setEnabled(false);
277 
278  int signal;
279  int ret = read(sigFd[1], &signal, sizeof(signal));
280  Q_UNUSED(ret);
281 
282  TRACE() << "signal received: " << signal;
283 
284  switch (signal) {
285  case SIGHUP: {
286  TRACE() << "\n\n SIGHUP \n\n";
287  //todo restart daemon
288  deleteLater();
289 
290  // reset the m_instance
291  m_instance = NULL;
292  QMetaObject::invokeMethod(instance(),
293  "init",
294  Qt::QueuedConnection);
295  break;
296  }
297  case SIGTERM: {
298  TRACE() << "\n\n SIGTERM \n\n";
299  //gently stop daemon
300  deleteLater();
301  QMetaObject::invokeMethod(QCoreApplication::instance(),
302  "quit",
303  Qt::QueuedConnection);
304  break;
305  }
306  case SIGINT: {
307  TRACE() << "\n\n SIGINT \n\n";
308  //gently stop daemon
309  deleteLater();
310  QMetaObject::invokeMethod(QCoreApplication::instance(),
311  "quit",
312  Qt::QueuedConnection);
313  break;
314  }
315  default: break;
316  }
317 
318  m_sigSn->setEnabled(true);
319 }
320 
321 SignonDaemon *SignonDaemon::instance()
322 {
323  if (m_instance != NULL)
324  return m_instance;
325 
326  QCoreApplication *app = QCoreApplication::instance();
327 
328  if (!app)
329  qFatal("SignonDaemon requires a QCoreApplication instance to be "
330  "constructed first");
331 
332  TRACE() << "Creating new daemon instance.";
333  m_instance = new SignonDaemon(app);
334  return m_instance;
335 }
336 
337 void SignonDaemon::init()
338 {
339  if (!(m_configuration = new SignonDaemonConfiguration))
340  qWarning("SignonDaemon could not create the configuration object.");
341 
342  m_configuration->load();
343 
344  if (getuid() != 0) {
345  BLAME() << "Failed to SUID root. Secure storage will not be available.";
346  }
347 
348  QCoreApplication *app = QCoreApplication::instance();
349  if (!app)
350  qFatal("SignonDaemon requires a QCoreApplication instance to be "
351  "constructed first");
352 
353  setupSignalHandlers();
354  m_backup = app->arguments().contains(QLatin1String("-backup"));
355  m_pCAMManager =
356  new CredentialsAccessManager(m_configuration->camConfiguration());
357 
358  /* backup dbus interface */
359  QDBusConnection sessionConnection = QDBusConnection::sessionBus();
360 
361  if (!sessionConnection.isConnected()) {
362  QDBusError err = sessionConnection.lastError();
363  TRACE() << "Session connection cannot be established:" <<
364  err.errorString(err.type());
365  TRACE() << err.message();
366 
367  qFatal("SignonDaemon requires session bus to start working");
368  }
369 
370  QDBusConnection::RegisterOptions registerSessionOptions =
371  QDBusConnection::ExportAdaptors;
372 
373  (void)new BackupIfAdaptor(this);
374 
375  if (!sessionConnection.registerObject(SIGNOND_DAEMON_OBJECTPATH
376  + QLatin1String("/Backup"),
377  this, registerSessionOptions)) {
378  TRACE() << "Object cannot be registered";
379 
380  qFatal("SignonDaemon requires to register backup object");
381  }
382 
383  if (!sessionConnection.registerService(SIGNOND_SERVICE +
384  QLatin1String(".Backup"))) {
385  QDBusError err = sessionConnection.lastError();
386  TRACE() << "Service cannot be registered: " <<
387  err.errorString(err.type());
388 
389  qFatal("SignonDaemon requires to register backup service");
390  }
391 
392  if (m_backup) {
393  TRACE() << "Signond initialized in backup mode.";
394  //skip rest of initialization in backup mode
395  return;
396  }
397 
398  /* DBus Service init */
399  QDBusConnection connection = SIGNOND_BUS;
400 
401  if (!connection.isConnected()) {
402  QDBusError err = connection.lastError();
403  TRACE() << "Connection cannot be established:" <<
404  err.errorString(err.type());
405  TRACE() << err.message();
406 
407  qFatal("SignonDaemon requires DBus to start working");
408  }
409 
410  QDBusConnection::RegisterOptions registerOptions =
411  QDBusConnection::ExportAllContents;
412 
413  (void)new SignonDaemonAdaptor(this);
414  registerOptions = QDBusConnection::ExportAdaptors;
415 
416  if (!connection.registerObject(SIGNOND_DAEMON_OBJECTPATH,
417  this, registerOptions)) {
418  TRACE() << "Object cannot be registered";
419 
420  qFatal("SignonDaemon requires to register daemon's object");
421  }
422 
423  if (!connection.registerService(SIGNOND_SERVICE)) {
424  QDBusError err = connection.lastError();
425  TRACE() << "Service cannot be registered: " <<
426  err.errorString(err.type());
427 
428  qFatal("SignonDaemon requires to register daemon's service");
429  }
430 
431  // handle D-Bus disconnection
432  connection.connect(QString(),
433  QLatin1String("/org/freedesktop/DBus/Local"),
434  QLatin1String("org.freedesktop.DBus.Local"),
435  QLatin1String("Disconnected"),
436  this, SLOT(onDisconnected()));
437 
438  initExtensions();
439 
440  if (!initStorage())
441  BLAME() << "Signond: Cannot initialize credentials storage.";
442 
443  if (m_configuration->daemonTimeout() > 0) {
444  SignonDisposable::invokeOnIdle(m_configuration->daemonTimeout(),
445  this, SLOT(deleteLater()));
446  }
447 
448  TRACE() << "Signond SUCCESSFULLY initialized.";
449 }
450 
451 void SignonDaemon::initExtensions()
452 {
453  /* Scan the directory containing signond extensions and attempt loading
454  * all of them.
455  */
456  QDir dir(m_configuration->extensionsDir());
457  QStringList filters(QLatin1String("lib*.so"));
458  QStringList extensionList = dir.entryList(filters, QDir::Files);
459  foreach(QString filename, extensionList)
460  initExtension(dir.filePath(filename));
461 }
462 
463 void SignonDaemon::initExtension(const QString &filePath)
464 {
465  TRACE() << "Loading plugin " << filePath;
466 
467  QPluginLoader pluginLoader(filePath);
468  QObject *plugin = pluginLoader.instance();
469  if (plugin == 0) {
470  qWarning() << "Couldn't load plugin:" << pluginLoader.errorString();
471  return;
472  }
473 
474  /* Check whether the extension implements some useful objects; if not,
475  * unload it. */
476  bool extensionInUse = false;
477  if (m_pCAMManager->initExtension(plugin))
478  extensionInUse = true;
479 
480  if (!extensionInUse) {
481  pluginLoader.unload();
482  }
483 }
484 
485 bool SignonDaemon::initStorage()
486 {
487  if (!m_pCAMManager->credentialsSystemOpened()) {
488  m_pCAMManager->finalize();
489 
490  if (!m_pCAMManager->init()) {
491  BLAME() << "CAM initialization failed";
492  return false;
493  }
494 
495  // If encryption is in use this will just open the metadata DB
496  if (!m_pCAMManager->openCredentialsSystem()) {
497  qCritical("Signond: Cannot open CAM credentials system...");
498  return false;
499  }
500  } else {
501  TRACE() << "Secure storage already initialized...";
502  return false;
503  }
504 
505  return true;
506 }
507 
508 void SignonDaemon::identityStored(SignonIdentity *identity)
509 {
510  if (m_unstoredIdentities.contains(identity->objectName())) {
511  m_unstoredIdentities.remove(identity->objectName());
512  m_storedIdentities.insert(identity->id(), identity);
513  }
514 }
515 
516 void SignonDaemon::registerNewIdentity(QDBusObjectPath &objectPath)
517 {
518  TRACE() << "Registering new identity:";
519 
520  SignonIdentity *identity =
521  SignonIdentity::createIdentity(SIGNOND_NEW_IDENTITY, this);
522 
523  if (identity == NULL) {
524  sendErrorReply(internalServerErrName,
526  QLatin1String("Could not create remote Identity "
527  "object."));
528  return;
529  }
530 
531  m_unstoredIdentities.insert(identity->objectName(), identity);
532 
533  objectPath = QDBusObjectPath(identity->objectName());
534 }
535 
536 int SignonDaemon::identityTimeout() const
537 {
538  return (m_configuration == NULL ?
539  300 :
540  m_configuration->identityTimeout());
541 }
542 
543 int SignonDaemon::authSessionTimeout() const
544 {
545  return (m_configuration == NULL ?
546  300 :
547  m_configuration->authSessionTimeout());
548 }
549 
550 void SignonDaemon::getIdentity(const quint32 id,
551  QDBusObjectPath &objectPath,
552  QVariantMap &identityData)
553 {
555 
556  TRACE() << "Registering identity:" << id;
557 
558  //1st check if the existing identity is in cache
559  SignonIdentity *identity = m_storedIdentities.value(id, NULL);
560 
561  //if not create it
562  if (identity == NULL)
563  identity = SignonIdentity::createIdentity(id, this);
564 
565  if (identity == NULL)
566  {
567  sendErrorReply(internalServerErrName,
569  QLatin1String("Could not create remote Identity "
570  "object."));
571  return;
572  }
573 
574  bool ok;
575  SignonIdentityInfo info = identity->queryInfo(ok, false);
576 
577  if (info.isNew())
578  {
579  sendErrorReply(SIGNOND_IDENTITY_NOT_FOUND_ERR_NAME,
580  SIGNOND_IDENTITY_NOT_FOUND_ERR_STR);
581  return;
582  }
583 
584  //cache the identity as stored
585  m_storedIdentities.insert(identity->id(), identity);
586  identity->keepInUse();
587 
588  identityData = info.toMap();
589 
590  TRACE() << "DONE REGISTERING IDENTITY";
591  objectPath = QDBusObjectPath(identity->objectName());
592 }
593 
594 QStringList SignonDaemon::queryMethods()
595 {
596  QDir pluginsDir(m_configuration->pluginsDir());
597  //TODO: in the future remove the sym links comment
598  QStringList fileNames = pluginsDir.entryList(
599  QStringList() << QLatin1String("*.so*"),
600  QDir::Files | QDir::NoDotAndDotDot);
601 
602  QStringList ret;
603  QString fileName;
604  foreach (fileName, fileNames) {
605  if (fileName.startsWith(QLatin1String("lib"))) {
606  fileName =
607  fileName.mid(3, fileName.indexOf(QLatin1String("plugin")) -3);
608  if ((fileName.length() > 0) && !ret.contains(fileName))
609  ret << fileName;
610  }
611  }
612 
613  return ret;
614 }
615 
616 QStringList SignonDaemon::queryMechanisms(const QString &method)
617 {
618  TRACE() << "\n\n\n Querying mechanisms\n\n";
619 
620  QStringList mechs = SignonSessionCore::loadedPluginMethods(method);
621 
622  if (mechs.size())
623  return mechs;
624 
625  PluginProxy *plugin = PluginProxy::createNewPluginProxy(method);
626 
627  if (!plugin) {
628  TRACE() << "Could not load plugin of type: " << method;
629  sendErrorReply(SIGNOND_METHOD_NOT_KNOWN_ERR_NAME,
630  SIGNOND_METHOD_NOT_KNOWN_ERR_STR +
631  QString::fromLatin1("Method %1 is not known or could "
632  "not load specific configuration.").
633  arg(method));
634  return QStringList();
635  }
636 
637  mechs = plugin->mechanisms();
638  delete plugin;
639 
640  return mechs;
641 }
642 
643 QList<QVariantMap> SignonDaemon::queryIdentities(const QVariantMap &filter)
644 {
645  SIGNON_RETURN_IF_CAM_UNAVAILABLE(QList<QVariantMap>());
646 
647  TRACE() << "Querying identities";
648 
649  CredentialsDB *db = m_pCAMManager->credentialsDB();
650  if (!db) {
651  qCritical() << Q_FUNC_INFO << m_pCAMManager->lastError();
652  return QList<QVariantMap>();
653  }
654 
655  QMap<QString, QString> filterLocal;
656  QMapIterator<QString, QVariant> it(filter);
657  while (it.hasNext()) {
658  it.next();
659  filterLocal.insert(it.key(), it.value().toString());
660  }
661 
662  QList<SignonIdentityInfo> credentials = db->credentials(filterLocal);
663 
664  if (db->errorOccurred()) {
665  sendErrorReply(internalServerErrName,
667  QLatin1String("Querying database error occurred."));
668  return QList<QVariantMap>();
669  }
670 
671  QList<QVariantMap> mapList;
672  foreach (SignonIdentityInfo info, credentials) {
673  mapList.append(info.toMap());
674  }
675  return mapList;
676 }
677 
678 bool SignonDaemon::clear()
679 {
681 
682  TRACE() << "\n\n\n Clearing DB\n\n";
683  CredentialsDB *db = m_pCAMManager->credentialsDB();
684  if (!db) {
685  qCritical() << Q_FUNC_INFO << m_pCAMManager->lastError();
686  return false;
687  }
688 
689  if (!db->clear()) {
690  sendErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
691  SIGNOND_INTERNAL_SERVER_ERR_STR +
692  QLatin1String("Database error occurred."));
693  return false;
694  }
695  return true;
696 }
697 
698 QString SignonDaemon::getAuthSessionObjectPath(const quint32 id,
699  const QString type)
700 {
701  bool supportsAuthMethod = false;
702  pid_t ownerPid = AccessControlManagerHelper::pidOfPeer(*this);
703  QString objectPath =
704  SignonAuthSession::getAuthSessionObjectPath(id, type, this,
705  supportsAuthMethod,
706  ownerPid);
707  if (objectPath.isEmpty() && !supportsAuthMethod) {
708  sendErrorReply(SIGNOND_METHOD_NOT_KNOWN_ERR_NAME,
709  SIGNOND_METHOD_NOT_KNOWN_ERR_STR);
710  return QString();
711  }
712  return objectPath;
713 }
714 
715 void SignonDaemon::eraseBackupDir() const
716 {
717  const CAMConfiguration config = m_configuration->camConfiguration();
718  QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
719 
720  QDir target(backupRoot);
721  if (!target.exists()) return;
722 
723  QStringList targetEntries = target.entryList(QDir::Files);
724  foreach (QString entry, targetEntries) {
725  target.remove(entry);
726  }
727 
728  target.rmdir(backupRoot);
729 }
730 
731 bool SignonDaemon::copyToBackupDir(const QStringList &fileNames) const
732 {
733  const CAMConfiguration config = m_configuration->camConfiguration();
734  QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
735 
736  QDir target(backupRoot);
737  if (!target.exists() && !target.mkpath(backupRoot)) {
738  qCritical() << "Cannot create target directory";
739  return false;
740  }
741 
742  setUserOwnership(backupRoot);
743 
744  /* Now copy the files to be backed up */
745  bool ok = true;
746  foreach (QString fileName, fileNames) {
747  /* Remove the target file, if it exists */
748  if (target.exists(fileName))
749  target.remove(fileName);
750 
751  /* Copy the source into the target directory */
752  QString source = config.m_storagePath + QDir::separator() + fileName;
753  if (!QFile::exists(source)) continue;
754 
755  QString destination = backupRoot + QDir::separator() + fileName;
756  ok = QFile::copy(source, destination);
757  if (!ok) {
758  BLAME() << "Copying" << source << "to" << destination << "failed";
759  break;
760  }
761 
762  setUserOwnership(destination);
763  }
764 
765  return ok;
766 }
767 
768 bool SignonDaemon::copyFromBackupDir(const QStringList &fileNames) const
769 {
770  const CAMConfiguration config = m_configuration->camConfiguration();
771  QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
772 
773  QDir sourceDir(backupRoot);
774  if (!sourceDir.exists()) {
775  TRACE() << "Backup directory does not exist!";
776  }
777 
778  if (!sourceDir.exists(config.m_dbName)) {
779  TRACE() << "Backup does not contain DB:" << config.m_dbName;
780  }
781 
782  /* Now restore the files from the backup */
783  bool ok = true;
784  QDir target(config.m_storagePath);
785  QStringList movedFiles, copiedFiles;
786  foreach (QString fileName, fileNames) {
787  /* Remove the target file, if it exists */
788  if (target.exists(fileName)) {
789  if (target.rename(fileName, fileName + QLatin1String(".bak")))
790  movedFiles += fileName;
791  }
792 
793  /* Copy the source into the target directory */
794  QString source = backupRoot + QDir::separator() + fileName;
795  if (!QFile::exists(source)) {
796  TRACE() << "Ignoring file not present in backup:" << source;
797  continue;
798  }
799 
800  QString destination =
801  config.m_storagePath + QDir::separator() + fileName;
802 
803  ok = QFile::copy(source, destination);
804  if (ok) {
805  copiedFiles << fileName;
806  } else {
807  qWarning() << "Copy failed for:" << source;
808  break;
809  }
810  }
811 
812  if (!ok) {
813  qWarning() << "Restore failed, recovering previous DB";
814 
815  foreach (QString fileName, copiedFiles) {
816  target.remove(fileName);
817  }
818 
819  foreach (QString fileName, movedFiles) {
820  if (!target.rename(fileName + QLatin1String(".bak"), fileName)) {
821  qCritical() << "Could not recover:" << fileName;
822  }
823  }
824  } else {
825  /* delete ".bak" files */
826  foreach (QString fileName, movedFiles) {
827  target.remove(fileName + QLatin1String(".bak"));
828  }
829 
830  }
831  return ok;
832 }
833 
834 bool SignonDaemon::createStorageFileTree(const QStringList &backupFiles) const
835 {
836  QString storageDirPath = m_configuration->camConfiguration().m_storagePath;
837  QDir storageDir(storageDirPath);
838 
839  if (!storageDir.exists()) {
840  if (!storageDir.mkpath(storageDirPath)) {
841  qCritical() << "Could not create storage dir for backup.";
842  return false;
843  }
844  }
845 
846  foreach (QString fileName, backupFiles) {
847  if (storageDir.exists(fileName)) continue;
848 
849  QString filePath = storageDir.path() + QDir::separator() + fileName;
850  QFile file(filePath);
851  if (!file.open(QIODevice::WriteOnly)) {
852  qCritical() << "Failed to create empty file for backup:" << filePath;
853  return false;
854  } else {
855  file.close();
856  }
857  }
858 
859  return true;
860 }
861 
862 uchar SignonDaemon::backupStarts()
863 {
864  TRACE() << "backup";
865  if (!m_backup && m_pCAMManager->credentialsSystemOpened())
866  {
867  m_pCAMManager->closeCredentialsSystem();
868  if (m_pCAMManager->credentialsSystemOpened())
869  {
870  qCritical() << "Cannot close credentials database";
871  return 2;
872  }
873  }
874 
875  const CAMConfiguration config = m_configuration->camConfiguration();
876 
877  /* do backup copy: prepare the list of files to be backed up */
878  QStringList backupFiles;
879  backupFiles << config.m_dbName;
880  backupFiles << m_pCAMManager->backupFiles();
881 
882  /* make sure that all the backup files and storage directory exist:
883  create storage dir and empty files if not so, as backup/restore
884  operations must be consistent */
885  if (!createStorageFileTree(backupFiles)) {
886  qCritical() << "Cannot create backup file tree.";
887  return 2;
888  }
889 
890  /* perform the copy */
891  eraseBackupDir();
892  if (!copyToBackupDir(backupFiles)) {
893  qCritical() << "Cannot copy database";
894  if (!m_backup)
895  m_pCAMManager->openCredentialsSystem();
896  return 2;
897  }
898 
899  if (!m_backup)
900  {
901  //mount file system back
902  if (!m_pCAMManager->openCredentialsSystem()) {
903  qCritical() << "Cannot reopen database";
904  }
905  }
906  return 0;
907 }
908 
909 uchar SignonDaemon::backupFinished()
910 {
911  TRACE() << "close";
912 
913  eraseBackupDir();
914 
915  if (m_backup)
916  {
917  //close daemon
918  TRACE() << "close daemon";
919  this->deleteLater();
920  }
921 
922  return 0;
923  }
924 
925 /*
926  * Does nothing but start-on-demand
927  * */
928 uchar SignonDaemon::restoreStarts()
929 {
930  TRACE();
931  return 0;
932 }
933 
934 uchar SignonDaemon::restoreFinished()
935 {
936  TRACE() << "restore";
937  //restore requested
938  if (m_pCAMManager->credentialsSystemOpened())
939  {
940  //umount file system
941  if (!m_pCAMManager->closeCredentialsSystem())
942  {
943  qCritical() << "database cannot be closed";
944  return 2;
945  }
946  }
947 
948  const CAMConfiguration config = m_configuration->camConfiguration();
949 
950  QStringList backupFiles;
951  backupFiles << config.m_dbName;
952  backupFiles << m_pCAMManager->backupFiles();
953 
954  /* perform the copy */
955  if (!copyFromBackupDir(backupFiles)) {
956  qCritical() << "Cannot copy database";
957  m_pCAMManager->openCredentialsSystem();
958  return 2;
959  }
960 
961  eraseBackupDir();
962 
963  //TODO check database integrity
964  if (!m_backup)
965  {
966  //mount file system back
967  if (!m_pCAMManager->openCredentialsSystem())
968  return 2;
969  }
970 
971  return 0;
972 }
973 
974 void SignonDaemon::onDisconnected()
975 {
976  TRACE() << "Disconnected from session bus: exiting";
977  this->deleteLater();
978  QMetaObject::invokeMethod(QCoreApplication::instance(),
979  "quit",
980  Qt::QueuedConnection);
981 }
982 
983 } //namespace SignonDaemonNS