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