17#include <sys/select.h>
23#include <QCoreApplication>
30#include "mythconfig.h"
61static inline void CLOSE(
int& fd)
67 delete fdMap.value(fd);
89 LOG(VB_GENERAL, LOG_INFO, QString(
"Starting IO manager (%1)")
90 .arg(
m_read ?
"read" :
"write"));
113 timeval tv {.tv_sec=0, .tv_usec=0};
119 retval = select(
m_maxfd+1, &fds,
nullptr,
nullptr, &tv);
121 retval = select(
m_maxfd+1,
nullptr, &fds,
nullptr, &tv);
125 LOG(VB_SYSTEM, LOG_ERR,
126 QString(
"MythSystemLegacyIOHandler: select(%1, %2) failed: %3")
130 else if( retval > 0 )
132 auto it =
m_pMap.keyValueBegin();
133 while (it !=
m_pMap.keyValueEnd())
135 auto [fd, buffer] = *it;
137 if( FD_ISSET(fd, &fds) )
159 if( errno != EAGAIN )
167 buff->buffer().append(
m_readbuf.data(), len);
174 if (fdType ==
nullptr)
194 int pos = buff->pos();
195 int len = buff->size() - pos;
196 len = (len > 32768 ? 32768 : len);
198 int rlen =
write(fd, buff->read(len).constData(), len);
201 if( errno != EAGAIN )
211 else if( rlen != len )
213 buff->seek(pos+rlen);
229 while (
m_pMap.contains(fd))
232 std::this_thread::sleep_for(10ms);
267 FD_SET(i.key(), &
m_fds);
275 LOG(VB_GENERAL, LOG_INFO,
"Starting process manager");
298 while( (pid = waitpid(-1, &status, WNOHANG)) > 0 )
302 if( !
m_pMap.contains(pid) )
304 LOG(VB_SYSTEM, LOG_INFO,
305 QString(
"Unmanaged child (PID: %1) has exited!") .arg(pid));
318 LOG(VB_SYSTEM, LOG_ERR,
319 QString(
"Structure for child PID %1 already deleted!")
338 status = ((status & 0x00FF) << 8) | ((status & 0xFF00) >> 8);
339 LOG(VB_SYSTEM, LOG_INFO,
340 QString(
"Odd return value: swapping from %1 to %2")
341 .arg(oldstatus) .arg(status));
348 LOG(VB_SYSTEM, LOG_INFO,
349 QString(
"Managed child (PID: %1) has exited! "
350 "command=%2, status=%3, result=%4")
351 .arg(pid) .arg(ms->
GetLogCmd()) .arg(status)
364 LOG(VB_SYSTEM, LOG_INFO,
365 QString(
"Managed child (PID: %1) has signalled! "
366 "command=%2, status=%3, result=%4, signal=%5")
367 .arg(pid) .arg(ms->
GetLogCmd()) .arg(status)
375 LOG(VB_SYSTEM, LOG_ERR,
376 QString(
"Managed child (PID: %1) has terminated! "
377 "command=%2, status=%3, result=%4")
378 .arg(pid) .arg(ms->
GetLogCmd()) .arg(status)
386 MSMap_t::iterator next;
387 auto now = SystemClock::now();
391 auto it =
m_pMap.keyValueBegin();
392 while (it !=
m_pMap.keyValueEnd())
394 auto [pid2, ms] = *it;
400 if( ms->m_timeout.time_since_epoch() > 0s && ms->m_timeout < now )
405 LOG(VB_SYSTEM, LOG_INFO,
406 QString(
"Managed child (PID: %1) timed out"
407 ", issuing KILL signal").arg(pid2));
416 LOG(VB_SYSTEM, LOG_INFO,
417 QString(
"Managed child (PID: %1) timed out"
418 ", issuing TERM signal").arg(pid2));
420 ms->m_timeout = now + 1s;
425 if (
m_jumpAbort && ms->GetSetting(
"AbortOnJump") )
459 QByteArray ba = ms->
GetBuffer(0)->data();
461 wtb.open(QIODevice::ReadOnly);
501 LOG(VB_GENERAL, LOG_INFO,
"Starting process signal handler");
600 QList<QChar> whitespace; whitespace <<
' ' <<
'\t' <<
'\n' <<
'\r';
601 QList<QChar> whitechr; whitechr <<
't' <<
'n' <<
'r';
603 QChar hardquote =
'\'';
606 bool hardquoted =
false;
607 bool escaped =
false;
610 QString::const_iterator i = cmd.begin();
611 while (i != cmd.end())
613 if (quoted || hardquoted)
617 if ((quote == *i) || (
escape == *i) ||
618 whitespace.contains(*i))
623 else if (whitechr.contains(*i))
626 tmp += whitespace[whitechr.indexOf(*i)+1];
651 else if ((quoted && (*i == quote)) ||
652 (hardquoted && (*i == hardquote)))
655 quoted = hardquoted =
false;
666 if ((*i == quote) || (*i == hardquote) || (*i ==
escape) ||
667 whitespace.contains(*i))
672 else if (whitechr.contains(*i))
675 tmp += whitespace[whitechr.indexOf(*i)+1];
687 else if (quote == *i)
691 else if (hardquote == *i)
701 else if (whitespace.contains(*i) && !tmp.isEmpty())
717 if (quoted || hardquoted || escaped)
730 abscmd =
args.takeFirst();
731 if (!abscmd.startsWith(
'/'))
734 QStringList path = qEnvironmentVariable(
"PATH").split(
':');
735 for (
const auto& pit : std::as_const(path))
737 QFile
file(QString(
"%1/%2").arg(pit, abscmd));
740 abscmd =
file.fileName();
755 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Terminate skipped. Status: %1")
775 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Signal skipped. Status: %1")
780 LOG(VB_GENERAL, LOG_INFO, QString(
"Child PID %1 killed with %2")
781 .arg(
m_pid).arg(strsignal(sig)));
790 std::string locerr = qPrintable(
LOC_ERR);
792 LOG(VB_SYSTEM, LOG_DEBUG, QString(
"Launching: %1").arg(
GetLogCmd()));
794 std::array<int,2> p_stdin {-1,-1};
795 std::array<int,2> p_stdout {-1,-1};
796 std::array<int,2> p_stderr {-1,-1};
801 if( pipe(p_stdin.data()) == -1 )
803 LOG(VB_SYSTEM, LOG_ERR,
LOC_ERR +
"stdin pipe() failed");
808 int flags = fcntl(p_stdin[1], F_GETFL, 0);
812 "fcntl on stdin pipe getting flags failed" +
818 if(fcntl(p_stdin[1], F_SETFL, flags) == -1)
821 "fcntl on stdin pipe setting non-blocking failed" +
829 if( pipe(p_stdout.data()) == -1 )
831 LOG(VB_SYSTEM, LOG_ERR,
LOC_ERR +
"stdout pipe() failed");
836 int flags = fcntl(p_stdout[0], F_GETFL, 0);
840 "fcntl on stdout pipe getting flags failed" +
846 if(fcntl(p_stdout[0], F_SETFL, flags) == -1)
849 "fcntl on stdout pipe setting non-blocking failed" +
857 if( pipe(p_stderr.data()) == -1 )
859 LOG(VB_SYSTEM, LOG_ERR,
LOC_ERR +
"stderr pipe() failed");
864 int flags = fcntl(p_stderr[0], F_GETFL, 0);
868 "fcntl on stderr pipe getting flags failed" +
874 if(fcntl(p_stderr[0], F_SETFL, flags) == -1)
877 "fcntl on stderr pipe setting non-blocking failed" +
887 QStringList
args = QStringList(
"-c");
890 QString cmd =
"/bin/sh";
900 QByteArrayList bargs;
901 bargs.reserve(
args.size());
902 for (
const auto& arg : std::as_const(
args))
903 bargs.push_back(arg.toUtf8());
906 std::vector<char*>cmdargs;
907 cmdargs.reserve(
args.size() + 1);
908 for (
const auto& barg : std::as_const(bargs))
909 cmdargs.push_back((
char*)barg.data());
910 cmdargs.push_back(
nullptr);
913 int ioprioval =
m_parent->GetIOPrio();
918 : SystemClock::time_point();
921 pid_t child = fork();
926 LOG(VB_SYSTEM, LOG_ERR,
"fork() failed: " +
ENO);
936 LOG(VB_SYSTEM, LOG_INFO,
937 QString(
"Managed child (PID: %1) has started! "
938 "%2%3 command=%4, timeout=%5")
939 .arg(QString::number(
m_pid),
943 QString::number(
timeout.count())));
948 if (p_stdout[1] >= 0)
950 if (p_stderr[1] >= 0)
974 if( p_stdin[0] >= 0 )
977 if( dup2(p_stdin[0], 0) < 0 )
980 <<
"Cannot redirect input pipe to standard input: "
981 << strerror(errno) <<
'\n';
988 int fd = open(
"/dev/null", O_RDONLY);
994 <<
"Cannot redirect /dev/null to standard input,"
995 "\n\t\t\tfailed to duplicate file descriptor: "
996 << strerror(errno) <<
'\n';
1003 <<
"Unable to close stdin redirect /dev/null: "
1004 << strerror(errno) <<
'\n';
1011 <<
"Cannot redirect /dev/null to standard input, "
1013 << strerror(errno) <<
'\n';
1018 if( p_stdout[1] >= 0 )
1021 if( dup2(p_stdout[1], 1) < 0)
1024 <<
"Cannot redirect output pipe to standard output: "
1025 << strerror(errno) <<
'\n';
1032 int fd = open(
"/dev/null", O_WRONLY);
1035 if( dup2(fd, 1) < 0)
1038 <<
"Cannot redirect standard output to /dev/null,"
1039 "\n\t\t\tfailed to duplicate file descriptor: "
1040 << strerror(errno) <<
'\n';
1047 <<
"Unable to close stdout redirect /dev/null: "
1048 << strerror(errno) <<
'\n';
1055 <<
"Cannot redirect standard output to /dev/null, "
1057 << strerror(errno) <<
'\n';
1062 if( p_stderr[1] >= 0 )
1065 if( dup2(p_stderr[1], 2) < 0)
1068 <<
"Cannot redirect error pipe to standard error: "
1069 << strerror(errno) <<
'\n';
1076 int fd = open(
"/dev/null", O_WRONLY);
1079 if( dup2(fd, 2) < 0)
1082 <<
"Cannot redirect standard error to /dev/null,"
1083 "\n\t\t\tfailed to duplicate file descriptor: "
1084 << strerror(errno) <<
'\n';
1091 <<
"Unable to close stderr redirect /dev/null: "
1092 << strerror(errno) <<
'\n';
1099 <<
"Cannot redirect standard error to /dev/null, "
1101 << strerror(errno) <<
'\n';
1107 close_range(3, sysconf(_SC_OPEN_MAX) - 1, 0);
1109 for(
int fd = sysconf(_SC_OPEN_MAX) - 1; fd > 2; fd-- )
1117 if( !directory.isEmpty() && chdir(directory.constData()) < 0 )
1120 <<
"chdir() failed: "
1121 << strerror(errno) <<
'\n';
1132 if( execv(cmdUTF8.constData(), cmdargs.data()) < 0 )
1136 <<
"execv() failed: "
1137 << strerror(errno) <<
'\n';
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
void HandleWrite(int fd, QBuffer *buff)
void insert(int fd, QBuffer *buff)
void HandleRead(int fd, QBuffer *buff)
std::array< char, 65536 > m_readbuf
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
void append(MythSystemLegacyUnix *ms)
void readDataReady(int fd)
QString & GetCommand(void)
void SetArgs(const QStringList &args)
QBuffer * GetBuffer(int index)
bool GetSetting(const char *setting)
void SetStatus(uint status)
QPointer< MythSystemLegacy > m_parent
QString & GetDirectory(void)
void SetCommand(const QString &cmd)
QStringList & GetArgs(void)
QString & GetLogCmd(void)
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
MythSystemLegacyUnix(MythSystemLegacy *parent)
std::array< int, 3 > m_stdpipe
void Signal(int sig) override
friend class MythSystemLegacySignalManager
void Term(bool force=false) override
void Fork(std::chrono::seconds timeout) override
void Manage(void) override
friend class MythSystemLegacyIOHandler
bool ParseShell(const QString &cmd, QString &abscmd, QStringList &args) override
friend class MythSystemLegacyManager
void JumpAbort(void) override
void readDataReady(int fd)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
@ GENERIC_EXIT_OK
Exited with no error.
@ GENERIC_EXIT_DAEMONIZING_ERROR
Error daemonizing or execl.
@ GENERIC_EXIT_PIPE_FAILURE
Error creating I/O pipes.
@ GENERIC_EXIT_RUNNING
Process is running.
@ GENERIC_EXIT_KILLED
Process killed or stopped.
@ GENERIC_EXIT_TIMEOUT
Process timed out.
@ GENERIC_EXIT_NOT_OK
Exited with error.
std::chrono::time_point< SystemClock > SystemTime
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
bool myth_ioprio(int)
Allows setting the I/O priority of the current process/thread.
static volatile bool run_system
static constexpr std::chrono::milliseconds kIOHandlerInterval
void ShutdownMythSystemLegacy(void)
static MythSystemLegacyIOHandler * writeThread
QMap< int, FDType_t * > FDMap_t
static void CLOSE(int &fd)
static constexpr std::chrono::milliseconds kSignalHandlerInterval
static MythSystemLegacyIOHandler * readThread
static MythSystemLegacySignalManager * smanager
static MythSystemLegacyManager * manager
QList< QPointer< MythSystemLegacyUnix > > MSList_t
def read(device=None, features=[])
def write(text, progress=True)
MythSystemLegacyUnix * m_ms