00001
00002
00003 #include "compat.h"
00004
00005
00006 #include "mythsystem.h"
00007 #include "system-windows.h"
00008
00009
00010 #include <cerrno>
00011 #include <unistd.h>
00012 #include <stdlib.h>
00013 #include <fcntl.h>
00014 #include <time.h>
00015 #include <signal.h>
00016 #include <string.h>
00017
00018
00019 #include <QCoreApplication>
00020 #include <QMutex>
00021 #include <QMap>
00022 #include <QString>
00023 #include <QStringList>
00024
00025
00026 #include "mythcorecontext.h"
00027 #include "mythlogging.h"
00028 #include "mythevent.h"
00029 #include "exitcodes.h"
00030
00031
00032 #include <windows.h>
00033 #include <tchar.h>
00034 #include <stdio.h>
00035
00036 #define CLOSE(x) \
00037 if( (x) ) { \
00038 CloseHandle((x)); \
00039 fdLock.lock(); \
00040 delete fdMap.value((x)); \
00041 fdMap.remove((x)); \
00042 fdLock.unlock(); \
00043 (x) = NULL; \
00044 }
00045
00046 typedef struct
00047 {
00048 MythSystemWindows *ms;
00049 int type;
00050 } FDType_t;
00051 typedef QMap<HANDLE, FDType_t*> FDMap_t;
00052
00053
00054
00055
00056 static bool run_system = true;
00057 static MythSystemManager *manager = NULL;
00058 static MythSystemSignalManager *smanager = NULL;
00059 static MythSystemIOHandler *readThread = NULL;
00060 static MythSystemIOHandler *writeThread = NULL;
00061 static MSList_t msList;
00062 static QMutex listLock;
00063 static FDMap_t fdMap;
00064 static QMutex fdLock;
00065
00066 void ShutdownMythSystem(void)
00067 {
00068 run_system = false;
00069 if (manager)
00070 manager->wait();
00071 if (smanager)
00072 smanager->wait();
00073 if (readThread)
00074 readThread->wait();
00075 if (writeThread)
00076 writeThread->wait();
00077 }
00078
00079 MythSystemIOHandler::MythSystemIOHandler(bool read) :
00080 MThread(QString("SystemIOHandler%1").arg(read ? "R" : "W")),
00081 m_pWaitLock(), m_pWait(), m_pLock(), m_pMap(PMap_t()),
00082 m_read(read)
00083 {
00084 }
00085
00086 void MythSystemIOHandler::run(void)
00087 {
00088 RunProlog();
00089
00090 LOG(VB_GENERAL, LOG_INFO, QString("Starting IO manager (%1)")
00091 .arg(m_read ? "read" : "write"));
00092
00093 while( run_system )
00094 {
00095 {
00096 QMutexLocker locker(&m_pWaitLock);
00097 m_pWait.wait(&m_pWaitLock);
00098 }
00099
00100 while( run_system )
00101 {
00102 usleep(10000);
00103 m_pLock.lock();
00104 if( m_pMap.isEmpty() )
00105 {
00106 m_pLock.unlock();
00107 break;
00108 }
00109
00110 bool datafound = true;
00111 m_pLock.unlock();
00112
00113 while ( datafound && run_system )
00114 {
00115 m_pLock.lock();
00116
00117 datafound = false;
00118 PMap_t::iterator i, next;
00119 for( i = m_pMap.begin(); i != m_pMap.end(); i = next )
00120 {
00121 next = i + 1;
00122
00123 if( m_read )
00124 datafound |= HandleRead(i.key(), i.value());
00125 else
00126 datafound |= HandleWrite(i.key(), i.value());
00127 }
00128 m_pLock.unlock();
00129 }
00130 }
00131 }
00132 RunEpilog();
00133 }
00134
00135 bool MythSystemIOHandler::HandleRead(HANDLE h, QBuffer *buff)
00136 {
00137 DWORD lenAvail;
00138
00139 if ( !PeekNamedPipe( h, NULL, 0, NULL, &lenAvail, NULL) )
00140 return false;
00141
00142 if ( lenAvail > 65536 )
00143 lenAvail = 65536;
00144
00145 DWORD lenRead;
00146
00147 if ( !ReadFile( h, &m_readbuf, lenAvail, &lenRead, NULL ) || lenRead == 0 )
00148 {
00149 m_pMap.remove(h);
00150 return false;
00151 }
00152
00153 buff->buffer().append(m_readbuf, lenRead);
00154
00155
00156
00157 fdLock.lock();
00158 FDType_t *fdType = fdMap.value(h);
00159 fdLock.unlock();
00160
00161
00162 MythSystemWindows *ms = fdType->ms;
00163 emit ms->readDataReady(fdType->type);
00164
00165 return true;
00166 }
00167
00168 bool MythSystemIOHandler::HandleWrite(HANDLE h, QBuffer *buff)
00169 {
00170 if( buff->atEnd() )
00171 {
00172 m_pMap.remove(h);
00173 return false;
00174 }
00175
00176 int pos = buff->pos();
00177 DWORD len = buff->size() - pos;
00178 DWORD rlen;
00179 len = (len > 32768 ? 32768 : len);
00180
00181 if( !WriteFile(h, buff->read(len).constData(), len, &rlen, NULL) )
00182 {
00183 m_pMap.remove(h);
00184 return false;
00185 }
00186
00187 else if( rlen != len )
00188 buff->seek(pos+rlen);
00189
00190 return true;
00191 }
00192
00193 void MythSystemIOHandler::insert(HANDLE h, QBuffer *buff)
00194 {
00195 m_pLock.lock();
00196 m_pMap.insert(h, buff);
00197 m_pLock.unlock();
00198 wake();
00199 }
00200
00201 void MythSystemIOHandler::remove(HANDLE h)
00202 {
00203 m_pLock.lock();
00204 if (m_read)
00205 {
00206 PMap_t::iterator i;
00207 i = m_pMap.find(h);
00208 HandleRead(i.key(), i.value());
00209 }
00210 m_pMap.remove(h);
00211 m_pLock.unlock();
00212 }
00213
00214 void MythSystemIOHandler::wake()
00215 {
00216 QMutexLocker locker(&m_pWaitLock);
00217 m_pWait.wakeAll();
00218 }
00219
00220
00221 MythSystemManager::MythSystemManager() :
00222 MThread("SystemManager")
00223 {
00224 m_jumpAbort = false;
00225 m_childCount = 0;
00226 m_children = NULL;
00227 }
00228
00229 MythSystemManager::~MythSystemManager()
00230 {
00231 if (m_children)
00232 free( m_children );
00233 wait();
00234 }
00235
00236 void MythSystemManager::run(void)
00237 {
00238 RunProlog();
00239
00240 LOG(VB_GENERAL, LOG_INFO, "Starting process manager");
00241
00242
00243
00244 while( run_system )
00245 {
00246
00247 m_mapLock.lock();
00248
00249 if( m_childCount == 0 )
00250 {
00251 m_mapLock.unlock();
00252 usleep( 100000 );
00253 continue;
00254 }
00255
00256 DWORD result = WaitForMultipleObjects( m_childCount, m_children,
00257 FALSE, 100 );
00258
00259 if ( result == WAIT_TIMEOUT || result == WAIT_FAILED )
00260 {
00261 m_mapLock.unlock();
00262 continue;
00263 }
00264
00265 int index = result - WAIT_OBJECT_0;
00266 if ( index < 0 || index > m_childCount - 1 )
00267 {
00268 m_mapLock.unlock();
00269 continue;
00270 }
00271 HANDLE child = m_children[index];
00272
00273
00274 MythSystemWindows *ms = m_pMap.take(child);
00275 ChildListRebuild();
00276 m_mapLock.unlock();
00277
00278
00279
00280 if (!ms)
00281 {
00282 LOG(VB_SYSTEM, LOG_ERR,
00283 QString("Structure for child handle %1 already deleted!")
00284 .arg((long long)child));
00285 continue;
00286 }
00287
00288 listLock.lock();
00289 msList.append(ms);
00290
00291 DWORD status;
00292 GetExitCodeProcess( child, &status );
00293
00294 ms->SetStatus(status);
00295 LOG(VB_SYSTEM, LOG_INFO,
00296 QString("Managed child (Handle: %1) has exited! "
00297 "command=%2, status=%3, result=%4")
00298 .arg((long long)child) .arg(ms->GetLogCmd()) .arg(status)
00299 .arg(ms->GetStatus()));
00300
00301
00302 MSMap_t::iterator i;
00303 time_t now = time(NULL);
00304
00305 m_mapLock.lock();
00306 m_jumpLock.lock();
00307 for (i = m_pMap.begin(); i != m_pMap.end(); ++i)
00308 {
00309 child = i.key();
00310 ms = i.value();
00311
00312
00313 if( ms->m_timeout > 0 && ms->m_timeout < now )
00314 {
00315
00316 if( ms->GetStatus() == GENERIC_EXIT_TIMEOUT )
00317 {
00318 LOG(VB_SYSTEM, LOG_INFO,
00319 QString("Managed child (Handle: %1) timed out, "
00320 "issuing KILL signal").arg((long long)child));
00321
00322 ms->m_timeout = 0;
00323 ms->Signal(SIGKILL);
00324 }
00325
00326
00327 else
00328 {
00329 LOG(VB_SYSTEM, LOG_INFO,
00330 QString("Managed child (Handle: %1) timed out"
00331 ", issuing TERM signal").arg((long long)child));
00332 ms->SetStatus( GENERIC_EXIT_TIMEOUT );
00333 ms->m_timeout = now + 1;
00334 ms->Term();
00335 }
00336 }
00337
00338 if ( m_jumpAbort && ms->GetSetting("AbortOnJump") )
00339 ms->Term();
00340 }
00341
00342 m_jumpAbort = false;
00343 m_jumpLock.unlock();
00344
00345 m_mapLock.unlock();
00346
00347
00348
00349
00350 listLock.unlock();
00351 }
00352
00353
00354 readThread->wake();
00355 writeThread->wake();
00356
00357 RunEpilog();
00358 }
00359
00360
00361 void MythSystemManager::ChildListRebuild()
00362 {
00363 int oldCount;
00364
00365 oldCount = m_childCount;
00366 m_childCount = m_pMap.size();
00367
00368 MSMap_t::iterator i;
00369 int j;
00370 HANDLE child;
00371
00372 if ( oldCount != m_childCount )
00373 {
00374 HANDLE *new_children;
00375 new_children = (HANDLE *)realloc(m_children,
00376 m_childCount * sizeof(HANDLE));
00377 if (!new_children && m_childCount)
00378 {
00379 LOG(VB_SYSTEM, LOG_CRIT, "No memory to allocate new children");
00380 free(m_children);
00381 m_children = NULL;
00382 return;
00383 }
00384
00385 m_children = new_children;
00386 }
00387
00388 for (i = m_pMap.begin(), j = 0; i != m_pMap.end(); ++i)
00389 {
00390 child = i.key();
00391 m_children[j++] = child;
00392 }
00393 }
00394
00395 void MythSystemManager::append(MythSystemWindows *ms)
00396 {
00397 m_mapLock.lock();
00398 m_pMap.insert(ms->m_child, ms);
00399 ChildListRebuild();
00400 m_mapLock.unlock();
00401
00402 fdLock.lock();
00403 if( ms->GetSetting("UseStdin") )
00404 writeThread->insert(ms->m_stdpipe[0], ms->GetBuffer(0));
00405
00406 if( ms->GetSetting("UseStdout") )
00407 {
00408 FDType_t *fdType = new FDType_t;
00409 fdType->ms = ms;
00410 fdType->type = 1;
00411 fdMap.insert( ms->m_stdpipe[1], fdType );
00412 readThread->insert(ms->m_stdpipe[1], ms->GetBuffer(1));
00413 }
00414
00415 if( ms->GetSetting("UseStderr") )
00416 {
00417 FDType_t *fdType = new FDType_t;
00418 fdType->ms = ms;
00419 fdType->type = 2;
00420 fdMap.insert( ms->m_stdpipe[2], fdType );
00421 readThread->insert(ms->m_stdpipe[2], ms->GetBuffer(2));
00422 }
00423 fdLock.unlock();
00424 }
00425
00426 void MythSystemManager::jumpAbort(void)
00427 {
00428 m_jumpLock.lock();
00429 m_jumpAbort = true;
00430 m_jumpLock.unlock();
00431 }
00432
00433
00434
00435 MythSystemSignalManager::MythSystemSignalManager() :
00436 MThread("SystemSignalManager")
00437 {
00438 }
00439
00440 void MythSystemSignalManager::run(void)
00441 {
00442 RunProlog();
00443
00444 LOG(VB_GENERAL, LOG_INFO, "Starting process signal handler");
00445 while( run_system )
00446 {
00447 usleep(50000);
00448 while( run_system )
00449 {
00450
00451 listLock.lock();
00452 if( msList.isEmpty() )
00453 {
00454 listLock.unlock();
00455 break;
00456 }
00457 MythSystemWindows *ms = msList.takeFirst();
00458 listLock.unlock();
00459
00460 ms->m_parent->HandlePostRun();
00461
00462 if (ms->m_stdpipe[0])
00463 writeThread->remove(ms->m_stdpipe[0]);
00464 CLOSE(ms->m_stdpipe[0]);
00465
00466 if (ms->m_stdpipe[1])
00467 readThread->remove(ms->m_stdpipe[1]);
00468 CLOSE(ms->m_stdpipe[1]);
00469
00470 if (ms->m_stdpipe[2])
00471 readThread->remove(ms->m_stdpipe[2]);
00472 CLOSE(ms->m_stdpipe[2]);
00473
00474 if( ms->GetStatus() == GENERIC_EXIT_OK )
00475 emit ms->finished();
00476 else
00477 emit ms->error(ms->GetStatus());
00478
00479 ms->disconnect();
00480
00481 ms->Unlock();
00482
00483 if( ms->m_parent->doAutoCleanup() )
00484 delete ms;
00485 }
00486 }
00487
00488 RunEpilog();
00489 }
00490
00491
00492
00493
00494
00495 MythSystemWindows::MythSystemWindows(MythSystem *parent)
00496 {
00497 m_parent = parent;
00498
00499 connect( this, SIGNAL(started()), m_parent, SIGNAL(started()) );
00500 connect( this, SIGNAL(finished()), m_parent, SIGNAL(finished()) );
00501 connect( this, SIGNAL(error(uint)), m_parent, SIGNAL(error(uint)) );
00502 connect( this, SIGNAL(readDataReady(int)), m_parent, SIGNAL(readDataReady(int)) );
00503
00504
00505 if( manager == NULL )
00506 {
00507 manager = new MythSystemManager;
00508 manager->start();
00509 }
00510
00511 if( smanager == NULL )
00512 {
00513 smanager = new MythSystemSignalManager;
00514 smanager->start();
00515 }
00516
00517 if( readThread == NULL )
00518 {
00519 readThread = new MythSystemIOHandler(true);
00520 readThread->start();
00521 }
00522
00523 if( writeThread == NULL )
00524 {
00525 writeThread = new MythSystemIOHandler(false);
00526 writeThread->start();
00527 }
00528 }
00529
00530
00531 MythSystemWindows::~MythSystemWindows(void)
00532 {
00533 }
00534
00535 bool MythSystemWindows::ParseShell(const QString cmd, QString &abscmd,
00536 QStringList &args)
00537 {
00538 return false;
00539 }
00540
00541 void MythSystemWindows::Term(bool force)
00542 {
00543 if( (GetStatus() != GENERIC_EXIT_RUNNING) || (!m_child) )
00544 return;
00545
00546 Signal(SIGTERM);
00547 if( force )
00548 {
00549
00550 if( m_parent->Wait(1) == GENERIC_EXIT_RUNNING )
00551 Signal(SIGKILL);
00552 }
00553 }
00554
00555 void MythSystemWindows::Signal( int sig )
00556 {
00557 if( (GetStatus() != GENERIC_EXIT_RUNNING) || (!m_child) )
00558 return;
00559 LOG(VB_SYSTEM, LOG_INFO, QString("Child Handle %1 killed with %2")
00560 .arg((long long)m_child).arg(sig));
00561 TerminateProcess( m_child, sig * 256 );
00562 }
00563
00564
00565 #define MAX_BUFLEN 1024
00566 void MythSystemWindows::Fork(time_t timeout)
00567 {
00568 QString LOC_ERR = QString("myth_system('%1'): Error: ").arg(GetLogCmd());
00569
00570
00571 char locerr[MAX_BUFLEN];
00572 strncpy(locerr, (const char *)LOC_ERR.toUtf8().constData(), MAX_BUFLEN);
00573 locerr[MAX_BUFLEN-1] = '\0';
00574
00575 LOG(VB_SYSTEM, LOG_DEBUG, QString("Launching: %1").arg(GetLogCmd()));
00576
00577 GetBuffer(0)->setBuffer(0);
00578 GetBuffer(1)->setBuffer(0);
00579 GetBuffer(2)->setBuffer(0);
00580
00581 HANDLE p_stdin[2] = { NULL, NULL };
00582 HANDLE p_stdout[2] = { NULL, NULL };
00583 HANDLE p_stderr[2] = { NULL, NULL };
00584
00585 SECURITY_ATTRIBUTES saAttr;
00586 STARTUPINFO si;
00587
00588 ZeroMemory(&si, sizeof(STARTUPINFO));
00589 si.cb = sizeof(STARTUPINFO);
00590
00591
00592 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
00593 saAttr.bInheritHandle = true;
00594 saAttr.lpSecurityDescriptor = NULL;
00595
00596
00597 if( GetSetting("UseStdin") )
00598 {
00599 if (!CreatePipe(&p_stdin[0], &p_stdin[1], &saAttr, 0))
00600 {
00601 LOG(VB_GENERAL, LOG_ERR, LOC_ERR + "stdin pipe() failed");
00602 SetStatus( GENERIC_EXIT_NOT_OK );
00603 }
00604 else
00605 {
00606
00607 if (!SetHandleInformation(p_stdin[1], HANDLE_FLAG_INHERIT, 0))
00608 {
00609 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdin inheritance error");
00610 SetStatus( GENERIC_EXIT_NOT_OK );
00611 }
00612 else
00613 {
00614 si.hStdInput = p_stdin[0];
00615 si.dwFlags |= STARTF_USESTDHANDLES;
00616 }
00617 }
00618 }
00619
00620 if( GetSetting("UseStdout") )
00621 {
00622 if (!CreatePipe(&p_stdout[0], &p_stdout[1], &saAttr, 0))
00623 {
00624 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout pipe() failed");
00625 SetStatus( GENERIC_EXIT_NOT_OK );
00626 }
00627 else
00628 {
00629
00630 if (!SetHandleInformation(p_stdout[0], HANDLE_FLAG_INHERIT, 0))
00631 {
00632 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout inheritance error");
00633 SetStatus( GENERIC_EXIT_NOT_OK );
00634 }
00635 else
00636 {
00637 si.hStdOutput = p_stdout[1];
00638 si.dwFlags |= STARTF_USESTDHANDLES;
00639 }
00640 }
00641 }
00642
00643 if( GetSetting("UseStderr") )
00644 {
00645 if (!CreatePipe(&p_stderr[0], &p_stderr[1], &saAttr, 0))
00646 {
00647 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr pipe() failed");
00648 SetStatus( GENERIC_EXIT_NOT_OK );
00649 }
00650 else
00651 {
00652
00653 if (!SetHandleInformation(p_stderr[0], HANDLE_FLAG_INHERIT, 0))
00654 {
00655 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr inheritance error");
00656 SetStatus( GENERIC_EXIT_NOT_OK );
00657 }
00658 else
00659 {
00660 si.hStdError = p_stderr[1];
00661 si.dwFlags |= STARTF_USESTDHANDLES;
00662 }
00663 }
00664 }
00665
00666
00667 QString cmd = GetCommand().replace('/','\\') + " " + GetArgs().join(" ");
00668 if (GetSetting("UseShell"))
00669 cmd.prepend("cmd.exe /c ");
00670
00671 SetCommand( cmd );
00672
00673 QByteArray cmdUTF8 = GetCommand().toUtf8();
00674 TCHAR *command = TEXT((char *)cmdUTF8.constData());
00675
00676 const char *directory = NULL;
00677 QString dir = GetDirectory();
00678 if (GetSetting("SetDirectory") && !dir.isEmpty())
00679 directory = strdup(dir.toUtf8().constData());
00680
00681 PROCESS_INFORMATION pi;
00682 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
00683
00684 m_timeout = timeout;
00685 if( timeout )
00686 m_timeout += time(NULL);
00687
00688 bool success = CreateProcess(NULL,
00689 command,
00690 NULL,
00691 NULL,
00692 TRUE,
00693 0,
00694 NULL,
00695 directory,
00696 &si,
00697 &pi);
00698
00699 if (!success)
00700 {
00701 LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "CreateProcess() failed");
00702 SetStatus( GENERIC_EXIT_NOT_OK );
00703 }
00704 else
00705 {
00706
00707 m_child = pi.hProcess;
00708 SetStatus( GENERIC_EXIT_RUNNING );
00709
00710 LOG(VB_SYSTEM, LOG_INFO,
00711 QString("Managed child (Handle: %1) has started! "
00712 "%2%3 command=%4, timeout=%5")
00713 .arg((long long)m_child)
00714 .arg(GetSetting("UseShell") ? "*" : "")
00715 .arg(GetSetting("RunInBackground") ? "&" : "")
00716 .arg(GetLogCmd()) .arg(timeout));
00717
00718
00719 CLOSE(p_stdin[0]);
00720 CLOSE(p_stdout[1]);
00721 CLOSE(p_stderr[1]);
00722
00723
00724 m_stdpipe[0] = p_stdin[1];
00725 m_stdpipe[1] = p_stdout[0];
00726 m_stdpipe[2] = p_stderr[0];
00727
00728
00729 if( directory )
00730 free((void *)directory);
00731 }
00732
00733
00734 if( GetStatus() != GENERIC_EXIT_RUNNING )
00735 {
00736 CLOSE(p_stdin[0]);
00737 CLOSE(p_stdin[1]);
00738 CLOSE(p_stdout[0]);
00739 CLOSE(p_stdout[1]);
00740 CLOSE(p_stderr[0]);
00741 CLOSE(p_stderr[1]);
00742 }
00743 }
00744
00745 void MythSystemWindows::Manage(void)
00746 {
00747 if( manager == NULL )
00748 {
00749 manager = new MythSystemManager;
00750 manager->start();
00751 }
00752 manager->append(this);
00753 }
00754
00755 void MythSystemWindows::JumpAbort(void)
00756 {
00757 if( manager == NULL )
00758 {
00759 manager = new MythSystemManager;
00760 manager->start();
00761 }
00762 manager->jumpAbort();
00763 }
00764
00765
00766
00767