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 auto it = m_recreateWorker.begin();
135 while (it != m_recreateWorker.end()) {
138 if (!createChild(worker, respawn)) {
139 std::cout <<
"CHEAPING worker: " << worker.id << std::endl;
142 it = m_recreateWorker.erase(it);
145 for (
int i = 0; i < m_processes; ++i) {
149 createChild(worker, respawn);
153 return !m_childs.empty();
156 void UnixFork::decreaseWorkerRespawn()
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;
168 if (missingRespawn) {
175 const auto childs = m_childs.keys();
176 for (qint64 pid : childs) {
184 ::kill(pid_t(pid), SIGKILL);
189 const auto childs = m_childs.keys();
190 for (qint64 pid : childs) {
198 ::kill(pid_t(pid), SIGQUIT);
201 void UnixFork::stopWSGI(
const QString &pidfile)
205 std::cerr <<
"Failed open pid file " << qPrintable(pidfile) << std::endl;
212 std::cerr <<
"Failed read pid file " << qPrintable(pidfile) << std::endl;
216 ::kill(pid_t(pid), SIGINT);
220 bool UnixFork::setUmask(
const QByteArray &valueStr)
222 if (valueStr.
size() < 3) {
223 std::cerr <<
"umask too small" << std::endl;
227 const char *value = valueStr.
constData();
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');
234 mode = (mode << 3) + (value[1] -
'0');
235 mode = (mode << 3) + (value[2] -
'0');
236 mode = (mode << 3) + (value[3] -
'0');
238 std::cout <<
"umask() " << value << std::endl;
245 void UnixFork::signalHandler(
int signal)
249 write(signalsFd[0], &sig,
sizeof(sig));
252 void UnixFork::setupCheckChildTimer()
254 if (!m_checkChildRestart) {
255 m_checkChildRestart =
new QTimer(
this);
256 m_checkChildRestart->start(std::chrono::milliseconds{500});
261 void UnixFork::postFork(
int workerId)
264 delete m_checkChildRestart;
266 Q_EMIT forked(workerId - 1);
269 bool UnixFork::setGidUid(
const QString &gid,
const QString &uid,
bool noInitgroups)
274 uint gidInt = gid.
toUInt(&ok);
276 struct group *ugroup = getgrnam(qUtf8Printable(gid));
278 gidInt = ugroup->gr_gid;
280 std::cerr <<
"setgid group %s not found." << qUtf8Printable(gid) << std::endl;
285 if (setgid(gidInt)) {
286 std::cerr <<
"Failed to set gid '%s'" << strerror(errno) << std::endl;
289 std::cout <<
"setgid() to " << gidInt << std::endl;
291 if (noInitgroups || uid.
isEmpty()) {
292 if (setgroups(0,
nullptr)) {
293 std::cerr <<
"Failed to setgroups()" << std::endl;
298 uint uidInt = uid.
toUInt(&ok);
300 struct passwd *pw = getpwuid(uidInt);
302 uidname = pw->pw_name;
308 if (initgroups(uidname.
constData(), gidInt)) {
309 std::cerr <<
"Failed to setgroups()" << std::endl;
316 uint uidInt = uid.
toUInt(&ok);
318 struct passwd *upasswd = getpwnam(qUtf8Printable(uid));
320 uidInt = upasswd->pw_uid;
322 std::cerr <<
"setuid user" << qUtf8Printable(uid) <<
"not found." << std::endl;
327 if (setuid(uidInt)) {
328 std::cerr <<
"Failed to set uid:" << strerror(errno) << std::endl;
331 std::cout <<
"setuid() to " << uidInt << std::endl;
336 void UnixFork::chownSocket(
const QString &filename,
const QString &uidGid)
338 struct group *new_group =
nullptr;
339 struct passwd *new_user =
nullptr;
344 uid_t new_uid = owner.
toUInt(&ok);
347 new_user = getpwnam(qUtf8Printable(owner));
349 qFatal(
"unable to find user '%s'", qUtf8Printable(owner));
351 new_uid = new_user->pw_uid;
357 new_gid = group.
toUInt(&ok);
359 new_group = getgrnam(qUtf8Printable(group));
361 qFatal(
"unable to find group '%s'", qUtf8Printable(group));
363 new_gid = new_group->gr_gid;
367 if (chown(qUtf8Printable(filename), new_uid, new_gid)) {
368 qFatal(
"chown() error '%s'", strerror(errno));
376 int parseProcCpuinfo()
383 QFile file(QStringLiteral(
"/proc/cpuinfo"));
385 qCWarning(C_SERVER_UNIX) <<
"Failed to open file" << file.errorString();
395 while ((lineLength = file.readLine(buf,
sizeof(buf))) != -1) {
397 if (line.startsWith(
"physical id\t: ")) {
411 if (physicalIds.
size()) {
412 cpuSockets = physicalIds.
size();
420 int UnixFork::idealProcessCount()
423 static int cpuSockets = parseProcCpuinfo();
431 int UnixFork::idealThreadCount()
442 void UnixFork::handleSigHup()
449 void UnixFork::handleSigTerm()
457 void UnixFork::handleSigInt()
461 m_terminating =
true;
462 if (m_child || (m_childs.isEmpty())) {
463 qDebug(C_SERVER_UNIX) <<
"SIGINT/SIGQUIT received, worker shutting down...";
466 std::cout <<
"SIGINT/SIGQUIT received, terminating workers..." << std::endl;
467 setupCheckChildTimer();
469 static int count = 0;
471 std::cout <<
"KILL workers..." << std::endl;
474 }
else if (count > 1) {
478 std::cout <<
"workers terminating timeout, KILL ..." << std::endl;
488 void UnixFork::handleSigChld()
493 while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
497 int exitStatus = WEXITSTATUS(status);
500 auto it = m_childs.find(p);
501 if (it != m_childs.end()) {
505 std::cout <<
"DAMN ! *UNKNOWN* worker (pid: " << p <<
") died, killed by signal " 506 << exitStatus <<
" :( ignoring .." << std::endl;
510 if (WIFEXITED(status) && exitStatus == 15 && worker.restart == 0) {
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 .." 524 m_recreateWorker.push_back(worker);
526 }
else if (!m_child && m_childs.isEmpty()) {
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) {
539 allRestarted =
false;
545 m_checkChildRestart->deleteLater();
546 m_checkChildRestart =
nullptr;
551 void UnixFork::setSched(
Cutelyst::Server *wsgi,
int workerId,
int workerCore)
553 int cpu_affinity = wsgi->cpuAffinity();
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 !!!";
563 #if defined(__linux__) || defined(__GNU_kFreeBSD__) 565 #elif defined(__FreeBSD__) 568 #if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) 569 int coreCount = idealThreadCount();
571 int workerThreads = 1;
573 workerThreads = coreCount;
579 if (workerThreads > 1) {
580 base_cpu = (workerId * workerThreads) + workerCore * cpu_affinity;
582 base_cpu = workerId * cpu_affinity;
585 if (base_cpu >= coreCount) {
586 base_cpu = base_cpu % coreCount;
590 for (
int i = 0; i < cpu_affinity; i++) {
591 if (base_cpu >= coreCount)
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 !!!";
603 #if defined(__linux__) || defined(__GNU_kFreeBSD__) 604 if (sched_setaffinity(0,
sizeof(cpu_set_t), &cpuset)) {
605 qFatal(
"failed to sched_setaffinity()");
607 #elif defined(__FreeBSD__) 608 if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
sizeof(cpuset), &cpuset)) {
609 qFatal(
"cpuset_setaffinity");
612 std::cout << buf << std::endl;
616 int UnixFork::setupUnixSignalHandlers()
618 setupSocketPair(
false,
true);
637 struct sigaction action;
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)
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)
655 memset(&action, 0,
sizeof(
struct sigaction));
656 action.sa_handler = UnixFork::signalHandler;
657 sigemptyset(&action.sa_mask);
658 action.sa_flags |= SA_RESTART;
660 if (sigaction(SIGCHLD, &action,
nullptr) > 0)
666 void UnixFork::setupSocketPair(
bool closeSignalsFD,
bool createPair)
668 if (closeSignalsFD) {
673 if (createPair && ::socketpair(AF_UNIX, SOCK_STREAM, 0, signalsFd)) {
674 qFatal(
"Couldn't create SIGNALS socketpair");
676 delete m_signalNotifier;
681 read(signalsFd[1], &signal,
sizeof(signal));
699 bool UnixFork::createChild(
const Worker &worker,
bool respawn)
705 delete m_signalNotifier;
706 m_signalNotifier =
nullptr;
708 qint64 childPID = fork();
712 if (worker.respawn >= 5) {
713 std::cout <<
"WSGI worker " << worker.id <<
" respawned too much, sleeping a bit" 718 #if defined(HAS_EventLoopEPoll) 725 setupSocketPair(
true,
true);
730 int ret = qApp->exec();
733 setupSocketPair(
false,
false);
736 std::cout <<
"Respawned WSGI worker " << worker.id <<
" (new pid: " << childPID
737 <<
", cores: " << m_threads <<
")" << std::endl;
739 if (m_processes == 1) {
740 std::cout <<
"spawned WSGI worker (and the only) (pid: " << childPID
741 <<
", cores: " << m_threads <<
")" << std::endl;
743 std::cout <<
"spawned WSGI worker " << worker.id <<
" (pid: " << childPID
744 <<
", cores: " << m_threads <<
")" << std::endl;
747 m_childs.insert(childPID, worker);
751 qFatal(
"Fork failed, quitting!!!!!!");
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)
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, QString::SectionFlags flags) const const
virtual int exec(bool lazy, bool master) override
qsizetype size() const const
int compare(const QString &other, Qt::CaseSensitivity cs) const const
virtual bool continueMaster(int *exit=nullptr) override
uint toUInt(bool *ok, int base) const const
QByteArray toUtf8() const const