Akonadi

akonadictl/main.cpp
1/***************************************************************************
2 * SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org> *
3 * *
4 * SPDX-License-Identifier: LGPL-2.0-or-later *
5 ***************************************************************************/
6
7#include <QCoreApplication>
8#include <QDBusConnection>
9#include <QDir>
10#include <QPluginLoader>
11#include <QSettings>
12#include <QString>
13#include <QStringList>
14
15#include <KAboutData>
16#include <KLocalizedString>
17
18#include "shared/akapplication.h"
19
20#include "akonadifull-version.h"
21#include "akonadistarter.h"
22#include "controlmanagerinterface.h"
23#include "janitorinterface.h"
24
25#include "private/dbus_p.h"
26#include "private/instance_p.h"
27#include "private/protocol_p.h"
28#include "private/standarddirs_p.h"
29
30#include <chrono>
31#include <iostream>
32#include <thread>
33
34using namespace Qt::Literals;
35
36static bool startServer(bool verbose)
37{
38 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Control))
39 || QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))) {
40 std::cerr << "Akonadi is already running." << std::endl;
41 return false;
42 }
43 AkonadiStarter starter;
44 return starter.start(verbose);
45}
46
47static bool stopServer()
48{
49 org::freedesktop::Akonadi::ControlManager iface(Akonadi::DBus::serviceName(Akonadi::DBus::Control),
50 QStringLiteral("/ControlManager"),
52 nullptr);
53 if (!iface.isValid()) {
54 std::cerr << "Akonadi is not running." << std::endl;
55 return false;
56 }
57
58 iface.shutdown();
59
60 return true;
61}
62
63static bool isAkonadiServerRunning()
64{
65 return QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server));
66}
67
68static bool checkAkonadiControlStatus()
69{
70 const bool registered = QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Control));
71 std::cerr << "Akonadi Control: " << (registered ? "running" : "stopped") << std::endl;
72 return registered;
73}
74
75static bool checkAkonadiServerStatus()
76{
77 const bool registered = isAkonadiServerRunning();
78 std::cerr << "Akonadi Server: " << (registered ? "running" : "stopped") << std::endl;
79 return registered;
80}
81
82static bool checkSearchSupportStatus()
83{
84 QStringList searchMethods{QStringLiteral("Remote Search")};
85
86 const QString pluginOverride = QString::fromLatin1(qgetenv("AKONADI_OVERRIDE_SEARCHPLUGIN"));
87 if (!pluginOverride.isEmpty()) {
88 searchMethods << pluginOverride;
89 } else {
91 for (const QString &pluginDir : dirs) {
92 const QDir dir(pluginDir + QLatin1StringView("/akonadi/"));
93 const QStringList pluginFiles = dir.entryList(QDir::Files);
94 for (const QString &pluginFileName : pluginFiles) {
95 QPluginLoader loader(dir.absolutePath() + QLatin1Char('/') + pluginFileName);
96 const QVariantMap metadata = loader.metaData().value(QStringLiteral("MetaData")).toVariant().toMap();
97 if (metadata.value(QStringLiteral("X-Akonadi-PluginType")).toString() != QLatin1StringView("SearchPlugin")) {
98 continue;
99 }
100 if (!metadata.value(QStringLiteral("X-Akonadi-LoadByDefault"), true).toBool()) {
101 continue;
102 }
103 searchMethods << metadata.value(QStringLiteral("X-Akonadi-PluginName")).toString();
104 }
105 }
106 }
107
108 // There's always at least server-search available
109 std::cerr << "Akonadi Server Search Support: available (" << searchMethods.join(QLatin1StringView(", ")).toStdString() << ")" << std::endl;
110 return true;
111}
112
113static bool checkAvailableAgentTypes()
114{
115 const auto dirs = Akonadi::StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/agents"));
116 QStringList types;
117 for (const QString &pluginDir : dirs) {
118 const QDir dir(pluginDir);
119 const QStringList plugins = dir.entryList(QStringList() << QStringLiteral("*.desktop"), QDir::Files);
120 for (const QString &plugin : plugins) {
121 QSettings pluginInfo(pluginDir + QLatin1Char('/') + plugin, QSettings::IniFormat);
122 pluginInfo.beginGroup(QStringLiteral("Desktop Entry"));
123 types << pluginInfo.value(QStringLiteral("X-Akonadi-Identifier")).toString();
124 }
125 }
126
127 // Remove duplicates from multiple pluginDirs
128 types.removeDuplicates();
129 types.sort();
130
131 std::cerr << "Available Agent Types: ";
132 if (types.isEmpty()) {
133 std::cerr << "No agent types found!" << std::endl;
134 } else {
135 std::cerr << types.join(QLatin1StringView(", ")).toStdString() << std::endl;
136 }
137
138 return true;
139}
140
141static bool instanceRunning(const QString &instanceName = {})
142{
143 const auto oldInstance = Akonadi::Instance::identifier();
144 Akonadi::Instance::setIdentifier(instanceName);
145 const auto service = Akonadi::DBus::serviceName(Akonadi::DBus::Control);
146 Akonadi::Instance::setIdentifier(oldInstance);
147
149}
150
151static void listInstances()
152{
153 struct Instance {
155 bool running;
156 };
157 QList<Instance> instances{{QStringLiteral("(default)"), instanceRunning()}};
158#ifdef Q_OS_WIN
159 const QDir instanceDir(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/akonadi/config/instance"));
160#else
161 const QDir instanceDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/akonadi/instance"));
162#endif
163 if (instanceDir.exists()) {
164 const auto list = instanceDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
165 for (const auto &e : list) {
166 instances.push_back({e, instanceRunning(e)});
167 }
168 }
169
170 for (const auto &i : std::as_const(instances)) {
171 std::cout << i.name.toStdString();
172 if (i.running) {
173 std::cout << " (running)";
174 }
175 std::cout << std::endl;
176 }
177}
178
179static bool statusServer()
180{
181 checkAkonadiControlStatus();
182 checkAkonadiServerStatus();
183 checkSearchSupportStatus();
184 checkAvailableAgentTypes();
185 return true;
186}
187
188static void runJanitor(const QString &operation)
189{
190 if (!isAkonadiServerRunning()) {
191 std::cerr << "Akonadi Server is not running, " << operation.toStdString() << " will not run" << std::endl;
192 return;
193 }
194
195 org::freedesktop::Akonadi::Janitor janitor(Akonadi::DBus::serviceName(Akonadi::DBus::StorageJanitor),
196 QStringLiteral(AKONADI_DBUS_STORAGEJANITOR_PATH),
198 QObject::connect(&janitor, &org::freedesktop::Akonadi::Janitor::information, &janitor, [](const QString &msg) {
199 std::cout << msg.toStdString() << std::endl;
200 });
201 QObject::connect(&janitor, &org::freedesktop::Akonadi::Janitor::done, &janitor, []() {
202 qApp->exit();
203 });
204 janitor.asyncCall(operation);
205 qApp->exec();
206}
207
208static void waitForShutdown()
209{
210 do {
211 std::this_thread::sleep_for(std::chrono::milliseconds(100));
212 } while (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Control)));
213}
214
215int main(int argc, char **argv)
216{
217 AkCoreApplication app(argc, argv);
218
219 app.setDescription(
220 i18n("Akonadi server manipulation tool\n\n"
221 "Commands:\n"
222 " start Starts the Akonadi server with all its processes\n"
223 " stop Stops the Akonadi server and all its processes cleanly\n"
224 " restart Restart Akonadi server with all its processes\n"
225 " status Shows a status overview of the Akonadi server\n"
226 " instances List all existing Akonadi instances\n"
227 " vacuum Vacuum internal storage (WARNING: needs a lot of time and disk\n"
228 " space!)\n"
229 " fsck Check (and attempt to fix) consistency of the internal storage\n"
230 " (can take some time)"));
231
232 KAboutData aboutData(QStringLiteral("akonadictl"),
233 QStringLiteral("akonadictl"),
234 QStringLiteral(AKONADI_FULL_VERSION),
235 QStringLiteral("akonadictl"),
238
239 app.addCommandLineOptions({u"wait"_s, i18n("Wait for server shutdown to complete.")});
240 app.addPositionalCommandLineOption(QStringLiteral("command"),
241 i18n("Command to execute"),
242 QStringLiteral("start|stop|restart|status|vacuum|fsck|instances"));
243
244 app.parseCommandLine();
245
246 const auto &cmdArgs = app.commandLineArguments();
247 const QStringList commands = cmdArgs.positionalArguments();
248 if (commands.size() != 1) {
249 app.printUsage();
250 return -1;
251 }
252 const bool verbose = cmdArgs.isSet(QStringLiteral("verbose"));
253
254 const QString command = commands[0];
255 if (command == QLatin1StringView("start")) {
256 if (!startServer(verbose)) {
257 return 3;
258 }
259 } else if (command == QLatin1StringView("stop")) {
260 if (!stopServer()) {
261 return 4;
262 }
263 if (cmdArgs.isSet(u"wait"_s)) {
264 waitForShutdown();
265 }
266 } else if (command == QLatin1StringView("status")) {
267 if (!statusServer()) {
268 return 5;
269 }
270 } else if (command == QLatin1StringView("restart")) {
271 if (!stopServer()) {
272 return 4;
273 } else {
274 waitForShutdown();
275 if (!startServer(verbose)) {
276 return 3;
277 }
278 }
279 } else if (command == QLatin1StringView("vacuum")) {
280 runJanitor(QStringLiteral("vacuum"));
281 } else if (command == QLatin1StringView("fsck")) {
282 runJanitor(QStringLiteral("check"));
283 } else if (command == QLatin1StringView("instances")) {
284 listInstances();
285 } else {
286 app.printUsage();
287 return -1;
288 }
289 return 0;
290}
static void setApplicationData(const KAboutData &aboutData)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString name(StandardAction id)
QStringList libraryPaths()
QDBusConnectionInterface * interface() const const
QDBusConnection sessionBus()
QDBusReply< bool > isServiceRegistered(const QString &serviceName) const const
bool isEmpty() const const
qsizetype size() const const
T value(qsizetype i) const const
T value(const Key &key, const T &defaultValue) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString writableLocation(StandardLocation type)
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
std::string toStdString() const const
QString join(QChar separator) const const
qsizetype removeDuplicates()
void sort(Qt::CaseSensitivity cs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:30 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.