17#include <sys/select.h>
23#include <QCoreApplication>
60static inline void CLOSE(
int& fd)
66 delete fdMap.value(fd);
88 LOG(VB_GENERAL, LOG_INFO, QString(
"Starting IO manager (%1)")
89 .arg(
m_read ?
"read" :
"write"));
118 retval = select(
m_maxfd+1, &fds,
nullptr,
nullptr, &tv);
120 retval = select(
m_maxfd+1,
nullptr, &fds,
nullptr, &tv);
124 LOG(VB_SYSTEM, LOG_ERR,
125 QString(
"MythSystemLegacyIOHandler: select(%1, %2) failed: %3")
129 else if( retval > 0 )
131 auto it =
m_pMap.keyValueBegin();
132 while (it !=
m_pMap.keyValueEnd())
134 auto [fd, buffer] = *it;
136 if( FD_ISSET(fd, &fds) )
158 if( errno != EAGAIN )
166 buff->buffer().append(
m_readbuf.data(), len);
173 if (fdType ==
nullptr)
193 int pos = buff->pos();
194 int len = buff->size() - pos;
195 len = (len > 32768 ? 32768 : len);
197 int rlen =
write(fd, buff->read(len).constData(), len);
200 if( errno != EAGAIN )
210 else if( rlen != len )
212 buff->seek(pos+rlen);
228 while (
m_pMap.contains(fd))
266 FD_SET(i.key(), &
m_fds);
274 LOG(VB_GENERAL, LOG_INFO,
"Starting process manager");
297 while( (pid = waitpid(-1, &status, WNOHANG)) > 0 )
301 if( !
m_pMap.contains(pid) )
303 LOG(VB_SYSTEM, LOG_INFO,
304 QString(
"Unmanaged child (PID: %1) has exited!") .arg(pid));
317 LOG(VB_SYSTEM, LOG_ERR,
318 QString(
"Structure for child PID %1 already deleted!")
337 status = ((status & 0x00FF) << 8) | ((status & 0xFF00) >> 8);
338 LOG(VB_SYSTEM, LOG_INFO,
339 QString(
"Odd return value: swapping from %1 to %2")
340 .arg(oldstatus) .arg(status));
347 LOG(VB_SYSTEM, LOG_INFO,
348 QString(
"Managed child (PID: %1) has exited! "
349 "command=%2, status=%3, result=%4")
350 .arg(pid) .arg(ms->
GetLogCmd()) .arg(status)
363 LOG(VB_SYSTEM, LOG_INFO,
364 QString(
"Managed child (PID: %1) has signalled! "
365 "command=%2, status=%3, result=%4, signal=%5")
366 .arg(pid) .arg(ms->
GetLogCmd()) .arg(status)
374 LOG(VB_SYSTEM, LOG_ERR,
375 QString(
"Managed child (PID: %1) has terminated! "
376 "command=%2, status=%3, result=%4")
377 .arg(pid) .arg(ms->
GetLogCmd()) .arg(status)
385 MSMap_t::iterator next;
386 auto now = SystemClock::now();
390 auto it =
m_pMap.keyValueBegin();
391 while (it !=
m_pMap.keyValueEnd())
393 auto [pid2, ms] = *it;
399 if( ms->m_timeout.time_since_epoch() > 0s && ms->m_timeout < now )
404 LOG(VB_SYSTEM, LOG_INFO,
405 QString(
"Managed child (PID: %1) timed out"
406 ", issuing KILL signal").arg(pid2));
415 LOG(VB_SYSTEM, LOG_INFO,
416 QString(
"Managed child (PID: %1) timed out"
417 ", issuing TERM signal").arg(pid2));
419 ms->m_timeout = now + 1s;
424 if (
m_jumpAbort && ms->GetSetting(
"AbortOnJump") )
458 QByteArray ba = ms->
GetBuffer(0)->data();
460 wtb.open(QIODevice::ReadOnly);
500 LOG(VB_GENERAL, LOG_INFO,
"Starting process signal handler");
599 QList<QChar> whitespace; whitespace <<
' ' <<
'\t' <<
'\n' <<
'\r';
600 QList<QChar> whitechr; whitechr <<
't' <<
'n' <<
'r';
602 QChar hardquote =
'\'';
605 bool hardquoted =
false;
606 bool escaped =
false;
609 QString::const_iterator i = cmd.begin();
610 while (i != cmd.end())
612 if (quoted || hardquoted)
616 if ((quote == *i) || (
escape == *i) ||
617 whitespace.contains(*i))
622 else if (whitechr.contains(*i))
625 tmp += whitespace[whitechr.indexOf(*i)+1];
650 else if ((quoted && (*i == quote)) ||
651 (hardquoted && (*i == hardquote)))
654 quoted = hardquoted =
false;
665 if ((*i == quote) || (*i == hardquote) || (*i ==
escape) ||
666 whitespace.contains(*i))
671 else if (whitechr.contains(*i))
674 tmp += whitespace[whitechr.indexOf(*i)+1];
686 else if (quote == *i)
690 else if (hardquote == *i)
700 else if (whitespace.contains(*i) && !
tmp.isEmpty())
716 if (quoted || hardquoted || escaped)
729 abscmd =
args.takeFirst();
730 if (!abscmd.startsWith(
'/'))
733 QStringList path = qEnvironmentVariable(
"PATH").split(
':');
734 for (
const auto& pit : std::as_const(path))
736 QFile
file(QString(
"%1/%2").arg(pit, abscmd));
739 abscmd =
file.fileName();
754 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Terminate skipped. Status: %1")
774 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Signal skipped. Status: %1")
779 LOG(VB_GENERAL, LOG_INFO, QString(
"Child PID %1 killed with %2")
780 .arg(
m_pid).arg(strsignal(sig)));
789 std::string locerr = qPrintable(
LOC_ERR);
791 LOG(VB_SYSTEM, LOG_DEBUG, QString(
"Launching: %1").arg(
GetLogCmd()));
793 std::array<int,2> p_stdin {-1,-1};
794 std::array<int,2> p_stdout {-1,-1};
795 std::array<int,2> p_stderr {-1,-1};
800 if( pipe(p_stdin.data()) == -1 )
802 LOG(VB_SYSTEM, LOG_ERR,
LOC_ERR +
"stdin pipe() failed");
807 int flags = fcntl(p_stdin[1], F_GETFL, 0);
811 "fcntl on stdin pipe getting flags failed" +
817 if(fcntl(p_stdin[1], F_SETFL, flags) == -1)
820 "fcntl on stdin pipe setting non-blocking failed" +
828 if( pipe(p_stdout.data()) == -1 )
830 LOG(VB_SYSTEM, LOG_ERR,
LOC_ERR +
"stdout pipe() failed");
835 int flags = fcntl(p_stdout[0], F_GETFL, 0);
839 "fcntl on stdout pipe getting flags failed" +
845 if(fcntl(p_stdout[0], F_SETFL, flags) == -1)
848 "fcntl on stdout pipe setting non-blocking failed" +
856 if( pipe(p_stderr.data()) == -1 )
858 LOG(VB_SYSTEM, LOG_ERR,
LOC_ERR +
"stderr pipe() failed");
863 int flags = fcntl(p_stderr[0], F_GETFL, 0);
867 "fcntl on stderr pipe getting flags failed" +
873 if(fcntl(p_stderr[0], F_SETFL, flags) == -1)
876 "fcntl on stderr pipe setting non-blocking failed" +
886 QStringList
args = QStringList(
"-c");
889 QString cmd =
"/bin/sh";
897 char *command = strdup(cmdUTF8.constData());
899 char **cmdargs = (
char **)malloc((
args.size() + 1) *
sizeof(
char *));
904 for (
auto it =
args.constBegin(); it !=
args.constEnd(); ++it)
906 cmdargs[i++] = strdup(it->toUtf8().constData());
908 cmdargs[i] = (
char *)
nullptr;
913 "Failed to allocate memory for cmdargs " +
919 char *directory =
nullptr;
921 if (
GetSetting(
"SetDirectory") && !dir.isEmpty())
922 directory = strdup(dir.toUtf8().constData());
925 int ioprioval =
m_parent->GetIOPrio();
930 : SystemClock::time_point();
933 pid_t child = fork();
938 LOG(VB_SYSTEM, LOG_ERR,
"fork() failed: " +
ENO);
948 LOG(VB_SYSTEM, LOG_INFO,
949 QString(
"Managed child (PID: %1) has started! "
950 "%2%3 command=%4, timeout=%5")
951 .arg(QString::number(
m_pid),
955 QString::number(
timeout.count())));
960 if (p_stdout[1] >= 0)
962 if (p_stderr[1] >= 0)
986 if( p_stdin[0] >= 0 )
989 if( dup2(p_stdin[0], 0) < 0 )
992 <<
"Cannot redirect input pipe to standard input: "
993 << strerror(errno) << std::endl;
1000 int fd = open(
"/dev/null", O_RDONLY);
1003 if( dup2(fd, 0) < 0)
1006 <<
"Cannot redirect /dev/null to standard input,"
1007 "\n\t\t\tfailed to duplicate file descriptor: "
1008 << strerror(errno) << std::endl;
1015 <<
"Unable to close stdin redirect /dev/null: "
1016 << strerror(errno) << std::endl;
1023 <<
"Cannot redirect /dev/null to standard input, "
1025 << strerror(errno) << std::endl;
1030 if( p_stdout[1] >= 0 )
1033 if( dup2(p_stdout[1], 1) < 0)
1036 <<
"Cannot redirect output pipe to standard output: "
1037 << strerror(errno) << std::endl;
1044 int fd = open(
"/dev/null", O_WRONLY);
1047 if( dup2(fd, 1) < 0)
1050 <<
"Cannot redirect standard output to /dev/null,"
1051 "\n\t\t\tfailed to duplicate file descriptor: "
1052 << strerror(errno) << std::endl;
1059 <<
"Unable to close stdout redirect /dev/null: "
1060 << strerror(errno) << std::endl;
1067 <<
"Cannot redirect standard output to /dev/null, "
1069 << strerror(errno) << std::endl;
1074 if( p_stderr[1] >= 0 )
1077 if( dup2(p_stderr[1], 2) < 0)
1080 <<
"Cannot redirect error pipe to standard error: "
1081 << strerror(errno) << std::endl;
1088 int fd = open(
"/dev/null", O_WRONLY);
1091 if( dup2(fd, 2) < 0)
1094 <<
"Cannot redirect standard error to /dev/null,"
1095 "\n\t\t\tfailed to duplicate file descriptor: "
1096 << strerror(errno) << std::endl;
1103 <<
"Unable to close stderr redirect /dev/null: "
1104 << strerror(errno) << std::endl;
1111 <<
"Cannot redirect standard error to /dev/null, "
1113 << strerror(errno) << std::endl;
1119 close_range(3, sysconf(_SC_OPEN_MAX) - 1, 0);
1121 for(
int fd = sysconf(_SC_OPEN_MAX) - 1; fd > 2; fd-- )
1126 if( directory && chdir(directory) < 0 )
1129 <<
"chdir() failed: "
1130 << strerror(errno) << std::endl;
1140 if( execv(command, cmdargs) < 0 )
1144 <<
"execv() failed: "
1145 << strerror(errno) << std::endl;
1161 for (
int i = 0; cmdargs[i]; i++)
1163 free(
reinterpret_cast<void*
>(cmdargs) );
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
static void usleep(std::chrono::microseconds time)
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 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