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"));
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";
898 char *command = strdup(cmdUTF8.constData());
900 char **cmdargs = (
char **)malloc((
args.size() + 1) *
sizeof(
char *));
905 for (
auto it =
args.constBegin(); it !=
args.constEnd(); ++it)
907 cmdargs[i++] = strdup(it->toUtf8().constData());
909 cmdargs[i] = (
char *)
nullptr;
914 "Failed to allocate memory for cmdargs " +
920 char *directory =
nullptr;
922 if (
GetSetting(
"SetDirectory") && !dir.isEmpty())
923 directory = strdup(dir.toUtf8().constData());
926 int ioprioval =
m_parent->GetIOPrio();
931 : SystemClock::time_point();
934 pid_t child = fork();
939 LOG(VB_SYSTEM, LOG_ERR,
"fork() failed: " +
ENO);
949 LOG(VB_SYSTEM, LOG_INFO,
950 QString(
"Managed child (PID: %1) has started! "
951 "%2%3 command=%4, timeout=%5")
952 .arg(QString::number(
m_pid),
956 QString::number(
timeout.count())));
961 if (p_stdout[1] >= 0)
963 if (p_stderr[1] >= 0)
987 if( p_stdin[0] >= 0 )
990 if( dup2(p_stdin[0], 0) < 0 )
993 <<
"Cannot redirect input pipe to standard input: "
994 << strerror(errno) << std::endl;
1001 int fd = open(
"/dev/null", O_RDONLY);
1004 if( dup2(fd, 0) < 0)
1007 <<
"Cannot redirect /dev/null to standard input,"
1008 "\n\t\t\tfailed to duplicate file descriptor: "
1009 << strerror(errno) << std::endl;
1016 <<
"Unable to close stdin redirect /dev/null: "
1017 << strerror(errno) << std::endl;
1024 <<
"Cannot redirect /dev/null to standard input, "
1026 << strerror(errno) << std::endl;
1031 if( p_stdout[1] >= 0 )
1034 if( dup2(p_stdout[1], 1) < 0)
1037 <<
"Cannot redirect output pipe to standard output: "
1038 << strerror(errno) << std::endl;
1045 int fd = open(
"/dev/null", O_WRONLY);
1048 if( dup2(fd, 1) < 0)
1051 <<
"Cannot redirect standard output to /dev/null,"
1052 "\n\t\t\tfailed to duplicate file descriptor: "
1053 << strerror(errno) << std::endl;
1060 <<
"Unable to close stdout redirect /dev/null: "
1061 << strerror(errno) << std::endl;
1068 <<
"Cannot redirect standard output to /dev/null, "
1070 << strerror(errno) << std::endl;
1075 if( p_stderr[1] >= 0 )
1078 if( dup2(p_stderr[1], 2) < 0)
1081 <<
"Cannot redirect error pipe to standard error: "
1082 << strerror(errno) << std::endl;
1089 int fd = open(
"/dev/null", O_WRONLY);
1092 if( dup2(fd, 2) < 0)
1095 <<
"Cannot redirect standard error to /dev/null,"
1096 "\n\t\t\tfailed to duplicate file descriptor: "
1097 << strerror(errno) << std::endl;
1104 <<
"Unable to close stderr redirect /dev/null: "
1105 << strerror(errno) << std::endl;
1112 <<
"Cannot redirect standard error to /dev/null, "
1114 << strerror(errno) << std::endl;
1120 close_range(3, sysconf(_SC_OPEN_MAX) - 1, 0);
1122 for(
int fd = sysconf(_SC_OPEN_MAX) - 1; fd > 2; fd-- )
1127 if( directory && chdir(directory) < 0 )
1130 <<
"chdir() failed: "
1131 << strerror(errno) << std::endl;
1141 if( execv(command, cmdargs) < 0 )
1145 <<
"execv() failed: "
1146 << strerror(errno) << std::endl;
1162 for (
int i = 0; cmdargs[i]; i++)
1164 free(
reinterpret_cast<void*
>(cmdargs) );
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 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