commit akonadi-server for openSUSE:Factory
Hello community, here is the log from the commit of package akonadi-server for openSUSE:Factory checked in at 2017-01-25 23:15:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/akonadi-server (Old) and /work/SRC/openSUSE:Factory/.akonadi-server.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "akonadi-server" Changes: -------- --- /work/SRC/openSUSE:Factory/akonadi-server/akonadi-server.changes 2016-12-29 22:43:03.455351682 +0100 +++ /work/SRC/openSUSE:Factory/.akonadi-server.new/akonadi-server.changes 2017-01-25 23:15:40.576075342 +0100 @@ -1,0 +2,14 @@ +Sun Jan 22 17:10:01 UTC 2017 - hrvoje.senjan@gmail.com + +- Add handle-mysql-process-crashes-gracefully.patch from upstream + +------------------------------------------------------------------- +Sat Jan 14 08:48:19 UTC 2017 - lbeltrame@kde.org + +- - Update to KDE Applications 16.12.1 + * KDE Applications 16.12.1 + * https://www.kde.org/announcements/announce-applications-16.12.1.php + +- Drop upstream patch fix-connect-api.patch + +------------------------------------------------------------------- Old: ---- akonadi-16.12.0.tar.xz fix-connect-api.patch New: ---- akonadi-16.12.1.tar.xz handle-mysql-process-crashes-gracefully.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ akonadi-server.spec ++++++ --- /var/tmp/diff_new_pack.qZneu0/_old 2017-01-25 23:15:41.323962578 +0100 +++ /var/tmp/diff_new_pack.qZneu0/_new 2017-01-25 23:15:41.327961975 +0100 @@ -1,7 +1,7 @@ # # spec file for package akonadi-server # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,10 +18,10 @@ %define rname akonadi %define kf5_version 5.26.0 -# Latest stable Applications (e.g. 16.08 in KA, but 16.12.0 in KUA) +# Latest stable Applications (e.g. 16.08 in KA, but 16.12.1 in KUA) %{!?_kapp_version: %global _kapp_version %(echo %{version}| awk -F. '{print $1"."$2}')} Name: akonadi-server -Version: 16.12.0 +Version: 16.12.1 Release: 0 Summary: PIM Storage Service License: LGPL-2.1+ @@ -29,8 +29,8 @@ Url: http://akonadi-project.org Source: %{rname}-%{version}.tar.xz Source99: akonadi-server-rpmlintrc -#PATCH-FIX-UPSTREAM fix-connect-api.patch montel@kde.org -Patch1: fix-connect-api.patch +# PATCH-FIX-UPSTREAM handle-mysql-process-crashes-gracefully.patch +Patch0: handle-mysql-process-crashes-gracefully.patch BuildRequires: boost-devel BuildRequires: cmake >= 2.8.12 BuildRequires: extra-cmake-modules >= %{kf5_version} @@ -161,7 +161,7 @@ %prep %setup -q -n %{rname}-%{version} -%patch1 -p1 +%patch0 -p1 %build %cmake_kf5 -d build -- -DINSTALL_QSQLITE_IN_QT_PREFIX=TRUE -DQT_PLUGINS_DIR=%{_kf5_plugindir} ++++++ akonadi-16.12.0.tar.xz -> akonadi-16.12.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/CMakeLists.txt new/akonadi-16.12.1/CMakeLists.txt --- old/akonadi-16.12.0/CMakeLists.txt 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/CMakeLists.txt 2017-01-08 16:41:33.000000000 +0100 @@ -22,7 +22,7 @@ include(AkonadiMacros) -set(PIM_VERSION "5.4.0") +set(PIM_VERSION "5.4.1") set(QT_REQUIRED_VERSION "5.6.0") set(AKONADI_VERSION ${PIM_VERSION}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/core/changerecorder_p.cpp new/akonadi-16.12.1/src/core/changerecorder_p.cpp --- old/akonadi-16.12.0/src/core/changerecorder_p.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/core/changerecorder_p.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -127,11 +127,11 @@ notificationsLoaded(); } -static const quint64 s_currentVersion = Q_UINT64_C(0x000500000000); +static const quint64 s_currentVersion = Q_UINT64_C(0x000600000000); static const quint64 s_versionMask = Q_UINT64_C(0xFFFF00000000); static const quint64 s_sizeMask = Q_UINT64_C(0x0000FFFFFFFF); -QQueue<Protocol::ChangeNotification> ChangeRecorderPrivate::loadFrom(QIODevice *device, bool &needsFullSave) const +QQueue<Protocol::ChangeNotification> ChangeRecorderPrivate::loadFrom(QFile *device, bool &needsFullSave) const { QDataStream stream(device); stream.setVersion(QDataStream::Qt_4_6); @@ -161,6 +161,11 @@ stream >> sessionId; stream >> type; + if (stream.status() != QDataStream::Ok) { + qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting. Corrupt file:" << device->fileName(); + break; + } + switch (static_cast<LegacyType>(type)) { case Item: msg = loadItemNotification(stream, version); @@ -463,6 +468,10 @@ stream >> remoteId; stream >> remoteRevision; stream >> mimeType; + if (stream.status() != QDataStream::Ok) { + qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting"; + return msg; + } msg.addItem(uid, remoteId, remoteRevision, mimeType); } stream >> resource; @@ -558,6 +567,10 @@ stream >> remoteId; stream >> remoteRevision; stream >> dummyString; + if (stream.status() != QDataStream::Ok) { + qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting"; + return msg; + } msg.setId(uid); msg.setRemoteId(remoteId); msg.setRemoteRevision(remoteRevision); @@ -637,6 +650,10 @@ stream >> remoteId; stream >> dummyString; stream >> dummyString; + if (stream.status() != QDataStream::Ok) { + qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting"; + return msg; + } msg.setId(uid); msg.setRemoteId(remoteId); } @@ -702,9 +719,20 @@ stream >> dummyString; stream >> dummyString; stream >> dummyString; + if (stream.status() != QDataStream::Ok) { + qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting"; + return msg; + } } stream >> dummyBa; - stream >> dummyBa; + if (version == 5) { + // there was a bug in version 5 serializer that serialized this + // field as qint64 (8 bytes) instead of empty QByteArray (which is + // 4 bytes) + stream >> dummyI; + } else { + stream >> dummyBa; + } stream >> dummyI; stream >> dummyI; stream >> itemParts; @@ -754,7 +782,7 @@ stream << QString(); stream << QString(); stream << QByteArray(); - stream << qint64(0); + stream << QByteArray(); stream << qint64(0); stream << qint64(0); stream << rv; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/core/changerecorder_p.h new/akonadi-16.12.1/src/core/changerecorder_p.h --- old/akonadi-16.12.0/src/core/changerecorder_p.h 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/core/changerecorder_p.h 2017-01-08 16:41:33.000000000 +0100 @@ -51,7 +51,7 @@ QString notificationsFileName() const; void loadNotifications(); - QQueue<Protocol::ChangeNotification> loadFrom(QIODevice *device, bool &needsFullSave) const; + QQueue<Protocol::ChangeNotification> loadFrom(QFile *device, bool &needsFullSave) const; QString dumpNotificationListToString() const; void addToStream(QDataStream &stream, const Protocol::ChangeNotification &msg); void saveNotifications(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/core/monitor_p.cpp new/akonadi-16.12.1/src/core/monitor_p.cpp --- old/akonadi-16.12.0/src/core/monitor_p.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/core/monitor_p.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -953,6 +953,13 @@ it.setRemoteId(msgItem.remoteId); it.setRemoteRevision(msgItem.remoteRevision); it.setMimeType(msgItem.mimeType); + } else if (msg.operation() == Protocol::ItemChangeNotification::Move) { + // For moves we remove the RID from the PimItemTable to prevent + // RID conflict during merge (see T3904 in Phab), so restore the + // RID from notification. + // TODO: Should we do this for all items with empty RID? Right now + // I only know about this usecase. + it.setRemoteId(msgItem.remoteId); } if (!it.parentCollection().isValid()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/collectionscheduler.cpp new/akonadi-16.12.1/src/server/collectionscheduler.cpp --- old/akonadi-16.12.0/src/server/collectionscheduler.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/collectionscheduler.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -66,12 +66,7 @@ Q_INVOKABLE void pause() { - if (!isActive()) { - qCCritical(AKONADISERVER_LOG) << "Cannot pause an inactive timer"; - return; - } - if (isPaused()) { - qCCritical(AKONADISERVER_LOG) << "Cannot pause an already paused timer"; + if (!isActive() || isPaused()) { return; } @@ -82,7 +77,6 @@ Q_INVOKABLE void resume() { if (!isPaused()) { - qCCritical(AKONADISERVER_LOG) << "Cannot resume a timer that is not paused."; return; } @@ -128,14 +122,12 @@ void CollectionScheduler::inhibit(bool inhibit) { - if (inhibit && mScheduler->isActive() && !mScheduler->isPaused()) { + if (inhibit) { const bool success = QMetaObject::invokeMethod(mScheduler, "pause", Qt::QueuedConnection); - Q_ASSERT(success); - Q_UNUSED(success); - } else if (!inhibit && mScheduler->isPaused()) { + Q_ASSERT(success); Q_UNUSED(success); + } else { const bool success = QMetaObject::invokeMethod(mScheduler, "resume", Qt::QueuedConnection); - Q_ASSERT(success); - Q_UNUSED(success); + Q_ASSERT(success); Q_UNUSED(success); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/connection.cpp new/akonadi-16.12.1/src/server/connection.cpp --- old/akonadi-16.12.0/src/server/connection.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/connection.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -96,7 +96,7 @@ connect(socket, &QIODevice::readyRead, this, &Connection::slotNewData); connect(socket, &QLocalSocket::disconnected, - this, &Connection::disconnected); + this, &Connection::slotSocketDisconnected); m_idleTimer = new QTimer(this); connect(m_idleTimer, &QTimer::timeout, @@ -169,8 +169,23 @@ } } +void Connection::slotSocketDisconnected() +{ + // If we have active handler, wait for it to finish, then we emit the signal + // from slotNewDate() + if (m_currentHandler) { + return; + } + + Q_EMIT disconnected(); +} + void Connection::slotNewData() { + if (m_socket->state() != QLocalSocket::ConnectedState) { + return; + } + m_idleTimer->stop(); // will only open() a previously idle backend. @@ -251,6 +266,11 @@ } delete m_currentHandler; m_currentHandler = 0; + + if (m_socket->state() != QLocalSocket::ConnectedState) { + Q_EMIT disconnected(); + return; + } } // reset, arm the timer @@ -394,7 +414,7 @@ { while (m_socket->bytesAvailable() < (int) sizeof(qint64)) { if (m_socket->state() == QLocalSocket::UnconnectedState) { - return Protocol::Command(); + throw ProtocolException("Socket disconnected"); } m_socket->waitForReadyRead(500); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/connection.h new/akonadi-16.12.1/src/server/connection.h --- old/akonadi-16.12.0/src/server/connection.h 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/connection.h 2017-01-08 16:41:33.000000000 +0100 @@ -85,7 +85,7 @@ void slotNewData(); void slotConnectionStateChange(ConnectionState state); void slotConnectionIdle(); - + void slotSocketDisconnected(); void slotSendHello(); protected: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/handler/akappend.cpp new/akonadi-16.12.1/src/server/handler/akappend.cpp --- old/akonadi-16.12.0/src/server/handler/akappend.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/handler/akappend.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -82,14 +82,14 @@ bool AkAppend::insertItem(const Protocol::CreateItemCommand &cmd, PimItem &item, const Collection &parentCol) { - if (!item.insert()) { - return failureResponse("Failed to append item"); - } - if (!item.datetime().isValid()) { item.setDatetime(QDateTime::currentDateTimeUtc()); } + if (!item.insert()) { + return failureResponse("Failed to append item"); + } + // set message flags const QSet<QByteArray> flags = cmd.mergeModes() == Protocol::CreateItemCommand::None ? cmd.flags() : cmd.addedFlags(); if (!flags.isEmpty()) { @@ -179,6 +179,7 @@ currentItem.setDatetime(newItem.datetime()); needsUpdate = true; } + if (newItem.size() > 0 && newItem.size() != currentItem.size()) { currentItem.setSize(newItem.size()); needsUpdate = true; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/handler/modify.cpp new/akonadi-16.12.1/src/server/handler/modify.cpp --- old/akonadi-16.12.0/src/server/handler/modify.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/handler/modify.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -189,7 +189,7 @@ cols.reserve(cmd.persistentSearchCollections().size()); QVector<qint64> inCols = cmd.persistentSearchCollections(); qSort(inCols); - Q_FOREACH (qint64 col, cmd.persistentSearchCollections()) { + Q_FOREACH (qint64 col, inCols) { cols.append(QString::number(col)); } const QString colStr = cols.join(QLatin1Char(' ')); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/handler/move.cpp new/akonadi-16.12.1/src/server/handler/move.cpp --- old/akonadi-16.12.0/src/server/handler/move.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/handler/move.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -56,6 +56,7 @@ // Split the list by source collection QMap<Entity::Id /* collection */, PimItem> toMove; QMap<Entity::Id /* collection */, Collection> sources; + ImapSet toMoveIds; Q_FOREACH (/*sic!*/ PimItem item, items) { //krazy:exclude=foreach if (!item.isValid()) { failureResponse("Invalid item in result set!?"); @@ -87,6 +88,7 @@ } toMove.insertMulti(source.id(), item); + toMoveIds.add(QVector<qint64>{ item.id() }); } if (!transaction.commit()) { @@ -94,8 +96,18 @@ return; } + // Batch-reset RID + // The item should have an empty RID in the destination collection to avoid + // RID conflicts with existing items (see T3904 in Phab). + QueryBuilder qb2(PimItem::tableName(), QueryBuilder::Update); + qb2.setColumnValue(PimItem::remoteIdColumn(), QString()); + ItemQueryHelper::itemSetToQuery(toMoveIds, connection()->context(), qb2); + if (!qb2.exec()) { + failureResponse("Unable to update RID"); + return; + } + // Emit notification for each source collection separately - QVector<PimItem::Id> itemsToResetRID; Collection source; PimItem::List itemsToMove; for (auto it = toMove.cbegin(), end = toMove.cend(); it != end; ++it) { @@ -108,29 +120,11 @@ } itemsToMove.push_back(*it); - - // reset RID on inter-resource moves, but only after generating the change notification - // so that this still contains the old one for the source resource - const bool isInterResourceMove = it->collection().resource().id() != source.resource().id(); - if (isInterResourceMove) { - itemsToResetRID.push_back(it->id()); - } } if (!itemsToMove.isEmpty()) { store->notificationCollector()->itemsMoved(itemsToMove, source, mDestination); } - - // Batch-reset RID for all inter-resource moves - if (!itemsToResetRID.isEmpty()) { - QueryBuilder qb(PimItem::tableName(), QueryBuilder::Update); - qb.setColumnValue(PimItem::remoteIdColumn(), QString()); - ItemQueryHelper::itemSetToQuery(itemsToResetRID, connection()->context(), qb); - if (!qb.exec()) { - failureResponse("Unable to update RID"); - return; - } - } } bool Move::parseStream() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/search/searchmanager.cpp new/akonadi-16.12.1/src/server/search/searchmanager.cpp --- old/akonadi-16.12.0/src/server/search/searchmanager.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/search/searchmanager.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -427,35 +427,54 @@ qCDebug(AKONADISERVER_LOG) << "Got" << newMatches.count() << "results, out of which" << existingMatches.count() << "are already in the collection"; newMatches = newMatches - existingMatches; + if (newMatches.isEmpty()) { + qCDebug(AKONADISERVER_LOG) << "Added results: 0 (fast path)"; + return; + } const bool existingTransaction = DataStore::self()->inTransaction(); if (!existingTransaction) { DataStore::self()->beginTransaction(); } + // First query all the IDs we got from search plugin/agent against the DB. + // This will remove IDs that no longer exist in the DB. QVariantList newMatchesVariant; newMatchesVariant.reserve(newMatches.count()); Q_FOREACH (qint64 id, newMatches) { newMatchesVariant << id; - Collection::addPimItem(collection.id(), id); } - qCDebug(AKONADISERVER_LOG) << "Added" << newMatches.count(); + SelectQueryBuilder<PimItem> qb; + qb.addValueCondition(PimItem::idFullColumnName(), Query::In, newMatchesVariant); + if (!qb.exec()) { + return; + } + + const auto items = qb.result(); + if (items.count() != newMatches.count()) { + qCDebug(AKONADISERVER_LOG) << "Search backend returned" << (newMatches.count() - items.count()) << "results that no longer exist in Akonadi."; + qCDebug(AKONADISERVER_LOG) << "Please reindex collection" << collection.id(); + // TODO: Request the reindexing directly from here + } + + if (items.isEmpty()) { + qCDebug(AKONADISERVER_LOG) << "Added results: 0"; + return; + } + + for (const auto item : items) { + Collection::addPimItem(collection.id(), item.id()); + } if (!existingTransaction && !DataStore::self()->commitTransaction()) { qCDebug(AKONADISERVER_LOG) << "Failed to commit transaction"; return; } - if (!newMatchesVariant.isEmpty()) { - SelectQueryBuilder<PimItem> qb; - qb.addValueCondition(PimItem::idFullColumnName(), Query::In, newMatchesVariant); - if (!qb.exec()) { - return ; - } - const QVector<PimItem> newItems = qb.result(); - DataStore::self()->notificationCollector()->itemsLinked(newItems, collection); - // Force collector to dispatch the notification now - DataStore::self()->notificationCollector()->dispatchNotifications(); - } + DataStore::self()->notificationCollector()->itemsLinked(items, collection); + // Force collector to dispatch the notification now + DataStore::self()->notificationCollector()->dispatchNotifications(); + + qCDebug(AKONADISERVER_LOG) << "Added results:" << items.count(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/server/storage/partstreamer.cpp new/akonadi-16.12.1/src/server/storage/partstreamer.cpp --- old/akonadi-16.12.0/src/server/storage/partstreamer.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/server/storage/partstreamer.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -64,13 +64,13 @@ { Q_EMIT responseAvailable(Protocol::StreamPayloadCommand(partName, Protocol::StreamPayloadCommand::MetaData)); - Protocol::StreamPayloadResponse response = mConnection->readCommand(); - if (response.isError() || !response.isValid()) { + const auto cmd = mConnection->readCommand(); + if (!cmd.isValid() || Protocol::Response(cmd).isError()) { mError = QStringLiteral("Client failed to provide part metadata"); return Protocol::PartMetaData(); } - return response.metaData(); + return Protocol::StreamPayloadResponse(cmd).metaData(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.0/src/widgets/subscriptiondialog.cpp new/akonadi-16.12.1/src/widgets/subscriptiondialog.cpp --- old/akonadi-16.12.0/src/widgets/subscriptiondialog.cpp 2016-12-07 11:27:47.000000000 +0100 +++ new/akonadi-16.12.1/src/widgets/subscriptiondialog.cpp 2017-01-08 16:41:33.000000000 +0100 @@ -223,7 +223,7 @@ mainLayout->addWidget(buttonBox); connect(d->model, SIGNAL(loaded()), SLOT(modelLoaded())); - connect(d->mOkButton, &QAbstractButton::clicked, this, &QDialog::done); + connect(d->mOkButton, SIGNAL(clicked(bool)), this, SLOT(done())); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &SubscriptionDialog::deleteLater); ControlGui::widgetNeedsAkonadi(mainWidget); d->readConfig(); ++++++ handle-mysql-process-crashes-gracefully.patch ++++++
From 4c5ca9bf32e5a9d569e83f66a439c65d8939a540 Mon Sep 17 00:00:00 2001 From: Martin Koller <kollix@aon.at> Date: Sat, 21 Jan 2017 12:11:14 +0100 Subject: handle mysql process crashes gracefully
This patch checks if the mysqld stops unexpectedly when it was started from akonadiserver and tells the latter to quit when a stopped mysqld was discovered. Also in this case the local socket file is removed so that a restart can work without problem. REVIEW: 129264 --- src/server/storage/dbconfigmysql.cpp | 69 +++++++++++++++++++++++++++++++++--- src/server/storage/dbconfigmysql.h | 11 ++++-- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/server/storage/dbconfigmysql.cpp b/src/server/storage/dbconfigmysql.cpp index 0962ccb..7631890 100644 --- a/src/server/storage/dbconfigmysql.cpp +++ b/src/server/storage/dbconfigmysql.cpp @@ -32,6 +32,7 @@ #include <QSqlDriver> #include <QSqlError> #include <QSqlQuery> +#include <QCoreApplication> using namespace Akonadi; using namespace Akonadi::Server; @@ -190,6 +191,8 @@ bool DbConfigMysql::startInternalServer() const QString dataDir = StandardDirs::saveDir("data", QStringLiteral("db_data")); #ifndef Q_OS_WIN const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc"))); + const QString socketFile = QStringLiteral("%1/mysql.socket").arg(socketDirectory); + const QString pidFileName = QStringLiteral("%1/mysql.pid").arg(socketDirectory); #endif // generate config file @@ -289,6 +292,39 @@ bool DbConfigMysql::startInternalServer() qCCritical(AKONADISERVER_LOG) << "MySQL cannot deal with a socket path this long. Path was: " << socketDirectory; return false; } + + // If mysql.socket file exists, check if also the server process is still running, + // else we can safely remove the socket file (cleanup after a system crash, etc.) + QFile pidFile(pidFileName); + if (QFile::exists(socketFile) && pidFile.open(QIODevice::ReadOnly)) { + qCDebug(AKONADISERVER_LOG) << "Found a mysqld pid file, checking whether the server is still running..."; + QByteArray pid = pidFile.readLine().trimmed(); + QFile proc(QString::fromLatin1("/proc/" + pid + "/stat")); + // Check whether the process with the PID from pidfile still exists and whether + // it's actually still mysqld or, whether the PID has been recycled in the meanwhile. + bool serverIsRunning = false; + if (proc.open(QIODevice::ReadOnly)) { + const QByteArray stat = proc.readAll(); + const QList<QByteArray> stats = stat.split(' '); + if (stats.count() > 1) { + // Make sure the PID actually belongs to mysql process + if (stats[1] == "(mysqld)") { + // Yup, our mysqld is actually running, so pretend we started the server + // and try to connect to it + qCWarning(AKONADISERVER_LOG) << "mysqld for Akonadi is already running, trying to connect to it."; + serverIsRunning = true; + } + } + proc.close(); + } + + if (!serverIsRunning) { + qCDebug(AKONADISERVER_LOG) << "No mysqld process with specified PID is running. Removing the pidfile and starting a new instance..."; + pidFile.close(); + pidFile.remove(); + QFile::remove(socketFile); + } + } #endif // synthesize the mysqld command @@ -296,14 +332,15 @@ bool DbConfigMysql::startInternalServer() arguments << QStringLiteral("--defaults-file=%1/mysql.conf").arg(akDir); arguments << QStringLiteral("--datadir=%1/").arg(dataDir); #ifndef Q_OS_WIN - arguments << QStringLiteral("--socket=%1/mysql.socket").arg(socketDirectory); + arguments << QStringLiteral("--socket=%1").arg(socketFile); + arguments << QStringLiteral("--pid-file=%1").arg(pidFileName); #else arguments << QString::fromLatin1("--shared-memory"); #endif // If mysql.socket file does not exists, then we must start the server, // otherwise we reconnect to it - if (!QFile::exists(QStringLiteral("%1/mysql.socket").arg(socketDirectory))) { + if (!QFile::exists(socketFile)) { // move mysql error log file out of the way const QFileInfo errorLog(dataDir + QDir::separator() + QLatin1String("mysql.err")); if (errorLog.exists()) { @@ -348,9 +385,11 @@ bool DbConfigMysql::startInternalServer() return false; } - #ifndef Q_OS_WIN + connect(mDatabaseProcess, static_cast<void(QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), + this, &DbConfigMysql::processFinished); + +#ifndef Q_OS_WIN // wait until mysqld has created the socket file (workaround for QTBUG-47475 in Qt5.5.0) - QString socketFile = QStringLiteral("%1/mysql.socket").arg(socketDirectory); int counter = 50; // avoid an endless loop in case mysqld terminated while ((counter-- > 0) && !QFileInfo::exists(socketFile)) { QThread::msleep(100); @@ -358,7 +397,6 @@ bool DbConfigMysql::startInternalServer() #endif } else { qCDebug(AKONADISERVER_LOG) << "Found mysql.socket file, reconnecting to the database"; - mDatabaseProcess = new QProcess(); } const QLatin1String initCon("initConnection"); @@ -378,7 +416,7 @@ bool DbConfigMysql::startInternalServer() if (opened) { break; } - if (mDatabaseProcess->waitForFinished(500)) { + if (mDatabaseProcess && mDatabaseProcess->waitForFinished(500)) { qCCritical(AKONADISERVER_LOG) << "Database process exited unexpectedly during initial connection!"; qCCritical(AKONADISERVER_LOG) << "executable:" << mMysqldPath; qCCritical(AKONADISERVER_LOG) << "arguments:" << arguments; @@ -459,12 +497,33 @@ bool DbConfigMysql::startInternalServer() return success; } +void DbConfigMysql::processFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitCode); + Q_UNUSED(exitStatus); + + qCCritical(AKONADISERVER_LOG) << "database server stopped unexpectedly"; + +#ifndef Q_OS_WIN + // when the server stopped unexpectedly, make sure to remove the stale socket file since otherwise + // it can not be started again + const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc"))); + const QString socketFile = QStringLiteral("%1/mysql.socket").arg(socketDirectory); + QFile::remove(socketFile); +#endif + + QCoreApplication::quit(); +} + void DbConfigMysql::stopInternalServer() { if (!mDatabaseProcess) { return; } + disconnect(mDatabaseProcess, static_cast<void(QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), + this, &DbConfigMysql::processFinished); + // first, try the nicest approach if (!mCleanServerShutdownCommand.isEmpty()) { QProcess::execute(mCleanServerShutdownCommand); diff --git a/src/server/storage/dbconfigmysql.h b/src/server/storage/dbconfigmysql.h index 27841e8..a25f28b 100644 --- a/src/server/storage/dbconfigmysql.h +++ b/src/server/storage/dbconfigmysql.h @@ -21,14 +21,16 @@ #define DBCONFIGMYSQL_H #include "dbconfig.h" - -class QProcess; +#include <QObject> +#include <QProcess> namespace Akonadi { namespace Server { -class DbConfigMysql : public DbConfig +class DbConfigMysql : public QObject, public DbConfig { + Q_OBJECT + public: DbConfigMysql(); @@ -75,6 +77,9 @@ public: /// reimpl void initSession(const QSqlDatabase &database) Q_DECL_OVERRIDE; +private Q_SLOTS: + void processFinished(int exitCode, QProcess::ExitStatus exitStatus); + private: int parseCommandLineToolsVersion() const; -- cgit v0.11.2
participants (1)
-
root@hilbertn.suse.de