Torc  0.1
torclocalcontext.cpp
Go to the documentation of this file.
1 /* Class TorcLocalContext
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2012-18
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
21 */
22 
23 // Std
24 #include <signal.h>
25 
26 // Qt
27 #include <QCoreApplication>
28 #include <QSqlDatabase>
29 #include <QThreadPool>
30 #include <QDateTime>
31 #include <QUuid>
32 #include <QDir>
33 #include <QMetaEnum>
34 #include <QTimer>
35 
36 // Torc
37 #include "torccompat.h"
38 #include "torcevent.h"
39 #include "torcloggingimp.h"
40 #include "torclogging.h"
41 #include "torclanguage.h"
42 #include "torcexitcodes.h"
43 #include "torcpower.h"
44 #include "torclocalcontext.h"
45 #include "torccoreutils.h"
46 #include "torcdirectories.h"
47 
49 
50 static void ExitHandler(int Sig)
51 {
52  if (SIGPIPE == Sig)
53  {
54  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Received SIGPIPE interrupt - ignoring"));
55  return;
56  }
57 
58  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Received %1")
59  .arg(Sig == SIGINT ? QStringLiteral("SIGINT") : QStringLiteral("SIGTERM")));
60 
61  signal(SIGINT, SIG_DFL);
63 }
64 
65 qint16 TorcLocalContext::Create(TorcCommandLine* CommandLine, bool Init /*=true*/)
66 {
67  if (gLocalContext)
68  return TORC_EXIT_OK;
69 
70  gLocalContext = new TorcLocalContext(CommandLine);
71  if (gLocalContext)
72  if ((Init && gLocalContext->Init()) || !Init)
73  return TORC_EXIT_OK;
74 
75  TearDown();
76  return TORC_EXIT_NO_CONTEXT;
77 }
78 
80 {
81  delete gLocalContext;
82  gLocalContext = nullptr;
83 }
84 
86 {
87  TorcEvent event(Event);
88  if (gLocalContext)
89  gLocalContext->Notify(event);
90 }
91 
95 TorcLocalContext::TorcLocalContext(TorcCommandLine* CommandLine)
96  : QObject(),
97  m_sqliteDB(nullptr),
98  m_dbName(QStringLiteral("")),
99  m_localSettings(),
100  m_localSettingsLock(QReadWriteLock::Recursive),
101  m_adminThread(nullptr),
102  m_language(nullptr),
103  m_uuid(),
104  m_shutdownDelay(0),
105  m_shutdownEvent(Torc::None),
106  m_startTime(QDateTime::currentMSecsSinceEpoch()),
107  m_rootSetting(nullptr)
108 {
109  // listen to ourselves:)
110  AddObserver(this);
111 
112  // set any custom database location
113  m_dbName = CommandLine->GetValue(QStringLiteral("db")).toString();
114 
115  // Initialise TorcQThread FIRST
117 
118  // install our message handler
119  qInstallMessageHandler(&TorcCoreUtils::QtMessage);
120 
121  setObjectName(QStringLiteral("LocalContext"));
122 
123  // Handle signals gracefully
124  signal(SIGINT, ExitHandler);
125  signal(SIGTERM, ExitHandler);
126  signal(SIGPIPE, ExitHandler);
127 
128  // Initialise local directories
129  InitialiseTorcDirectories(CommandLine);
130 
131  // Start logging at the first opportunity
132  QString logfile = CommandLine->GetValue(QStringLiteral("logfile")).toString();
133  if (logfile.isEmpty())
134  logfile = QStringLiteral("%1/%2.log").arg(GetTorcConfigDir(), TORC_TORC);
135 
136  ParseVerboseArgument(CommandLine->GetValue(QStringLiteral("l")).toString());
137 
138  gVerboseMask |= VB_STDIO | VB_FLUSH;
139 
140  StartLogging(logfile, 0, 0, CommandLine->GetValue(QStringLiteral("v")).toString(), false);
141 
142  // Debug the config directory
143  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Dir: Using '%1'")
144  .arg(GetTorcConfigDir()));
145 
146  // Version info
147  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Version: %1").arg(QStringLiteral(GIT_VERSION)));
148  LOG(VB_GENERAL, LOG_NOTICE,
149  QStringLiteral("Enabled verbose msgs: %1").arg(gVerboseString));
150 }
151 
152 TorcLocalContext::~TorcLocalContext()
153 {
154  // delete language
155  delete m_language;
156 
157  // close and cleanup the admin thread
158  if (m_adminThread)
159  {
160  m_adminThread->quit();
161  m_adminThread->wait();
162  delete m_adminThread;
163  m_adminThread = nullptr;
164  }
165  else
166  {
168  }
169 
170  // wait for threads to exit
171  QThreadPool::globalInstance()->waitForDone();
172 
173  // remove root setting
174  if (m_rootSetting)
175  {
176  m_rootSetting->Remove();
177  m_rootSetting->DownRef();
178  m_rootSetting = nullptr;
179  }
180 
181  // delete the database connection(s)
182  delete m_sqliteDB;
183  m_sqliteDB = nullptr;
184 
185  // revert to the default message handler
186  qInstallMessageHandler(nullptr);
187 
188  // stop listening to self..
189  RemoveObserver(this);
190 
191  StopLogging();
192 }
193 
194 bool TorcLocalContext::Init(void)
195 {
196  // Create the configuration directory
197  QString configdir = GetTorcConfigDir();
198 
199  QDir dir(configdir);
200  if (!dir.exists())
201  {
202  if (!dir.mkpath(configdir))
203  {
204  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to create config directory ('%1')")
205  .arg(configdir));
206  return false;
207  }
208  }
209 
210  // Open the local database
211  if (m_dbName.isEmpty())
212  m_dbName = configdir + "/" + TORC_TORC + "-settings.sqlite";
213 
214  m_sqliteDB = new TorcSQLiteDB(m_dbName);
215 
216  if (!m_sqliteDB || !m_sqliteDB->IsValid())
217  return false;
218 
219  // Load the settings
220  {
221  QWriteLocker locker(&m_localSettingsLock);
222  m_sqliteDB->LoadSettings(m_localSettings);
223  }
224 
225  // Create the root settings object
226  m_rootSetting = new TorcSettingGroup(nullptr, TORC_ROOT_SETTING);
227 
228  // create/load the UUID - make this persistent to ensure peers do not think
229  // there are multiple devices after a number of restarts.
230  QString uuid = QUuid::createUuid().toString();
231  if (uuid.startsWith('{'))
232  uuid = uuid.mid(1);
233  if (uuid.endsWith('}'))
234  uuid.chop(1);
235  TorcSetting* uuidsaved = new TorcSetting(nullptr, QStringLiteral("uuid"),QStringLiteral("UUID"), TorcSetting::String, TorcSetting::Persistent, QVariant(uuid));
236  m_uuid = uuidsaved->GetValue().toString();
237  uuidsaved->Remove();
238  uuidsaved->DownRef();
239  uuidsaved = nullptr;
240 
241  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("UUID: %1").arg(m_uuid));
242 
243  // Load language and translation preferences
244  m_language = new TorcLanguage(m_rootSetting);
245 
246  /* We no longer use QRunnables, so ignore this for now at least
247  // don't expire threads
248  QThreadPool::globalInstance()->setExpiryTimeout(-1);
249 
250  // Increase the thread count
251  int ideal = QThreadPool::globalInstance()->maxThreadCount();
252  int want = ideal * 8;
253  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Setting thread pool size to %1 (was %2)").arg(want).arg(ideal));
254  QThreadPool::globalInstance()->setMaxThreadCount(want);
255  */
256 
257  // Bump the UI thread priority
258  QThread::currentThread()->setPriority(QThread::TimeCriticalPriority);
259 
260  // Qt version?
261  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Qt runtime version '%1' (compiled with '%2')")
262  .arg(qVersion(), QStringLiteral(QT_VERSION_STR)));
263 
264  // we don't use an admin thread as purely a server (i.e. no gui) - may need to revisit
265 #ifdef TORC_TEST
266  m_adminThread = new TorcAdminThread();
267  m_adminThread->start();
268 #else
270 #endif
271 
272  return true;
273 }
274 
275 QString TorcLocalContext::GetSetting(const QString &Name, const QString &DefaultValue)
276 {
277  return GetDBSetting(Name, DefaultValue);
278 }
279 
280 bool TorcLocalContext::GetSetting(const QString &Name, bool DefaultValue)
281 {
282  QString value = GetDBSetting(Name, DefaultValue ? QStringLiteral("1") : QStringLiteral("0"));
283  return value.trimmed() == QStringLiteral("1");
284 }
285 
286 int TorcLocalContext::GetSetting(const QString &Name, int DefaultValue)
287 {
288  QString value = GetDBSetting(Name, QString::number(DefaultValue));
289  return value.toInt();
290 }
291 
292 void TorcLocalContext::SetSetting(const QString &Name, const QString &Value)
293 {
294  SetDBSetting(Name, Value);
295 }
296 
297 void TorcLocalContext::SetSetting(const QString &Name, bool Value)
298 {
299  SetDBSetting(Name, Value ? QStringLiteral("1") : QStringLiteral("0"));
300 }
301 
302 void TorcLocalContext::SetSetting(const QString &Name, int Value)
303 {
304  SetDBSetting(Name, QString::number(Value));
305 }
306 
308 {
309  QReadLocker locker(&m_localSettingsLock);
310  return m_language->GetLocale();
311 }
312 
314 {
315  QReadLocker locker(&m_localSettingsLock);
316  return m_language;
317 }
318 
320 {
321  QReadLocker locker(&m_localSettingsLock);
322  if (m_sqliteDB)
323  m_sqliteDB->CloseThreadConnection();
324 }
325 
327 {
328  QWriteLocker locker(&m_localSettingsLock);
329  if (Delay < 1 || Delay > 300) // set in XSD
330  {
331  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Not setting shutdown delay to %1: must be 1<->300 seconds").arg(Delay));
332  }
333  else if (Delay > m_shutdownDelay)
334  {
335  m_shutdownDelay = Delay;
336  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Set shutdown delay to %1 seconds").arg(m_shutdownDelay));
337  }
338 }
339 
341 {
342  QReadLocker locker(&m_localSettingsLock);
343  return m_shutdownDelay;
344 }
345 
351 {
353 }
354 
357 {
360 }
361 
363 {
364  (void)HandleShutdown(m_shutdownEvent);
365 }
366 
368 {
369  QWriteLocker locker(&m_localSettingsLock);
370 
371  if (m_shutdownDelay < 1)
372  return false;
373 
374  int newevent = Torc::None;
375  switch (Event)
376  {
377  case Torc::RestartTorc:
378  case Torc::Stop:
379  case Torc::Shutdown:
380  case Torc::Restart:
381  case Torc::Hibernate:
382  case Torc::Suspend:
383  newevent = Event;
384  break;
385  default: break;
386  }
387 
388  if (newevent == Torc::None)
389  return false;
390 
391  if (m_shutdownEvent != Torc::None)
392  {
393  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Shutdown already queued - ignoring '%1' event").arg(TorcCoreUtils::EnumToString<Torc::Actions>((Torc::Actions)newevent)));
394  return true;
395  }
396 
397  m_shutdownEvent = newevent;
398  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Queued '%1' event for %2 seconds").arg(TorcCoreUtils::EnumToString<Torc::Actions>((Torc::Actions)m_shutdownEvent)).arg(m_shutdownDelay));
399  TorcEvent torcevent(Torc::TorcWillStop);
400  Notify(torcevent);
401  QTimer::singleShot(m_shutdownDelay * 1000, this, &TorcLocalContext::ShutdownTimeout);
402  return true;
403 }
404 
405 bool TorcLocalContext::HandleShutdown(int Event)
406 {
407  int event = Event == Torc::None ? m_shutdownEvent : Event;
408  switch (event)
409  {
410  case Torc::RestartTorc:
411  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Restarting application"));
413  QCoreApplication::exit(TORC_EXIT_RESTART);
414  return true;
415  case Torc::Stop:
416  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Stopping application"));
418  QCoreApplication::quit();
419  return true;
420  case Torc::Suspend:
421  {
423  Notify(event);
424  }
425  return true;
426  case Torc::Hibernate:
427  {
429  Notify(event);
430  }
431  return true;
432  case Torc::Shutdown:
433  {
435  Notify(event);
436  }
437  return true;
438  case Torc::Restart:
439  {
441  Notify(event);
442  }
443  return true;
444  default:
445  break;
446  }
447  return false;
448 }
449 
450 bool TorcLocalContext::event(QEvent *Event)
451 {
452  TorcEvent* torcevent = dynamic_cast<TorcEvent*>(Event);
453  if (torcevent)
454  {
455  int event = torcevent->GetEvent();
457  return true;
458  else if (HandleShutdown(event))
459  return true;
460  }
461 
462  return QObject::event(Event);
463 }
464 
465 QString TorcLocalContext::GetUuid(void) const
466 {
467  return m_uuid;
468 }
469 
471 {
472  QReadLocker locker(&m_localSettingsLock);
473  return m_rootSetting;
474 }
475 
477 {
478  QReadLocker locker(&m_localSettingsLock);
479  return m_startTime;
480 }
481 
483 {
484  return 0;
485 }
486 
487 QString TorcLocalContext::GetDBSetting(const QString &Name, const QString &DefaultValue)
488 {
489  {
490  QReadLocker locker(&m_localSettingsLock);
491  if (m_localSettings.contains(Name))
492  return m_localSettings.value(Name);
493  }
494 
495  SetDBSetting(Name, DefaultValue);
496  return DefaultValue;
497 }
498 
499 void TorcLocalContext::SetDBSetting(const QString &Name, const QString &Value)
500 {
501  QWriteLocker locker(&m_localSettingsLock);
502  if (m_sqliteDB)
503  m_sqliteDB->SetSetting(Name, Value);
504  m_localSettings[Name] = Value;
505 }
A simple thread to launch helper objects outside of the main loop.
static qint16 Create(TorcCommandLine *CommandLine, bool Init=true)
static void EventLoopEnding(bool Ending)
bool IsValid(void)
Returns true if the datbase has been opened/created.
Definition: torcdb.cpp:70
#define TORC_ROOT_SETTING
Definition: torclocaldefs.h:17
QLocale GetLocale(void)
void CloseThreadConnection(void)
Close the database connection for the current thread.
Definition: torcdb.cpp:104
TorcLocalContext * gLocalContext
QVariant GetValue(void)
High level group of related settings.
Definition: torcsetting.h:123
TorcLocalContext is the core Torc object.
A wrapper around a database setting.
Definition: torcsetting.h:15
int GetEvent(void)
Return the Torc action associated with this event.
Definition: torcevent.cpp:65
Torc command line handler.
QString GetUuid(void) const
virtual bool DownRef(void)
#define TORC_EXIT_RESTART
Definition: torcexitcodes.h:5
void SetSetting(const QString &Name, const QString &Value)
int ParseVerboseArgument(const QString &arg)
static void NotifyEvent(int Event)
void StopLogging(void)
void Remove(void)
A class to track and manage language and locale settings and available translations.
Definition: torclanguage.h:17
#define TORC_EXIT_NO_CONTEXT
Definition: torcexitcodes.h:8
static void CreateObjects(void)
Iterates through the list of registered TorcAdminObject&#39;s and creates them.
void ShutdownTimeout(void)
static void TearDown(void)
QString GetSetting(const QString &Name, const QString &DefaultValue)
#define TORC_TORC
Definition: torclocaldefs.h:8
uint GetShutdownDelay(void)
void InitialiseTorcDirectories(TorcCommandLine *CommandLine)
Statically initialise the various directories that Torc uses.
static void SetMainThread(void)
Definition: torcqthread.cpp:45
TorcLanguage * GetLanguage(void)
bool event(QEvent *Event) override
void CloseDatabaseConnections(void)
A general purpose event object.
Definition: torcevent.h:9
QString gVerboseString
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
#define TORC_EXIT_OK
Definition: torcexitcodes.h:4
void DeregisterQThread(void)
Deregister a non-Torc QThread from logging and close any database connections.
void RegisterLoggingThread(void)
void AddObserver(QObject *Observer)
brief Register the given object to receive events.
void Notify(const TorcEvent &Event)
Brief Send the given event to each registered listener/observer.
uint64_t gVerboseMask
void RegisterQThread(void)
Register a non-Torc QThread for logging and database access.
TorcSetting * GetRootSetting(void)
QLocale GetLocale(void)
void StartLogging(const QString &Logfile, int progress=0, int quiet=0, const QString &level=QStringLiteral("info"), bool Propagate=false)
qint64 GetStartTime(void)
void SetShutdownDelay(uint Delay)
void RemoveObserver(QObject *Observer)
brief Deregister the given object.
QVariant GetValue(const QString &Key)
Return the value associated with Key or an invalid QVariant if the option is not present.
bool QueueShutdownEvent(int Event)
static void DestroyObjects(void)
Destroys each created admin object.
void SetSetting(const QString &Name, const QString &Value)
Set the setting Name to the value Value.
Definition: torcdb.cpp:250
void QtMessage(QtMsgType Type, const QMessageLogContext &Context, const QString &Message)
A handler routine for Qt messages.
void DeregisterLoggingThread(void)
QString GetTorcConfigDir(void)
Return the path to the application configuration directory.
void LoadSettings(QMap< QString, QString > &Settings)
Retrieve all persistent settings stored in the database.
Definition: torcdb.cpp:229
SQLite implementation of TorcDB.
Definition: torcsqlitedb.h:6
static void ExitHandler(int Sig)