MythTV  master
mythsystemwindows.cpp
Go to the documentation of this file.
1 
2 // compat header
3 #include "compat.h"
4 
5 // Own header
6 #include "mythsystemlegacy.h"
7 #include "mythsystemwindows.h"
8 
9 // C++/C headers
10 #include <cerrno>
11 #include <csignal> // for kill()
12 #include <cstdio>
13 #include <cstdlib>
14 #include <cstring>
15 #include <ctime>
16 #include <fcntl.h>
17 #include <unistd.h>
18 
19 // QT headers
20 #include <QCoreApplication>
21 #include <QMutex>
22 #include <QMap>
23 #include <QString>
24 #include <QStringList>
25 
26 // libmythbase headers
27 #include "mythcorecontext.h"
28 #include "mythlogging.h"
29 #include "mythevent.h"
30 #include "exitcodes.h"
31 
32 // Windows headers
33 #include <windows.h>
34 #include <tchar.h>
35 
36 #define CLOSE(x) \
37 if( (x) ) { \
38  CloseHandle((x)); \
39  fdLock.lock(); \
40  delete fdMap.value((x)); \
41  fdMap.remove((x)); \
42  fdLock.unlock(); \
43  (x) = nullptr; \
44 }
45 
46 struct FDType_t
47 {
49  int type;
50 };
51 using FDMap_t = QMap<HANDLE, FDType_t*>;
52 
53 /**********************************
54  * MythSystemLegacyManager method defines
55  *********************************/
56 static bool run_system = true;
57 static MythSystemLegacyManager *manager = nullptr;
62 static QMutex listLock;
63 static FDMap_t fdMap;
64 static QMutex fdLock;
65 
67 {
68  run_system = false;
69  if (manager)
70  manager->wait();
71  if (smanager)
72  smanager->wait();
73  if (readThread)
74  readThread->wait();
75  if (writeThread)
76  writeThread->wait();
77 }
78 
80  MThread(QString("SystemIOHandler%1").arg(read ? "R" : "W")),
81  m_pWaitLock(), m_pWait(), m_pLock(), m_pMap(PMap_t()),
82  m_read(read)
83 {
84  m_readbuf[0] = '\0';
85 }
86 
88 {
89  RunProlog();
90 
91  LOG(VB_GENERAL, LOG_INFO, QString("Starting IO manager (%1)")
92  .arg(m_read ? "read" : "write"));
93 
94  while( run_system )
95  {
96  {
97  QMutexLocker locker(&m_pWaitLock);
98  m_pWait.wait(&m_pWaitLock);
99  }
100 
101  while( run_system )
102  {
103  usleep(10000); // ~100x per second, for ~3MBps throughput
104  m_pLock.lock();
105  if( m_pMap.isEmpty() )
106  {
107  m_pLock.unlock();
108  break;
109  }
110 
111  bool datafound = true;
112  m_pLock.unlock();
113 
114  while ( datafound && run_system )
115  {
116  m_pLock.lock();
117 
118  datafound = false;
119  PMap_t::iterator i, next;
120  for( i = m_pMap.begin(); i != m_pMap.end(); i = next )
121  {
122  next = i + 1;
123 
124  if( m_read )
125  datafound |= HandleRead(i.key(), i.value());
126  else
127  datafound |= HandleWrite(i.key(), i.value());
128  }
129  m_pLock.unlock();
130  }
131  }
132  }
133  RunEpilog();
134 }
135 
136 bool MythSystemLegacyIOHandler::HandleRead(HANDLE h, QBuffer *buff)
137 {
138  DWORD lenAvail;
139 
140  if ( !PeekNamedPipe( h, nullptr, 0, nullptr, &lenAvail, nullptr) )
141  return false;
142 
143  if ( lenAvail > 65536 )
144  lenAvail = 65536;
145 
146  DWORD lenRead;
147 
148  if ( !ReadFile( h, &m_readbuf, lenAvail, &lenRead, nullptr ) || lenRead == 0 )
149  {
150  m_pMap.remove(h);
151  return false;
152  }
153 
154  buff->buffer().append(m_readbuf, lenRead);
155 
156  // Get the corresponding MythSystemLegacy instance, and the stdout/stderr
157  // type
158  fdLock.lock();
159  FDType_t *fdType = fdMap.value(h);
160  fdLock.unlock();
161 
162  // Emit the data ready signal (1 = stdout, 2 = stderr)
163  MythSystemLegacyWindows *ms = fdType->ms;
164  emit ms->readDataReady(fdType->type);
165 
166  return true;
167 }
168 
169 bool MythSystemLegacyIOHandler::HandleWrite(HANDLE h, QBuffer *buff)
170 {
171  if( buff->atEnd() )
172  {
173  m_pMap.remove(h);
174  return false;
175  }
176 
177  int pos = buff->pos();
178  DWORD len = buff->size() - pos;
179  DWORD rlen;
180  len = (len > 32768 ? 32768 : len);
181 
182  if( !WriteFile(h, buff->read(len).constData(), len, &rlen, nullptr) )
183  {
184  m_pMap.remove(h);
185  return false;
186  }
187 
188  if( rlen != len )
189  buff->seek(pos+rlen);
190 
191  return true;
192 }
193 
194 void MythSystemLegacyIOHandler::insert(HANDLE h, QBuffer *buff)
195 {
196  m_pLock.lock();
197  m_pMap.insert(h, buff);
198  m_pLock.unlock();
199  wake();
200 }
201 
203 {
204  QMutexLocker locker(&m_pLock);
205  while (m_pMap.contains(h))
206  {
207  locker.unlock();
208  usleep(10 * 1000);
209  locker.relock();
210  }
211 }
212 
214 {
215  m_pLock.lock();
216  if (m_read)
217  {
218  PMap_t::iterator i;
219  i = m_pMap.find(h);
220  HandleRead(i.key(), i.value());
221  }
222  m_pMap.remove(h);
223  m_pLock.unlock();
224 }
225 
227 {
228  QMutexLocker locker(&m_pWaitLock);
229  m_pWait.wakeAll();
230 }
231 
232 
234 {
235  if (m_children)
236  free( m_children );
237  wait();
238 }
239 
241 {
242  RunProlog();
243 
244  LOG(VB_GENERAL, LOG_INFO, "Starting process manager");
245 
246  // run_system is set to false during shutdown, and we need this thread to
247  // exit during shutdown.
248  while( run_system )
249  {
250  // check for any running processes
251  m_mapLock.lock();
252 
253  if( m_childCount == 0 )
254  {
255  m_mapLock.unlock();
256  usleep( 100000 );
257  continue;
258  }
259 
260  DWORD result = WaitForMultipleObjects( m_childCount, m_children,
261  FALSE, 100 );
262 
263  if ( result == WAIT_TIMEOUT || result == WAIT_FAILED )
264  {
265  m_mapLock.unlock();
266  continue;
267  }
268 
269  int index = result - WAIT_OBJECT_0;
270  if ( index < 0 || index > m_childCount - 1 )
271  {
272  m_mapLock.unlock();
273  continue;
274  }
275  HANDLE child = m_children[index];
276 
277  // pop exited process off managed list, add to cleanup list
278  MythSystemLegacyWindows *ms = m_pMap.take(child);
280  m_mapLock.unlock();
281 
282  // Occasionally, the caller has deleted the structure from under
283  // our feet. If so, just log and move on.
284  if (!ms || !ms->m_parent)
285  {
286  LOG(VB_SYSTEM, LOG_ERR,
287  QString("Structure for child handle %1 already deleted!")
288  .arg((long long)child));
289  if (ms)
290  {
291  listLock.lock();
292  msList.append(ms);
293  listLock.unlock();
294  }
295  continue;
296  }
297 
298  listLock.lock();
299  msList.append(ms);
300 
301  DWORD status;
302  GetExitCodeProcess( child, &status );
303 
304  ms->SetStatus(status);
305  LOG(VB_SYSTEM, LOG_INFO,
306  QString("Managed child (Handle: %1) has exited! "
307  "command=%2, status=%3, result=%4")
308  .arg((long long)child) .arg(ms->GetLogCmd()) .arg(status)
309  .arg(ms->GetStatus()));
310 
311  // loop through running processes for any that require action
312  MSMap_t::iterator i;
313  time_t now = time(nullptr);
314 
315  m_mapLock.lock();
316  m_jumpLock.lock();
317  for (i = m_pMap.begin(); i != m_pMap.end(); ++i)
318  {
319  child = i.key();
320  ms = i.value();
321 
322  // handle processes beyond marked timeout
323  if( ms->m_timeout > 0 && ms->m_timeout < now )
324  {
325  // issuing KILL signal after TERM failed in a timely manner
326  if( ms->GetStatus() == GENERIC_EXIT_TIMEOUT )
327  {
328  LOG(VB_SYSTEM, LOG_INFO,
329  QString("Managed child (Handle: %1) timed out, "
330  "issuing KILL signal").arg((long long)child));
331  // Prevent constant attempts to kill an obstinate child
332  ms->m_timeout = 0;
333  ms->Signal(SIGKILL);
334  }
335 
336  // issuing TERM signal
337  else
338  {
339  LOG(VB_SYSTEM, LOG_INFO,
340  QString("Managed child (Handle: %1) timed out"
341  ", issuing TERM signal").arg((long long)child));
343  ms->m_timeout = now + 1;
344  ms->Term();
345  }
346  }
347 
348  if ( m_jumpAbort && ms->GetSetting("AbortOnJump") )
349  ms->Term();
350  }
351 
352  m_jumpAbort = false;
353  m_jumpLock.unlock();
354 
355  m_mapLock.unlock();
356 
357  // hold off unlocking until all the way down here to
358  // give the buffer handling a chance to run before
359  // being closed down by signal thread
360  listLock.unlock();
361  }
362 
363  // kick to allow them to close themselves cleanly
364  readThread->wake();
365  writeThread->wake();
366 
367  RunEpilog();
368 }
369 
370 // NOTE: This is only to be run while m_mapLock is locked!!!
372 {
373  int oldCount;
374 
375  oldCount = m_childCount;
376  m_childCount = m_pMap.size();
377 
378  MSMap_t::iterator i;
379  int j;
380  HANDLE child;
381 
382  if ( oldCount != m_childCount )
383  {
384  HANDLE *new_children;
385  new_children = (HANDLE *)realloc(m_children,
386  m_childCount * sizeof(HANDLE));
387  if (!new_children && m_childCount)
388  {
389  LOG(VB_SYSTEM, LOG_CRIT, "No memory to allocate new children");
390  free(m_children);
391  m_children = nullptr;
392  return;
393  }
394 
395  m_children = new_children;
396  }
397 
398  for (i = m_pMap.begin(), j = 0; i != m_pMap.end(); ++i)
399  {
400  child = i.key();
401  m_children[j++] = child;
402  }
403 }
404 
406 {
407  m_mapLock.lock();
408  ms->IncrRef();
409  m_pMap.insert(ms->m_child, ms);
411  m_mapLock.unlock();
412 
413  if (ms->m_stdpipe[0])
414  {
415  QByteArray ba = ms->GetBuffer(0)->data();
416  QBuffer wtb(&ba);
417  wtb.open(QIODevice::ReadOnly);
418  writeThread->insert(ms->m_stdpipe[0], &wtb);
419  writeThread->Wait(ms->m_stdpipe[0]);
420  writeThread->remove(ms->m_stdpipe[0]);
421  CLOSE(ms->m_stdpipe[0]);
422  }
423 
424  if( ms->GetSetting("UseStdout") )
425  {
426  FDType_t *fdType = new FDType_t;
427  fdType->ms = ms;
428  fdType->type = 1;
429  fdLock.lock();
430  fdMap.insert( ms->m_stdpipe[1], fdType );
431  fdLock.unlock();
432  readThread->insert(ms->m_stdpipe[1], ms->GetBuffer(1));
433  }
434 
435  if( ms->GetSetting("UseStderr") )
436  {
437  FDType_t *fdType = new FDType_t;
438  fdType->ms = ms;
439  fdType->type = 2;
440  fdLock.lock();
441  fdMap.insert( ms->m_stdpipe[2], fdType );
442  fdLock.unlock();
443  readThread->insert(ms->m_stdpipe[2], ms->GetBuffer(2));
444  }
445 }
446 
448 {
449  m_jumpLock.lock();
450  m_jumpAbort = true;
451  m_jumpLock.unlock();
452 }
453 
454 // spawn separate thread for signals to prevent manager
456 {
457  RunProlog();
458 
459  LOG(VB_GENERAL, LOG_INFO, "Starting process signal handler");
460  while( run_system )
461  {
462  usleep(50000); // sleep 50ms
463  while( run_system )
464  {
465  // handle cleanup and signalling for closed processes
466  listLock.lock();
467  if( msList.isEmpty() )
468  {
469  listLock.unlock();
470  break;
471  }
472  MythSystemLegacyWindows *ms = msList.takeFirst();
473  listLock.unlock();
474 
475  if (!ms)
476  continue;
477 
478  if (ms->m_parent)
479  {
480  ms->m_parent->HandlePostRun();
481  }
482 
483  if (ms->m_stdpipe[0])
484  writeThread->remove(ms->m_stdpipe[0]);
485  CLOSE(ms->m_stdpipe[0]);
486 
487  if (ms->m_stdpipe[1])
488  readThread->remove(ms->m_stdpipe[1]);
489  CLOSE(ms->m_stdpipe[1]);
490 
491  if (ms->m_stdpipe[2])
492  readThread->remove(ms->m_stdpipe[2]);
493  CLOSE(ms->m_stdpipe[2]);
494 
495  if (ms->m_parent)
496  {
497  if( ms->GetStatus() == GENERIC_EXIT_OK )
498  emit ms->finished();
499  else
500  emit ms->error(ms->GetStatus());
501 
502  ms->disconnect();
503  ms->Unlock();
504  }
505 
506  ms->DecrRef();
507  }
508  }
509 
510  RunEpilog();
511 }
512 
513 /*******************************
514  * MythSystemLegacy method defines
515  ******************************/
516 
518  MythSystemLegacyPrivate("MythSystemLegacyWindows")
519 {
520  m_parent = parent;
521 
522  m_stdpipe[0] = nullptr;
523  m_stdpipe[1] = nullptr;
524  m_stdpipe[2] = nullptr;
525 
526  connect(this, SIGNAL(started()), m_parent, SIGNAL(started()));
527  connect(this, SIGNAL(finished()), m_parent, SIGNAL(finished()));
528  connect(this, SIGNAL(error(uint)), m_parent, SIGNAL(error(uint)));
529  connect(this, SIGNAL(readDataReady(int)),
530  m_parent, SIGNAL(readDataReady(int)));
531 
532  // Start the threads if they haven't been started yet.
533  if (manager == nullptr)
534  {
536  manager->start();
537  }
538 
539  if (smanager == nullptr)
540  {
542  smanager->start();
543  }
544 
545  if (readThread == nullptr)
546  {
548  readThread->start();
549  }
550 
551  if (writeThread == nullptr)
552  {
554  writeThread->start();
555  }
556 }
557 
558 bool MythSystemLegacyWindows::ParseShell(const QString&, QString &, QStringList&)
559 {
560  return false;
561 }
562 
564 {
565  if( (GetStatus() != GENERIC_EXIT_RUNNING) || (!m_child) )
566  return;
567 
568  Signal(SIGTERM);
569  if( force )
570  {
571  // send KILL if it does not exit within one second
572  if( m_parent->Wait(1) == GENERIC_EXIT_RUNNING )
573  Signal(SIGKILL);
574  }
575 }
576 
578 {
579  if( (GetStatus() != GENERIC_EXIT_RUNNING) || (!m_child) )
580  return;
581  LOG(VB_SYSTEM, LOG_INFO, QString("Child Handle %1 killed with %2")
582  .arg((long long)m_child).arg(sig));
583  TerminateProcess( m_child, sig * 256 );
584 }
585 
586 
587 #define MAX_BUFLEN 1024
589 {
590  BOOL bInherit = FALSE;
591 
592  QString LOC_ERR = QString("myth_system('%1'): Error: ").arg(GetLogCmd());
593 
594  LOG(VB_SYSTEM, LOG_DEBUG, QString("Launching: %1").arg(GetLogCmd()));
595 
596  HANDLE p_stdin[2] = { nullptr, nullptr };
597  HANDLE p_stdout[2] = { nullptr, nullptr };
598  HANDLE p_stderr[2] = { nullptr, nullptr };
599 
600  SECURITY_ATTRIBUTES saAttr;
601  STARTUPINFO si;
602 
603  ZeroMemory(&si, sizeof(STARTUPINFO));
604  si.cb = sizeof(STARTUPINFO);
605 
606  // Set the bInheritHandle flag so pipe handles are inherited.
607  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
608  saAttr.bInheritHandle = true;
609  saAttr.lpSecurityDescriptor = nullptr;
610 
611  /* set up pipes */
612  if( GetSetting("UseStdin") )
613  {
614  bInherit = TRUE;
615 
616  if (!CreatePipe(&p_stdin[0], &p_stdin[1], &saAttr, 0))
617  {
618  LOG(VB_GENERAL, LOG_ERR, LOC_ERR + "stdin pipe() failed");
620  }
621  else
622  {
623  // Ensure the write handle to the pipe for STDIN is not inherited.
624  if (!SetHandleInformation(p_stdin[1], HANDLE_FLAG_INHERIT, 0))
625  {
626  LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdin inheritance error");
628  }
629  else
630  {
631  si.hStdInput = p_stdin[0];
632  si.dwFlags |= STARTF_USESTDHANDLES;
633  }
634  }
635  }
636 
637  if( GetSetting("UseStdout") )
638  {
639  bInherit = TRUE;
640 
641  if (!CreatePipe(&p_stdout[0], &p_stdout[1], &saAttr, 0))
642  {
643  LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout pipe() failed");
645  }
646  else
647  {
648  // Ensure the read handle to the pipe for STDOUT is not inherited.
649  if (!SetHandleInformation(p_stdout[0], HANDLE_FLAG_INHERIT, 0))
650  {
651  LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout inheritance error");
653  }
654  else
655  {
656  si.hStdOutput = p_stdout[1];
657  si.dwFlags |= STARTF_USESTDHANDLES;
658  }
659  }
660  }
661 
662  if( GetSetting("UseStderr") )
663  {
664  bInherit = TRUE;
665 
666  if (!CreatePipe(&p_stderr[0], &p_stderr[1], &saAttr, 0))
667  {
668  LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr pipe() failed");
670  }
671  else
672  {
673  // Ensure the read handle to the pipe for STDERR is not inherited.
674  if (!SetHandleInformation(p_stderr[0], HANDLE_FLAG_INHERIT, 0))
675  {
676  LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr inheritance error");
678  }
679  else
680  {
681  si.hStdError = p_stderr[1];
682  si.dwFlags |= STARTF_USESTDHANDLES;
683  }
684  }
685  }
686 
687  // set up command args
688  QString cmd = GetCommand() + " " + GetArgs().join(" ");
689 
690  if (GetSetting("UseShell"))
691  cmd.prepend("cmd.exe /c ");
692 
693  SetCommand( cmd );
694 
695  QString sCmd = GetCommand();
696 
697  QString dir = GetDirectory();
698 
699  PROCESS_INFORMATION pi;
700  ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
701 
702  m_timeout = timeout;
703  if( timeout )
704  m_timeout += time(nullptr);
705 
706  LPCWSTR pDir = nullptr;
707  if (dir.length() > 0)
708  pDir = (LPCWSTR)dir.utf16();
709 
710  bool success = CreateProcess( nullptr,
711  (LPWSTR)sCmd.utf16(), // command line
712  nullptr, // process security attributes
713  nullptr, // primary thread security attributes
714  bInherit, // handles are inherited
715  0, // creation flags
716  nullptr, // use parent's environment
717  pDir, // use parent's current directory
718  &si, // STARTUPINFO pointer
719  &pi); // receives PROCESS_INFORMATION
720 
721  if (!success)
722  {
723  DWORD dwErr = GetLastError();
724  LOG(VB_SYSTEM, LOG_ERR,
725  QString( "%1 CreateProcess() failed (%2)")
726  .arg( LOC_ERR )
727  .arg( dwErr ));
729  }
730  else
731  {
732  /* parent */
733  m_child = pi.hProcess;
735 
736  LOG(VB_SYSTEM, LOG_INFO,
737  QString("Managed child (Handle: %1) has started! "
738  "%2%3 command=%4, timeout=%5")
739  .arg((long long)m_child)
740  .arg(GetSetting("UseShell") ? "*" : "")
741  .arg(GetSetting("RunInBackground") ? "&" : "")
742  .arg(GetLogCmd()) .arg(timeout));
743 
744  /* close unused pipe ends */
745  CLOSE(p_stdin[0]);
746  CLOSE(p_stdout[1]);
747  CLOSE(p_stderr[1]);
748 
749  // store the rest
750  m_stdpipe[0] = p_stdin[1];
751  m_stdpipe[1] = p_stdout[0];
752  m_stdpipe[2] = p_stderr[0];
753 
754  }
755 
756  /* Parent */
758  {
759  CLOSE(p_stdin[0]);
760  CLOSE(p_stdin[1]);
761  CLOSE(p_stdout[0]);
762  CLOSE(p_stdout[1]);
763  CLOSE(p_stderr[0]);
764  CLOSE(p_stderr[1]);
765  }
766 }
767 
769 {
770  if( manager == nullptr )
771  {
773  manager->start();
774  }
775  manager->append(this);
776 }
777 
779 {
780  if( manager == nullptr )
781  {
783  manager->start();
784  }
785  manager->jumpAbort();
786 }
787 
788 /*
789  * vim:ts=4:sw=4:ai:et:si:sts=4
790  */
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void SetCommand(QString &cmd)
void error(uint status)
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
#define GENERIC_EXIT_TIMEOUT
Process timed out.
Definition: exitcodes.h:24
QMap< int, QBuffer * > PMap_t
MythSystemLegacyWindows * ms
void Fork(time_t timeout) override
friend class MythSystemLegacyIOHandler
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
void SetStatus(uint status)
void HandleRead(int fd, QBuffer *buff)
friend class MythSystemLegacySignalManager
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
QMap< int, FDType_t * > FDMap_t
void ShutdownMythSystemLegacy(void)
static MSList_t msList
static MythSystemLegacyManager * manager
def read(device=None, features=[])
Definition: disc.py:35
void insert(int fd, QBuffer *buff)
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
void Signal(int sig) override
static MythSystemLegacyIOHandler * readThread
virtual int IncrRef(void)
Increments reference count.
QStringList & GetArgs(void)
static QMutex listLock
#define CLOSE(x)
void HandleWrite(int fd, QBuffer *buff)
#define SIGKILL
Definition: compat.h:215
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
#define GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:25
bool GetSetting(const char *setting)
void readDataReady(int fd)
unsigned int uint
Definition: compat.h:140
static MythSystemLegacySignalManager * smanager
MythSystemLegacyWindows(MythSystemLegacy *parent)
MythSystemLegacyIOHandler(bool read)
static bool run_system
PictureAttribute next(PictureAttributeSupported supported, PictureAttribute attribute)
QString & GetDirectory(void)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void JumpAbort(void) override
void Term(bool force=false) override
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
QBuffer * GetBuffer(int index)
QList< QPointer< MythSystemLegacyUnix > > MSList_t
friend class MythSystemLegacyManager
static MythSystemLegacyIOHandler * writeThread
bool ParseShell(const QString &cmd, QString &abscmd, QStringList &args) override
static void usleep(unsigned long time)
Definition: mthread.cpp:348
QPointer< MythSystemLegacy > m_parent
#define GENERIC_EXIT_NOT_OK
Exited with error.
Definition: exitcodes.h:11
static QMutex fdLock
#define LOC_ERR
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 *)
static FDMap_t fdMap
void Manage(void) override
QString & GetCommand(void)