cutelyst  4.4.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
unixfork.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2014-2020 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "unixfork.h"
6 
7 #include "server.h"
8 
9 #if defined(HAS_EventLoopEPoll)
10 # include "EventLoopEPoll/eventdispatcher_epoll.h"
11 #endif
12 
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 
17 #if defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
18 # include <sys/cpuset.h>
19 # include <sys/param.h>
20 #endif
21 
22 #include <errno.h>
23 #include <grp.h>
24 #include <iostream>
25 #include <pwd.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/socket.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32 
33 #include <QAbstractEventDispatcher>
34 #include <QCoreApplication>
35 #include <QFile>
36 #include <QLoggingCategory>
37 #include <QMutex>
38 #include <QSocketNotifier>
39 #include <QThread>
40 #include <QTimer>
41 
42 Q_LOGGING_CATEGORY(C_SERVER_UNIX, "cutelyst.server.unix", QtWarningMsg)
43 
44 #pragma GCC diagnostic push
45 #pragma GCC diagnostic ignored "-Wunused-result"
46 
47 static int signalsFd[2];
48 
49 UnixFork::UnixFork(int process, int threads, bool setupSignals, QObject *parent)
50  : AbstractFork(parent)
51  , m_threads(threads)
52  , m_processes(process)
53 {
54  if (setupSignals) {
55  setupUnixSignalHandlers();
56  }
57 }
58 
59 UnixFork::~UnixFork()
60 {
61  if (m_child) {
62  _exit(0);
63  }
64 }
65 
66 bool UnixFork::continueMaster(int *exit)
67 {
68  Q_UNUSED(exit)
69  return true;
70 }
71 
72 int UnixFork::exec(bool lazy, bool master)
73 {
74  if (master) {
75  std::cout << "spawned WSGI master process (pid: " << QCoreApplication::applicationPid()
76  << ")" << std::endl;
77  }
78 
79  int ret;
80  if (lazy) {
81  if (master) {
82  ret = internalExec();
83  } else {
84  std::cerr << "*** Master mode must be set on lazy mode" << std::endl;
85  ret = -1;
86  }
87  } else {
88  if (m_processes > 0) {
89  ret = internalExec();
90  } else {
91  Q_EMIT forked(0);
92  ret = qApp->exec();
93  }
94  }
95 
96  return ret;
97 }
98 
100 {
101  auto it = m_childs.begin();
102  while (it != m_childs.end()) {
103  it.value().restart = 1; // Mark as requiring restart
104  terminateChild(it.key());
105  ++it;
106  }
107 
108  setupCheckChildTimer();
109 }
110 
111 int UnixFork::internalExec()
112 {
113  int ret;
114  bool respawn = false;
115  do {
116  if (!createProcess(respawn)) {
117  return 1;
118  }
119  respawn = true;
120 
121  installTouchReload();
122 
123  ret = qApp->exec();
124 
125  removeTouchReload();
126  } while (!m_terminating);
127 
128  return ret;
129 }
130 
131 bool UnixFork::createProcess(bool respawn)
132 {
133  if (respawn) {
134  m_recreateWorker.removeIf([this, respawn](Worker worker) {
135  worker.restart = 0;
136  if (!createChild(worker, respawn)) {
137  std::cout << "CHEAPING worker: " << worker.id << std::endl;
138  --m_processes;
139  }
140 
141  return true; // Clean recreate worker list
142  });
143  } else {
144  for (int i = 0; i < m_processes; ++i) {
145  Worker worker;
146  worker.id = i + 1;
147  worker.null = false;
148  createChild(worker, respawn);
149  }
150  }
151 
152  return !m_childs.empty();
153 }
154 
155 void UnixFork::decreaseWorkerRespawn()
156 {
157  int missingRespawn = 0;
158  auto it = m_childs.begin();
159  while (it != m_childs.end()) {
160  if (it.value().respawn > 0) {
161  --it.value().respawn;
162  missingRespawn += it.value().respawn;
163  }
164  ++it;
165  }
166 
167  if (missingRespawn) {
168  QTimer::singleShot(std::chrono::seconds{1}, this, &UnixFork::decreaseWorkerRespawn);
169  }
170 }
171 
173 {
174  const auto childs = m_childs.keys();
175  for (qint64 pid : childs) {
176  killChild(pid);
177  }
178 }
179 
180 void UnixFork::killChild(qint64 pid)
181 {
182  // qCDebug(C_SERVER_UNIX) << "SIGKILL " << pid;
183  ::kill(pid_t(pid), SIGKILL);
184 }
185 
187 {
188  const auto childs = m_childs.keys();
189  for (qint64 pid : childs) {
190  terminateChild(pid);
191  }
192 }
193 
194 void UnixFork::terminateChild(qint64 pid)
195 {
196  // qCDebug(C_SERVER_UNIX) << "SIGQUIT " << pid;
197  ::kill(pid_t(pid), SIGQUIT);
198 }
199 
200 void UnixFork::stopWSGI(const QString &pidfile)
201 {
202  QFile file(pidfile);
203  if (!file.open(QFile::ReadOnly | QFile::Text)) {
204  std::cerr << "Failed open pid file " << qPrintable(pidfile) << std::endl;
205  exit(1);
206  }
207 
208  QByteArray piddata = file.readLine().simplified();
209  qint64 pid = piddata.toLongLong();
210  if (pid < 2) {
211  std::cerr << "Failed read pid file " << qPrintable(pidfile) << std::endl;
212  exit(1);
213  }
214 
215  ::kill(pid_t(pid), SIGINT);
216  exit(0);
217 }
218 
219 bool UnixFork::setUmask(const QByteArray &valueStr)
220 {
221  if (valueStr.size() < 3) {
222  std::cerr << "umask too small" << std::endl;
223  return false;
224  }
225 
226  const char *value = valueStr.constData();
227  mode_t mode = 0;
228  if (valueStr.size() == 3) {
229  mode = (mode << 3) + (value[0] - '0');
230  mode = (mode << 3) + (value[1] - '0');
231  mode = (mode << 3) + (value[2] - '0');
232  } else {
233  mode = (mode << 3) + (value[1] - '0');
234  mode = (mode << 3) + (value[2] - '0');
235  mode = (mode << 3) + (value[3] - '0');
236  }
237  std::cout << "umask() " << value << std::endl;
238 
239  umask(mode);
240 
241  return true;
242 }
243 
244 void UnixFork::signalHandler(int signal)
245 {
246  // qDebug() << Q_FUNC_INFO << signal << QCoreApplication::applicationPid();
247  char sig = signal;
248  write(signalsFd[0], &sig, sizeof(sig));
249 }
250 
251 void UnixFork::setupCheckChildTimer()
252 {
253  if (!m_checkChildRestart) {
254  m_checkChildRestart = new QTimer(this);
255  m_checkChildRestart->start(std::chrono::milliseconds{500});
256  connect(m_checkChildRestart, &QTimer::timeout, this, &UnixFork::handleSigChld);
257  }
258 }
259 
260 void UnixFork::postFork(int workerId)
261 {
262  // Child must not have parent timers
263  delete m_checkChildRestart;
264 
265  Q_EMIT forked(workerId - 1);
266 }
267 
268 bool UnixFork::setGidUid(const QString &gid, const QString &uid, bool noInitgroups)
269 {
270  bool ok;
271 
272  if (!gid.isEmpty()) {
273  uint gidInt = gid.toUInt(&ok);
274  if (!ok) {
275  struct group *ugroup = getgrnam(qUtf8Printable(gid));
276  if (ugroup) {
277  gidInt = ugroup->gr_gid;
278  } else {
279  std::cerr << "setgid group %s not found." << qUtf8Printable(gid) << std::endl;
280  return false;
281  }
282  }
283 
284  if (setgid(gidInt)) {
285  std::cerr << "Failed to set gid '%s'" << strerror(errno) << std::endl;
286  return false;
287  }
288  std::cout << "setgid() to " << gidInt << std::endl;
289 
290  if (noInitgroups || uid.isEmpty()) {
291  if (setgroups(0, nullptr)) {
292  std::cerr << "Failed to setgroups()" << std::endl;
293  return false;
294  }
295  } else {
296  QByteArray uidname;
297  uint uidInt = uid.toUInt(&ok);
298  if (ok) {
299  struct passwd *pw = getpwuid(uidInt);
300  if (pw) {
301  uidname = pw->pw_name;
302  }
303  } else {
304  uidname = uid.toUtf8();
305  }
306 
307  if (initgroups(uidname.constData(), gidInt)) {
308  std::cerr << "Failed to setgroups()" << std::endl;
309  return false;
310  }
311  }
312  }
313 
314  if (!uid.isEmpty()) {
315  uint uidInt = uid.toUInt(&ok);
316  if (!ok) {
317  struct passwd *upasswd = getpwnam(qUtf8Printable(uid));
318  if (upasswd) {
319  uidInt = upasswd->pw_uid;
320  } else {
321  std::cerr << "setuid user" << qUtf8Printable(uid) << "not found." << std::endl;
322  return false;
323  }
324  }
325 
326  if (setuid(uidInt)) {
327  std::cerr << "Failed to set uid:" << strerror(errno) << std::endl;
328  return false;
329  }
330  std::cout << "setuid() to " << uidInt << std::endl;
331  }
332  return true;
333 }
334 
335 void UnixFork::chownSocket(const QString &filename, const QString &uidGid)
336 {
337  struct group *new_group = nullptr;
338  struct passwd *new_user = nullptr;
339 
340  const QString owner = uidGid.section(QLatin1Char(':'), 0, 0);
341 
342  bool ok;
343  uid_t new_uid = owner.toUInt(&ok);
344 
345  if (!ok) {
346  new_user = getpwnam(qUtf8Printable(owner));
347  if (!new_user) {
348  qFatal("unable to find user '%s'", qUtf8Printable(owner));
349  }
350  new_uid = new_user->pw_uid;
351  }
352 
353  gid_t new_gid = -1u;
354  const QString group = uidGid.section(QLatin1Char(':'), 1, 1);
355  if (!group.isEmpty()) {
356  new_gid = group.toUInt(&ok);
357  if (!ok) {
358  new_group = getgrnam(qUtf8Printable(group));
359  if (!new_group) {
360  qFatal("unable to find group '%s'", qUtf8Printable(group));
361  }
362  new_gid = new_group->gr_gid;
363  }
364  }
365 
366  if (chown(qUtf8Printable(filename), new_uid, new_gid)) {
367  qFatal("chown() error '%s'", strerror(errno));
368  }
369 }
370 
371 #ifdef Q_OS_LINUX
372 // static int cpuSockets = -1;
373 
374 // socket/cores
375 int parseProcCpuinfo()
376 {
377  int cpuSockets = 1;
378  // std::pair<int, int> ret;
379 
380  // static QMutex mutex;
381  // QMutexLocker locker(&mutex);
382  QFile file(QStringLiteral("/proc/cpuinfo"));
383  if (!file.open(QFile::ReadOnly | QFile::Text)) {
384  qCWarning(C_SERVER_UNIX) << "Failed to open file" << file.errorString();
385  // cpuSockets = 1;
386  // cpuCores = QThread::idealThreadCount();
387  return cpuSockets;
388  }
389 
390  char buf[1024];
391  qint64 lineLength;
392  QByteArrayList physicalIds;
393  // cpuCores = 0;
394  while ((lineLength = file.readLine(buf, sizeof(buf))) != -1) {
395  const QByteArray line(buf, int(lineLength));
396  if (line.startsWith("physical id\t: ")) {
397  const QByteArray id = line.mid(14).trimmed();
398  if (!physicalIds.contains(id)) {
399  physicalIds.push_back(id);
400  }
401  } /* else if (line.startsWith("processor \t: ")) {
402  ++cpuCores;
403  }*/
404  }
405 
406  // if (cpuCores == 0) {
407  // cpuCores = QThread::idealThreadCount();
408  // }
409 
410  if (physicalIds.size()) {
411  cpuSockets = physicalIds.size();
412  } else {
413  cpuSockets = 1;
414  }
415  return cpuSockets;
416 }
417 #endif
418 
419 int UnixFork::idealProcessCount()
420 {
421 #ifdef Q_OS_LINUX
422  static int cpuSockets = parseProcCpuinfo();
423 
424  return cpuSockets;
425 #else
426  return 1;
427 #endif
428 }
429 
430 int UnixFork::idealThreadCount()
431 {
432 #ifdef Q_OS_LINUX
433  static int cpuCores = qMax(1, QThread::idealThreadCount());
434 
435  return cpuCores;
436 #else
437  return qMax(1, QThread::idealThreadCount());
438 #endif
439 }
440 
441 void UnixFork::handleSigHup()
442 {
443  // do Qt stuff
444  // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
445  // m_proc->kill();
446 }
447 
448 void UnixFork::handleSigTerm()
449 {
450  // do Qt stuff
451  // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
452  // qApp->quit();
453  // m_proc->terminate();
454 }
455 
456 void UnixFork::handleSigInt()
457 {
458  // do Qt stuff
459  // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
460  m_terminating = true;
461  if (m_child || (m_childs.isEmpty())) {
462  qDebug(C_SERVER_UNIX) << "SIGINT/SIGQUIT received, worker shutting down...";
463  Q_EMIT shutdown();
464  } else {
465  std::cout << "SIGINT/SIGQUIT received, terminating workers..." << std::endl;
466  setupCheckChildTimer();
467 
468  static int count = 0;
469  if (count++ > 2) {
470  std::cout << "KILL workers..." << std::endl;
471  killChild();
472  QTimer::singleShot(std::chrono::seconds{3}, qApp, &QCoreApplication::quit);
473  } else if (count > 1) {
474  terminateChild();
475  } else {
476  QTimer::singleShot(std::chrono::seconds{30}, this, [this]() {
477  std::cout << "workers terminating timeout, KILL ..." << std::endl;
478  killChild();
479  QTimer::singleShot(std::chrono::seconds{30}, qApp, &QCoreApplication::quit);
480  });
481 
482  terminateChild();
483  }
484  }
485 }
486 
487 void UnixFork::handleSigChld()
488 {
489  pid_t p;
490  int status;
491 
492  while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
493  /* Handle the death of pid p */
494  // qCDebug(C_SERVER_UNIX) << "SIGCHLD worker died" << p << WEXITSTATUS(status);
495  // SIGTERM is used when CHEAPED (ie post fork failed)
496  int exitStatus = WEXITSTATUS(status);
497 
498  Worker worker;
499  auto it = m_childs.constFind(p);
500  if (it != m_childs.constEnd()) {
501  worker = it.value();
502  m_childs.erase(it);
503  } else {
504  std::cout << "DAMN ! *UNKNOWN* worker (pid: " << p << ") died, killed by signal "
505  << exitStatus << " :( ignoring .." << std::endl;
506  continue;
507  }
508 
509  if (WIFEXITED(status) && exitStatus == 15 && worker.restart == 0) {
510  // Child process cheaping
511  worker.null = true;
512  }
513 
514  if (!worker.null && !m_terminating) {
515  if (worker.restart == 0) {
516  std::cout << "DAMN ! worker " << worker.id << " (pid: " << p
517  << ") died, killed by signal " << exitStatus << " :( trying respawn .."
518  << std::endl;
519  }
520  worker.restart = 0;
521  ++worker.respawn;
522  QTimer::singleShot(std::chrono::seconds{1}, this, &UnixFork::decreaseWorkerRespawn);
523  m_recreateWorker.push_back(worker);
524  qApp->quit();
525  } else if (!m_child && m_childs.isEmpty()) {
526  qApp->quit();
527  }
528  }
529 
530  if (m_checkChildRestart) {
531  bool allRestarted = true;
532  auto it = m_childs.begin();
533  while (it != m_childs.end()) {
534  if (it.value().restart) {
535  if (++it.value().restart > 10) {
536  killChild(it.key());
537  }
538  allRestarted = false;
539  }
540  ++it;
541  }
542 
543  if (allRestarted) {
544  m_checkChildRestart->deleteLater();
545  m_checkChildRestart = nullptr;
546  }
547  }
548 }
549 
550 void UnixFork::setSched(Cutelyst::Server *wsgi, int workerId, int workerCore)
551 {
552  int cpu_affinity = wsgi->cpuAffinity();
553  if (cpu_affinity) {
554  char buf[4096];
555 
556  int pos =
557  snprintf(buf, 4096, "mapping worker %d core %d to CPUs:", workerId + 1, workerCore + 1);
558  if (pos < 25 || pos >= 4096) {
559  qCCritical(C_SERVER_UNIX) << "unable to initialize cpu affinity !!!";
560  exit(1);
561  }
562 #if defined(__linux__) || defined(__GNU_kFreeBSD__)
563  cpu_set_t cpuset;
564 #elif defined(__FreeBSD__)
565  cpuset_t cpuset;
566 #endif
567 #if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
568  int coreCount = idealThreadCount();
569 
570  int workerThreads = 1;
571  if (wsgi->threads().compare(u"auto") == 0) {
572  workerThreads = coreCount;
573  } else if (wsgi->threads().toInt() > 1) {
574  workerThreads = wsgi->threads().toInt();
575  }
576 
577  int base_cpu;
578  if (workerThreads > 1) {
579  base_cpu = (workerId * workerThreads) + workerCore * cpu_affinity;
580  } else {
581  base_cpu = workerId * cpu_affinity;
582  }
583 
584  if (base_cpu >= coreCount) {
585  base_cpu = base_cpu % coreCount;
586  }
587 
588  CPU_ZERO(&cpuset);
589  for (int i = 0; i < cpu_affinity; i++) {
590  if (base_cpu >= coreCount)
591  base_cpu = 0;
592  CPU_SET(base_cpu, &cpuset);
593  int ret = snprintf(buf + pos, 4096 - pos, " %d", base_cpu + 1);
594  if (ret < 2 || ret >= 4096) {
595  qCCritical(C_SERVER_UNIX) << "unable to initialize cpu affinity !!!";
596  exit(1);
597  }
598  pos += ret;
599  base_cpu++;
600  }
601 #endif
602 #if defined(__linux__) || defined(__GNU_kFreeBSD__)
603  if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset)) {
604  qFatal("failed to sched_setaffinity()");
605  }
606 #elif defined(__FreeBSD__)
607  if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset), &cpuset)) {
608  qFatal("cpuset_setaffinity");
609  }
610 #endif
611  std::cout << buf << std::endl;
612  }
613 }
614 
615 int UnixFork::setupUnixSignalHandlers()
616 {
617  setupSocketPair(false, true);
618 
619  // struct sigaction hup;
620  // hup.sa_handler = UnixFork::signalHandler;
621  // sigemptyset(&hup.sa_mask);
622  // hup.sa_flags = 0;
623  // hup.sa_flags |= SA_RESTART;
624 
625  // if (sigaction(SIGHUP, &hup, 0) > 0)
626  // return 1;
627 
628  // struct sigaction term;
629  // term.sa_handler = UnixFork::signalHandler;
630  // sigemptyset(&term.sa_mask);
631  // term.sa_flags |= SA_RESTART;
632 
633  // if (sigaction(SIGTERM, &term, 0) > 0)
634  // return 2;
635 
636  struct sigaction action;
637 
638  // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
639 
640  memset(&action, 0, sizeof(struct sigaction));
641  action.sa_handler = UnixFork::signalHandler;
642  sigemptyset(&action.sa_mask);
643  action.sa_flags |= SA_RESTART;
644  if (sigaction(SIGINT, &action, nullptr) > 0)
645  return SIGINT;
646 
647  memset(&action, 0, sizeof(struct sigaction));
648  action.sa_handler = UnixFork::signalHandler;
649  sigemptyset(&action.sa_mask);
650  action.sa_flags |= SA_RESTART;
651  if (sigaction(SIGQUIT, &action, nullptr) > 0)
652  return SIGQUIT;
653 
654  memset(&action, 0, sizeof(struct sigaction));
655  action.sa_handler = UnixFork::signalHandler;
656  sigemptyset(&action.sa_mask);
657  action.sa_flags |= SA_RESTART;
658 
659  if (sigaction(SIGCHLD, &action, nullptr) > 0)
660  return SIGCHLD;
661 
662  return 0;
663 }
664 
665 void UnixFork::setupSocketPair(bool closeSignalsFD, bool createPair)
666 {
667  if (closeSignalsFD) {
668  close(signalsFd[0]);
669  close(signalsFd[1]);
670  }
671 
672  if (createPair && ::socketpair(AF_UNIX, SOCK_STREAM, 0, signalsFd)) {
673  qFatal("Couldn't create SIGNALS socketpair");
674  }
675  delete m_signalNotifier;
676 
677  m_signalNotifier = new QSocketNotifier(signalsFd[1], QSocketNotifier::Read, this);
678  connect(m_signalNotifier, &QSocketNotifier::activated, this, [this]() {
679  char signal;
680  read(signalsFd[1], &signal, sizeof(signal));
681 
682  // qCDebug(C_SERVER_UNIX) << "Got signal:" << static_cast<int>(signal) << "pid:" <<
683  // QCoreApplication::applicationPid();
684  switch (signal) {
685  case SIGCHLD:
686  QTimer::singleShot(std::chrono::seconds{0}, this, &UnixFork::handleSigChld);
687  break;
688  case SIGINT:
689  case SIGQUIT:
690  handleSigInt();
691  break;
692  default:
693  break;
694  }
695  });
696 }
697 
698 bool UnixFork::createChild(const Worker &worker, bool respawn)
699 {
700  if (m_child) {
701  return false;
702  }
703 
704  delete m_signalNotifier;
705  m_signalNotifier = nullptr;
706 
707  qint64 childPID = fork();
708 
709  if (childPID >= 0) {
710  if (childPID == 0) {
711  if (worker.respawn >= 5) {
712  std::cout << "WSGI worker " << worker.id << " respawned too much, sleeping a bit"
713  << std::endl;
714  sleep(2);
715  }
716 
717 #if defined(HAS_EventLoopEPoll)
718  auto epoll = qobject_cast<EventDispatcherEPoll *>(QAbstractEventDispatcher::instance());
719  if (epoll) {
720  epoll->reinstall();
721  }
722 #endif
723 
724  setupSocketPair(true, true);
725 
726  m_child = true;
727  postFork(worker.id);
728 
729  int ret = qApp->exec();
730  _exit(ret);
731  } else {
732  setupSocketPair(false, false);
733 
734  if (respawn) {
735  std::cout << "Respawned WSGI worker " << worker.id << " (new pid: " << childPID
736  << ", cores: " << m_threads << ")" << std::endl;
737  } else {
738  if (m_processes == 1) {
739  std::cout << "spawned WSGI worker (and the only) (pid: " << childPID
740  << ", cores: " << m_threads << ")" << std::endl;
741  } else {
742  std::cout << "spawned WSGI worker " << worker.id << " (pid: " << childPID
743  << ", cores: " << m_threads << ")" << std::endl;
744  }
745  }
746  m_childs.insert(childPID, worker);
747  return true;
748  }
749  } else {
750  qFatal("Fork failed, quitting!!!!!!");
751  }
752 
753  return false;
754 }
755 
756 #include "moc_unixfork.cpp"
Implements a web server.
Definition: server.h:60
QString threads
Definition: server.h:150
virtual void terminateChild() override
Definition: unixfork.cpp:186
virtual bool continueMaster(int *exit=nullptr) override
Definition: unixfork.cpp:66
virtual void restart() override
Definition: unixfork.cpp:99
virtual int exec(bool lazy, bool master) override
Definition: unixfork.cpp:72
virtual void killChild() override
Definition: unixfork.cpp:172
QAbstractEventDispatcher * instance(QThread *thread)
const char * constData() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray simplified() const const
qsizetype size() const const
qlonglong toLongLong(bool *ok, int base) const const
QByteArray trimmed() const const
qint64 applicationPid()
T & value() const const
QHash::iterator begin()
QHash::const_iterator constEnd() const const
QHash::const_iterator constFind(const Key &key) const const
bool empty() const const
QHash::iterator end()
QHash::iterator erase(QHash::const_iterator pos)
QHash::iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QList< Key > keys() const const
bool contains(const AT &value) const const
void push_back(QList::parameter_type value)
qsizetype size() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool isEmpty() const const
QString section(QChar sep, qsizetype start, qsizetype end, QString::SectionFlags flags) const const
int toInt(bool *ok, int base) const const
uint toUInt(bool *ok, int base) const const
QByteArray toUtf8() const const
int idealThreadCount()
void start()
void timeout()