9 #if defined(HAS_EventLoopEPoll) 10 # include "EventLoopEPoll/eventdispatcher_epoll.h" 14 #include <sys/types.h> 17 #if defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) 18 # include <sys/cpuset.h> 19 # include <sys/param.h> 29 #include <sys/socket.h> 33 #include <QAbstractEventDispatcher> 34 #include <QCoreApplication> 36 #include <QLoggingCategory> 38 #include <QSocketNotifier> 42 Q_LOGGING_CATEGORY(C_SERVER_UNIX,
"cutelyst.server.unix", QtWarningMsg)
44 #pragma GCC diagnostic push 45 #pragma GCC diagnostic ignored "-Wunused-result" 47 static int signalsFd[2];
49 UnixFork::UnixFork(
int process,
int threads,
bool setupSignals,
QObject *parent)
52 , m_processes(process)
55 setupUnixSignalHandlers();
84 std::cerr <<
"*** Master mode must be set on lazy mode" << std::endl;
88 if (m_processes > 0) {
101 auto it = m_childs.begin();
102 while (it != m_childs.end()) {
103 it.value().restart = 1;
104 terminateChild(it.key());
108 setupCheckChildTimer();
111 int UnixFork::internalExec()
114 bool respawn =
false;
116 if (!createProcess(respawn)) {
121 installTouchReload();
126 }
while (!m_terminating);
131 bool UnixFork::createProcess(
bool respawn)
134 m_recreateWorker.removeIf([
this, respawn](
Worker worker) {
136 if (!createChild(worker, respawn)) {
137 std::cout <<
"CHEAPING worker: " << worker.id << std::endl;
144 for (
int i = 0; i < m_processes; ++i) {
148 createChild(worker, respawn);
152 return !m_childs.empty();
155 void UnixFork::decreaseWorkerRespawn()
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;
167 if (missingRespawn) {
174 const auto childs = m_childs.keys();
175 for (qint64 pid : childs) {
183 ::kill(pid_t(pid), SIGKILL);
188 const auto childs = m_childs.keys();
189 for (qint64 pid : childs) {
197 ::kill(pid_t(pid), SIGQUIT);
200 void UnixFork::stopWSGI(
const QString &pidfile)
204 std::cerr <<
"Failed open pid file " << qPrintable(pidfile) << std::endl;
211 std::cerr <<
"Failed read pid file " << qPrintable(pidfile) << std::endl;
215 ::kill(pid_t(pid), SIGINT);
219 bool UnixFork::setUmask(
const QByteArray &valueStr)
221 if (valueStr.
size() < 3) {
222 std::cerr <<
"umask too small" << std::endl;
226 const char *value = valueStr.
constData();
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');
233 mode = (mode << 3) + (value[1] -
'0');
234 mode = (mode << 3) + (value[2] -
'0');
235 mode = (mode << 3) + (value[3] -
'0');
237 std::cout <<
"umask() " << value << std::endl;
244 void UnixFork::signalHandler(
int signal)
248 write(signalsFd[0], &sig,
sizeof(sig));
251 void UnixFork::setupCheckChildTimer()
253 if (!m_checkChildRestart) {
254 m_checkChildRestart =
new QTimer(
this);
255 m_checkChildRestart->start(std::chrono::milliseconds{500});
260 void UnixFork::postFork(
int workerId)
263 delete m_checkChildRestart;
265 Q_EMIT forked(workerId - 1);
268 bool UnixFork::setGidUid(
const QString &gid,
const QString &uid,
bool noInitgroups)
273 uint gidInt = gid.
toUInt(&ok);
275 struct group *ugroup = getgrnam(qUtf8Printable(gid));
277 gidInt = ugroup->gr_gid;
279 std::cerr <<
"setgid group %s not found." << qUtf8Printable(gid) << std::endl;
284 if (setgid(gidInt)) {
285 std::cerr <<
"Failed to set gid '%s'" << strerror(errno) << std::endl;
288 std::cout <<
"setgid() to " << gidInt << std::endl;
290 if (noInitgroups || uid.
isEmpty()) {
291 if (setgroups(0,
nullptr)) {
292 std::cerr <<
"Failed to setgroups()" << std::endl;
297 uint uidInt = uid.
toUInt(&ok);
299 struct passwd *pw = getpwuid(uidInt);
301 uidname = pw->pw_name;
307 if (initgroups(uidname.
constData(), gidInt)) {
308 std::cerr <<
"Failed to setgroups()" << std::endl;
315 uint uidInt = uid.
toUInt(&ok);
317 struct passwd *upasswd = getpwnam(qUtf8Printable(uid));
319 uidInt = upasswd->pw_uid;
321 std::cerr <<
"setuid user" << qUtf8Printable(uid) <<
"not found." << std::endl;
326 if (setuid(uidInt)) {
327 std::cerr <<
"Failed to set uid:" << strerror(errno) << std::endl;
330 std::cout <<
"setuid() to " << uidInt << std::endl;
335 void UnixFork::chownSocket(
const QString &filename,
const QString &uidGid)
337 struct group *new_group =
nullptr;
338 struct passwd *new_user =
nullptr;
343 uid_t new_uid = owner.
toUInt(&ok);
346 new_user = getpwnam(qUtf8Printable(owner));
348 qFatal(
"unable to find user '%s'", qUtf8Printable(owner));
350 new_uid = new_user->pw_uid;
356 new_gid = group.
toUInt(&ok);
358 new_group = getgrnam(qUtf8Printable(group));
360 qFatal(
"unable to find group '%s'", qUtf8Printable(group));
362 new_gid = new_group->gr_gid;
366 if (chown(qUtf8Printable(filename), new_uid, new_gid)) {
367 qFatal(
"chown() error '%s'", strerror(errno));
375 int parseProcCpuinfo()
382 QFile file(QStringLiteral(
"/proc/cpuinfo"));
384 qCWarning(C_SERVER_UNIX) <<
"Failed to open file" << file.errorString();
394 while ((lineLength = file.readLine(buf,
sizeof(buf))) != -1) {
396 if (line.startsWith(
"physical id\t: ")) {
410 if (physicalIds.
size()) {
411 cpuSockets = physicalIds.
size();
419 int UnixFork::idealProcessCount()
422 static int cpuSockets = parseProcCpuinfo();
430 int UnixFork::idealThreadCount()
441 void UnixFork::handleSigHup()
448 void UnixFork::handleSigTerm()
456 void UnixFork::handleSigInt()
460 m_terminating =
true;
461 if (m_child || (m_childs.isEmpty())) {
462 qDebug(C_SERVER_UNIX) <<
"SIGINT/SIGQUIT received, worker shutting down...";
465 std::cout <<
"SIGINT/SIGQUIT received, terminating workers..." << std::endl;
466 setupCheckChildTimer();
468 static int count = 0;
470 std::cout <<
"KILL workers..." << std::endl;
473 }
else if (count > 1) {
477 std::cout <<
"workers terminating timeout, KILL ..." << std::endl;
487 void UnixFork::handleSigChld()
492 while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
496 int exitStatus = WEXITSTATUS(status);
499 auto it = m_childs.constFind(p);
500 if (it != m_childs.constEnd()) {
504 std::cout <<
"DAMN ! *UNKNOWN* worker (pid: " << p <<
") died, killed by signal " 505 << exitStatus <<
" :( ignoring .." << std::endl;
509 if (WIFEXITED(status) && exitStatus == 15 && worker.restart == 0) {
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 .." 523 m_recreateWorker.push_back(worker);
525 }
else if (!m_child && m_childs.isEmpty()) {
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) {
538 allRestarted =
false;
544 m_checkChildRestart->deleteLater();
545 m_checkChildRestart =
nullptr;
550 void UnixFork::setSched(
Cutelyst::Server *wsgi,
int workerId,
int workerCore)
552 int cpu_affinity = wsgi->cpuAffinity();
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 !!!";
562 #if defined(__linux__) || defined(__GNU_kFreeBSD__) 564 #elif defined(__FreeBSD__) 567 #if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) 568 int coreCount = idealThreadCount();
570 int workerThreads = 1;
572 workerThreads = coreCount;
578 if (workerThreads > 1) {
579 base_cpu = (workerId * workerThreads) + workerCore * cpu_affinity;
581 base_cpu = workerId * cpu_affinity;
584 if (base_cpu >= coreCount) {
585 base_cpu = base_cpu % coreCount;
589 for (
int i = 0; i < cpu_affinity; i++) {
590 if (base_cpu >= coreCount)
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 !!!";
602 #if defined(__linux__) || defined(__GNU_kFreeBSD__) 603 if (sched_setaffinity(0,
sizeof(cpu_set_t), &cpuset)) {
604 qFatal(
"failed to sched_setaffinity()");
606 #elif defined(__FreeBSD__) 607 if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
sizeof(cpuset), &cpuset)) {
608 qFatal(
"cpuset_setaffinity");
611 std::cout << buf << std::endl;
615 int UnixFork::setupUnixSignalHandlers()
617 setupSocketPair(
false,
true);
636 struct sigaction action;
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)
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)
654 memset(&action, 0,
sizeof(
struct sigaction));
655 action.sa_handler = UnixFork::signalHandler;
656 sigemptyset(&action.sa_mask);
657 action.sa_flags |= SA_RESTART;
659 if (sigaction(SIGCHLD, &action,
nullptr) > 0)
665 void UnixFork::setupSocketPair(
bool closeSignalsFD,
bool createPair)
667 if (closeSignalsFD) {
672 if (createPair && ::socketpair(AF_UNIX, SOCK_STREAM, 0, signalsFd)) {
673 qFatal(
"Couldn't create SIGNALS socketpair");
675 delete m_signalNotifier;
680 read(signalsFd[1], &signal,
sizeof(signal));
698 bool UnixFork::createChild(
const Worker &worker,
bool respawn)
704 delete m_signalNotifier;
705 m_signalNotifier =
nullptr;
707 qint64 childPID = fork();
711 if (worker.respawn >= 5) {
712 std::cout <<
"WSGI worker " << worker.id <<
" respawned too much, sleeping a bit" 717 #if defined(HAS_EventLoopEPoll) 724 setupSocketPair(
true,
true);
729 int ret = qApp->exec();
732 setupSocketPair(
false,
false);
735 std::cout <<
"Respawned WSGI worker " << worker.id <<
" (new pid: " << childPID
736 <<
", cores: " << m_threads <<
")" << std::endl;
738 if (m_processes == 1) {
739 std::cout <<
"spawned WSGI worker (and the only) (pid: " << childPID
740 <<
", cores: " << m_threads <<
")" << std::endl;
742 std::cout <<
"spawned WSGI worker " << worker.id <<
" (pid: " << childPID
743 <<
", cores: " << m_threads <<
")" << std::endl;
746 m_childs.insert(childPID, worker);
750 qFatal(
"Fork failed, quitting!!!!!!");
756 #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(parameter_type value)
qsizetype size() const const
virtual void killChild() override
int toInt(bool *ok, int base) const const
bool isEmpty() const const
const char * constData() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
virtual void terminateChild() override
bool contains(const AT &value) const const
QByteArray simplified() const const
qlonglong toLongLong(bool *ok, int base) const const
virtual void restart() override
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
virtual int exec(bool lazy, bool master) override
qsizetype size() const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
virtual bool continueMaster(int *exit=nullptr) override
uint toUInt(bool *ok, int base) const const
QByteArray toUtf8() const const